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.adocdiffbeforeafterboth--- a/README.adoc
+++ b/README.adoc
@@ -6,3 +6,4 @@
- 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.rsdiffbeforeafterboth1//! Calling nix eval for everything is slow, it is not easy to link nix evaluator itself,2//! and tvix-nix doesn't have proper flake support. Fleets solution: automating nix repl calls.3//!4//! Api is synchronous, yet it is good enough with pooling, and in environment without IFDs for using5//! those blocking calls from async code.67use std::borrow::Cow;8use std::sync::{Arc, Mutex};9use std::time::Instant;1011use anyhow::{anyhow, bail, ensure, Context, Result};12use itertools::Itertools;13use r2d2::PooledConnection;14use rexpect::session::{PtyReplSession, PtySession};15use serde::de::DeserializeOwned;16use std::ffi::OsString;17use tracing::info_span;1819fn parse_error(res: &str) -> Option<String> {20 let res = if let Some(v) = res.strip_prefix("error: ") {21 if let Some((first_line, next)) = v.split_once('\n') {22 format!("{first_line}\n{}", unindent::unindent(next))23 } else {24 v.trim_start().to_owned()25 }26 } else if let Some(v) = res.strip_prefix("error:\n") {27 let mut v = v.to_owned();28 v.insert(0, '\n');29 unindent::unindent(&v).trim_start().to_owned()30 } else {31 return None;32 };33 let res = res.trim_end();34 Some(35 res.replace('Â', "")36 .split('\n')37 .map(|l| l.strip_prefix("â\u{80}¦ ").unwrap_or(l))38 .join("\n"),39 )40}41pub struct NixSessionPool {42 pub flake: OsString,43 pub nix_args: Vec<OsString>,44}4546#[derive(Debug)]47pub struct NixPoolError(anyhow::Error);48impl From<anyhow::Error> for NixPoolError {49 fn from(value: anyhow::Error) -> Self {50 Self(value)51 }52}53impl std::error::Error for NixPoolError {}54impl std::fmt::Display for NixPoolError {55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {56 self.0.fmt(f)57 }58}5960impl r2d2::ManageConnection for NixSessionPool {61 type Connection = NixSession;62 type Error = NixPoolError;6364 fn connect(&self) -> std::result::Result<Self::Connection, Self::Error> {65 Ok(NixSession::new(&self.flake, &self.nix_args, None)?)66 }6768 fn is_valid(&self, conn: &mut Self::Connection) -> std::result::Result<(), Self::Error> {69 let res = conn.expression_result("2 + 2")?;70 if res != "4" {71 return Err(anyhow!("basic expression failed").into());72 }73 Ok(())74 }7576 fn has_broken(&self, conn: &mut Self::Connection) -> bool {77 conn.finished78 }79}8081pub struct NixSession {82 session: PtyReplSession,83 next_id: u32,84 free_list: Vec<u32>,85 finished: bool,86}87impl NixSession {88 fn new(flake: &OsString, args: &[OsString], timeout: Option<u64>) -> Result<Self> {89 let mut cmd = std::process::Command::new("nix");90 cmd.arg("repl");91 cmd.arg(flake);92 for arg in args {93 cmd.arg(arg);94 }95 cmd.env("TERM", "dumb");96 cmd.env("NO_COLOR", "1");97 let pty_session = rexpect::session::spawn_command(cmd, timeout)?;98 let mut repl = PtyReplSession {99 prompt: "nix-repl> ".to_string(),100 pty_session,101 quit_command: Some(":q".to_string()),102 echo_on: true,103 };104 repl.wait_for_prompt()?;105 Ok(Self {106 session: repl,107 next_id: 0,108 free_list: vec![],109 finished: false,110 })111 }112 fn expression_result(&mut self, cmd: &str) -> Result<String> {113 dbg!(cmd);114 self.session.send_line(cmd)?;115 dbg!("waiting");116 let result = self.session.wait_for_prompt()?;117 let result = strip_ansi_escapes::strip_str(&result);118 let result = result.trim();119 dbg!(result);120 Ok(result.to_owned())121 }122 fn json_result<V: DeserializeOwned>(&mut self, cmd: &str) -> Result<V> {123 let v = match self.expression_result(&format!("builtins.toJSON ({cmd})")) {124 Ok(v) => {125 if let Some(e) = parse_error(&v) {126 bail!("{e}")127 }128 v129 }130 Err(e) => {131 self.finished = true;132 bail!("{e}")133 }134 };135 // Remove outer quoting136 let v: String = serde_json::from_str(&v)?;137 Ok(serde_json::from_str(&v)?)138 }139 /// Id should be immediately used140 fn allocate_id(&mut self) -> u32 {141 if let Some(free) = self.free_list.pop() {142 free143 } else {144 let v = self.next_id;145 self.next_id += 1;146 v147 }148 }149 fn allocate_result(&mut self, cmd: &str) -> Result<u32> {150 let id = self.allocate_id();151 match self.expression_result(&format!("sess_field_{id} = ({cmd})")) {152 Ok(v) => {153 if let Some(e) = parse_error(&v) {154 self.free_list.push(id);155 bail!("{e}")156 }157 }158 Err(e) => {159 self.finished = true;160 }161 }162163 Ok(id)164 }165 /// Nix has no way to deallocate variable, yet GC will correct everything not reachable.166 fn free_id(&mut self, id: u32) {167 if let Err(e) = self.expression_result(&format!("sess_field_{id} = null")) {168 self.finished = true;169 } else {170 self.free_list.push(id)171 }172 }173}174175#[derive(Clone, Debug)]176enum Index {177 String(String),178 Idx(u32),179}180181pub struct Field {182 full_path: Vec<Index>,183 session: Arc<Mutex<PooledConnection<NixSessionPool>>>,184 value: Option<u32>,185}186impl Field {187 pub fn root(conn: PooledConnection<NixSessionPool>) -> Self {188 Self {189 full_path: vec![],190 session: Arc::new(Mutex::new(conn)),191 value: None,192 }193 }194 pub fn get_field_deep<'a>(&self, name: impl IntoIterator<Item = &'a str>) -> Result<Self> {195 let mut iter = name.into_iter();196197 let mut full_path = self.full_path.clone();198 let mut query = if let Some(id) = self.value {199 format!("sess_field_{id}")200 } else {201 let first = iter.next().expect("name not empty");202 ensure!(203 !(first.contains('.') | first.contains(' ')),204 "bad name for root query: {first}"205 );206 full_path.push(Index::String(first.to_string()));207 first.to_string()208 };209 for v in iter {210 full_path.push(Index::String(v.to_string()));211 // Escape212 let escaped = nixlike::serialize(v)?;213 let escaped = escaped.trim();214 query.push('.');215 query.push_str(escaped);216 }217218 let vid = self219 .session220 .lock()221 .unwrap()222 .allocate_result(&query)223 .with_context(|| format!("full path: {:?}", full_path))?;224 Ok(Self {225 full_path,226 session: self.session.clone(),227 value: Some(vid),228 })229 }230 pub fn get_field<'a>(&self, name: &str) -> Result<Self> {231 self.get_field_deep([name])232 }233 pub fn as_json<V: DeserializeOwned>(&self) -> Result<V> {234 let id = self.value.expect("can't serialize root field");235 self.session236 .lock()237 .unwrap()238 .json_result(&format!("sess_field_{id}"))239 .with_context(|| format!("full path: {:?}", self.full_path))240 }241 pub fn list_fields(&self) -> Result<Vec<String>> {242 let id = self.value.expect("can't list root fields");243 self.session244 .lock()245 .unwrap()246 .json_result(&format!("builtins.attrNames sess_field_{id}"))247 .with_context(|| format!("full path: {:?}", self.full_path))248 }249}250impl Drop for Field {251 fn drop(&mut self) {252 if let Some(id) = self.value {253 self.session.lock().unwrap().free_id(id)254 }255 }256}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 = {