git.delta.rocks / jrsonnet / refs/commits / 213ad7de4b85

difftreelog

feat experimental opentofu integration

Yaroslav Bolyukin2024-08-18parent: #505f82e.patch.diff
in: trunk

9 files changed

modifiedCargo.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",
modifiedcmds/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;
addedcmds/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(())
+	}
+}
modifiedcmds/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?
modifiedcrates/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"
modifiedcrates/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)?)
 	}
modifiedflake.nixdiffbeforeafterboth
before · flake.nix
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        # 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}
modifiedlib/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
addedmodules/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;
+  };
+}