difftreelog
feat experimental opentofu integration
in: trunk
9 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1438,6 +1438,7 @@
"itertools",
"nixlike",
"r2d2",
+ "regex",
"serde",
"serde_json",
"thiserror",
@@ -1866,9 +1867,9 @@
[[package]]
name = "regex"
-version = "1.10.4"
+version = "1.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
dependencies = [
"aho-corasick",
"memchr",
cmds/fleet/src/cmds/mod.rsdiffbeforeafterboth--- a/cmds/fleet/src/cmds/mod.rs
+++ b/cmds/fleet/src/cmds/mod.rs
@@ -2,3 +2,4 @@
pub mod complete;
pub mod info;
pub mod secrets;
+pub mod tf;
cmds/fleet/src/cmds/tf.rsdiffbeforeafterboth--- /dev/null
+++ b/cmds/fleet/src/cmds/tf.rs
@@ -0,0 +1,23 @@
+use anyhow::Result;
+use clap::Parser;
+use nix_eval::nix_go_json;
+use serde_json::Value;
+use tokio::fs::write;
+use tracing::info;
+
+use crate::host::Config;
+
+#[derive(Parser)]
+pub struct Tf;
+impl Tf {
+ pub async fn run(&self, config: &Config) -> Result<()> {
+ let system = &config.local_system;
+ let config = &config.config_field;
+ let data: Value = nix_go_json!(config.tf({ system }).config);
+ let str = serde_json::to_string_pretty(&data)?;
+
+ write("fleet.tf.json", str.as_bytes()).await?;
+
+ Ok(())
+ }
+}
cmds/fleet/src/main.rsdiffbeforeafterboth--- a/cmds/fleet/src/main.rs
+++ b/cmds/fleet/src/main.rs
@@ -19,6 +19,7 @@
complete::Complete,
info::Info,
secrets::Secret,
+ tf::Tf,
};
use futures::{future::LocalBoxFuture, stream::FuturesUnordered, TryStreamExt};
use host::{Config, FleetOpts};
@@ -86,6 +87,8 @@
/// Command completions
#[clap(hide(true))]
Complete(Complete),
+ /// Compile and evaluate terranix configuration
+ Tf(Tf),
}
#[derive(Parser)]
@@ -104,6 +107,7 @@
Opts::Secret(s) => s.run(config).await?,
Opts::Info(i) => i.run(config).await?,
Opts::Prefetch(p) => p.run(config).await?,
+ Opts::Tf(t) => t.run(config).await?,
// TODO: actually parse commands before starting the async runtime
Opts::Complete(c) => {
tokio::task::spawn_blocking(move || c.run(RootOpts::command())).await?
crates/nix-eval/Cargo.tomldiffbeforeafterboth--- a/crates/nix-eval/Cargo.toml
+++ b/crates/nix-eval/Cargo.toml
@@ -11,6 +11,7 @@
itertools = "0.13.0"
nixlike.workspace = true
r2d2 = "0.8.10"
+regex = "1.10.6"
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
thiserror = "1.0.61"
crates/nix-eval/src/session.rsdiffbeforeafterboth--- a/crates/nix-eval/src/session.rs
+++ b/crates/nix-eval/src/session.rs
@@ -12,7 +12,7 @@
sync::{mpsc, oneshot, Mutex},
};
use tokio_util::codec::{FramedRead, LinesCodec};
-use tracing::{debug, error, warn, Level};
+use tracing::{debug, error, info, warn, Level};
#[derive(Error, Debug)]
pub enum Error {
@@ -327,8 +327,16 @@
n.parse::<u64>().map_err(Error::Int)
}
async fn execute_expression_string(&mut self, expr: impl AsRef<[u8]>) -> Result<String> {
+ // builtins.toJSON escapes some thing in incorrect way, e.g escaped "$" in "\${" is being outputed as "\$",
+ // while this escape should be removed as it is intended for nix itself, not for json output.
+ //
+ // This regex only allows \$ in the beginning of the string, it is easier to implement correctly.
+ // TODO: Add peg parser for nix-produced JSON?..
+ let regex = regex::Regex::new(r#"(?<prefix>[: {,\[]\\")\\\$"#).expect("fixup json");
+
let num = self.string_wrapping.clone();
let n = self.execute_expression_wrapping(expr, &num).await?;
+ let n = regex.replace_all(&n, "$prefix$$");
let str: String = serde_json::from_str(&n)?;
Ok(str)
}
@@ -339,6 +347,7 @@
let mut fexpr = b"builtins.toJSON (".to_vec();
fexpr.extend_from_slice(expr.as_ref());
fexpr.push(b')');
+ let s = String::from_utf8_lossy(expr.as_ref());
let v = self.execute_expression_string(fexpr).await?;
Ok(serde_json::from_str(&v)?)
}
flake.nixdiffbeforeafterboth1{2 description = "NixOS configuration management";34 inputs = {5 nixpkgs.url = "github:nixos/nixpkgs/master";6 rust-overlay = {7 url = "github:oxalica/rust-overlay";8 inputs = {9 nixpkgs.follows = "nixpkgs";10 };11 };12 flake-parts.url = "github:hercules-ci/flake-parts";13 crane = {14 url = "github:ipetkov/crane";15 inputs.nixpkgs.follows = "nixpkgs";16 };17 };18 outputs = inputs @ {19 self,20 flake-parts,21 crane,22 ...23 }:24 flake-parts.lib.mkFlake {25 inherit inputs;26 } {27 flake = rec {28 lib =29 (import ./lib {30 inherit (inputs.nixpkgs) lib;31 })32 // {33 fleetConfiguration = throw "function-based interface is deprecated, use flake-parts syntax instead";34 };35 flakeModules.default = import ./lib/flakePart.nix {36 inherit crane;37 };38 flakeModule = flakeModules.default;3940 # To be used with https://github.com/NixOS/nix/pull/889241 schemas = let42 inherit (inputs.nixpkgs.lib) mapAttrs;43 in {44 fleetConfigurations = {45 version = 1;46 doc = ''47 The `fleetConfigurations` flake output defines fleet cluster configurations.48 '';49 inventory = output: {50 children =51 mapAttrs (configName: cluster: {52 what = "fleet cluster configuration";5354 children =55 mapAttrs (hostName: host: {56 what = "host [${host.system}]";57 })58 cluster.config.hosts;59 # It is possible to implement this inventory right now, but I want to60 # get rid of `fleet.nix` file in the future.61 # children.secrets = { };62 })63 output;64 };65 };66 };67 };68 # Supported and tested list of deployment targets.69 systems = ["x86_64-linux" "aarch64-linux" "armv7l-linux" "armv6l-linux"];70 perSystem = {71 config,72 system,73 pkgs,74 ...75 }: let76 inherit (lib.attrSets) mapAttrs';77 inherit (lib.lists) elem;78 # Can also be built for darwin, through it is not usual to deploy nixos systems from macos machines.79 # I have no hardware for such testing, thus only adding machines I actually have and use.80 #81 # It is not possible to deploy any host from armv6/armv7 hardware, and I don't think it even makes sense.82 deployerSystems = ["aarch64-linux" "x86_64-linux"];83 deployerSystem = elem system deployerSystems;84 lib = pkgs.lib;85 rust = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;86 craneLib = (crane.mkLib pkgs).overrideToolchain rust;87 in {88 _module.args.pkgs = import inputs.nixpkgs {89 inherit system;90 overlays = [(inputs.rust-overlay.overlays.default)];91 };92 # Reference fleet package should be built with nightly rust, specified in rust-toolchain.toml.93 packages = lib.mkIf deployerSystem (let94 packages = pkgs.callPackages ./pkgs {95 inherit craneLib;96 };97 in98 packages // {default = packages.fleet;});99 # TODO: It should be possible to move lib.mkIf to default attribute, instead of disabling the whole100 # devShells block, yet nix flake check fails here, due to no default shell found. It is nix or flake-parts bug?101 devShells = lib.mkIf deployerSystem {102 default = craneLib.devShell {103 packages = with pkgs; [104 rust105 alejandra106 cargo-edit107 cargo-udeps108 cargo-fuzz109 cargo-watch110 cargo-outdated111112 pkg-config113 openssl114 bacon115 nil116 ];117 };118 };119 # fleet-install-secrets will not be built normally, because they are not ran directly by user most of the time.120 # checks there build packages for default nixpkgs rustPlatform packages.121 checks = let122 packages = pkgs.callPackages ./pkgs {};123 prefixAttrs = prefix: attrs:124 mapAttrs' (name: value: {125 name = "${prefix}${name}";126 value = value.overrideAttrs (prev: {127 pname = "${prefix}${prev.pname}";128 });129 })130 attrs;131 in132 # `fleet` crate wants nightly rust, also little sense of supporting it on stable nixpkgs.133 (prefixAttrs "nixpkgs-" (removeAttrs packages ["fleet"]));134 formatter = pkgs.alejandra;135 };136 };137}1{2 description = "NixOS configuration management";34 inputs = {5 nixpkgs.url = "github:nixos/nixpkgs/master";6 rust-overlay = {7 url = "github:oxalica/rust-overlay";8 inputs = {9 nixpkgs.follows = "nixpkgs";10 };11 };12 flake-parts.url = "github:hercules-ci/flake-parts";13 crane = {14 url = "github:ipetkov/crane";15 inputs.nixpkgs.follows = "nixpkgs";16 };17 };18 outputs = inputs @ {19 self,20 flake-parts,21 crane,22 ...23 }:24 flake-parts.lib.mkFlake {25 inherit inputs;26 } {27 flake = rec {28 lib =29 (import ./lib {30 inherit (inputs.nixpkgs) lib;31 })32 // {33 fleetConfiguration = throw "function-based interface is deprecated, use flake-parts syntax instead";34 };35 flakeModules.default = import ./lib/flakePart.nix {36 inherit crane;37 };38 flakeModule = flakeModules.default;3940 fleetModules.tf = ./modules/extras/tf.nix;4142 # To be used with https://github.com/NixOS/nix/pull/889243 schemas = let44 inherit (inputs.nixpkgs.lib) mapAttrs;45 in {46 fleetConfigurations = {47 version = 1;48 doc = ''49 The `fleetConfigurations` flake output defines fleet cluster configurations.50 '';51 inventory = output: {52 children =53 mapAttrs (configName: cluster: {54 what = "fleet cluster configuration";5556 children =57 mapAttrs (hostName: host: {58 what = "host [${host.system}]";59 })60 cluster.config.hosts;61 # It is possible to implement this inventory right now, but I want to62 # get rid of `fleet.nix` file in the future.63 # children.secrets = { };64 })65 output;66 };67 };68 };69 };70 # Supported and tested list of deployment targets.71 systems = ["x86_64-linux" "aarch64-linux" "armv7l-linux" "armv6l-linux"];72 perSystem = {73 config,74 system,75 pkgs,76 ...77 }: let78 inherit (lib.attrSets) mapAttrs';79 inherit (lib.lists) elem;80 # Can also be built for darwin, through it is not usual to deploy nixos systems from macos machines.81 # I have no hardware for such testing, thus only adding machines I actually have and use.82 #83 # It is not possible to deploy any host from armv6/armv7 hardware, and I don't think it even makes sense.84 deployerSystems = ["aarch64-linux" "x86_64-linux"];85 deployerSystem = elem system deployerSystems;86 lib = pkgs.lib;87 rust = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;88 craneLib = (crane.mkLib pkgs).overrideToolchain rust;89 in {90 _module.args.pkgs = import inputs.nixpkgs {91 inherit system;92 overlays = [(inputs.rust-overlay.overlays.default)];93 };94 # Reference fleet package should be built with nightly rust, specified in rust-toolchain.toml.95 packages = lib.mkIf deployerSystem (let96 packages = pkgs.callPackages ./pkgs {97 inherit craneLib;98 };99 in100 packages // {default = packages.fleet;});101 # TODO: It should be possible to move lib.mkIf to default attribute, instead of disabling the whole102 # devShells block, yet nix flake check fails here, due to no default shell found. It is nix or flake-parts bug?103 devShells = lib.mkIf deployerSystem {104 default = craneLib.devShell {105 packages = with pkgs; [106 rust107 alejandra108 cargo-edit109 cargo-udeps110 cargo-fuzz111 cargo-watch112 cargo-outdated113114 pkg-config115 openssl116 bacon117 nil118 ];119 };120 };121 # fleet-install-secrets will not be built normally, because they are not ran directly by user most of the time.122 # checks there build packages for default nixpkgs rustPlatform packages.123 checks = let124 packages = pkgs.callPackages ./pkgs {};125 prefixAttrs = prefix: attrs:126 mapAttrs' (name: value: {127 name = "${prefix}${name}";128 value = value.overrideAttrs (prev: {129 pname = "${prefix}${prev.pname}";130 });131 })132 attrs;133 in134 # `fleet` crate wants nightly rust, also little sense of supporting it on stable nixpkgs.135 (prefixAttrs "nixpkgs-" (removeAttrs packages ["fleet"]));136 formatter = pkgs.alejandra;137 };138 };139}lib/flakePart.nixdiffbeforeafterboth--- a/lib/flakePart.nix
+++ b/lib/flakePart.nix
@@ -2,6 +2,7 @@
fleetLib,
lib,
config,
+ inputs ? {},
...
}: let
inherit (lib.options) mkOption;
@@ -58,8 +59,11 @@
};
}
];
- specialArgs.fleetLib = import ../lib {
- inherit (bootstrapNixpkgs) lib;
+ specialArgs = {
+ fleetLib = import ../lib {
+ inherit (bootstrapNixpkgs) lib;
+ };
+ inputs = inputs;
};
};
in
modules/extras/tf.nixdiffbeforeafterboth--- /dev/null
+++ b/modules/extras/tf.nix
@@ -0,0 +1,26 @@
+{
+ config,
+ lib,
+ inputs,
+ ...
+}: let
+ inherit (lib) mkOption;
+ inherit (lib.types) deferredModule;
+in {
+ options.tf = mkOption {
+ type = deferredModule;
+ apply = module: system:
+ inputs.terranix.lib.terranixConfigurationAst {
+ inherit system;
+ pkgs = config.nixpkgs.buildUsing.legacyPackages.${system};
+ modules = [module];
+ };
+ };
+ config.tf.output.fleet = {
+ value = {
+ managed = true;
+ };
+ # Just to avoid printing this attribute on every apply.
+ sensitive = true;
+ };
+}