difftreelog
refactor replace eval with repl where possible
in: trunk
22 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,6 +3,15 @@
version = 3
[[package]]
+name = "abort-on-drop"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dd6d700ad9af641490c1f7d67980d2de4d1433016e5b12f819448d3c832142a"
+dependencies = [
+ "tokio",
+]
+
+[[package]]
name = "addr2line"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -141,10 +150,29 @@
]
[[package]]
+name = "ansi-str"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cf4578926a981ab0ca955dc023541d19de37112bc24c1a197bd806d3d86ad1d"
+dependencies = [
+ "ansitok",
+]
+
+[[package]]
+name = "ansitok"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "220044e6a1bb31ddee4e3db724d29767f352de47445a6cd75e1a173142136c83"
+dependencies = [
+ "nom",
+ "vte 0.10.1",
+]
+
+[[package]]
name = "anstream"
-version = "0.5.0"
+version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c"
+checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -156,15 +184,15 @@
[[package]]
name = "anstyle"
-version = "1.0.3"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46"
+checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]]
name = "anstyle-parse"
-version = "0.2.1"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
+checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"
dependencies = [
"utf8parse",
]
@@ -180,9 +208,9 @@
[[package]]
name = "anstyle-wincon"
-version = "2.1.0"
+version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd"
+checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
dependencies = [
"anstyle",
"windows-sys 0.48.0",
@@ -202,22 +230,39 @@
[[package]]
name = "arrayvec"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
+
+[[package]]
+name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "async-trait"
-version = "0.1.73"
+version = "0.1.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
+checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.37",
+ "syn 2.0.38",
]
[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi 0.1.19",
+ "libc",
+ "winapi",
+]
+
+[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -246,9 +291,9 @@
[[package]]
name = "base64"
-version = "0.21.4"
+version = "0.21.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2"
+checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
[[package]]
name = "base64ct"
@@ -281,9 +326,9 @@
[[package]]
name = "bitflags"
-version = "2.4.0"
+version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
dependencies = [
"serde",
]
@@ -323,6 +368,12 @@
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]]
+name = "bytecount"
+version = "0.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205"
+
+[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -419,9 +470,9 @@
[[package]]
name = "clap"
-version = "4.4.4"
+version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136"
+checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b"
dependencies = [
"clap_builder",
"clap_derive",
@@ -429,9 +480,9 @@
[[package]]
name = "clap_builder"
-version = "4.4.4"
+version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56"
+checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663"
dependencies = [
"anstream",
"anstyle",
@@ -444,21 +495,21 @@
[[package]]
name = "clap_derive"
-version = "4.4.2"
+version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873"
+checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
dependencies = [
"heck",
"proc-macro2",
"quote",
- "syn 2.0.37",
+ "syn 2.0.38",
]
[[package]]
name = "clap_lex"
-version = "0.5.1"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
+checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[package]]
name = "colorchoice"
@@ -552,7 +603,7 @@
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
dependencies = [
"cfg-if",
- "hashbrown 0.14.0",
+ "hashbrown 0.14.1",
"lock_api",
"once_cell",
"parking_lot_core",
@@ -570,10 +621,11 @@
[[package]]
name = "deranged"
-version = "0.3.8"
+version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946"
+checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3"
dependencies = [
+ "powerfmt",
"serde",
]
@@ -606,7 +658,7 @@
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.37",
+ "syn 2.0.38",
]
[[package]]
@@ -642,30 +694,19 @@
[[package]]
name = "errno"
-version = "0.3.3"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd"
+checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860"
dependencies = [
- "errno-dragonfly",
"libc",
"windows-sys 0.48.0",
]
[[package]]
-name = "errno-dragonfly"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
-dependencies = [
- "cc",
- "libc",
-]
-
-[[package]]
name = "fastrand"
-version = "2.0.0"
+version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
+checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "find-crate"
@@ -680,11 +721,12 @@
name = "fleet"
version = "0.1.0"
dependencies = [
+ "abort-on-drop",
"age",
"age-core",
"anyhow",
"async-trait",
- "base64 0.21.4",
+ "base64 0.21.5",
"chrono",
"clap",
"futures",
@@ -693,9 +735,13 @@
"itertools",
"nixlike",
"once_cell",
+ "owo-colors",
"peg",
+ "r2d2",
"serde",
"serde_json",
+ "shlex",
+ "tabled",
"tempfile",
"time",
"tokio",
@@ -703,6 +749,7 @@
"tracing",
"tracing-indicatif",
"tracing-subscriber",
+ "unindent",
"z85",
]
@@ -767,10 +814,16 @@
]
[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
name = "futures"
-version = "0.3.28"
+version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
+checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335"
dependencies = [
"futures-channel",
"futures-core",
@@ -783,9 +836,9 @@
[[package]]
name = "futures-channel"
-version = "0.3.28"
+version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
+checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"
dependencies = [
"futures-core",
"futures-sink",
@@ -793,15 +846,15 @@
[[package]]
name = "futures-core"
-version = "0.3.28"
+version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
+checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
[[package]]
name = "futures-executor"
-version = "0.3.28"
+version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
+checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc"
dependencies = [
"futures-core",
"futures-task",
@@ -810,38 +863,38 @@
[[package]]
name = "futures-io"
-version = "0.3.28"
+version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
+checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa"
[[package]]
name = "futures-macro"
-version = "0.3.28"
+version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
+checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.37",
+ "syn 2.0.38",
]
[[package]]
name = "futures-sink"
-version = "0.3.28"
+version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
+checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
[[package]]
name = "futures-task"
-version = "0.3.28"
+version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
+checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
[[package]]
name = "futures-util"
-version = "0.3.28"
+version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
+checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
dependencies = [
"futures-channel",
"futures-core",
@@ -911,9 +964,9 @@
[[package]]
name = "hashbrown"
-version = "0.14.0"
+version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
+checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12"
[[package]]
name = "heck"
@@ -923,6 +976,15 @@
[[package]]
name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hermit-abi"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
@@ -1014,7 +1076,7 @@
"proc-macro2",
"quote",
"strsim",
- "syn 2.0.37",
+ "syn 2.0.38",
"unic-langid",
]
@@ -1028,7 +1090,7 @@
"i18n-config",
"proc-macro2",
"quote",
- "syn 2.0.37",
+ "syn 2.0.38",
]
[[package]]
@@ -1056,12 +1118,12 @@
[[package]]
name = "indexmap"
-version = "2.0.0"
+version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
+checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897"
dependencies = [
"equivalent",
- "hashbrown 0.14.0",
+ "hashbrown 0.14.1",
]
[[package]]
@@ -1128,12 +1190,18 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
- "hermit-abi",
+ "hermit-abi 0.3.3",
"rustix",
"windows-sys 0.48.0",
]
[[package]]
+name = "is_ci"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb"
+
+[[package]]
name = "itertools"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1168,9 +1236,9 @@
[[package]]
name = "libc"
-version = "0.2.148"
+version = "0.2.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
+checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
[[package]]
name = "libm"
@@ -1196,9 +1264,9 @@
[[package]]
name = "linux-raw-sys"
-version = "0.4.7"
+version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128"
+checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
[[package]]
name = "lock_api"
@@ -1233,24 +1301,15 @@
[[package]]
name = "memchr"
-version = "2.6.3"
+version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "memoffset"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "memoffset"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
dependencies = [
"autocfg",
]
@@ -1292,15 +1351,13 @@
[[package]]
name = "nix"
-version = "0.26.4"
+version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
+checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.4.1",
"cfg-if",
"libc",
- "memoffset 0.7.1",
- "pin-utils",
]
[[package]]
@@ -1310,7 +1367,6 @@
"alejandra",
"linked-hash-map",
"peg",
- "rnix",
"ron",
"serde",
"serde-transcode",
@@ -1378,9 +1434,9 @@
[[package]]
name = "num-traits"
-version = "0.2.16"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
+checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
"libm",
@@ -1392,7 +1448,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
- "hermit-abi",
+ "hermit-abi 0.3.3",
"libc",
]
@@ -1430,6 +1486,28 @@
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
+name = "owo-colors"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
+dependencies = [
+ "supports-color",
+]
+
+[[package]]
+name = "papergrid"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2ccbe15f2b6db62f9a9871642746427e297b0ceb85f9a7f1ee5ff47d184d0c8"
+dependencies = [
+ "ansi-str",
+ "ansitok",
+ "bytecount",
+ "fnv",
+ "unicode-width",
+]
+
+[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1447,7 +1525,7 @@
dependencies = [
"cfg-if",
"libc",
- "redox_syscall",
+ "redox_syscall 0.3.5",
"smallvec",
"windows-targets 0.48.5",
]
@@ -1463,9 +1541,9 @@
[[package]]
name = "peg"
-version = "0.8.1"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a07f2cafdc3babeebc087e499118343442b742cc7c31b4d054682cc598508554"
+checksum = "400bcab7d219c38abf8bd7cc2054eb9bbbd4312d66f6a5557d572a203f646f61"
dependencies = [
"peg-macros",
"peg-runtime",
@@ -1473,9 +1551,9 @@
[[package]]
name = "peg-macros"
-version = "0.8.1"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a90084dc05cf0428428e3d12399f39faad19b0909f64fb9170c9fdd6d9cd49b"
+checksum = "46e61cce859b76d19090f62da50a9fe92bab7c2a5f09e183763559a2ac392c90"
dependencies = [
"peg-runtime",
"proc-macro2",
@@ -1484,9 +1562,9 @@
[[package]]
name = "peg-runtime"
-version = "0.8.1"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fa00462b37ead6d11a82c9d568b26682d78e0477dc02d1966c013af80969739"
+checksum = "36bae92c60fa2398ce4678b98b2c4b5a7c61099961ca1fa305aec04a9ad28922"
[[package]]
name = "pin-project"
@@ -1505,7 +1583,7 @@
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.37",
+ "syn 2.0.38",
]
[[package]]
@@ -1572,6 +1650,12 @@
checksum = "31114a898e107c51bb1609ffaf55a0e011cf6a4d7f1170d0015a165082c0338b"
[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1603,9 +1687,9 @@
[[package]]
name = "proc-macro2"
-version = "1.0.67"
+version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
+checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
dependencies = [
"unicode-ident",
]
@@ -1620,6 +1704,17 @@
]
[[package]]
+name = "r2d2"
+version = "0.8.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
+dependencies = [
+ "log",
+ "parking_lot",
+ "scheduled-thread-pool",
+]
+
+[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1700,6 +1795,15 @@
]
[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
name = "regex"
version = "1.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1760,8 +1864,8 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
dependencies = [
- "base64 0.21.4",
- "bitflags 2.4.0",
+ "base64 0.21.5",
+ "bitflags 2.4.1",
"serde",
"serde_derive",
]
@@ -1774,7 +1878,7 @@
dependencies = [
"countme",
"hashbrown 0.9.1",
- "memoffset 0.6.5",
+ "memoffset",
"rustc-hash",
"text-size",
]
@@ -1820,7 +1924,7 @@
"proc-macro2",
"quote",
"rust-embed-utils",
- "syn 2.0.37",
+ "syn 2.0.38",
"walkdir",
]
@@ -1848,11 +1952,11 @@
[[package]]
name = "rustix"
-version = "0.38.14"
+version = "0.38.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f"
+checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
dependencies = [
- "bitflags 2.4.0",
+ "bitflags 2.4.1",
"errno",
"libc",
"linux-raw-sys",
@@ -1884,6 +1988,15 @@
]
[[package]]
+name = "scheduled-thread-pool"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19"
+dependencies = [
+ "parking_lot",
+]
+
+[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1918,9 +2031,9 @@
[[package]]
name = "serde"
-version = "1.0.188"
+version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
+checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
dependencies = [
"serde_derive",
]
@@ -1936,13 +2049,13 @@
[[package]]
name = "serde_derive"
-version = "1.0.188"
+version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
+checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.37",
+ "syn 2.0.38",
]
[[package]]
@@ -1967,9 +2080,9 @@
[[package]]
name = "sha2"
-version = "0.10.7"
+version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -1986,6 +2099,12 @@
]
[[package]]
+name = "shlex"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
+
+[[package]]
name = "signal-hook-registry"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2067,20 +2186,31 @@
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
+name = "supports-color"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ba6faf2ca7ee42fdd458f4347ae0a9bd6bcc445ad7cb57ad82b383f18870d6f"
+dependencies = [
+ "atty",
+ "is_ci",
+]
+
+[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
+ "quote",
"unicode-ident",
]
[[package]]
name = "syn"
-version = "2.0.37"
+version = "2.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8"
+checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
dependencies = [
"proc-macro2",
"quote",
@@ -2088,14 +2218,40 @@
]
[[package]]
+name = "tabled"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfe9c3632da101aba5131ed63f9eed38665f8b3c68703a6bb18124835c1a5d22"
+dependencies = [
+ "ansi-str",
+ "ansitok",
+ "papergrid",
+ "tabled_derive",
+ "unicode-width",
+]
+
+[[package]]
+name = "tabled_derive"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99f688a08b54f4f02f0a3c382aefdb7884d3d69609f785bd253dc033243e3fe4"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
name = "tempfile"
-version = "3.8.0"
+version = "3.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
+checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
dependencies = [
"cfg-if",
"fastrand",
- "redox_syscall",
+ "redox_syscall 0.4.1",
"rustix",
"windows-sys 0.48.0",
]
@@ -2127,22 +2283,22 @@
[[package]]
name = "thiserror"
-version = "1.0.48"
+version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7"
+checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.48"
+version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
+checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.37",
+ "syn 2.0.38",
]
[[package]]
@@ -2157,11 +2313,12 @@
[[package]]
name = "time"
-version = "0.3.28"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48"
+checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
dependencies = [
"deranged",
+ "powerfmt",
"serde",
"time-core",
"time-macros",
@@ -2169,15 +2326,15 @@
[[package]]
name = "time-core"
-version = "0.1.1"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
-version = "0.2.14"
+version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a942f44339478ef67935ab2bbaec2fb0322496cf3cbe84b261e06ac3814c572"
+checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20"
dependencies = [
"time-core",
]
@@ -2193,9 +2350,9 @@
[[package]]
name = "tokio"
-version = "1.32.0"
+version = "1.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9"
+checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653"
dependencies = [
"backtrace",
"bytes",
@@ -2218,14 +2375,14 @@
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.37",
+ "syn 2.0.38",
]
[[package]]
name = "tokio-util"
-version = "0.7.9"
+version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d"
+checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
dependencies = [
"bytes",
"futures-core",
@@ -2298,7 +2455,7 @@
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.37",
+ "syn 2.0.38",
]
[[package]]
@@ -2408,6 +2565,12 @@
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
+name = "unindent"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce"
+
+[[package]]
name = "universal-hash"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2444,16 +2607,27 @@
"itoa",
"log",
"unicode-width",
- "vte",
+ "vte 0.11.1",
]
[[package]]
name = "vte"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983"
+dependencies = [
+ "arrayvec 0.5.2",
+ "utf8parse",
+ "vte_generate_state_changes",
+]
+
+[[package]]
+name = "vte"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197"
dependencies = [
- "arrayvec",
+ "arrayvec 0.7.4",
"utf8parse",
"vte_generate_state_changes",
]
@@ -2511,7 +2685,7 @@
"once_cell",
"proc-macro2",
"quote",
- "syn 2.0.37",
+ "syn 2.0.38",
"wasm-bindgen-shared",
]
@@ -2533,7 +2707,7 @@
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.37",
+ "syn 2.0.38",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -2759,5 +2933,5 @@
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.37",
+ "syn 2.0.38",
]
README.adocdiffbeforeafterbothEarly prototype stage, yet it is used in production
Advantages over existing configuration systems (NixOps/Morph)
-
Modules can configure multiple hosts at once (I.e for wireguard/kubernetes installation)
-
Secrets can be securely stored in Git (No one except target hosts can decrypt them), automatically regenerated, reencrypted, etc.
Early prototype stage, yet it is used in production
Advantages over existing configuration systems (NixOps/Morph)
-
Modules can configure multiple hosts at once (I.e for wireguard/kubernetes installation)
-
Secrets can be securely stored in Git (No one except target hosts can decrypt them), automatically regenerated, reencrypted, etc.
-
Automatic rollback on deployment failure, which will work, as long as system is passing initrd stage (So still be carefull with root filesystem mount)
cmds/fleet/Cargo.tomldiffbeforeafterboth--- a/cmds/fleet/Cargo.toml
+++ b/cmds/fleet/Cargo.toml
@@ -9,29 +9,35 @@
anyhow = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
-time = { version = "0.3.2", features = ["serde"] }
-tempfile = "3.2"
-once_cell = "1.5"
+time = { version = "0.3.30", features = ["serde"] }
+tempfile = "3.8"
+once_cell = "1.18"
hostname = "0.3.1"
age-core = "0.9.0"
-peg = "0.8.0"
+peg = "0.8.2"
nixlike = { path = "../../crates/nixlike" }
-age = { version = "0.9.0", features = ["ssh", "armor"] }
-base64 = "0.21.0"
-chrono = { version = "0.4.19", features = ["serde"] }
-z85 = "3.0.3"
-clap = { version = "4.0.29", features = [
+age = { version = "0.9.2", features = ["ssh", "armor"] }
+base64 = "0.21.5"
+chrono = { version = "0.4.31", features = ["serde"] }
+z85 = "3.0.5"
+clap = { version = "4.4.7", features = [
"derive",
"env",
"wrap_help",
"unicode",
] }
-tokio = { version = "1.14.0", features = ["full"] }
+tokio = { version = "1.33.0", features = ["full"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] }
-tokio-util = { version = "0.7.0", features = ["codec"] }
-async-trait = "0.1.52"
-futures = "0.3.17"
+tokio-util = { version = "0.7.10", features = ["codec"] }
+async-trait = "0.1.74"
+futures = "0.3.29"
tracing-indicatif = "0.3.5"
indicatif = "0.17.7"
itertools = "0.11.0"
+shlex = "1.2.0"
+tabled = { version = "0.14.0", features = ["color"] }
+owo-colors = { version = "3.5.0", features = ["supports-color", "supports-colors"] }
+r2d2 = "0.8.10"
+abort-on-drop = "0.2.2"
+unindent = "0.2.3"
cmds/fleet/src/cmds/build_systems.rsdiffbeforeafterboth--- a/cmds/fleet/src/cmds/build_systems.rs
+++ b/cmds/fleet/src/cmds/build_systems.rs
@@ -121,11 +121,11 @@
cmd.comparg("--profile", "/nix/var/nix/profiles/system")
.arg("--list-generations");
// Sudo is required due to --list-generations acquiring lock on the profile.
- let data = config.run_string_on(&host, cmd, true).await?;
+ let data = config.run_string_on(host, cmd, true).await?;
let generations = data
.split('\n')
.map(|e| e.trim())
- .filter(|&l| l != "")
+ .filter(|&l| !l.is_empty())
.filter_map(|g| {
let gen: Option<Generation> = try {
let mut parts = g.split_whitespace();
@@ -170,13 +170,13 @@
async fn systemctl_stop(config: &Config, host: &str, unit: &str) -> Result<()> {
let mut cmd = MyCommand::new("systemctl");
cmd.arg("stop").arg(unit);
- config.run_on(&host, cmd, true).await
+ config.run_on(host, cmd, true).await
}
async fn systemctl_start(config: &Config, host: &str, unit: &str) -> Result<()> {
let mut cmd = MyCommand::new("systemctl");
cmd.arg("start").arg(unit);
- config.run_on(&host, cmd, true).await
+ config.run_on(host, cmd, true).await
}
async fn execute_upload(
@@ -195,7 +195,7 @@
if !build.disable_rollback {
let _span = info_span!("preparing").entered();
info!("preparing for rollback");
- let generation = get_current_generation(&config, &host).await?;
+ let generation = get_current_generation(config, host).await?;
info!(
"rollback target would be {} {}",
generation.id, generation.datetime
@@ -203,7 +203,7 @@
{
let mut cmd = MyCommand::new("sh");
cmd.arg("-c").arg(format!("mark=$(mktemp -p /etc -t fleet_rollback_marker.XXXXX) && echo -n {} > $mark && mv --no-clobber $mark /etc/fleet_rollback_marker", generation.id));
- if let Err(e) = config.run_on(&host, cmd, true).await {
+ if let Err(e) = config.run_on(host, cmd, true).await {
error!("failed to set rollback marker: {e}");
failed = true;
}
@@ -225,7 +225,7 @@
.arg("systemctl")
.arg("start")
.arg("rollback-watchdog.service");
- if let Err(e) = config.run_on(&host, cmd, true).await {
+ if let Err(e) = config.run_on(host, cmd, true).await {
error!("failed to schedule rollback run: {e}");
failed = true;
}
@@ -236,7 +236,7 @@
let mut cmd = MyCommand::new("nix-env");
cmd.comparg("--profile", "/nix/var/nix/profiles/system")
.comparg("--set", &built);
- if let Err(e) = config.run_on(&host, cmd, true).await {
+ if let Err(e) = config.run_on(host, cmd, true).await {
error!("failed to switch generation: {e}");
failed = true;
}
@@ -249,7 +249,7 @@
switch_script.push("switch-to-configuration");
let mut cmd = MyCommand::new(switch_script);
cmd.arg(action.name());
- if let Err(e) = config.run_on(&host, cmd, true).in_current_span().await {
+ if let Err(e) = config.run_on(host, cmd, true).in_current_span().await {
error!("failed to activate: {e}");
failed = true;
}
@@ -257,7 +257,7 @@
if !build.disable_rollback {
if failed {
info!("executing rollback");
- if let Err(e) = systemctl_start(&config, &host, "rollback-watchdog.service")
+ if let Err(e) = systemctl_start(config, host, "rollback-watchdog.service")
.instrument(info_span!("rollback"))
.await
{
@@ -267,23 +267,23 @@
info!("trying to mark upgrade as successful");
let mut cmd = MyCommand::new("rm");
cmd.arg("-f").arg("/etc/fleet_rollback_marker");
- if let Err(e) = config.run_on(&host, cmd, true).in_current_span().await {
+ if let Err(e) = config.run_on(host, cmd, true).in_current_span().await {
error!("failed to remove rollback marker. This is bad, as the system will be rolled back by watchdog: {e}")
}
}
info!("disarming watchdog, just in case");
- if let Err(_e) = systemctl_stop(&config, &host, "rollback-watchdog.timer").await {
+ if let Err(_e) = systemctl_stop(config, host, "rollback-watchdog.timer").await {
// It is ok, if there was no reboot - then timer might not be running.
}
if action.should_schedule_rollback_run() {
- if let Err(e) = systemctl_stop(&config, &host, "rollback-watchdog-run.timer").await {
+ if let Err(e) = systemctl_stop(config, host, "rollback-watchdog-run.timer").await {
error!("failed to disarm rollback run: {e}");
}
}
} else {
let mut cmd = MyCommand::new("rm");
cmd.arg("-f").arg("/etc/fleet_rollback_marker");
- if let Err(_e) = config.run_on(&host, cmd, true).in_current_span().await {
+ if let Err(_e) = config.run_on(host, cmd, true).in_current_span().await {
// Marker might not exist, yet better try to remove it.
}
}
@@ -341,7 +341,7 @@
sign.arg("nix")
.arg("store")
.arg("sign")
- .comparg("-k", "/etc/nix/private-key")
+ .comparg("--key-file", "/etc/nix/private-key")
.arg("-r")
.arg(&built);
if let Err(e) = sign.run_nix().await {
@@ -353,7 +353,7 @@
let mut nix = MyCommand::new("nix");
nix.arg("copy")
.arg("--substitute-on-destination")
- .comparg("--to", format!("ssh-ng://root@{host}"))
+ .comparg("--to", format!("ssh-ng://{host}"))
.arg(&built);
match nix.run_nix().await {
Ok(()) => break,
@@ -423,17 +423,17 @@
let hosts = config.list_hosts().await?;
let set = LocalSet::new();
let this = &self;
- for host in hosts.iter() {
- if config.should_skip(host) {
+ for host in hosts.into_iter() {
+ if config.should_skip(&host.name) {
continue;
}
let config = config.clone();
- let host = host.clone();
let this = this.clone();
- let span = info_span!("deployment", host = field::display(&host));
+ let span = info_span!("deployment", host = field::display(&host.name));
+ let hostname = host.name;
set.spawn_local(
(async move {
- match this.build_task(config, host).await {
+ match this.build_task(config, hostname).await {
Ok(_) => {}
Err(e) => {
error!("failed to deploy host: {}", e)
cmds/fleet/src/cmds/info.rsdiffbeforeafterboth--- a/cmds/fleet/src/cmds/info.rs
+++ b/cmds/fleet/src/cmds/info.rs
@@ -36,14 +36,19 @@
InfoCmd::ListHosts { ref tagged } => {
'host: for host in config.list_hosts().await? {
if !tagged.is_empty() {
- let tags: Vec<String> = config.config_attr(&host, "tags").await?;
+ let tags: Vec<String> = config
+ .fleet_field
+ .get_field_deep(["configuredSystems", &host.name, "config", "tags"])
+ .await?
+ .as_json()
+ .await?;
for tag in tagged {
if !tags.contains(tag) {
continue 'host;
}
}
}
- data.push(host);
+ data.push(host.name);
}
}
InfoCmd::HostIps {
@@ -56,17 +61,20 @@
"at leas one of --external or --internal must be set"
);
let mut out = <BTreeSet<String>>::new();
+ let host = config.system_config(&host).await?;
if external {
out.extend(
- config
- .config_attr::<Vec<String>>(&host, "network.externalIps")
+ host.get_field_deep(["network", "externalIps"])
+ .await?
+ .as_json::<Vec<String>>()
.await?,
);
}
if internal {
out.extend(
- config
- .config_attr::<Vec<String>>(&host, "network.internalIps")
+ host.get_field_deep(["network", "internalIps"])
+ .await?
+ .as_json::<Vec<String>>()
.await?,
);
}
cmds/fleet/src/cmds/secrets/mod.rsdiffbeforeafterboth--- a/cmds/fleet/src/cmds/secrets/mod.rs
+++ b/cmds/fleet/src/cmds/secrets/mod.rs
@@ -3,15 +3,18 @@
host::Config,
};
use anyhow::{bail, ensure, Context, Result};
+use chrono::Utc;
use clap::Parser;
use futures::{StreamExt, TryStreamExt};
+use owo_colors::OwoColorize;
use std::{
collections::HashSet,
io::{self, Cursor, Read},
path::PathBuf,
};
+use tabled::{Table, Tabled};
use tokio::fs::read_to_string;
-use tracing::{error, info, warn};
+use tracing::{error, info, info_span, warn};
#[derive(Parser)]
pub enum Secrets {
@@ -73,6 +76,7 @@
#[clap(long)]
prefer_identities: Vec<String>,
},
+ List {},
}
impl Secrets {
@@ -80,10 +84,10 @@
match self {
Secrets::ForceKeys => {
for host in config.list_hosts().await? {
- if config.should_skip(&host) {
+ if config.should_skip(&host.name) {
continue;
}
- config.key(&host).await?;
+ config.key(&host.name).await?;
}
}
Secrets::AddShared {
@@ -128,7 +132,8 @@
FleetSharedSecret {
owners: machines,
secret: FleetSecret {
- expire_at: None,
+ created_at: Utc::now(),
+ expires_at: None,
secret,
public: match (public, public_file) {
(Some(v), None) => Some(v),
@@ -175,7 +180,8 @@
&machine,
name,
FleetSecret {
- expire_at: None,
+ created_at: Utc::now(),
+ expires_at: None,
secret,
public: match (public, public_file) {
(Some(v), None) => Some(v),
@@ -291,7 +297,7 @@
target_recipients.into_iter().collect::<Result<Vec<_>>>()?;
let encrypted = config
- .reencrypt_on_host(&identity_holder, secret.secret.secret, target_recipients)
+ .reencrypt_on_host(identity_holder, secret.secret.secret, target_recipients)
.await?;
secret.owners = target_machines;
@@ -300,13 +306,14 @@
}
Secrets::Regenerate { prefer_identities } => {
{
- let expected_shared_set =
- config.shared_config_attr_names("sharedSecrets").await?;
- let expected_shared_set = expected_shared_set.iter().collect::<HashSet<_>>();
- let shared_set = config.list_shared();
- let shared_set = shared_set.iter().collect::<HashSet<_>>();
+ let expected_shared_set = config
+ .list_configured_shared()
+ .await?
+ .into_iter()
+ .collect::<HashSet<_>>();
+ let shared_set = config.list_shared().into_iter().collect::<HashSet<_>>();
for removed in expected_shared_set.difference(&shared_set) {
- warn!("secret needs to be generated: {removed}")
+ error!("secret needs to be generated: {removed}")
}
}
let mut to_remove = Vec::new();
@@ -314,7 +321,8 @@
info!("updating secret: {name}");
let mut data = config.shared_secret(name)?;
let expected_owners: Vec<String> = config
- .shared_config_attr(&format!("sharedSecrets.\"{name}\".expectedOwners"))
+ .config_field
+ .get_json_deep(["sharedSecrets", name, "expectedOwners"])
.await?;
if expected_owners.is_empty() {
warn!("secret was removed from fleet config: {name}, removing from data");
@@ -326,7 +334,8 @@
let should_remove = set.difference(&expected_set).next().is_some();
if set != expected_set {
let owner_dependent: bool = config
- .shared_config_attr(&format!("sharedSecrets.\"{name}\".ownerDependent"))
+ .config_field
+ .get_json_deep(["sharedSecrets", name, "ownerDependent"])
.await?;
if !owner_dependent {
warn!("reencrypting secret '{name}' for new owner set");
@@ -355,7 +364,7 @@
let encrypted = config
.reencrypt_on_host(
- &identity_holder,
+ identity_holder,
data.secret.secret,
target_recipients,
)
@@ -364,13 +373,6 @@
data.secret.secret = encrypted;
data.owners = expected_owners;
config.replace_shared(name.to_owned(), data);
- } else if let Some(generator) = config
- .shared_config_attr::<Option<String>>(&format!(
- "sharedSecrets.\"{name}\".generator"
- ))
- .await?
- {
- todo!("regenerate secret {name} with {generator}");
} else {
error!("secret '{name}' should be regenerated manually");
}
@@ -382,6 +384,39 @@
config.remove_shared(&k);
}
}
+ Secrets::List {} => {
+ let _span = info_span!("loading secrets").entered();
+ let configured = config.list_configured_shared().await?;
+ #[derive(Tabled)]
+ struct SecretDisplay {
+ #[tabled(rename = "Name")]
+ name: String,
+ #[tabled(rename = "Owners")]
+ owners: String,
+ }
+ let mut table = vec![];
+ for name in configured.iter().cloned() {
+ let config = config.clone();
+ let expected_owners = config.shared_secret_expected_owners(&name).await?;
+ let data = config.shared_secret(&name)?;
+ let owners = data
+ .owners
+ .iter()
+ .map(|o| {
+ if expected_owners.contains(o) {
+ o.green().to_string()
+ } else {
+ o.red().to_string()
+ }
+ })
+ .collect::<Vec<_>>();
+ table.push(SecretDisplay {
+ owners: owners.join(", "),
+ name,
+ })
+ }
+ info!("loaded\n{}", Table::new(table).to_string())
+ }
}
Ok(())
}
cmds/fleet/src/command.rsdiffbeforeafterboth--- a/cmds/fleet/src/command.rs
+++ b/cmds/fleet/src/command.rs
@@ -1,11 +1,14 @@
-use std::{collections::HashMap, ffi::OsStr, process::Stdio, task::Poll};
+use std::{
+ collections::HashMap,
+ ffi::OsStr,
+ process::Stdio,
+ sync::{Arc, Mutex},
+ task::Poll,
+};
-use anyhow::{Context, Result};
+use anyhow::Result;
use futures::StreamExt;
-use serde::{
- de::{DeserializeOwned, Visitor},
- Deserialize,
-};
+use serde::{de::Visitor, Deserialize};
use tokio::{io::AsyncRead, process::Command, select};
use tokio_util::codec::{BytesCodec, FramedRead, LinesCodec};
use tracing::{info, info_span, warn, Span};
@@ -49,12 +52,12 @@
if !self.env.is_empty() {
out.push("env".to_owned());
for (k, v) in self.env {
- assert!(!k.contains("="));
+ assert!(!k.contains('='));
out.push(format!("{k}={v}"));
}
}
out.push(self.command);
- out.extend(self.args.into_iter());
+ out.extend(self.args);
out
}
fn into_string(self) -> String {
@@ -63,7 +66,7 @@
out.push_str("env");
for (k, v) in self.env {
out.push(' ');
- assert!(!k.contains("="));
+ assert!(!k.contains('='));
escape_bash(&k, &mut out);
out.push('=');
escape_bash(&v, &mut out);
@@ -135,10 +138,6 @@
let cmd = self.into_command();
let v = run_nix_inner_stdout(str, cmd, &mut PlainHandler).await?;
Ok(v)
- }
- pub async fn run_nix_json<T: DeserializeOwned>(self) -> Result<T> {
- let str = self.run_nix_string().await?;
- serde_json::from_str(&str).with_context(|| format!("{:?}", str))
}
pub async fn run_nix_string(self) -> Result<String> {
@@ -172,38 +171,55 @@
cmd: Command,
handler: &mut dyn Handler,
) -> Result<String> {
- Ok(run_nix_inner_raw(str, cmd, true, handler)
+ Ok(run_nix_inner_raw(str, cmd, true, handler, None)
.await?
.expect("has out"))
}
async fn run_nix_inner(str: String, cmd: Command, handler: &mut dyn Handler) -> Result<()> {
- let v = run_nix_inner_raw(str, cmd, false, handler).await?;
+ let v = run_nix_inner_raw(str, cmd, false, handler, None).await?;
assert!(v.is_none());
Ok(())
}
-trait Handler {
- fn handle_err(&mut self, e: &str);
- fn handle_info(&mut self, e: &str);
+pub trait Handler: Send {
+ fn handle_line(&mut self, e: &str);
+}
+
+pub struct ClonableHandler<H>(Arc<Mutex<H>>);
+impl<H> Clone for ClonableHandler<H> {
+ fn clone(&self) -> Self {
+ Self(self.0.clone())
+ }
+}
+impl<H> ClonableHandler<H> {
+ pub fn new(inner: H) -> Self {
+ Self(Arc::new(Mutex::new(inner)))
+ }
+}
+impl<H: Handler> Handler for ClonableHandler<H> {
+ fn handle_line(&mut self, e: &str) {
+ self.0.lock().unwrap().handle_line(e)
+ }
}
struct PlainHandler;
impl Handler for PlainHandler {
- fn handle_err(&mut self, e: &str) {
+ fn handle_line(&mut self, e: &str) {
info!(target: "log", "{e}");
}
+}
- fn handle_info(&mut self, e: &str) {
- info!(target: "log", "{e}");
- }
+pub struct NoopHandler;
+impl Handler for NoopHandler {
+ fn handle_line(&mut self, _e: &str) {}
}
#[derive(Default)]
-struct NixHandler {
+pub struct NixHandler {
spans: HashMap<u64, Span>,
}
impl Handler for NixHandler {
- fn handle_err(&mut self, e: &str) {
+ fn handle_line(&mut self, e: &str) {
if let Some(e) = e.strip_prefix("@nix ") {
let log: NixLog = match serde_json::from_str(e) {
Ok(l) => l,
@@ -214,6 +230,7 @@
};
match log {
NixLog::Msg { msg, raw_msg, .. } => {
+ #[allow(clippy::nonminimal_bool)]
if !(msg.starts_with("\u{1b}[35;1mwarning:\u{1b}[0m Git tree '") && msg.ends_with("' is dirty"))
&& !msg.starts_with("\u{1b}[35;1mwarning:\u{1b}[0m not writing modified lock file of flake")
&& msg != "\u{1b}[35;1mwarning:\u{1b}[0m \u{1b}[31;1merror:\u{1b}[0m SQLite database '\u{1b}[35;1m/nix/var/nix/db/db.sqlite\u{1b}[0m' is busy" {
@@ -397,11 +414,12 @@
_ => warn!("unknown log: {:?}", log),
};
} else {
- warn!(target = "nix", "unknown: {}", e.trim())
+ let e = e.trim();
+ if e.starts_with("Failed tcsetattr(TCSADRAIN): ") {
+ return;
+ }
+ info!("{e}")
}
- }
- fn handle_info(&mut self, o: &str) {
- self.handle_err(o)
}
}
@@ -409,9 +427,9 @@
str: String,
mut cmd: Command,
want_stdout: bool,
- handler: &mut dyn Handler,
+ err_handler: &mut dyn Handler,
+ mut out_handler: Option<&mut dyn Handler>,
) -> Result<Option<String>> {
- info!("running {str}");
cmd.stderr(Stdio::piped());
cmd.stdout(Stdio::piped());
let mut child = cmd.spawn()?;
@@ -436,7 +454,7 @@
e = err.next() => {
if let Some(e) = e {
let e = e?;
- handler.handle_err(&e);
+ err_handler.handle_line(&e);
}
},
o = ob.next() => {
@@ -447,7 +465,12 @@
o = ol.next() => {
if let Some(o) = o {
let o = o?;
- handler.handle_info(&o);
+ if let Some(out) = out_handler.as_mut() {
+ out.handle_line(&o)
+ } else {
+ err_handler.handle_line(&o)
+ }
+ // out_handler.handle_info(&o);
}
},
code = child.wait() => {
@@ -463,6 +486,11 @@
Ok(out_buf.map(String::from_utf8).transpose()?)
}
+pub trait ErrorRecorder: Send {
+ /// Return true to discard message from logging
+ fn push_message(&mut self, msg: &str) -> bool;
+}
+
#[derive(Debug)]
enum LogField {
String(String),
cmds/fleet/src/extra_args.rsdiffbeforeafterboth--- a/cmds/fleet/src/extra_args.rs
+++ b/cmds/fleet/src/extra_args.rs
@@ -12,7 +12,7 @@
})
.collect())
}
-pub fn parse(s: &str) -> Result<Vec<OsString>> {
- let osstr = OsString::try_from(s)?;
- parse_os(&osstr)
-}
+// pub fn parse(s: &str) -> Result<Vec<OsString>> {
+// let osstr = OsString::try_from(s)?;
+// parse_os(&osstr)
+// }
cmds/fleet/src/fleetdata.rsdiffbeforeafterboth--- a/cmds/fleet/src/fleetdata.rs
+++ b/cmds/fleet/src/fleetdata.rs
@@ -44,9 +44,11 @@
#[serde(rename_all = "camelCase")]
#[must_use]
pub struct FleetSecret {
+ #[serde(default = "Utc::now")]
+ pub created_at: DateTime<Utc>,
#[serde(default)]
- #[serde(skip_serializing_if = "Option::is_none")]
- pub expire_at: Option<DateTime<Utc>>,
+ #[serde(skip_serializing_if = "Option::is_none", alias = "expire_at")]
+ pub expires_at: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub public: Option<String>,
#[serde(
cmds/fleet/src/host.rsdiffbeforeafterboth--- a/cmds/fleet/src/host.rs
+++ b/cmds/fleet/src/host.rs
@@ -1,19 +1,18 @@
use std::{
- cell::{Ref, RefCell, RefMut},
env::current_dir,
ffi::OsString,
io::Write,
ops::Deref,
path::PathBuf,
- sync::Arc,
+ sync::{Arc, Mutex, MutexGuard},
};
use anyhow::{bail, Context, Result};
use clap::{ArgGroup, Parser};
-use serde::de::DeserializeOwned;
use tempfile::NamedTempFile;
use crate::{
+ better_nix_eval::{Field, NixSessionPool},
command::MyCommand,
fleetdata::{FleetData, FleetSecret, FleetSharedSecret},
};
@@ -22,8 +21,12 @@
pub local_system: String,
pub directory: PathBuf,
pub opts: FleetOpts,
- pub data: RefCell<FleetData>,
+ pub data: Mutex<FleetData>,
pub nix_args: Vec<OsString>,
+ // fleetConfigurations.<name>
+ pub fleet_field: Field,
+ // fleet_config.configUnchecked
+ pub config_field: Field,
}
#[derive(Clone)]
@@ -37,6 +40,10 @@
}
}
+pub struct ConfigHost {
+ pub name: String,
+}
+
impl Config {
pub fn should_skip(&self, host: &str) -> bool {
if !self.opts.skip.is_empty() {
@@ -60,7 +67,6 @@
}
command.run().await
}
- #[must_use]
pub async fn run_string_on(
&self,
host: &str,
@@ -86,52 +92,39 @@
str
}
- pub async fn list_hosts(&self) -> Result<Vec<String>> {
- let mut cmd = MyCommand::new("nix");
- cmd.arg("eval")
- .arg(self.configuration_attr_name("configuredHosts"))
- .args(["--apply", "builtins.attrNames", "--json", "--show-trace"])
- .args(&self.nix_args);
- cmd.run_nix_json().await
- }
- pub async fn shared_config_attr<T: DeserializeOwned>(&self, attr: &str) -> Result<T> {
- let mut cmd = MyCommand::new("nix");
- cmd.arg("eval")
- .arg(self.configuration_attr_name(&format!("configUnchecked.{}", attr)))
- .args(["--json", "--show-trace"])
- .args(&self.nix_args);
- cmd.run_nix_json().await
- }
- pub async fn shared_config_attr_names(&self, attr: &str) -> Result<Vec<String>> {
- let mut cmd = MyCommand::new("nix");
- cmd.arg("eval")
- .arg(self.configuration_attr_name(&format!("configUnchecked.{}", attr)))
- .args(["--apply", "builtins.attrNames"])
- .args(["--json", "--show-trace"])
- .args(&self.nix_args);
- cmd.run_nix_json().await
+ pub async fn list_hosts(&self) -> Result<Vec<ConfigHost>> {
+ let names = self.fleet_field
+ .get_field_deep(["configuredHosts"])
+ .await?
+ .list_fields()
+ .await?;
+ let mut out = vec![];
+ for name in names {
+ out.push(ConfigHost {
+ name,
+ })
+ }
+ Ok(out)
}
- pub async fn config_attr<T: DeserializeOwned>(&self, host: &str, attr: &str) -> Result<T> {
- let mut cmd = MyCommand::new("nix");
- cmd.arg("eval")
- .arg(
- self.configuration_attr_name(&format!(
- "configuredSystems.{}.config.{}",
- host, attr
- )),
- )
- .args(["--json", "--show-trace"])
- .args(&self.nix_args);
- cmd.run_nix_json().await
+ pub async fn system_config(&self, host: &str) -> Result<Field> {
+ self.fleet_field.get_field_deep(["configuredSystems", host, "config"]).await
}
- pub(super) fn data(&self) -> Ref<FleetData> {
- self.data.borrow()
+ pub(super) fn data(&self) -> MutexGuard<FleetData> {
+ self.data.lock().unwrap()
+ }
+ pub(super) fn data_mut(&self) -> MutexGuard<FleetData> {
+ self.data.lock().unwrap()
}
- pub(super) fn data_mut(&self) -> RefMut<FleetData> {
- self.data.borrow_mut()
+ /// Shared secrets configured in fleet.nix or in flake
+ pub async fn list_configured_shared(&self) -> Result<Vec<String>> {
+ self.config_field
+ .get_field("sharedSecrets")
+ .await?
+ .list_fields()
+ .await
}
-
+ /// Shared secrets configured in fleet.nix
pub fn list_shared(&self) -> Vec<String> {
let data = self.data();
data.shared_secrets.keys().cloned().collect()
@@ -149,13 +142,6 @@
data.shared_secrets.remove(secret);
}
- pub fn list_secrets(&self, host: &str) -> Vec<String> {
- let data = self.data();
- let Some(host_secrets) = data.host_secrets.get(host) else {
- return Vec::new();
- };
- host_secrets.keys().cloned().collect()
- }
pub fn has_secret(&self, host: &str, secret: &str) -> bool {
let data = self.data();
let Some(host_secrets) = data.host_secrets.get(host) else {
@@ -180,7 +166,7 @@
.context("failed to call remote host for decrypt")?
.trim()
.to_owned();
- Ok(z85::decode(encoded).context("bad encoded data? outdated host?")?)
+ z85::decode(encoded).context("bad encoded data? outdated host?")
}
pub async fn reencrypt_on_host(
&self,
@@ -201,10 +187,9 @@
.context("failed to call remote host for decrypt")?
.trim()
.to_owned();
- Ok(z85::decode(encoded).context("bad encoded data? outdated host?")?)
+ z85::decode(encoded).context("bad encoded data? outdated host?")
}
- #[must_use]
pub fn host_secret(&self, host: &str, secret: &str) -> Result<FleetSecret> {
let data = self.data();
let Some(host_secrets) = data.host_secrets.get(host) else {
@@ -215,7 +200,6 @@
};
Ok(secret.clone())
}
- #[must_use]
pub fn shared_secret(&self, secret: &str) -> Result<FleetSharedSecret> {
let data = self.data();
let Some(secret) = data.shared_secrets.get(secret) else {
@@ -223,13 +207,20 @@
};
Ok(secret.clone())
}
+ pub async fn shared_secret_expected_owners(&self, secret: &str) -> Result<Vec<String>> {
+ self.config_field
+ .get_field_deep(["sharedSecrets", secret, "expectedOwners"])
+ .await?
+ .as_json()
+ .await
+ }
pub fn save(&self) -> Result<()> {
let mut tempfile = NamedTempFile::new_in(self.directory.clone())?;
let data = nixlike::serialize(&self.data() as &FleetData)?;
tempfile.write_all(
format!(
- "# This file contains fleet state and shouldn't be edited by hand\n\n{}\n",
+ "# This file contains fleet state and shouldn't be edited by hand\n\n{}\n\n# vim: ts=2 et nowrap\n",
data
)
.as_bytes(),
@@ -259,19 +250,35 @@
// TODO: unhardcode x86_64-linux
/// Override detected system for host, to perform builds via
/// binfmt-declared qemu instead of trying to crosscompile
- #[clap(long, default_value = "x86_64-linux")]
+ #[clap(long, default_value = "detect")]
pub local_system: String,
}
impl FleetOpts {
pub async fn build(mut self, nix_args: Vec<OsString>) -> Result<Config> {
- let local_system = self.local_system.clone();
if self.localhost.is_none() {
self.localhost
.replace(hostname::get().unwrap().to_str().unwrap().to_owned());
}
let directory = current_dir()?;
+ let pool = NixSessionPool::new(directory.as_os_str().to_owned(), nix_args.clone()).await?;
+ let root_field = pool.get().await?;
+
+ if self.local_system == "detect" {
+ let builtins_field = Field::field(root_field.clone(), "builtins").await?;
+ let system = builtins_field.get_field("currentSystem").await?;
+ self.local_system = system.as_json().await?;
+ }
+ let local_system = self.local_system.clone();
+
+ let fleet_root = Field::field(root_field, "fleetConfigurations").await?;
+
+ let fleet_field = fleet_root
+ .get_field_deep(["default", &local_system])
+ .await?;
+ let config_field = fleet_field.get_field("configUnchecked").await?;
+
let mut fleet_data_path = directory.clone();
fleet_data_path.push("fleet.nix");
let bytes = std::fs::read_to_string(fleet_data_path)?;
@@ -283,6 +290,8 @@
data,
local_system,
nix_args,
+ fleet_field,
+ config_field,
})))
}
}
cmds/fleet/src/keys.rsdiffbeforeafterboth--- a/cmds/fleet/src/keys.rs
+++ b/cmds/fleet/src/keys.rs
@@ -3,6 +3,7 @@
use crate::command::MyCommand;
use crate::host::Config;
use anyhow::{anyhow, Result};
+use itertools::Itertools;
use tracing::warn;
impl Config {
@@ -40,9 +41,15 @@
age::ssh::Recipient::from_str(&key).map_err(|e| anyhow!("parse recipient error: {:?}", e))
}
+ #[allow(dead_code)]
pub async fn orphaned_data(&self) -> Result<Vec<String>> {
let mut out = Vec::new();
- let host_names = self.list_hosts().await?;
+ let host_names = self
+ .list_hosts()
+ .await?
+ .into_iter()
+ .map(|h| h.name)
+ .collect_vec();
for hostname in self
.data()
.hosts
cmds/fleet/src/main.rsdiffbeforeafterboth--- a/cmds/fleet/src/main.rs
+++ b/cmds/fleet/src/main.rs
@@ -1,9 +1,12 @@
#![feature(try_blocks)]
-pub mod cmds;
-pub mod command;
-pub mod host;
-pub mod keys;
+pub(crate) mod cmds;
+pub(crate) mod command;
+pub(crate) mod host;
+pub(crate) mod keys;
+
+pub(crate) mod extra_args;
+pub(crate) mod better_nix_eval;
mod fleetdata;
@@ -14,13 +17,18 @@
use clap::Parser;
use cmds::{build_systems::BuildSystems, info::Info, secrets::Secrets};
+use futures::future::LocalBoxFuture;
+use futures::stream::FuturesUnordered;
+use futures::TryStreamExt;
use host::{Config, FleetOpts};
use indicatif::{ProgressState, ProgressStyle};
-use tokio::process::Command;
use tracing::{info, metadata::LevelFilter};
+use tracing::{info_span, Instrument};
use tracing_indicatif::IndicatifLayer;
use tracing_subscriber::{prelude::*, EnvFilter};
+use crate::command::MyCommand;
+
#[derive(Parser)]
struct Prefetch {}
impl Prefetch {
@@ -31,20 +39,28 @@
info!("nothing to prefetch: no prefetch directory");
return Ok(());
}
+ let tasks = <FuturesUnordered<LocalBoxFuture<Result<()>>>>::new();
for entry in std::fs::read_dir(&prefetch_dir)? {
- let entry = entry?;
- if !entry.metadata()?.is_file() {
- bail!("only files should exist in prefetch directory");
- }
- info!("prefetching {:?}", entry.file_name());
- let mut path = OsString::new();
- path.push("file://");
- path.push(entry.path());
- let status = Command::new("nix-prefetch-url").arg(path).status().await?;
- if !status.success() {
- bail!("failed with {status}");
- }
+ tasks.push(Box::pin(async {
+ let entry = entry?;
+ if !entry.metadata()?.is_file() {
+ bail!("only files should exist in prefetch directory");
+ }
+ let span = info_span!(
+ "prefetching",
+ name = entry.file_name().to_string_lossy().as_ref()
+ );
+ let mut path = OsString::new();
+ path.push("file://");
+ path.push(entry.path());
+
+ let mut status = MyCommand::new("nix");
+ status.arg("store").arg("prefetch-file").arg(path);
+ status.run_nix_string().instrument(span).await?;
+ Ok(())
+ }));
}
+ tasks.try_collect::<Vec<()>>().await?;
Ok(())
}
}
@@ -81,8 +97,28 @@
Ok(())
}
-#[tokio::main]
-async fn main() -> Result<()> {
+// fn main() -> Result<()> {
+// let pool = r2d2::Builder::<NixSessionPool>::new()
+// .min_idle(Some(1))
+// .max_lifetime(Some(Duration::from_secs(10)))
+// .build(NixSessionPool {
+// flake: ".".to_owned(),
+// nix_args: vec![],
+// })?;
+// let conn = pool.get()?;
+// let field = Field::root(conn);
+// // let builtins = field.get_field("builtins")?;
+// let cur_sys: String = field.get_field("builtins")?.as_json()?;
+// eprintln!("current system = {cur_sys}");
+// let v = field.get_field("fleetConfigurations")?;
+// eprintln!("configs = {:?}", v.list_fields()?);
+// let d = v.get_field("default")?;
+// dbg!(d.list_fields());
+// Ok(())
+// }
+//
+
+fn setup_logging() {
let indicatif_layer = IndicatifLayer::new().with_progress_style(
ProgressStyle::with_template(
"{color_start}{span_child_prefix} {span_name}{{{span_fields}}}{color_end} {wide_msg} {color_start}{pos:>7}/{len:7}{elapsed}{color_end}",
@@ -124,10 +160,19 @@
)
.with(indicatif_layer)
.init();
- info!("Starting");
- let mut os_args = std::env::args_os();
- let opts = RootOpts::parse_from((&mut os_args).take_while(|v| v != "--"));
- let config = opts.fleet_opts.build(os_args.collect()).await?;
+}
+
+#[tokio::main]
+async fn main() -> Result<()> {
+ setup_logging();
+ let _ = better_nix_eval::TOKIO_RUNTIME.set(tokio::runtime::Handle::current());
+
+ let nix_args = std::env::var_os("NIX_ARGS")
+ .map(|a| extra_args::parse_os(&a))
+ .transpose()?
+ .unwrap_or_default();
+ let opts = RootOpts::parse();
+ let config = opts.fleet_opts.build(nix_args).await?;
match run_command(&config, opts.command).await {
Ok(()) => {
cmds/fleet/src/nix_eval.rsdiffbeforeafterboth--- a/cmds/fleet/src/nix_eval.rs
+++ /dev/null
@@ -1,256 +0,0 @@
-//! Calling nix eval for everything is slow, it is not easy to link nix evaluator itself,
-//! and tvix-nix doesn't have proper flake support. Fleets solution: automating nix repl calls.
-//!
-//! Api is synchronous, yet it is good enough with pooling, and in environment without IFDs for using
-//! those blocking calls from async code.
-
-use std::borrow::Cow;
-use std::sync::{Arc, Mutex};
-use std::time::Instant;
-
-use anyhow::{anyhow, bail, ensure, Context, Result};
-use itertools::Itertools;
-use r2d2::PooledConnection;
-use rexpect::session::{PtyReplSession, PtySession};
-use serde::de::DeserializeOwned;
-use std::ffi::OsString;
-use tracing::info_span;
-
-fn parse_error(res: &str) -> Option<String> {
- let res = if let Some(v) = res.strip_prefix("error: ") {
- if let Some((first_line, next)) = v.split_once('\n') {
- format!("{first_line}\n{}", unindent::unindent(next))
- } else {
- v.trim_start().to_owned()
- }
- } else if let Some(v) = res.strip_prefix("error:\n") {
- let mut v = v.to_owned();
- v.insert(0, '\n');
- unindent::unindent(&v).trim_start().to_owned()
- } else {
- return None;
- };
- let res = res.trim_end();
- Some(
- res.replace('Â', "")
- .split('\n')
- .map(|l| l.strip_prefix("â\u{80}¦ ").unwrap_or(l))
- .join("\n"),
- )
-}
-pub struct NixSessionPool {
- pub flake: OsString,
- pub nix_args: Vec<OsString>,
-}
-
-#[derive(Debug)]
-pub struct NixPoolError(anyhow::Error);
-impl From<anyhow::Error> for NixPoolError {
- fn from(value: anyhow::Error) -> Self {
- Self(value)
- }
-}
-impl std::error::Error for NixPoolError {}
-impl std::fmt::Display for NixPoolError {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- self.0.fmt(f)
- }
-}
-
-impl r2d2::ManageConnection for NixSessionPool {
- type Connection = NixSession;
- type Error = NixPoolError;
-
- fn connect(&self) -> std::result::Result<Self::Connection, Self::Error> {
- Ok(NixSession::new(&self.flake, &self.nix_args, None)?)
- }
-
- fn is_valid(&self, conn: &mut Self::Connection) -> std::result::Result<(), Self::Error> {
- let res = conn.expression_result("2 + 2")?;
- if res != "4" {
- return Err(anyhow!("basic expression failed").into());
- }
- Ok(())
- }
-
- fn has_broken(&self, conn: &mut Self::Connection) -> bool {
- conn.finished
- }
-}
-
-pub struct NixSession {
- session: PtyReplSession,
- next_id: u32,
- free_list: Vec<u32>,
- finished: bool,
-}
-impl NixSession {
- fn new(flake: &OsString, args: &[OsString], timeout: Option<u64>) -> Result<Self> {
- let mut cmd = std::process::Command::new("nix");
- cmd.arg("repl");
- cmd.arg(flake);
- for arg in args {
- cmd.arg(arg);
- }
- cmd.env("TERM", "dumb");
- cmd.env("NO_COLOR", "1");
- let pty_session = rexpect::session::spawn_command(cmd, timeout)?;
- let mut repl = PtyReplSession {
- prompt: "nix-repl> ".to_string(),
- pty_session,
- quit_command: Some(":q".to_string()),
- echo_on: true,
- };
- repl.wait_for_prompt()?;
- Ok(Self {
- session: repl,
- next_id: 0,
- free_list: vec![],
- finished: false,
- })
- }
- fn expression_result(&mut self, cmd: &str) -> Result<String> {
- dbg!(cmd);
- self.session.send_line(cmd)?;
- dbg!("waiting");
- let result = self.session.wait_for_prompt()?;
- let result = strip_ansi_escapes::strip_str(&result);
- let result = result.trim();
- dbg!(result);
- Ok(result.to_owned())
- }
- fn json_result<V: DeserializeOwned>(&mut self, cmd: &str) -> Result<V> {
- let v = match self.expression_result(&format!("builtins.toJSON ({cmd})")) {
- Ok(v) => {
- if let Some(e) = parse_error(&v) {
- bail!("{e}")
- }
- v
- }
- Err(e) => {
- self.finished = true;
- bail!("{e}")
- }
- };
- // Remove outer quoting
- let v: String = serde_json::from_str(&v)?;
- Ok(serde_json::from_str(&v)?)
- }
- /// Id should be immediately used
- fn allocate_id(&mut self) -> u32 {
- if let Some(free) = self.free_list.pop() {
- free
- } else {
- let v = self.next_id;
- self.next_id += 1;
- v
- }
- }
- fn allocate_result(&mut self, cmd: &str) -> Result<u32> {
- let id = self.allocate_id();
- match self.expression_result(&format!("sess_field_{id} = ({cmd})")) {
- Ok(v) => {
- if let Some(e) = parse_error(&v) {
- self.free_list.push(id);
- bail!("{e}")
- }
- }
- Err(e) => {
- self.finished = true;
- }
- }
-
- Ok(id)
- }
- /// Nix has no way to deallocate variable, yet GC will correct everything not reachable.
- fn free_id(&mut self, id: u32) {
- if let Err(e) = self.expression_result(&format!("sess_field_{id} = null")) {
- self.finished = true;
- } else {
- self.free_list.push(id)
- }
- }
-}
-
-#[derive(Clone, Debug)]
-enum Index {
- String(String),
- Idx(u32),
-}
-
-pub struct Field {
- full_path: Vec<Index>,
- session: Arc<Mutex<PooledConnection<NixSessionPool>>>,
- value: Option<u32>,
-}
-impl Field {
- pub fn root(conn: PooledConnection<NixSessionPool>) -> Self {
- Self {
- full_path: vec![],
- session: Arc::new(Mutex::new(conn)),
- value: None,
- }
- }
- pub fn get_field_deep<'a>(&self, name: impl IntoIterator<Item = &'a str>) -> Result<Self> {
- let mut iter = name.into_iter();
-
- let mut full_path = self.full_path.clone();
- let mut query = if let Some(id) = self.value {
- format!("sess_field_{id}")
- } else {
- let first = iter.next().expect("name not empty");
- ensure!(
- !(first.contains('.') | first.contains(' ')),
- "bad name for root query: {first}"
- );
- full_path.push(Index::String(first.to_string()));
- first.to_string()
- };
- for v in iter {
- full_path.push(Index::String(v.to_string()));
- // Escape
- let escaped = nixlike::serialize(v)?;
- let escaped = escaped.trim();
- query.push('.');
- query.push_str(escaped);
- }
-
- let vid = self
- .session
- .lock()
- .unwrap()
- .allocate_result(&query)
- .with_context(|| format!("full path: {:?}", full_path))?;
- Ok(Self {
- full_path,
- session: self.session.clone(),
- value: Some(vid),
- })
- }
- pub fn get_field<'a>(&self, name: &str) -> Result<Self> {
- self.get_field_deep([name])
- }
- pub fn as_json<V: DeserializeOwned>(&self) -> Result<V> {
- let id = self.value.expect("can't serialize root field");
- self.session
- .lock()
- .unwrap()
- .json_result(&format!("sess_field_{id}"))
- .with_context(|| format!("full path: {:?}", self.full_path))
- }
- pub fn list_fields(&self) -> Result<Vec<String>> {
- let id = self.value.expect("can't list root fields");
- self.session
- .lock()
- .unwrap()
- .json_result(&format!("builtins.attrNames sess_field_{id}"))
- .with_context(|| format!("full path: {:?}", self.full_path))
- }
-}
-impl Drop for Field {
- fn drop(&mut self) {
- if let Some(id) = self.value {
- self.session.lock().unwrap().free_id(id)
- }
- }
-}
cmds/install-secrets/Cargo.tomldiffbeforeafterboth--- a/cmds/install-secrets/Cargo.toml
+++ b/cmds/install-secrets/Cargo.toml
@@ -4,18 +4,18 @@
edition = "2021"
[dependencies]
-age = { version = "0.9.0", features = ["ssh"] }
-anyhow = "1.0.44"
+age = { version = "0.9.2", features = ["ssh"] }
+anyhow = "1.0.75"
env_logger = "0.10.0"
-log = "0.4.14"
-nix = "0.26.1"
-serde = { version = "1.0.130", features = ["derive"] }
-serde_json = "1.0.89"
-clap = { version = "4.0.29", features = [
+log = "0.4.20"
+nix = {version = "0.27.1", features = ["user", "fs"]}
+serde = { version = "1.0.190", features = ["derive"] }
+serde_json = "1.0.107"
+clap = { version = "4.4.7", features = [
"derive",
"env",
"wrap_help",
"unicode",
] }
-tempfile = "3.2.0"
-z85 = "3.0.3"
+tempfile = "3.8.1"
+z85 = "3.0.5"
cmds/install-secrets/src/main.rsdiffbeforeafterboth--- a/cmds/install-secrets/src/main.rs
+++ b/cmds/install-secrets/src/main.rs
@@ -4,7 +4,7 @@
use clap::Parser;
use log::{error, info, warn};
use nix::sys::stat::Mode;
-use nix::unistd::{chown, Group, User};
+use nix::unistd::{User, Group, chown};
use serde::{Deserialize, Deserializer};
use std::fmt::{self, Display};
use std::fs::{self, File};
@@ -161,7 +161,7 @@
let mut hashed = File::create(&value.secret_path)?;
// File is owned by root, and only root can modify it
- let decrypted = decrypt(&secret, identity)?;
+ let decrypted = decrypt(secret, identity)?;
if decrypted.is_empty() {
warn!("secret is decoded as empty, something is broken?");
}
crates/nixlike/Cargo.tomldiffbeforeafterboth--- a/crates/nixlike/Cargo.toml
+++ b/crates/nixlike/Cargo.toml
@@ -5,11 +5,10 @@
[dependencies]
alejandra = {git = "https://github.com/kamadorueda/alejandra"}
-rnix = "0.10.2"
-linked-hash-map = "0.5.4"
-peg = "0.8.0"
-serde = "1.0.130"
-thiserror = "1.0.29"
-serde_json = "1.0.91"
-ron = "0.8.0"
+linked-hash-map = "0.5.6"
+peg = "0.8.2"
+serde = "1.0.190"
+thiserror = "1.0.50"
+serde_json = "1.0.107"
+ron = "0.8.1"
serde-transcode = "1.1.1"
crates/nixlike/src/lib.rsdiffbeforeafterboth--- a/crates/nixlike/src/lib.rs
+++ b/crates/nixlike/src/lib.rs
@@ -119,6 +119,12 @@
Ok(serialize_value_pretty(value))
}
+pub fn format_identifier(i: &str) -> String {
+ let mut out = String::new();
+ to_string::write_identifier(i, &mut out);
+ out
+}
+
#[test]
fn test() {
assert_eq!(serialize("Hello\nworld").unwrap(), "\"Hello\\nworld\"\n");
crates/nixlike/src/se_impl.rsdiffbeforeafterboth--- a/crates/nixlike/src/se_impl.rs
+++ b/crates/nixlike/src/se_impl.rs
@@ -212,7 +212,7 @@
}
fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
- Ok(Value::Number(v as i64))
+ Ok(Value::Number(v))
}
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
crates/nixlike/src/to_string.rsdiffbeforeafterboth--- a/crates/nixlike/src/to_string.rs
+++ b/crates/nixlike/src/to_string.rs
@@ -1,24 +1,26 @@
use crate::Value;
-fn write_nix_obj_key_buf(k: &str, v: &Value, out: &mut String) {
- if k.contains('.') {
- out.push_str("\"");
- out.push_str(k);
- out.push_str("\"");
+pub fn write_identifier(k: &str, out: &mut String) {
+ if k.contains(['.', '\'', '\"', '\\', '\n', '\t', '\r', '$']) {
+ write_nix_str(k, out);
} else {
out.push_str(k);
}
+}
+
+fn write_nix_obj_key_buf(k: &str, v: &Value, out: &mut String) {
+ write_identifier(k, out);
match v {
Value::Object(o) if o.len() == 1 => {
let (k, v) = o.iter().next().unwrap();
- out.push_str(".");
+ out.push('.');
write_nix_obj_key_buf(k, v, out);
}
v => {
out.push_str(" = ");
write_nix_buf(v, out);
- out.push_str(";");
+ out.push(';');
}
}
}
flake.lockdiffbeforeafterboth--- a/flake.lock
+++ b/flake.lock
@@ -38,11 +38,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1696884899,
- "narHash": "sha256-SZILkoh8KZxjvFHO3yzOUw7n1Mf9WqMdUqoxf8eKPM4=",
+ "lastModified": 1698350982,
+ "narHash": "sha256-zoEV8Ad3bOAejp0ys/mOpaHSWrzK+GupZwGGYfuWuEY=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "ba10489eae3b2b2f665947b516e7043594a235c8",
+ "rev": "dd83f9de26ff7c0326468b659ea4729fa5cf6262",
"type": "github"
},
"original": {
@@ -67,11 +67,11 @@
]
},
"locked": {
- "lastModified": 1696817516,
- "narHash": "sha256-Xt9OY4Wnk9/vuUfA0OHFtmSlaen5GyiS9msgwOz3okI=",
+ "lastModified": 1698199907,
+ "narHash": "sha256-n8RtHBIb0rLuYs4RDehW6mj6r6Yam/ODY1af/VCcurw=",
"owner": "oxalica",
"repo": "rust-overlay",
- "rev": "c0df7f2a856b5ff27a3ce314f6d7aacf5fda546f",
+ "rev": "22b8d29fd22cfaa2c311e0d6fd8a0ed9c2a1152b",
"type": "github"
},
"original": {
flake.nixdiffbeforeafterboth--- a/flake.nix
+++ b/flake.nix
@@ -15,7 +15,7 @@
inherit system; overlays = [ (import rust-overlay) ];
};
llvmPkgs = pkgs.buildPackages.llvmPackages_11;
- rust = (pkgs.rustChannelOf { date = "2023-10-05"; channel = "nightly"; }).default.override { extensions = [ "rust-src" "rust-analyzer" ]; };
+ rust = (pkgs.rustChannelOf { date = "2023-10-20"; channel = "nightly"; }).default.override { extensions = [ "rust-src" "rust-analyzer" ]; };
rustPlatform = pkgs.makeRustPlatform { cargo = rust; rustc = rust; };
in
{
@@ -29,6 +29,7 @@
pkg-config
openssl
+ bacon
];
};
});
modules/fleet/secrets.nixdiffbeforeafterboth--- a/modules/fleet/secrets.nix
+++ b/modules/fleet/secrets.nix
@@ -55,6 +55,14 @@
description = "Time in hours, in which this secret should be regenerated";
default = null;
};
+ createdAt = mkOption {
+ type = nullOr str;
+ default = null;
+ };
+ expiresAt = mkOption {
+ type = nullOr str;
+ default = null;
+ };
owners = mkOption {
type = listOf str;
@@ -82,23 +90,24 @@
};
hostSecret = with types; {
options = {
- generator = mkOption {
- type = package;
- description = "Derivation to execute for secret generation";
+ createdAt = mkOption {
+ type = nullOr str;
+ default = null;
};
- expireIn = mkOption {
- type = nullOr int;
- description = "Time in hours, in which this secret should be regenerated";
+ expiresAt = mkOption {
+ type = nullOr str;
default = null;
};
public = mkOption {
type = nullOr str;
- description = "Secret public data";
+ description = "Secret public data. Imported from fleet.nix";
default = null;
};
secret = mkOption {
- type = str;
- description = "Encrypted secret data";
+ type = nullOr str;
+ description = "Encrypted secret data. Imported from fleet.nix";
+ default = null;
+ internal = true;
};
};
};
@@ -113,7 +122,8 @@
hostSecrets = mkOption {
type = attrsOf (attrsOf (submodule hostSecret));
default = { };
- description = "Host secrets";
+ description = "Host secrets. Imported from fleet.nix";
+ internal = true;
};
};
config = {