From 9a0f60e81974b62238b4e61746b2b8e863dc1dc4 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Mon, 6 Oct 2025 13:11:03 +0200 Subject: [PATCH] add a TF http backend to store state, see #515 (#536) Signed-off-by: Kiara Grouwstra Reviewed-on: https://git.fediversity.eu/fediversity/fediversity/pulls/536 --- .../02-opentofu-sandboxed-init.patch | 14 +++++++ deployment/check/data-model-tf/default.nix | 34 +++++++++++++++- deployment/check/data-model-tf/nixosTest.nix | 19 ++++++++- deployment/data-model.nix | 3 +- deployment/flake-part.nix | 3 +- .../modules/terraform-backend/default.nix | 39 +++++++++++++++++++ .../modules/terraform-backend/package.nix | 32 +++++++++++++++ deployment/run/tf-setup.nix | 2 +- deployment/run/tf-single-host/main.tf | 11 ++++++ deployment/run/tf-single-host/run.sh | 2 +- deployment/run/tf-single-host/tf-env.nix | 2 +- deployment/run/tf-single-host/tf.nix | 30 +++++++++++--- npins/sources.json | 13 +++++++ 13 files changed, 187 insertions(+), 17 deletions(-) create mode 100644 deployment/check/data-model-tf/02-opentofu-sandboxed-init.patch create mode 100644 deployment/modules/terraform-backend/default.nix create mode 100644 deployment/modules/terraform-backend/package.nix diff --git a/deployment/check/data-model-tf/02-opentofu-sandboxed-init.patch b/deployment/check/data-model-tf/02-opentofu-sandboxed-init.patch new file mode 100644 index 00000000..125f9a38 --- /dev/null +++ b/deployment/check/data-model-tf/02-opentofu-sandboxed-init.patch @@ -0,0 +1,14 @@ +diff --git a/internal/command/init.go b/internal/command/init.go +index 4491590ea9..56241d93e9 100644 +--- a/internal/command/init.go ++++ b/internal/command/init.go +@@ -263,8 +263,7 @@ func (c *InitCommand) Run(args []string) int { + } + + if err := sMgr.RefreshState(); err != nil { +- c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err)) +- return 1 ++ c.Ui.Warn(fmt.Sprintf("Issue refreshing state: %s", err)) + } + + state = sMgr.State() diff --git a/deployment/check/data-model-tf/default.nix b/deployment/check/data-model-tf/default.nix index 1815f19a..63a1c9dd 100644 --- a/deployment/check/data-model-tf/default.nix +++ b/deployment/check/data-model-tf/default.nix @@ -1,10 +1,40 @@ { - runNixOSTest, inputs, sources, + system, }: -runNixOSTest { +let + overlay = _: prev: { + terraform-backend = prev.callPackage ../../modules/terraform-backend/package.nix { }; + # FIXME centralize overlays + # XXX using recent revision for https://github.com/NixOS/nixpkgs/pull/447849 + opentofu = + (pkgs.callPackage "${sources.nixpkgs-unstable}/pkgs/by-name/op/opentofu/package.nix" { }) + .overrideAttrs + (old: rec { + patches = (old.patches or [ ]) ++ [ + # TF with back-end poses a problem for nix: initialization involves both + # mutation (nix: only inside build) and a network call (nix: not inside build) + ../../check/data-model-tf/02-opentofu-sandboxed-init.patch + ]; + # versions > 1.9.0 need go 1.24+ + version = "1.9.0"; + src = pkgs.fetchFromGitHub { + owner = "opentofu"; + repo = "opentofu"; + tag = "v${version}"; + hash = "sha256-e0ZzbQdex0DD7Bj9WpcVI5roh0cMbJuNr5nsSVaOSu4="; + }; + vendorHash = "sha256-fMTbLSeW+pw6GK8/JLZzG2ER90ss2g1FSDX5+f292do="; + }); + }; + pkgs = import sources.nixpkgs { + inherit system; + overlays = [ overlay ]; + }; +in +pkgs.testers.runNixOSTest { imports = [ ../../data-model.nix ../../function.nix diff --git a/deployment/check/data-model-tf/nixosTest.nix b/deployment/check/data-model-tf/nixosTest.nix index 32f281a6..c9fc5560 100644 --- a/deployment/check/data-model-tf/nixosTest.nix +++ b/deployment/check/data-model-tf/nixosTest.nix @@ -1,14 +1,15 @@ { lib, pkgs, + sources, ... }: let inherit (pkgs) system; + inherit (import ./constants.nix) pathToRoot; nodeName = "target"; deployment-config = { - inherit nodeName; - inherit (import ./constants.nix) pathToRoot; + inherit nodeName pathToRoot; targetSystem = system; sshOpts = [ ]; }; @@ -33,8 +34,13 @@ in nodes.deployer = { ... }: { + imports = [ + ../../modules/terraform-backend + ]; + environment.systemPackages = [ deploy + (pkgs.callPackage ../../run/tf-single-host/tf.nix { inherit sources; }) ]; # needed only when building from deployer @@ -45,6 +51,13 @@ in hello ]; }; + services.terraform-backend = { + enable = true; + settings = { + LISTEN_ADDR = ":8080"; + KMS_KEY = "l99yC7MhbuuraACQ8bjaU1rMrT6L4PXEYupX6BzhJvY="; + }; + }; }; extraTestScript = '' @@ -52,6 +65,8 @@ in target.fail("hello 1>&2") with subtest("Run the deployment"): + deployer.wait_for_unit("multi-user.target") + deployer.succeed("curl -u basic:fake-secret -X GET http://localhost:8080/state/project1/example") output = deployer.fail(""" ${lib.getExe deploy} """) diff --git a/deployment/data-model.nix b/deployment/data-model.nix index 4118994f..7f59f594 100644 --- a/deployment/data-model.nix +++ b/deployment/data-model.nix @@ -3,7 +3,6 @@ config, inputs, pkgs, - sources ? import ../npins, ... }: let @@ -266,7 +265,7 @@ let pkgs.writers.writeBashBin "deploy-tf.sh" (withPackages [ pkgs.jq - (pkgs.callPackage ./run/tf-single-host/tf.nix { inherit sources; }) + (pkgs.callPackage ./run/tf-single-host/tf.nix { }) ]) '' env ${toString (lib.mapAttrsToList (k: v: "TF_VAR_${k}=\"${toBash v}\"") environment)} \ diff --git a/deployment/flake-part.nix b/deployment/flake-part.nix index 024ab86d..5d058499 100644 --- a/deployment/flake-part.nix +++ b/deployment/flake-part.nix @@ -38,8 +38,7 @@ }; deployment-model-tf = import ./check/data-model-tf { - inherit (pkgs.testers) runNixOSTest; - inherit inputs sources; + inherit inputs sources system; }; }; }; diff --git a/deployment/modules/terraform-backend/default.nix b/deployment/modules/terraform-backend/default.nix new file mode 100644 index 00000000..8493359c --- /dev/null +++ b/deployment/modules/terraform-backend/default.nix @@ -0,0 +1,39 @@ +{ + lib, + pkgs, + config, + ... +}: +let + cfg = config.services.terraform-backend; +in +{ + options.services.terraform-backend = { + enable = lib.mkEnableOption "Nimbolus Terraform HTTP back-end"; + package = lib.mkPackageOption pkgs "terraform-backend" { }; + settings = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + default = { }; + description = '' + [Environment variables](https://github.com/nimbolus/terraform-backend#default-settings) + for the Terraform HTTP back-end. + ''; + }; + }; + config = lib.mkIf cfg.enable { + systemd.services.terraform-backend = { + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "exec"; + DynamicUser = true; + ExecStart = lib.getExe cfg.package; + Environment = lib.mapAttrsToList (k: v: "${k}=${v}") cfg.settings; + + # FIXME remove after switching away from file storage? + StateDirectory = "terraform-backend"; + WorkingDirectory = "/var/lib/terraform-backend"; + StateDirectoryMode = "0700"; + }; + }; + }; +} diff --git a/deployment/modules/terraform-backend/package.nix b/deployment/modules/terraform-backend/package.nix new file mode 100644 index 00000000..58d73d92 --- /dev/null +++ b/deployment/modules/terraform-backend/package.nix @@ -0,0 +1,32 @@ +{ + lib, + buildGoModule, + fetchFromGitHub, +}: + +# FIXME upstream: https://github.com/NixOS/nixpkgs/pull/447753 +buildGoModule rec { + pname = "terraform-backend"; + version = "0.1.3"; + + src = fetchFromGitHub { + owner = "nimbolus"; + repo = "terraform-backend"; + tag = "v${version}"; + hash = "sha256-S3ih7dLSQs3xJMHyQyWy43OG1maizBPVT8IsrWcSRUM="; + }; + + vendorHash = "sha256-5L8MNhjEPI3OOmtHdkB9ZQp02d7nzPp5h0/gVHTiCws="; + + ldflags = [ + "-s" + "-w" + ]; + + meta = { + description = "State backend server which implements the Terraform HTTP backend API with pluggable modules for authentication, storage, locking and state encryption"; + homepage = "https://github.com/nimbolus/terraform-backend"; + license = lib.licenses.bsd3; + mainProgram = "cmd"; + }; +} diff --git a/deployment/run/tf-setup.nix b/deployment/run/tf-setup.nix index 4166812e..5c94e6d0 100644 --- a/deployment/run/tf-setup.nix +++ b/deployment/run/tf-setup.nix @@ -4,7 +4,7 @@ sources, }: pkgs.writeScriptBin "setup" '' - set -xe + set -e # calculated pins echo '${lib.strings.toJSON sources}' > ./.npins.json # generate TF lock for nix's TF providers diff --git a/deployment/run/tf-single-host/main.tf b/deployment/run/tf-single-host/main.tf index be76b19f..05bebc52 100644 --- a/deployment/run/tf-single-host/main.tf +++ b/deployment/run/tf-single-host/main.tf @@ -1,3 +1,14 @@ +terraform { + # TODO un-hardcode + backend "http" { + username = "basic" + password = "fake-secret" + address = "http://localhost:8080/state/project1/example" + lock_address = "http://localhost:8080/state/project1/example" + unlock_address = "http://localhost:8080/state/project1/example" + } +} + # hash of our code directory, used to trigger re-deploy # FIXME calculate separately to reduce false positives data "external" "hash" { diff --git a/deployment/run/tf-single-host/run.sh b/deployment/run/tf-single-host/run.sh index 203466b9..c82b85b5 100644 --- a/deployment/run/tf-single-host/run.sh +++ b/deployment/run/tf-single-host/run.sh @@ -6,4 +6,4 @@ export TF_LOG=info cd "${tf_env}/deployment/run/tf-single-host" # parallelism=1: limit OOM risk -tofu apply --auto-approve -lock=false -parallelism=1 +tofu apply --auto-approve -parallelism=1 diff --git a/deployment/run/tf-single-host/tf-env.nix b/deployment/run/tf-single-host/tf-env.nix index 8f3e707b..68ad111e 100644 --- a/deployment/run/tf-single-host/tf-env.nix +++ b/deployment/run/tf-single-host/tf-env.nix @@ -13,7 +13,7 @@ pkgs.stdenv.mkDerivation { fileset = intersection (gitTracked ../../../.) ../../../.; }; buildInputs = [ - (pkgs.callPackage ./tf.nix { }) + (pkgs.callPackage ./tf.nix { inherit sources; }) (pkgs.callPackage ../tf-setup.nix { inherit sources; }) ]; buildPhase = '' diff --git a/deployment/run/tf-single-host/tf.nix b/deployment/run/tf-single-host/tf.nix index 8551cb82..b34b0be3 100644 --- a/deployment/run/tf-single-host/tf.nix +++ b/deployment/run/tf-single-host/tf.nix @@ -1,11 +1,29 @@ # FIXME: use overlays so this gets imported just once? { pkgs, + sources ? import ../../../npins, ... }: -let - tf = pkgs.opentofu; -in -tf.withPlugins (p: [ - p.external -]) +# FIXME centralize overlays +# XXX using recent revision for https://github.com/NixOS/nixpkgs/pull/447849 +( + (pkgs.callPackage "${sources.nixpkgs-unstable}/pkgs/by-name/op/opentofu/package.nix" { }) + .overrideAttrs + (old: rec { + patches = (old.patches or [ ]) ++ [ + # TF with back-end poses a problem for nix: initialization involves both + # mutation (nix: only inside build) and a network call (nix: not inside build) + ../../check/data-model-tf/02-opentofu-sandboxed-init.patch + ]; + # versions > 1.9.0 need go 1.24+ + version = "1.9.0"; + src = pkgs.fetchFromGitHub { + owner = "opentofu"; + repo = "opentofu"; + tag = "v${version}"; + hash = "sha256-e0ZzbQdex0DD7Bj9WpcVI5roh0cMbJuNr5nsSVaOSu4="; + }; + vendorHash = "sha256-fMTbLSeW+pw6GK8/JLZzG2ER90ss2g1FSDX5+f292do="; + }) +).withPlugins + (p: [ p.external ]) diff --git a/npins/sources.json b/npins/sources.json index 352ccbb5..c23f4a34 100644 --- a/npins/sources.json +++ b/npins/sources.json @@ -180,6 +180,19 @@ "url": "https://github.com/nixos/nixpkgs/archive/a1ae8ef72f64a845ecce5c6dcf65d546bf7deeb4.tar.gz", "hash": "0d7lp30wyy5647gpm8rnihvdcpmgmfr9c5yg4fhl31lsg8mlbg16" }, + "nixpkgs-unstable": { + "type": "Git", + "repository": { + "type": "GitHub", + "owner": "nixos", + "repo": "nixpkgs" + }, + "branch": "nixpkgs-unstable", + "submodules": false, + "revision": "d7f52a7a640bc54c7bb414cca603835bf8dd4b10", + "url": "https://github.com/nixos/nixpkgs/archive/d7f52a7a640bc54c7bb414cca603835bf8dd4b10.tar.gz", + "hash": "0c9kjncpmbdx6gwww9fn81hyr3bngi4hg51g4n2q4808c321kf4j" + }, "proxmox-nixos": { "type": "Git", "repository": {