From 765183cd0d65b957954049499d0bc8c00b5580af Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Thu, 17 Jul 2025 19:02:14 +0200 Subject: [PATCH 01/76] fix typo in users (#475) Reviewed-on: https://git.fediversity.eu/Fediversity/Fediversity/pulls/475 Co-authored-by: Kiara Grouwstra Co-committed-by: Kiara Grouwstra --- infra/common/nixos/users.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infra/common/nixos/users.nix b/infra/common/nixos/users.nix index 5b7e7579..ddaf9691 100644 --- a/infra/common/nixos/users.nix +++ b/infra/common/nixos/users.nix @@ -6,7 +6,7 @@ _class = "nixos"; users.users = { - root.openssh.authorizedKeys.keys = config.user.users.procolix.openssh.authorizedKeys.keys; + root.openssh.authorizedKeys.keys = config.users.users.procolix.openssh.authorizedKeys.keys; procolix = { isNormalUser = true; -- 2.48.1 From e488230d7baff0a5a65fbd6a7446cb1898a5c2ed Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sat, 19 Jul 2025 13:00:33 +0200 Subject: [PATCH 02/76] updater: make npins command verbose (#477) Reviewed-on: https://git.fediversity.eu/Fediversity/Fediversity/pulls/477 Co-authored-by: Kiara Grouwstra Co-committed-by: Kiara Grouwstra --- .forgejo/workflows/update.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/update.yaml b/.forgejo/workflows/update.yaml index 7304fd7b..19dacf15 100644 --- a/.forgejo/workflows/update.yaml +++ b/.forgejo/workflows/update.yaml @@ -13,7 +13,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Update pins - run: nix-shell --run "npins update" + run: nix-shell --run "npins --verbose update" - name: Create PR uses: https://github.com/KiaraGrouwstra/gitea-create-pull-request@f9f80aa5134bc5c03c38f5aaa95053492885b397 with: -- 2.48.1 From 4509d277d324e67c54d13a04c7c8da8723639273 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Wed, 23 Jul 2025 18:12:55 +0200 Subject: [PATCH 03/76] move arguments from `_module.args` to `specialArgs` (#469) Reviewed-on: https://git.fediversity.eu/Fediversity/Fediversity/pulls/469 Reviewed-by: Valentin Gagarin Co-authored-by: Kiara Grouwstra Co-committed-by: Kiara Grouwstra --- infra/flake-part.nix | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/infra/flake-part.nix b/infra/flake-part.nix index e970d190..41a8d72d 100644 --- a/infra/flake-part.nix +++ b/infra/flake-part.nix @@ -23,21 +23,17 @@ let makeResourceModule = { vmName, isTestVm }: { - # TODO(@fricklerhandwerk): this is terrible but IMO we should just ditch flake-parts and have our own data model for how the project is organised internally - _module.args = { - inherit - inputs - keys - secrets - ; - }; - nixos.module.imports = [ ./common/proxmox-qemu-vm.nix ]; nixos.specialArgs = { - inherit sources; + inherit + sources + inputs + keys + secrets + ; }; imports = @@ -79,7 +75,13 @@ let # TODO(@fricklerhandwerk): we may want to pass through all of `specialArgs` # once we're sure it's sane. leaving it here for better control during refactoring. specialArgs = { - inherit sources; + inherit + sources + inputs + keys + secrets + + ; }; }); }; -- 2.48.1 From 1b66028f32638169d5192add24649cf7cdd60be3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Wed, 30 Jul 2025 12:31:03 +0200 Subject: [PATCH 04/76] Fix infra and add more tests (#478) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains a bunch of small fixes having to do with infra code. The goal is not to fix everything as that would require a full rewrite. Instead, we fix just what is necessary to get some testing going on. Once that is available, we will be able to work on a full refactor with more guarantees. Something of note is that most of the difficulty was to find code that would make both `nixops4 apply` _and_ `nix build .#nixosConfigurations.` happy. The takeaway is that the tests that we are adding now will not catch a whole class of tests having to do with how NixOps4 wires up the resources. Still, this is probably less significant as we are supposed to use NixOps4 every now and then. The commits should be read separately. Reviewed-on: https://git.fediversity.eu/Fediversity/Fediversity/pulls/478 Reviewed-by: kiara Grouwstra Co-authored-by: Nicolas “Niols” Jeannerod Co-committed-by: Nicolas “Niols” Jeannerod --- .forgejo/workflows/ci.yaml | 19 +++++++ infra/common/proxmox-qemu-vm.nix | 12 +++-- infra/common/resource.nix | 3 -- infra/flake-part.nix | 79 ++++++++++++------------------ machines/dev/fedi200/default.nix | 6 --- machines/dev/fedi201/default.nix | 1 - machines/dev/fedi201/fedipanel.nix | 2 - machines/dev/vm02116/default.nix | 1 - machines/dev/vm02187/default.nix | 1 - 9 files changed, 59 insertions(+), 65 deletions(-) diff --git a/.forgejo/workflows/ci.yaml b/.forgejo/workflows/ci.yaml index 7bc68bf5..3f553ef1 100644 --- a/.forgejo/workflows/ci.yaml +++ b/.forgejo/workflows/ci.yaml @@ -56,3 +56,22 @@ jobs: steps: - uses: actions/checkout@v4 - run: nix build .#checks.x86_64-linux.deployment-panel -L + + ## NOTE: NixOps4 does not provide a good “dry run” mode, so we instead check + ## proxies for resources, namely whether their `.#vmOptions.` and + ## `.#nixosConfigurations.` outputs evaluate and build correctly, and + ## whether we can dry run `infra/proxmox-*.sh` on them. This will not catch + ## everything, and in particular not issues in how NixOps4 wires up the + ## resources, but that is still something. + check-resources: + runs-on: native + steps: + - uses: actions/checkout@v4 + - run: | + set -euC + machines=$(nix eval --impure --raw --expr 'with builtins; toString (attrNames (getFlake (toString ./.)).nixosConfigurations)') + for machine in $machines; do + echo ==================== [ $machine ] ==================== + nix eval .#vmOptions.$machine + nix build .#nixosConfigurations.$machine.config.system.build.toplevel + done diff --git a/infra/common/proxmox-qemu-vm.nix b/infra/common/proxmox-qemu-vm.nix index 9176d0eb..6b4970b3 100644 --- a/infra/common/proxmox-qemu-vm.nix +++ b/infra/common/proxmox-qemu-vm.nix @@ -1,10 +1,14 @@ -{ sources, ... }: +{ ... }: + { _class = "nixos"; - imports = [ - "${sources.nixpkgs}/nixos/modules/profiles/qemu-guest.nix" - ]; + ## FIXME: It would be nice, but the following leads to infinite recursion + ## in the way we currently plug `sources` in. + ## + # imports = [ + # "${sources.nixpkgs}/nixos/modules/profiles/qemu-guest.nix" + # ]; boot = { initrd = { diff --git a/infra/common/resource.nix b/infra/common/resource.nix index 26b57c29..d50103da 100644 --- a/infra/common/resource.nix +++ b/infra/common/resource.nix @@ -2,7 +2,6 @@ inputs, lib, config, - sources, keys, secrets, ... @@ -33,8 +32,6 @@ in ## should go into the `./nixos` subdirectory. nixos.module = { imports = [ - "${sources.agenix}/modules/age.nix" - "${sources.disko}/module.nix" ./options.nix ./nixos ]; diff --git a/infra/flake-part.nix b/infra/flake-part.nix index 41a8d72d..cad2b1b3 100644 --- a/infra/flake-part.nix +++ b/infra/flake-part.nix @@ -23,19 +23,30 @@ let makeResourceModule = { vmName, isTestVm }: { - nixos.module.imports = [ - ./common/proxmox-qemu-vm.nix - ]; - - nixos.specialArgs = { + # TODO(@fricklerhandwerk): this is terrible but IMO we should just ditch flake-parts and have our own data model for how the project is organised internally + _module.args = { inherit - sources inputs keys secrets + sources ; }; + nixos.module.imports = [ + ## FIXME: It would be preferrable to have those `sources`-related + ## imports in the modules that use them. However, doing so triggers + ## infinite recursions because of the way we propagate `sources`. + ## `sources` must be propagated by means of `specialArgs`, but this + ## requires a bigger change. + "${sources.nixpkgs}/nixos/modules/profiles/qemu-guest.nix" + "${sources.agenix}/modules/age.nix" + "${sources.disko}/module.nix" + "${sources.home-manager}/nixos" + + ./common/proxmox-qemu-vm.nix + ]; + imports = [ ./common/resource.nix @@ -65,39 +76,17 @@ let vmNames: { providers, ... }: { - # XXX: this type merge is for adding `specialArgs` to resource modules - options.resources = mkOption { - type = - with lib.types; - lazyAttrsOf (submoduleWith { - class = "nixops4Resource"; - modules = [ ]; - # TODO(@fricklerhandwerk): we may want to pass through all of `specialArgs` - # once we're sure it's sane. leaving it here for better control during refactoring. - specialArgs = { - inherit - sources - inputs - keys - secrets - - ; - }; - }); - }; - config = { - providers.local = inputs.nixops4.modules.nixops4Provider.local; - resources = genAttrs vmNames (vmName: { - type = providers.local.exec; - imports = [ - inputs.nixops4-nixos.modules.nixops4Resource.nixos - (makeResourceModule { - inherit vmName; - isTestVm = false; - }) - ]; - }); - }; + providers.local = inputs.nixops4.modules.nixops4Provider.local; + resources = genAttrs vmNames (vmName: { + type = providers.local.exec; + imports = [ + inputs.nixops4-nixos.modules.nixops4Resource.nixos + (makeResourceModule { + inherit vmName; + isTestVm = false; + }) + ]; + }); }; makeDeployment' = vmName: makeDeployment [ vmName ]; @@ -139,7 +128,7 @@ let ## this is only needed to expose NixOS configurations for provisioning ## purposes, and eventually all of this should be handled by NixOps4. options = { - nixos.module = mkOption { }; # NOTE: not just `nixos` otherwise merging will go wrong + nixos.module = mkOption { type = lib.types.deferredModule; }; # NOTE: not just `nixos` otherwise merging will go wrong nixpkgs = mkOption { }; ssh = mkOption { }; }; @@ -157,13 +146,9 @@ let ## Given a VM name, make a NixOS configuration for this machine. makeConfiguration = isTestVm: vmName: - let - inherit (sources) nixpkgs; - in - import "${nixpkgs}/nixos" { - modules = [ - (makeResourceConfig { inherit vmName isTestVm; }).nixos.module - ]; + import "${sources.nixpkgs}/nixos" { + configuration = (makeResourceConfig { inherit vmName isTestVm; }).nixos.module; + system = "x86_64-linux"; }; makeVmOptions = isTestVm: vmName: { diff --git a/machines/dev/fedi200/default.nix b/machines/dev/fedi200/default.nix index c92c8d52..23ba6de8 100644 --- a/machines/dev/fedi200/default.nix +++ b/machines/dev/fedi200/default.nix @@ -16,10 +16,4 @@ gateway = "2a00:51c0:13:1305::1"; }; }; - - nixos.module = { - imports = [ - ../../../infra/common/proxmox-qemu-vm.nix - ]; - }; } diff --git a/machines/dev/fedi201/default.nix b/machines/dev/fedi201/default.nix index 00717597..bb5058b8 100644 --- a/machines/dev/fedi201/default.nix +++ b/machines/dev/fedi201/default.nix @@ -19,7 +19,6 @@ nixos.module = { imports = [ - ../../../infra/common/proxmox-qemu-vm.nix ./fedipanel.nix ]; }; diff --git a/machines/dev/fedi201/fedipanel.nix b/machines/dev/fedi201/fedipanel.nix index 96a826cf..494212de 100644 --- a/machines/dev/fedi201/fedipanel.nix +++ b/machines/dev/fedi201/fedipanel.nix @@ -1,6 +1,5 @@ { config, - sources, ... }: let @@ -11,7 +10,6 @@ in imports = [ (import ../../../panel { }).module - "${sources.home-manager}/nixos" ]; security.acme = { diff --git a/machines/dev/vm02116/default.nix b/machines/dev/vm02116/default.nix index 77253a7c..e9338028 100644 --- a/machines/dev/vm02116/default.nix +++ b/machines/dev/vm02116/default.nix @@ -14,7 +14,6 @@ { lib, ... }: { imports = [ - ../../../infra/common/proxmox-qemu-vm.nix ./forgejo.nix ]; diff --git a/machines/dev/vm02187/default.nix b/machines/dev/vm02187/default.nix index ab3e5d12..2f91d753 100644 --- a/machines/dev/vm02187/default.nix +++ b/machines/dev/vm02187/default.nix @@ -14,7 +14,6 @@ { lib, ... }: { imports = [ - ../../../infra/common/proxmox-qemu-vm.nix ./wiki.nix ]; -- 2.48.1 From be72b82875f91d82c8682e24f304f77337b78ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Wed, 30 Jul 2025 13:25:22 +0200 Subject: [PATCH 05/76] Link to upstreaming PR for `lib.types.fileset` (#487) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-on: https://git.fediversity.eu/Fediversity/Fediversity/pulls/487 Reviewed-by: kiara Grouwstra Co-authored-by: Nicolas “Niols” Jeannerod Co-committed-by: Nicolas “Niols” Jeannerod --- deployment/check/common/nixosTest.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deployment/check/common/nixosTest.nix b/deployment/check/common/nixosTest.nix index cb52ed9f..bc30edba 100644 --- a/deployment/check/common/nixosTest.nix +++ b/deployment/check/common/nixosTest.nix @@ -48,7 +48,8 @@ in extraTestScript = mkOption { }; sourceFileset = mkOption { - ## REVIEW: Upstream to nixpkgs? + ## FIXME: grab `lib.types.fileset` from NixOS, once upstreaming PR + ## https://github.com/NixOS/nixpkgs/pull/428293 lands. type = types.mkOptionType { name = "fileset"; description = "fileset"; -- 2.48.1 From df3a070fa40c7698547ccc7e8fdfc15a854d81d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Thu, 31 Jul 2025 00:49:00 +0200 Subject: [PATCH 06/76] Infra: get rid of `makeResourceModule` (#485) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-on: https://git.fediversity.eu/Fediversity/Fediversity/pulls/485 Reviewed-by: Valentin Gagarin Reviewed-by: kiara Grouwstra Co-authored-by: Nicolas “Niols” Jeannerod Co-committed-by: Nicolas “Niols” Jeannerod --- infra/common/resource.nix | 1 + infra/flake-part.nix | 121 ++++++++++++--------------- machines/dev/fedi200/default.nix | 1 + machines/dev/fedi201/default.nix | 1 + machines/dev/forgejo-ci/default.nix | 1 + machines/dev/vm02116/default.nix | 1 + machines/dev/vm02187/default.nix | 1 + machines/operator/test01/default.nix | 1 + machines/operator/test02/default.nix | 1 + machines/operator/test03/default.nix | 1 + machines/operator/test04/default.nix | 1 + machines/operator/test05/default.nix | 1 + machines/operator/test06/default.nix | 1 + machines/operator/test11/default.nix | 1 + machines/operator/test12/default.nix | 1 + machines/operator/test13/default.nix | 1 + machines/operator/test14/default.nix | 1 + 17 files changed, 68 insertions(+), 69 deletions(-) diff --git a/infra/common/resource.nix b/infra/common/resource.nix index d50103da..55aa64d4 100644 --- a/infra/common/resource.nix +++ b/infra/common/resource.nix @@ -34,6 +34,7 @@ in imports = [ ./options.nix ./nixos + ./proxmox-qemu-vm.nix ]; ## Inject the shared options from the resource's `config` into the NixOS diff --git a/infra/flake-part.nix b/infra/flake-part.nix index cad2b1b3..4b341ded 100644 --- a/infra/flake-part.nix +++ b/infra/flake-part.nix @@ -17,59 +17,35 @@ let ; inherit (lib.attrsets) genAttrs; - ## Given a machine's name and whether it is a test VM, make a resource module, - ## except for its missing provider. (Depending on the use of that resource, we - ## will provide a different one.) - makeResourceModule = - { vmName, isTestVm }: - { - # TODO(@fricklerhandwerk): this is terrible but IMO we should just ditch flake-parts and have our own data model for how the project is organised internally - _module.args = { - inherit - inputs - keys - secrets - sources - ; - }; - - nixos.module.imports = [ - ## FIXME: It would be preferrable to have those `sources`-related - ## imports in the modules that use them. However, doing so triggers - ## infinite recursions because of the way we propagate `sources`. - ## `sources` must be propagated by means of `specialArgs`, but this - ## requires a bigger change. - "${sources.nixpkgs}/nixos/modules/profiles/qemu-guest.nix" - "${sources.agenix}/modules/age.nix" - "${sources.disko}/module.nix" - "${sources.home-manager}/nixos" - - ./common/proxmox-qemu-vm.nix - ]; - - imports = - [ - ./common/resource.nix - ] - ++ ( - if isTestVm then - [ - ../machines/operator/${vmName} - { - nixos.module.users.users.root.openssh.authorizedKeys.keys = [ - # allow our panel vm access to the test machines - keys.panel - ]; - } - ] - else - [ - ../machines/dev/${vmName} - ] - ); - fediversityVm.name = vmName; + commonResourceModule = { + # TODO(@fricklerhandwerk): this is terrible but IMO we should just ditch + # flake-parts and have our own data model for how the project is organised + # internally + _module.args = { + inherit + inputs + keys + secrets + sources + ; }; + ## FIXME: It would be preferrable to have those `sources`-related imports in + ## the modules that use them. However, doing so triggers infinite recursions + ## because of the way we propagate `sources`. `sources` must be propagated by + ## means of `specialArgs`, but this requires a bigger change. + nixos.module.imports = [ + "${sources.nixpkgs}/nixos/modules/profiles/qemu-guest.nix" + "${sources.agenix}/modules/age.nix" + "${sources.disko}/module.nix" + "${sources.home-manager}/nixos" + ]; + + imports = [ + ./common/resource.nix + ]; + }; + ## Given a list of machine names, make a deployment with those machines' ## configurations as resources. makeDeployment = @@ -81,10 +57,8 @@ let type = providers.local.exec; imports = [ inputs.nixops4-nixos.modules.nixops4Resource.nixos - (makeResourceModule { - inherit vmName; - isTestVm = false; - }) + commonResourceModule + ../machines/dev/${vmName} ]; }); }; @@ -101,21 +75,29 @@ let fediversity = import ../services/fediversity; } { - garageConfigurationResource = makeResourceModule { - vmName = "test01"; - isTestVm = true; + garageConfigurationResource = { + imports = [ + commonResourceModule + ../machines/operator/test01 + ]; }; - mastodonConfigurationResource = makeResourceModule { - vmName = "test06"; # somehow `test02` has a problem - use test06 instead - isTestVm = true; + mastodonConfigurationResource = { + imports = [ + commonResourceModule + ../machines/operator/test06 # somehow `test02` has a problem - use test06 instead + ]; }; - peertubeConfigurationResource = makeResourceModule { - vmName = "test05"; - isTestVm = true; + peertubeConfigurationResource = { + imports = [ + commonResourceModule + ../machines/operator/test05 + ]; }; - pixelfedConfigurationResource = makeResourceModule { - vmName = "test04"; - isTestVm = true; + pixelfedConfigurationResource = { + imports = [ + commonResourceModule + ../machines/operator/test04 + ]; }; }; @@ -135,11 +117,12 @@ let }; makeResourceConfig = - vm: + { vmName, isTestVm }: (evalModules { modules = [ nixops4ResourceNixosMockOptions - (makeResourceModule vm) + commonResourceModule + (if isTestVm then ../machines/operator/${vmName} else ../machines/dev/${vmName}) ]; }).config; diff --git a/machines/dev/fedi200/default.nix b/machines/dev/fedi200/default.nix index 23ba6de8..6014da4f 100644 --- a/machines/dev/fedi200/default.nix +++ b/machines/dev/fedi200/default.nix @@ -2,6 +2,7 @@ _class = "nixops4Resource"; fediversityVm = { + name = "fedi200"; vmId = 200; proxmox = "fediversity"; description = "Testing machine for Hans"; diff --git a/machines/dev/fedi201/default.nix b/machines/dev/fedi201/default.nix index bb5058b8..3197b157 100644 --- a/machines/dev/fedi201/default.nix +++ b/machines/dev/fedi201/default.nix @@ -2,6 +2,7 @@ _class = "nixops4Resource"; fediversityVm = { + name = "fedi201"; vmId = 201; proxmox = "fediversity"; description = "FediPanel"; diff --git a/machines/dev/forgejo-ci/default.nix b/machines/dev/forgejo-ci/default.nix index 901f11c0..ee212b17 100644 --- a/machines/dev/forgejo-ci/default.nix +++ b/machines/dev/forgejo-ci/default.nix @@ -20,6 +20,7 @@ in ssh.host = mkForce "forgejo-ci"; fediversityVm = { + name = "forgejo-ci"; domain = "procolix.com"; ipv4 = { diff --git a/machines/dev/vm02116/default.nix b/machines/dev/vm02116/default.nix index e9338028..0ffd24ab 100644 --- a/machines/dev/vm02116/default.nix +++ b/machines/dev/vm02116/default.nix @@ -2,6 +2,7 @@ _class = "nixops4Resource"; fediversityVm = { + name = "vm02116"; vmId = 2116; proxmox = "procolix"; description = "Forgejo"; diff --git a/machines/dev/vm02187/default.nix b/machines/dev/vm02187/default.nix index 2f91d753..bc4e63f3 100644 --- a/machines/dev/vm02187/default.nix +++ b/machines/dev/vm02187/default.nix @@ -2,6 +2,7 @@ _class = "nixops4Resource"; fediversityVm = { + name = "vm02187"; vmId = 2187; proxmox = "procolix"; description = "Wiki"; diff --git a/machines/operator/test01/default.nix b/machines/operator/test01/default.nix index fd5dc710..d644b6fe 100644 --- a/machines/operator/test01/default.nix +++ b/machines/operator/test01/default.nix @@ -2,6 +2,7 @@ _class = "nixops4Resource"; fediversityVm = { + name = "test01"; vmId = 7001; proxmox = "fediversity"; diff --git a/machines/operator/test02/default.nix b/machines/operator/test02/default.nix index c7e8fc04..53385da7 100644 --- a/machines/operator/test02/default.nix +++ b/machines/operator/test02/default.nix @@ -2,6 +2,7 @@ _class = "nixops4Resource"; fediversityVm = { + name = "test02"; vmId = 7002; proxmox = "fediversity"; diff --git a/machines/operator/test03/default.nix b/machines/operator/test03/default.nix index 55b86f59..ebfa3efb 100644 --- a/machines/operator/test03/default.nix +++ b/machines/operator/test03/default.nix @@ -2,6 +2,7 @@ _class = "nixops4Resource"; fediversityVm = { + name = "test03"; vmId = 7003; proxmox = "fediversity"; diff --git a/machines/operator/test04/default.nix b/machines/operator/test04/default.nix index 78f9ee09..f234393f 100644 --- a/machines/operator/test04/default.nix +++ b/machines/operator/test04/default.nix @@ -2,6 +2,7 @@ _class = "nixops4Resource"; fediversityVm = { + name = "test04"; vmId = 7004; proxmox = "fediversity"; diff --git a/machines/operator/test05/default.nix b/machines/operator/test05/default.nix index 277c7067..de461d57 100644 --- a/machines/operator/test05/default.nix +++ b/machines/operator/test05/default.nix @@ -2,6 +2,7 @@ _class = "nixops4Resource"; fediversityVm = { + name = "test05"; vmId = 7005; proxmox = "fediversity"; diff --git a/machines/operator/test06/default.nix b/machines/operator/test06/default.nix index 42a40dc3..a43090de 100644 --- a/machines/operator/test06/default.nix +++ b/machines/operator/test06/default.nix @@ -2,6 +2,7 @@ _class = "nixops4Resource"; fediversityVm = { + name = "test06"; vmId = 7006; proxmox = "fediversity"; diff --git a/machines/operator/test11/default.nix b/machines/operator/test11/default.nix index fe955029..848192d2 100644 --- a/machines/operator/test11/default.nix +++ b/machines/operator/test11/default.nix @@ -2,6 +2,7 @@ _class = "nixops4Resource"; fediversityVm = { + name = "test11"; vmId = 7011; proxmox = "fediversity"; diff --git a/machines/operator/test12/default.nix b/machines/operator/test12/default.nix index cfed2f84..a33b24c3 100644 --- a/machines/operator/test12/default.nix +++ b/machines/operator/test12/default.nix @@ -2,6 +2,7 @@ _class = "nixops4Resource"; fediversityVm = { + name = "test12"; vmId = 7012; proxmox = "fediversity"; diff --git a/machines/operator/test13/default.nix b/machines/operator/test13/default.nix index 1d71b6b7..97cdfb6b 100644 --- a/machines/operator/test13/default.nix +++ b/machines/operator/test13/default.nix @@ -2,6 +2,7 @@ _class = "nixops4Resource"; fediversityVm = { + name = "test13"; vmId = 7013; proxmox = "fediversity"; diff --git a/machines/operator/test14/default.nix b/machines/operator/test14/default.nix index 6832b2c7..34b4c3c6 100644 --- a/machines/operator/test14/default.nix +++ b/machines/operator/test14/default.nix @@ -2,6 +2,7 @@ _class = "nixops4Resource"; fediversityVm = { + name = "test14"; vmId = 7014; proxmox = "fediversity"; -- 2.48.1 From 588bb77a947372a44425a788775cbf17c93aa08e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Thu, 31 Jul 2025 15:41:02 +0200 Subject: [PATCH 07/76] Infra: expose and use checks for vmOptions and nixosConfigurations (#488) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following https://git.fediversity.eu/Fediversity/Fediversity/pulls/478#issuecomment-9163, here is a PR that plugs the infra's `vmOptions` and `nixosConfigurations` outputs into flake checks, instead of calling random Nix commands from the CI. There is still a bit of magic in the CI, but that's because we don't have yet a Nix-aware CI that exposes one job per flake check. Reviewed-on: https://git.fediversity.eu/Fediversity/Fediversity/pulls/488 Reviewed-by: kiara Grouwstra Co-authored-by: Nicolas “Niols” Jeannerod Co-committed-by: Nicolas “Niols” Jeannerod --- .forgejo/workflows/ci.yaml | 13 +++-- infra/common/options.nix | 15 +++--- infra/flake-part.nix | 81 ++++++++++++++++++++-------- infra/proxmox-provision.sh | 6 --- infra/proxmox-remove.sh | 10 +--- machines/dev/fedi200/default.nix | 2 +- machines/dev/fedi201/default.nix | 2 +- machines/dev/forgejo-ci/default.nix | 1 + machines/dev/vm02116/default.nix | 2 +- machines/dev/vm02187/default.nix | 2 +- machines/operator/test01/default.nix | 2 +- machines/operator/test02/default.nix | 2 +- machines/operator/test03/default.nix | 2 +- machines/operator/test04/default.nix | 2 +- machines/operator/test05/default.nix | 2 +- machines/operator/test06/default.nix | 2 +- machines/operator/test11/default.nix | 2 +- machines/operator/test12/default.nix | 2 +- machines/operator/test13/default.nix | 2 +- machines/operator/test14/default.nix | 2 +- 20 files changed, 93 insertions(+), 61 deletions(-) diff --git a/.forgejo/workflows/ci.yaml b/.forgejo/workflows/ci.yaml index 3f553ef1..5015d407 100644 --- a/.forgejo/workflows/ci.yaml +++ b/.forgejo/workflows/ci.yaml @@ -69,9 +69,16 @@ jobs: - uses: actions/checkout@v4 - run: | set -euC + echo ==================== [ VM Options ] ==================== + machines=$(nix eval --impure --raw --expr 'with builtins; toString (attrNames (getFlake (toString ./.)).vmOptions)') + for machine in $machines; do + echo ~~~~~~~~~~~~~~~~~~~~~: $machine :~~~~~~~~~~~~~~~~~~~~~ + nix build .#checks.x86_64-linux.vmOptions-$machine + done + echo + echo ==================== [ NixOS Configurations ] ==================== machines=$(nix eval --impure --raw --expr 'with builtins; toString (attrNames (getFlake (toString ./.)).nixosConfigurations)') for machine in $machines; do - echo ==================== [ $machine ] ==================== - nix eval .#vmOptions.$machine - nix build .#nixosConfigurations.$machine.config.system.build.toplevel + echo ~~~~~~~~~~~~~~~~~~~~~: $machine :~~~~~~~~~~~~~~~~~~~~~ + nix build .#checks.x86_64-linux.nixosConfigurations-$machine done diff --git a/infra/common/options.nix b/infra/common/options.nix index 413f9fb9..0bf629b5 100644 --- a/infra/common/options.nix +++ b/infra/common/options.nix @@ -20,16 +20,13 @@ in ''; }; - proxmox = mkOption { - type = types.nullOr ( - types.enum [ - "procolix" - "fediversity" - ] - ); + isFediversityVm = mkOption { + type = types.bool; description = '' - The Proxmox instance. This is used for provisioning only and should be - set to `null` if the machine is not a VM. + Whether the machine is a Fediversity VM or not. This is used to + determine whether the machine should be provisioned via Proxmox or not. + Machines that are _not_ Fediversity VM could be physical machines, or + VMs that live outside Fediversity, eg. on Procolix's Proxmox. ''; }; diff --git a/infra/flake-part.nix b/infra/flake-part.nix index 4b341ded..9bfd2269 100644 --- a/infra/flake-part.nix +++ b/infra/flake-part.nix @@ -14,6 +14,10 @@ let mkOption evalModules filterAttrs + attrsToList + map + listToAttrs + deepSeq ; inherit (lib.attrsets) genAttrs; @@ -134,27 +138,39 @@ let system = "x86_64-linux"; }; - makeVmOptions = isTestVm: vmName: { - inherit ((makeResourceConfig { inherit vmName isTestVm; }).fediversityVm) - proxmox - vmId - description - - sockets - cores - memory - diskSize - - hostPublicKey - unsafeHostPrivateKey - ; - }; + makeVmOptions = + isTestVm: vmName: + let + config = (makeResourceConfig { inherit vmName isTestVm; }).fediversityVm; + in + if config.isFediversityVm then + { + inherit (config) + vmId + description + sockets + cores + memory + diskSize + hostPublicKey + unsafeHostPrivateKey + ; + } + else + null; listSubdirectories = path: attrNames (filterAttrs (_: type: type == "directory") (readDir path)); machines = listSubdirectories ../machines/dev; testMachines = listSubdirectories ../machines/operator; + nixosConfigurations = + genAttrs machines (makeConfiguration false) + // genAttrs testMachines (makeConfiguration true); + vmOptions = + filterAttrs (_: value: value != null) # Filter out non-Fediversity VMs + (genAttrs machines (makeVmOptions false) // genAttrs testMachines (makeVmOptions true)); + in { _class = "flake"; @@ -178,10 +194,33 @@ in ) ); }; - flake.nixosConfigurations = - genAttrs machines (makeConfiguration false) - // genAttrs testMachines (makeConfiguration true); - flake.vmOptions = - genAttrs machines (makeVmOptions false) - // genAttrs testMachines (makeVmOptions true); + flake = { inherit nixosConfigurations vmOptions; }; + + perSystem = + { pkgs, ... }: + { + checks = + listToAttrs ( + map ( + { name, value }: + { + name = "nixosConfigurations-${name}"; + value = value.config.system.build.toplevel; + } + ) (attrsToList nixosConfigurations) + ) + // listToAttrs ( + map ( + { name, value }: + { + name = "vmOptions-${name}"; + ## Check that VM options builds/evaluates correctly. `deepSeq e1 + ## e2` evaluates `e1` strictly in depth before returning `e2`. We + ## use this trick because checks need to be derivations, which VM + ## options are not. + value = deepSeq value pkgs.hello; + } + ) (attrsToList vmOptions) + ); + }; } diff --git a/infra/proxmox-provision.sh b/infra/proxmox-provision.sh index 42aec63b..35ceb863 100755 --- a/infra/proxmox-provision.sh +++ b/infra/proxmox-provision.sh @@ -179,15 +179,9 @@ grab_vm_options () { --log-format raw --quiet ) - proxmox=$(echo "$options" | jq -r .proxmox) vm_id=$(echo "$options" | jq -r .vmId) description=$(echo "$options" | jq -r .description) - if [ "$proxmox" != fediversity ]; then - die "I do not know how to provision things that are not Fediversity VMs, -but I got proxmox = '%s' for VM %s." "$proxmox" "$vm_name" - fi - sockets=$(echo "$options" | jq -r .sockets) cores=$(echo "$options" | jq -r .cores) memory=$(echo "$options" | jq -r .memory) diff --git a/infra/proxmox-remove.sh b/infra/proxmox-remove.sh index a8ee2de9..e2795a01 100755 --- a/infra/proxmox-remove.sh +++ b/infra/proxmox-remove.sh @@ -167,16 +167,10 @@ grab_vm_options () { --log-format raw --quiet ) - proxmox=$(echo "$options" | jq -r .proxmox) vm_id=$(echo "$options" | jq -r .vmId) - if [ "$proxmox" != fediversity ]; then - die "I do not know how to remove things that are not Fediversity VMs, - but I got proxmox = '%s' for VM %s." "$proxmox" "$vm_name" - fi - - printf 'done grabing VM options for VM %s. Found VM %d on %s Proxmox.\n' \ - "$vm_name" "$vm_id" "$proxmox" + printf 'done grabing VM options for VM %s. Got id: %d.\n' \ + "$vm_name" "$vm_id" fi } diff --git a/machines/dev/fedi200/default.nix b/machines/dev/fedi200/default.nix index 6014da4f..36383199 100644 --- a/machines/dev/fedi200/default.nix +++ b/machines/dev/fedi200/default.nix @@ -3,8 +3,8 @@ fediversityVm = { name = "fedi200"; + isFediversityVm = true; vmId = 200; - proxmox = "fediversity"; description = "Testing machine for Hans"; domain = "abundos.eu"; diff --git a/machines/dev/fedi201/default.nix b/machines/dev/fedi201/default.nix index 3197b157..f9b5123d 100644 --- a/machines/dev/fedi201/default.nix +++ b/machines/dev/fedi201/default.nix @@ -3,8 +3,8 @@ fediversityVm = { name = "fedi201"; + isFediversityVm = true; vmId = 201; - proxmox = "fediversity"; description = "FediPanel"; domain = "abundos.eu"; diff --git a/machines/dev/forgejo-ci/default.nix b/machines/dev/forgejo-ci/default.nix index ee212b17..fc520136 100644 --- a/machines/dev/forgejo-ci/default.nix +++ b/machines/dev/forgejo-ci/default.nix @@ -22,6 +22,7 @@ in fediversityVm = { name = "forgejo-ci"; domain = "procolix.com"; + isFediversityVm = false; ipv4 = { interface = "enp1s0f0"; diff --git a/machines/dev/vm02116/default.nix b/machines/dev/vm02116/default.nix index 0ffd24ab..169b2149 100644 --- a/machines/dev/vm02116/default.nix +++ b/machines/dev/vm02116/default.nix @@ -3,8 +3,8 @@ fediversityVm = { name = "vm02116"; + isFediversityVm = false; vmId = 2116; - proxmox = "procolix"; description = "Forgejo"; ipv4.address = "185.206.232.34"; diff --git a/machines/dev/vm02187/default.nix b/machines/dev/vm02187/default.nix index bc4e63f3..c085cab3 100644 --- a/machines/dev/vm02187/default.nix +++ b/machines/dev/vm02187/default.nix @@ -3,8 +3,8 @@ fediversityVm = { name = "vm02187"; + isFediversityVm = false; vmId = 2187; - proxmox = "procolix"; description = "Wiki"; ipv4.address = "185.206.232.187"; diff --git a/machines/operator/test01/default.nix b/machines/operator/test01/default.nix index d644b6fe..d4c7e235 100644 --- a/machines/operator/test01/default.nix +++ b/machines/operator/test01/default.nix @@ -3,8 +3,8 @@ fediversityVm = { name = "test01"; + isFediversityVm = true; vmId = 7001; - proxmox = "fediversity"; hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub; unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key; diff --git a/machines/operator/test02/default.nix b/machines/operator/test02/default.nix index 53385da7..28bed0a1 100644 --- a/machines/operator/test02/default.nix +++ b/machines/operator/test02/default.nix @@ -3,8 +3,8 @@ fediversityVm = { name = "test02"; + isFediversityVm = true; vmId = 7002; - proxmox = "fediversity"; hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub; unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key; diff --git a/machines/operator/test03/default.nix b/machines/operator/test03/default.nix index ebfa3efb..4dd77d91 100644 --- a/machines/operator/test03/default.nix +++ b/machines/operator/test03/default.nix @@ -3,8 +3,8 @@ fediversityVm = { name = "test03"; + isFediversityVm = true; vmId = 7003; - proxmox = "fediversity"; hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub; unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key; diff --git a/machines/operator/test04/default.nix b/machines/operator/test04/default.nix index f234393f..87bb0778 100644 --- a/machines/operator/test04/default.nix +++ b/machines/operator/test04/default.nix @@ -3,8 +3,8 @@ fediversityVm = { name = "test04"; + isFediversityVm = true; vmId = 7004; - proxmox = "fediversity"; hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub; unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key; diff --git a/machines/operator/test05/default.nix b/machines/operator/test05/default.nix index de461d57..44043af9 100644 --- a/machines/operator/test05/default.nix +++ b/machines/operator/test05/default.nix @@ -3,8 +3,8 @@ fediversityVm = { name = "test05"; + isFediversityVm = true; vmId = 7005; - proxmox = "fediversity"; hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub; unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key; diff --git a/machines/operator/test06/default.nix b/machines/operator/test06/default.nix index a43090de..83f9f996 100644 --- a/machines/operator/test06/default.nix +++ b/machines/operator/test06/default.nix @@ -3,8 +3,8 @@ fediversityVm = { name = "test06"; + isFediversityVm = true; vmId = 7006; - proxmox = "fediversity"; hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub; unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key; diff --git a/machines/operator/test11/default.nix b/machines/operator/test11/default.nix index 848192d2..1015ac76 100644 --- a/machines/operator/test11/default.nix +++ b/machines/operator/test11/default.nix @@ -3,8 +3,8 @@ fediversityVm = { name = "test11"; + isFediversityVm = true; vmId = 7011; - proxmox = "fediversity"; hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub; unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key; diff --git a/machines/operator/test12/default.nix b/machines/operator/test12/default.nix index a33b24c3..8f2d345f 100644 --- a/machines/operator/test12/default.nix +++ b/machines/operator/test12/default.nix @@ -3,8 +3,8 @@ fediversityVm = { name = "test12"; + isFediversityVm = true; vmId = 7012; - proxmox = "fediversity"; hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub; unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key; diff --git a/machines/operator/test13/default.nix b/machines/operator/test13/default.nix index 97cdfb6b..dd7abef1 100644 --- a/machines/operator/test13/default.nix +++ b/machines/operator/test13/default.nix @@ -3,8 +3,8 @@ fediversityVm = { name = "test13"; + isFediversityVm = true; vmId = 7013; - proxmox = "fediversity"; hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub; unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key; diff --git a/machines/operator/test14/default.nix b/machines/operator/test14/default.nix index 34b4c3c6..5a3b96e8 100644 --- a/machines/operator/test14/default.nix +++ b/machines/operator/test14/default.nix @@ -3,8 +3,8 @@ fediversityVm = { name = "test14"; + isFediversityVm = true; vmId = 7014; - proxmox = "fediversity"; hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub; unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key; -- 2.48.1 From 1f99a4c6c3fbfbf60d7a9a5985070dd56670bf04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Fri, 1 Aug 2025 13:09:26 +0200 Subject: [PATCH 08/76] =?UTF-8?q?`listToAttrs`=20o=20`map`=20o=20`attrsToL?= =?UTF-8?q?ist`=20=E2=86=92=20`mapAttrs'`=20(#489)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-on: https://git.fediversity.eu/Fediversity/Fediversity/pulls/489 Reviewed-by: kiara Grouwstra Co-authored-by: Nicolas “Niols” Jeannerod Co-committed-by: Nicolas “Niols” Jeannerod --- infra/flake-part.nix | 38 +++++++++++++------------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/infra/flake-part.nix b/infra/flake-part.nix index 9bfd2269..34bc6e50 100644 --- a/infra/flake-part.nix +++ b/infra/flake-part.nix @@ -14,9 +14,7 @@ let mkOption evalModules filterAttrs - attrsToList - map - listToAttrs + mapAttrs' deepSeq ; inherit (lib.attrsets) genAttrs; @@ -200,27 +198,17 @@ in { pkgs, ... }: { checks = - listToAttrs ( - map ( - { name, value }: - { - name = "nixosConfigurations-${name}"; - value = value.config.system.build.toplevel; - } - ) (attrsToList nixosConfigurations) - ) - // listToAttrs ( - map ( - { name, value }: - { - name = "vmOptions-${name}"; - ## Check that VM options builds/evaluates correctly. `deepSeq e1 - ## e2` evaluates `e1` strictly in depth before returning `e2`. We - ## use this trick because checks need to be derivations, which VM - ## options are not. - value = deepSeq value pkgs.hello; - } - ) (attrsToList vmOptions) - ); + mapAttrs' (name: nixosConfiguration: { + name = "nixosConfigurations-${name}"; + value = nixosConfiguration.config.system.build.toplevel; + }) nixosConfigurations + // mapAttrs' (name: vmOptions: { + name = "vmOptions-${name}"; + ## Check that VM options builds/evaluates correctly. `deepSeq e1 + ## e2` evaluates `e1` strictly in depth before returning `e2`. We + ## use this trick because checks need to be derivations, which VM + ## options are not. + value = deepSeq vmOptions pkgs.hello; + }) vmOptions; }; } -- 2.48.1 From 9d903f3ef79bc90ed3ea2b752e25b7909d33e54a Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 27 Aug 2025 00:45:49 +0200 Subject: [PATCH 09/76] Complete the data model with a runtime environment and end-to-end test (#481) Closes #103 At last, a fully fledged data model for what Fediversity really is and does. This comes with a test that exercises a very simple but functionally complete arrangement with all ingredients fo the business logic: a dummy resource (login shell), a dummy application (`hello`, which needs a shell to live in), a dummy environment (a single NixOS VM that allows for one, the operator's, login shell), and a deployment of that environment given a dummy configuration (that enables `hello`). The next step will be to lift this purely evaluation-level test into a VM test which verifies that the resulting VM indeed has `hello` deployed to the operator's user account. Caveats: - The exact naming has a bit of room for improvement, and may have diverged from the design document - The test is not as pedantically type safe as it could be, since we simply use `types.raw` for resources such as NixOS users settings which *could* be more finely delineated Co-authored-by: Kiara Grouwstra Co-authored-by: kiara Grouwstra Reviewed-on: https://git.fediversity.eu/Fediversity/Fediversity/pulls/481 Reviewed-by: kiara Grouwstra Co-authored-by: Valentin Gagarin Co-committed-by: Valentin Gagarin --- default.nix | 4 +- deployment/data-model-test.nix | 161 ++++++++++++++++++++++++++++++--- deployment/data-model.nix | 120 +++++++++++++++++++++++- deployment/function.nix | 9 +- 4 files changed, 271 insertions(+), 23 deletions(-) diff --git a/default.nix b/default.nix index 24b73cd2..e929e516 100644 --- a/default.nix +++ b/default.nix @@ -11,7 +11,8 @@ let ; inherit (pkgs) lib; inherit (import sources.flake-inputs) import-flake; - inherit ((import-flake { src = ./.; }).inputs) nixops4; + inputs = (import-flake { src = ./.; }).inputs; + inherit (inputs) nixops4; panel = import ./panel { inherit sources system; }; pre-commit-check = (import "${git-hooks}/nix" { @@ -78,6 +79,7 @@ in # re-export inputs so they can be overridden granularly # (they can't be accessed from the outside any other way) inherit + inputs sources system pkgs diff --git a/deployment/data-model-test.nix b/deployment/data-model-test.nix index ac35df39..24d5cd6c 100644 --- a/deployment/data-model-test.nix +++ b/deployment/data-model-test.nix @@ -1,7 +1,7 @@ let inherit (import ../default.nix { }) pkgs inputs; inherit (pkgs) lib; - inherit (lib) mkOption; + inherit (lib) mkOption types; eval = module: (lib.evalModules { @@ -13,17 +13,83 @@ let ./data-model.nix ]; }).config; + nixops4Deployment = inputs.nixops4.modules.nixops4Deployment.default; + inherit (inputs.nixops4.lib) mkDeployment; in { _class = "nix-unit"; test-eval = { + /** + This tests a very simple arrangement that features all ingredients of the Fediversity business logic: + application, resource, environment, deployment; and wires it all up in one end-to-end exercise. + - The dummy resource is a login shell made available for some user. + - The dummy application is `hello` that requires a shell to be deployed. + - The dummy environment is a single NixOS VM that hosts one login shell, for the operator. + - The dummy configuration enables the `hello` application. + This will produce a NixOps4 deployment for a NixOS VM with a login shell for the operator and `hello` available. + */ expr = let fediversity = eval ( { config, ... }: { config = { + resources.login-shell = { + description = "The operator needs to be able to log into the shell"; + request = + { ... }: + { + _class = "fediversity-resource-request"; + options = { + wheel = mkOption { + description = "Whether the login user needs root permissions"; + type = types.bool; + default = false; + }; + packages = mkOption { + description = "Packages that need to be available in the user environment"; + type = with types; attrsOf package; + }; + }; + }; + policy = + { config, ... }: + { + _class = "fediversity-resource-policy"; + options = { + username = mkOption { + description = "Username for the operator"; + type = types.str; # TODO: use the proper constraints from NixOS + }; + wheel = mkOption { + description = "Whether to allow login with root permissions"; + type = types.bool; + default = false; + }; + }; + config = { + resource-type = types.raw; # TODO: splice out the user type from NixOS + apply = + requests: + let + # Filter out requests that need wheel if policy doesn't allow it + validRequests = lib.filterAttrs ( + _name: req: !req.login-shell.wheel || config.wheel + ) requests.resources; + in + lib.optionalAttrs (validRequests != { }) { + ${config.username} = { + isNormalUser = true; + packages = + with lib; + attrValues (concatMapAttrs (_name: request: request.login-shell.packages) validRequests); + extraGroups = lib.optional config.wheel "wheel"; + }; + }; + }; + }; + }; applications.hello = { ... }: { @@ -31,15 +97,42 @@ in module = { ... }: { - options = { - enable = lib.mkEnableOption "Hello in the shell"; + options.enable = lib.mkEnableOption "Hello in the shell"; + }; + implementation = cfg: { + input = cfg; + output = lib.optionalAttrs cfg.enable { + resources.hello.login-shell.packages.hello = pkgs.hello; + }; + }; + }; + environments.single-nixos-vm = + { config, ... }: + { + resources.operator-environment.login-shell.username = "operator"; + implementation = requests: { + input = requests; + output = + { providers, ... }: + { + providers = { + inherit (inputs.nixops4.modules.nixops4Provider) local; + }; + resources.the-machine = { + type = providers.local.exec; + imports = [ + inputs.nixops4-nixos.modules.nixops4Resource.nixos + ]; + nixos.module = + { ... }: + { + users.users = config.resources.shell.login-shell.apply ( + lib.filterAttrs (_name: value: value ? login-shell) requests + ); + }; + }; }; - }; - implementation = - cfg: - lib.optionalAttrs cfg.enable { - dummy.login-shell.packages.hello = pkgs.hello; - }; + }; }; }; options = { @@ -51,20 +144,64 @@ in applications.hello.enable = true; }; }; + example-deployment = mkOption { + type = types.submodule nixops4Deployment; + readOnly = true; + default = config.environments.single-nixos-vm.deployment config.example-configuration; + }; }; } ); + resources = fediversity.applications.hello.resources fediversity.example-configuration.applications.hello; + hello-shell = resources.resources.hello.login-shell; + environment = fediversity.environments.single-nixos-vm.resources.operator-environment.login-shell; + result = mkDeployment { + modules = [ + (fediversity.environments.single-nixos-vm.deployment fediversity.example-configuration) + ]; + }; + in { - inherit (fediversity) - example-configuration - ; + number-of-resources = with lib; length (attrNames fediversity.resources); + inherit (fediversity) example-configuration; + hello-package-exists = hello-shell.packages ? hello; + wheel-required = hello-shell.wheel; + wheel-allowed = environment.wheel; + operator-shell = + let + operator = (environment.apply resources).operator; + in + { + inherit (operator) isNormalUser; + packages = map (p: "${p.pname}") operator.packages; + extraGroups = operator.extraGroups; + }; + deployment = { + inherit (result) _type; + deploymentFunction = lib.isFunction result.deploymentFunction; + getProviders = lib.isFunction result.getProviders; + }; }; expected = { + number-of-resources = 1; example-configuration = { enable = true; applications.hello.enable = true; }; + hello-package-exists = true; + wheel-required = false; + wheel-allowed = false; + operator-shell = { + isNormalUser = true; + packages = [ "hello" ]; + extraGroups = [ ]; + }; + deployment = { + _type = "nixops4Deployment"; + deploymentFunction = true; + getProviders = true; + }; }; }; } diff --git a/deployment/data-model.nix b/deployment/data-model.nix index 8f584af4..c3d5d53a 100644 --- a/deployment/data-model.nix +++ b/deployment/data-model.nix @@ -1,6 +1,7 @@ { lib, config, + inputs, ... }: let @@ -15,19 +16,73 @@ let ; functionType = import ./function.nix; - application-resources = { + application-resources = submodule { options.resources = mkOption { # TODO: maybe transpose, and group the resources by type instead type = attrsOf ( - attrTag (lib.mapAttrs (_name: resource: mkOption { type = resource.request; }) config.resources) + attrTag ( + lib.mapAttrs (_name: resource: mkOption { type = submodule resource.request; }) config.resources + ) ); }; }; + nixops4Deployment = types.deferredModuleWith { + staticModules = [ + inputs.nixops4.modules.nixops4Deployment.default + + { + _class = "nixops4Deployment"; + _module.args = { + resourceProviderSystem = builtins.currentSystem; + resources = { }; + }; + } + ]; + }; in { - _class = "nixops4Deployment"; - options = { + resources = mkOption { + description = "Collection of deployment resources that can be required by applications and policed by hosting providers"; + type = attrsOf ( + submodule ( + { ... }: + { + _class = "fediversity-resource"; + options = { + description = mkOption { + description = "Description of the resource to help application module authors and hosting providers to work with it"; + type = types.str; + }; + request = mkOption { + description = "Options for declaring resource requirements by an application, a description of how the resource is consumed or accessed"; + type = deferredModuleWith { staticModules = [ { _class = "fediversity-resource-request"; } ]; }; + }; + policy = mkOption { + description = "Options for configuring the resource policy for the hosting provider, a description of how the resource is made available"; + type = deferredModuleWith { + staticModules = [ + (policy: { + _class = "fediversity-resource-policy"; + options.resource-type = mkOption { + description = "The type of resource this policy configures"; + type = types.optionType; + }; + # TODO(@fricklerhandwerk): we may want to make the function type explict here: `request -> resource-type` + # and then also rename this to be consistent with the application's resource mapping + options.apply = mkOption { + description = "Apply the policy to a request"; + type = functionTo policy.config.resource-type; + }; + }) + ]; + }; + }; + }; + } + ) + ); + }; applications = mkOption { description = "Collection of Fediversity applications"; type = attrsOf ( @@ -52,12 +107,13 @@ in readOnly = true; default = input: (application.config.implementation input).output; }; + # TODO(@fricklerhandwerk): this needs a better name, it's just the type config-mapping = mkOption { description = "Function type for the mapping from application configuration to required resources"; type = submodule functionType; readOnly = true; default = { - input-type = application.config.module; + input-type = submodule application.config.module; output-type = application-resources; }; }; @@ -65,6 +121,60 @@ in }) ); }; + environments = mkOption { + description = "Run-time environments for Fediversity applications to be deployed to"; + type = attrsOf ( + submodule (environment: { + _class = "fediversity-environment"; + options = { + resources = mkOption { + description = '' + Resources made available by the hosting provider, and their policies. + + Setting this is optional, but provides a place to declare that information for programmatic use in the resource mapping. + ''; + # TODO: maybe transpose, and group the resources by type instead + type = attrsOf ( + attrTag ( + lib.mapAttrs (_name: resource: mkOption { type = submodule resource.policy; }) config.resources + ) + ); + }; + implementation = mkOption { + description = "Mapping of resources required by applications to available resources; the result can be deployed"; + type = environment.config.resource-mapping.function-type; + }; + resource-mapping = mkOption { + description = "Function type for the mapping from resources to a (NixOps4) deployment"; + type = submodule functionType; + readOnly = true; + default = { + input-type = application-resources; + output-type = nixops4Deployment; + }; + }; + # TODO(@fricklerhandwerk): maybe this should be a separate thing such as `fediversity-setup`, + # which makes explicit which applications and environments are available. + # then the deployments can simply be the result of the function application baked into this module. + deployment = mkOption { + description = "Generate a deployment from a configuration, by applying an environment's resource policies to the applications' resource mappings"; + type = functionTo (environment.config.resource-mapping.output-type); + readOnly = true; + default = + cfg: + # TODO: check cfg.enable.true + let + required-resources = lib.mapAttrs ( + name: application-settings: config.applications.${name}.resources application-settings + ) cfg.applications; + in + (environment.config.implementation required-resources).output; + + }; + }; + }) + ); + }; configuration = mkOption { description = "Configuration type declaring options to be set by operators"; type = optionType; diff --git a/deployment/function.nix b/deployment/function.nix index d1a047f0..f0210a34 100644 --- a/deployment/function.nix +++ b/deployment/function.nix @@ -5,7 +5,6 @@ let inherit (lib) mkOption types; inherit (types) - deferredModule submodule functionTo optionType @@ -14,10 +13,10 @@ in { options = { input-type = mkOption { - type = deferredModule; + type = optionType; }; output-type = mkOption { - type = deferredModule; + type = optionType; }; function-type = mkOption { type = optionType; @@ -25,10 +24,10 @@ in default = functionTo (submodule { options = { input = mkOption { - type = submodule config.input-type; + type = config.input-type; }; output = mkOption { - type = submodule config.output-type; + type = config.output-type; }; }; }); -- 2.48.1 From 8dc2f05749abad7ac466f64bad57f3d9c40c5488 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sat, 9 Aug 2025 12:50:22 +0200 Subject: [PATCH 10/76] allow different deployment types --- deployment/data-model-test.nix | 7 +++---- deployment/data-model.nix | 10 ++++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/deployment/data-model-test.nix b/deployment/data-model-test.nix index 24d5cd6c..c65e37ae 100644 --- a/deployment/data-model-test.nix +++ b/deployment/data-model-test.nix @@ -13,7 +13,6 @@ let ./data-model.nix ]; }).config; - nixops4Deployment = inputs.nixops4.modules.nixops4Deployment.default; inherit (inputs.nixops4.lib) mkDeployment; in { @@ -32,7 +31,7 @@ in expr = let fediversity = eval ( - { config, ... }: + { config, options, ... }: { config = { resources.login-shell = { @@ -112,7 +111,7 @@ in resources.operator-environment.login-shell.username = "operator"; implementation = requests: { input = requests; - output = + output.nixops4 = { providers, ... }: { providers = { @@ -145,7 +144,7 @@ in }; }; example-deployment = mkOption { - type = types.submodule nixops4Deployment; + type = options.deployments.nestedType; readOnly = true; default = config.environments.single-nixos-vm.deployment config.example-configuration; }; diff --git a/deployment/data-model.nix b/deployment/data-model.nix index c3d5d53a..412f9bae 100644 --- a/deployment/data-model.nix +++ b/deployment/data-model.nix @@ -39,6 +39,12 @@ let } ]; }; + deployment = attrTag { + nixops4 = mkOption { + description = "A NixOps4 NixOS deployment. For an example, see https://github.com/nixops4/nixops4-nixos/blob/main/example/deployment.nix."; + type = nixops4Deployment; + }; + }; in { options = { @@ -145,12 +151,12 @@ in type = environment.config.resource-mapping.function-type; }; resource-mapping = mkOption { - description = "Function type for the mapping from resources to a (NixOps4) deployment"; + description = "Function type for the mapping from resources to a deployment"; type = submodule functionType; readOnly = true; default = { input-type = application-resources; - output-type = nixops4Deployment; + output-type = deployment; }; }; # TODO(@fricklerhandwerk): maybe this should be a separate thing such as `fediversity-setup`, -- 2.48.1 From ed027c9f7c2722ab72de2db5199bac810a60fb3a Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Fri, 1 Aug 2025 16:20:22 +0200 Subject: [PATCH 11/76] scaffold deployment/check/data-model from ./basic modelify --- .forgejo/workflows/ci.yaml | 6 + .../check/data-model/common-nixosTest.nix | 200 ++++++++++++++++++ deployment/check/data-model/constants.nix | 8 + deployment/check/data-model/default.nix | 14 ++ deployment/check/data-model/deployment.nix | 36 ++++ .../check/data-model/flake-under-test.nix | 22 ++ deployment/check/data-model/nixosTest.nix | 50 +++++ deployment/flake-part.nix | 5 + 8 files changed, 341 insertions(+) create mode 100644 deployment/check/data-model/common-nixosTest.nix create mode 100644 deployment/check/data-model/constants.nix create mode 100644 deployment/check/data-model/default.nix create mode 100644 deployment/check/data-model/deployment.nix create mode 100644 deployment/check/data-model/flake-under-test.nix create mode 100644 deployment/check/data-model/nixosTest.nix diff --git a/.forgejo/workflows/ci.yaml b/.forgejo/workflows/ci.yaml index 5015d407..629cce26 100644 --- a/.forgejo/workflows/ci.yaml +++ b/.forgejo/workflows/ci.yaml @@ -57,6 +57,12 @@ jobs: - uses: actions/checkout@v4 - run: nix build .#checks.x86_64-linux.deployment-panel -L + check-deployment-model: + runs-on: native + steps: + - uses: actions/checkout@v4 + - run: nix build .#checks.x86_64-linux.deployment-model -L + ## NOTE: NixOps4 does not provide a good “dry run” mode, so we instead check ## proxies for resources, namely whether their `.#vmOptions.` and ## `.#nixosConfigurations.` outputs evaluate and build correctly, and diff --git a/deployment/check/data-model/common-nixosTest.nix b/deployment/check/data-model/common-nixosTest.nix new file mode 100644 index 00000000..cb52ed9f --- /dev/null +++ b/deployment/check/data-model/common-nixosTest.nix @@ -0,0 +1,200 @@ +{ + inputs, + lib, + config, + hostPkgs, + sources, + ... +}: + +let + inherit (builtins) + concatStringsSep + toJSON + ; + inherit (lib) + types + fileset + mkOption + genAttrs + attrNames + optionalString + ; + inherit (hostPkgs) + runCommandNoCC + writeText + system + ; + + forConcat = xs: f: concatStringsSep "\n" (map f xs); + + ## We will need to override some inputs by the empty flake, so we make one. + emptyFlake = runCommandNoCC "empty-flake" { } '' + mkdir $out + echo "{ outputs = { self }: {}; }" > $out/flake.nix + ''; + +in +{ + _class = "nixosTest"; + + imports = [ + ./sharedOptions.nix + ]; + + options = { + ## FIXME: I wish I could just use `testScript` but with something like + ## `mkOrder` to put this module's string before something else. + extraTestScript = mkOption { }; + + sourceFileset = mkOption { + ## REVIEW: Upstream to nixpkgs? + type = types.mkOptionType { + name = "fileset"; + description = "fileset"; + descriptionClass = "noun"; + check = (x: (builtins.tryEval (fileset.unions [ x ])).success); + merge = (_: defs: fileset.unions (map (x: x.value) defs)); + }; + description = '' + A fileset that will be copied to the deployer node in the current + working directory. This should contain all the files that are + necessary to run that particular test, such as the NixOS + modules necessary to evaluate a deployment. + ''; + }; + }; + + config = { + sourceFileset = fileset.unions [ + # NOTE: not the flake itself; it will be overridden. + ../../../mkFlake.nix + ../../../flake.lock + ../../../npins + + ./sharedOptions.nix + ./targetNode.nix + ./targetResource.nix + + (config.pathToCwd + "/flake-under-test.nix") + ]; + + acmeNodeIP = config.nodes.acme.networking.primaryIPAddress; + + nodes = + { + deployer = { + imports = [ ./deployerNode.nix ]; + _module.args = { inherit inputs sources; }; + enableAcme = config.enableAcme; + acmeNodeIP = config.nodes.acme.networking.primaryIPAddress; + }; + } + + // + + ( + if config.enableAcme then + { + acme = { + ## FIXME: This makes `nodes.acme` into a local resolver. Maybe this will + ## break things once we play with DNS? + imports = [ "${inputs.nixpkgs}/nixos/tests/common/acme/server" ]; + ## We aren't testing ACME - we just want certificates. + systemd.services.pebble.environment.PEBBLE_VA_ALWAYS_VALID = "1"; + }; + } + else + { } + ) + + // + + genAttrs config.targetMachines (_: { + imports = [ ./targetNode.nix ]; + _module.args = { inherit inputs sources; }; + enableAcme = config.enableAcme; + acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; + }); + + testScript = '' + ${forConcat (attrNames config.nodes) (n: '' + ${n}.start() + '')} + + ${forConcat (attrNames config.nodes) (n: '' + ${n}.wait_for_unit("multi-user.target") + '')} + + ## A subset of the repository that is necessary for this test. It will be + ## copied inside the test. The smaller this set, the faster our CI, because we + ## won't need to re-run when things change outside of it. + with subtest("Unpacking"): + deployer.succeed("cp -r --no-preserve=mode ${ + fileset.toSource { + root = ../../..; + fileset = config.sourceFileset; + } + }/* .") + + with subtest("Configure the network"): + ${forConcat config.targetMachines ( + tm: + let + targetNetworkJSON = writeText "target-network.json" ( + toJSON config.nodes.${tm}.system.build.networkConfig + ); + in + '' + deployer.copy_from_host("${targetNetworkJSON}", "${config.pathFromRoot}/${tm}-network.json") + '' + )} + + with subtest("Configure the deployer key"): + deployer.succeed("""mkdir -p ~/.ssh && ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa""") + deployer_key = deployer.succeed("cat ~/.ssh/id_rsa.pub").strip() + ${forConcat config.targetMachines (tm: '' + ${tm}.succeed(f"mkdir -p /root/.ssh && echo '{deployer_key}' >> /root/.ssh/authorized_keys") + '')} + + with subtest("Configure the target host key"): + ${forConcat config.targetMachines (tm: '' + host_key = ${tm}.succeed("ssh-keyscan ${tm} | grep -v '^#' | cut -f 2- -d ' ' | head -n 1") + deployer.succeed(f"echo '{host_key}' > ${config.pathFromRoot}/${tm}_host_key.pub") + '')} + + ## NOTE: This is super slow. It could probably be optimised in Nix, for + ## instance by allowing to grab things directly from the host's store. + ## + ## NOTE: We use the repository as-is (cf `src` above), overriding only + ## `flake.nix` by our `flake-under-test.nix`. We also override the flake + ## lock file to use locally available inputs, as we cannot download them. + ## + with subtest("Override the flake and its lock"): + deployer.succeed("cp ${config.pathFromRoot}/flake-under-test.nix flake.nix") + deployer.succeed(""" + nix flake lock --extra-experimental-features 'flakes nix-command' \ + --offline -v \ + --override-input nixops4 ${inputs.nixops4.packages.${system}.flake-in-a-bottle} \ + \ + --override-input nixops4-nixos ${inputs.nixops4-nixos} \ + --override-input nixops4-nixos/flake-parts ${inputs.nixops4-nixos.inputs.flake-parts} \ + --override-input nixops4-nixos/flake-parts/nixpkgs-lib ${inputs.nixops4-nixos.inputs.flake-parts.inputs.nixpkgs-lib} \ + --override-input nixops4-nixos/nixops4-nixos ${emptyFlake} \ + --override-input nixops4-nixos/nixpkgs ${inputs.nixops4-nixos.inputs.nixpkgs} \ + --override-input nixops4-nixos/nixops4 ${ + inputs.nixops4-nixos.inputs.nixops4.packages.${system}.flake-in-a-bottle + } \ + --override-input nixops4-nixos/git-hooks-nix ${emptyFlake} \ + ; + """) + + ${optionalString config.enableAcme '' + with subtest("Set up handmade DNS"): + deployer.succeed("echo '${config.nodes.acme.networking.primaryIPAddress}' > ${config.pathFromRoot}/acme_server_ip") + ''} + + ${config.extraTestScript} + ''; + }; +} diff --git a/deployment/check/data-model/constants.nix b/deployment/check/data-model/constants.nix new file mode 100644 index 00000000..3cf28d8f --- /dev/null +++ b/deployment/check/data-model/constants.nix @@ -0,0 +1,8 @@ +{ + targetMachines = [ + "hello" + "cowsay" + ]; + pathToRoot = ../../..; + pathFromRoot = ./.; +} diff --git a/deployment/check/data-model/default.nix b/deployment/check/data-model/default.nix new file mode 100644 index 00000000..6479b6be --- /dev/null +++ b/deployment/check/data-model/default.nix @@ -0,0 +1,14 @@ +{ + runNixOSTest, + inputs, + sources, +}: + +runNixOSTest { + imports = [ + ../common/nixosTest.nix + ./nixosTest.nix + ]; + _module.args = { inherit inputs sources; }; + inherit (import ./constants.nix) targetMachines pathToRoot pathFromRoot; +} diff --git a/deployment/check/data-model/deployment.nix b/deployment/check/data-model/deployment.nix new file mode 100644 index 00000000..14a35ac6 --- /dev/null +++ b/deployment/check/data-model/deployment.nix @@ -0,0 +1,36 @@ +{ + inputs, + sources, + lib, + providers, + ... +}: + +let + inherit (import ./constants.nix) targetMachines pathToRoot pathFromRoot; +in + +{ + providers = { + inherit (inputs.nixops4.modules.nixops4Provider) local; + }; + + resources = lib.genAttrs targetMachines (nodeName: { + type = providers.local.exec; + + imports = [ + inputs.nixops4-nixos.modules.nixops4Resource.nixos + ../common/targetResource.nix + ]; + + _module.args = { inherit inputs sources; }; + + inherit nodeName pathToRoot pathFromRoot; + + nixos.module = + { pkgs, ... }: + { + environment.systemPackages = [ pkgs.${nodeName} ]; + }; + }); +} diff --git a/deployment/check/data-model/flake-under-test.nix b/deployment/check/data-model/flake-under-test.nix new file mode 100644 index 00000000..b9e3fb4b --- /dev/null +++ b/deployment/check/data-model/flake-under-test.nix @@ -0,0 +1,22 @@ +{ + inputs = { + nixops4.follows = "nixops4-nixos/nixops4"; + nixops4-nixos.url = "github:nixops4/nixops4-nixos"; + }; + + outputs = + inputs: + import ./mkFlake.nix inputs ( + { inputs, sources, ... }: + { + imports = [ + inputs.nixops4.modules.flake.default + ]; + + nixops4Deployments.check-deployment-basic = { + imports = [ ./deployment/check/basic/deployment.nix ]; + _module.args = { inherit inputs sources; }; + }; + } + ); +} diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix new file mode 100644 index 00000000..d17ee833 --- /dev/null +++ b/deployment/check/data-model/nixosTest.nix @@ -0,0 +1,50 @@ +{ inputs, lib, ... }: + +{ + _class = "nixosTest"; + + name = "deployment-model"; + + sourceFileset = lib.fileset.unions [ + ../../data-model.nix + ../../function.nix + ./constants.nix + ./deployment.nix + ]; + + nodes.deployer = + { pkgs, ... }: + { + environment.systemPackages = [ + inputs.nixops4.packages.${pkgs.system}.default + ]; + + # FIXME: sad times + system.extraDependencies = with pkgs; [ + jq + jq.inputDerivation + ]; + + system.extraDependenciesFromModule = + { pkgs, ... }: + { + environment.systemPackages = with pkgs; [ + hello + cowsay + ]; + }; + }; + + extraTestScript = '' + with subtest("Check the status before deployment"): + hello.fail("hello 1>&2") + cowsay.fail("cowsay 1>&2") + + with subtest("Run the deployment"): + deployer.succeed("nixops4 apply check-deployment-basic --show-trace --no-interactive 1>&2") + + with subtest("Check the deployment"): + hello.succeed("hello 1>&2") + cowsay.succeed("cowsay hi 1>&2") + ''; +} diff --git a/deployment/flake-part.nix b/deployment/flake-part.nix index 952fc694..ca80f247 100644 --- a/deployment/flake-part.nix +++ b/deployment/flake-part.nix @@ -21,6 +21,11 @@ inherit (pkgs.testers) runNixOSTest; inherit inputs sources; }; + + deployment-model = import ./check/data-model { + inherit (pkgs.testers) runNixOSTest; + inherit inputs sources; + }; }; }; } -- 2.48.1 From 3e7c0c577c8d587541b078dc2d018b594add8b03 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sat, 9 Aug 2025 23:31:25 +0200 Subject: [PATCH 12/76] un-nixops --- deployment/check/common/deployerNode.nix | 5 +- .../check/data-model/common-nixosTest.nix | 52 ++++------------- deployment/check/data-model/default.nix | 4 +- deployment/check/data-model/deployment.nix | 58 ++++++++++--------- .../check/data-model/flake-under-test.nix | 22 ------- deployment/check/data-model/nixosTest.nix | 8 +-- deployment/data-model.nix | 18 ------ 7 files changed, 49 insertions(+), 118 deletions(-) delete mode 100644 deployment/check/data-model/flake-under-test.nix diff --git a/deployment/check/common/deployerNode.nix b/deployment/check/common/deployerNode.nix index 987a0a7c..4e0dd2de 100644 --- a/deployment/check/common/deployerNode.nix +++ b/deployment/check/common/deployerNode.nix @@ -54,11 +54,8 @@ in system.extraDependencies = [ - inputs.nixops4 - inputs.nixops4-nixos - inputs.nixpkgs + sources.nixpkgs - sources.flake-parts sources.flake-inputs sources.git-hooks diff --git a/deployment/check/data-model/common-nixosTest.nix b/deployment/check/data-model/common-nixosTest.nix index cb52ed9f..aa912dc3 100644 --- a/deployment/check/data-model/common-nixosTest.nix +++ b/deployment/check/data-model/common-nixosTest.nix @@ -21,25 +21,17 @@ let optionalString ; inherit (hostPkgs) - runCommandNoCC writeText - system ; forConcat = xs: f: concatStringsSep "\n" (map f xs); - ## We will need to override some inputs by the empty flake, so we make one. - emptyFlake = runCommandNoCC "empty-flake" { } '' - mkdir $out - echo "{ outputs = { self }: {}; }" > $out/flake.nix - ''; - in { _class = "nixosTest"; imports = [ - ./sharedOptions.nix + ../common/sharedOptions.nix ]; options = { @@ -67,16 +59,15 @@ in config = { sourceFileset = fileset.unions [ - # NOTE: not the flake itself; it will be overridden. ../../../mkFlake.nix ../../../flake.lock ../../../npins + ../../data-model.nix + ../../function.nix - ./sharedOptions.nix - ./targetNode.nix - ./targetResource.nix - - (config.pathToCwd + "/flake-under-test.nix") + ../common/sharedOptions.nix + ../common/targetNode.nix + ../common/targetResource.nix ]; acmeNodeIP = config.nodes.acme.networking.primaryIPAddress; @@ -84,7 +75,7 @@ in nodes = { deployer = { - imports = [ ./deployerNode.nix ]; + imports = [ ../common/deployerNode.nix ]; _module.args = { inherit inputs sources; }; enableAcme = config.enableAcme; acmeNodeIP = config.nodes.acme.networking.primaryIPAddress; @@ -111,7 +102,7 @@ in // genAttrs config.targetMachines (_: { - imports = [ ./targetNode.nix ]; + imports = [ ../common/targetNode.nix ]; _module.args = { inherit inputs sources; }; enableAcme = config.enableAcme; acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; @@ -163,31 +154,8 @@ in deployer.succeed(f"echo '{host_key}' > ${config.pathFromRoot}/${tm}_host_key.pub") '')} - ## NOTE: This is super slow. It could probably be optimised in Nix, for - ## instance by allowing to grab things directly from the host's store. - ## - ## NOTE: We use the repository as-is (cf `src` above), overriding only - ## `flake.nix` by our `flake-under-test.nix`. We also override the flake - ## lock file to use locally available inputs, as we cannot download them. - ## - with subtest("Override the flake and its lock"): - deployer.succeed("cp ${config.pathFromRoot}/flake-under-test.nix flake.nix") - deployer.succeed(""" - nix flake lock --extra-experimental-features 'flakes nix-command' \ - --offline -v \ - --override-input nixops4 ${inputs.nixops4.packages.${system}.flake-in-a-bottle} \ - \ - --override-input nixops4-nixos ${inputs.nixops4-nixos} \ - --override-input nixops4-nixos/flake-parts ${inputs.nixops4-nixos.inputs.flake-parts} \ - --override-input nixops4-nixos/flake-parts/nixpkgs-lib ${inputs.nixops4-nixos.inputs.flake-parts.inputs.nixpkgs-lib} \ - --override-input nixops4-nixos/nixops4-nixos ${emptyFlake} \ - --override-input nixops4-nixos/nixpkgs ${inputs.nixops4-nixos.inputs.nixpkgs} \ - --override-input nixops4-nixos/nixops4 ${ - inputs.nixops4-nixos.inputs.nixops4.packages.${system}.flake-in-a-bottle - } \ - --override-input nixops4-nixos/git-hooks-nix ${emptyFlake} \ - ; - """) + # with subtest("Override the flake and its lock"): + # deployer.succeed("cp ${config.pathFromRoot}/flake-under-test.nix flake.nix") ${optionalString config.enableAcme '' with subtest("Set up handmade DNS"): diff --git a/deployment/check/data-model/default.nix b/deployment/check/data-model/default.nix index 6479b6be..5eafbbbc 100644 --- a/deployment/check/data-model/default.nix +++ b/deployment/check/data-model/default.nix @@ -6,7 +6,9 @@ runNixOSTest { imports = [ - ../common/nixosTest.nix + ../../data-model.nix + ../../function.nix + ./common-nixosTest.nix ./nixosTest.nix ]; _module.args = { inherit inputs sources; }; diff --git a/deployment/check/data-model/deployment.nix b/deployment/check/data-model/deployment.nix index 14a35ac6..2074edc0 100644 --- a/deployment/check/data-model/deployment.nix +++ b/deployment/check/data-model/deployment.nix @@ -1,36 +1,40 @@ { inputs, - sources, + # sources, lib, - providers, + # providers, ... }: let - inherit (import ./constants.nix) targetMachines pathToRoot pathFromRoot; -in - -{ - providers = { - inherit (inputs.nixops4.modules.nixops4Provider) local; - }; - - resources = lib.genAttrs targetMachines (nodeName: { - type = providers.local.exec; - - imports = [ - inputs.nixops4-nixos.modules.nixops4Resource.nixos - ../common/targetResource.nix - ]; - - _module.args = { inherit inputs sources; }; - - inherit nodeName pathToRoot pathFromRoot; - - nixos.module = - { pkgs, ... }: - { - environment.systemPackages = [ pkgs.${nodeName} ]; + # inherit (import ./constants.nix) targetMachines pathToRoot pathFromRoot; + eval = + module: + (lib.evalModules { + specialArgs = { + inherit inputs; }; - }); + modules = [ + module + ../../data-model.nix + ]; + }).config; + fediversity = eval ( + { ... }: + { + config = { + environments.single-nixos-vm = + { ... }: + { + implementation = requests: { + input = requests; + output = { }; + }; + }; + }; + } + ); +in +fediversity.environments.single-nixos-vm.deployment { + enable = true; } diff --git a/deployment/check/data-model/flake-under-test.nix b/deployment/check/data-model/flake-under-test.nix deleted file mode 100644 index b9e3fb4b..00000000 --- a/deployment/check/data-model/flake-under-test.nix +++ /dev/null @@ -1,22 +0,0 @@ -{ - inputs = { - nixops4.follows = "nixops4-nixos/nixops4"; - nixops4-nixos.url = "github:nixops4/nixops4-nixos"; - }; - - outputs = - inputs: - import ./mkFlake.nix inputs ( - { inputs, sources, ... }: - { - imports = [ - inputs.nixops4.modules.flake.default - ]; - - nixops4Deployments.check-deployment-basic = { - imports = [ ./deployment/check/basic/deployment.nix ]; - _module.args = { inherit inputs sources; }; - }; - } - ); -} diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index d17ee833..b07b376f 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -1,4 +1,7 @@ -{ inputs, lib, ... }: +{ + lib, + ... +}: { _class = "nixosTest"; @@ -15,9 +18,6 @@ nodes.deployer = { pkgs, ... }: { - environment.systemPackages = [ - inputs.nixops4.packages.${pkgs.system}.default - ]; # FIXME: sad times system.extraDependencies = with pkgs; [ diff --git a/deployment/data-model.nix b/deployment/data-model.nix index 412f9bae..dd7ada19 100644 --- a/deployment/data-model.nix +++ b/deployment/data-model.nix @@ -1,7 +1,6 @@ { lib, config, - inputs, ... }: let @@ -26,24 +25,7 @@ let ); }; }; - nixops4Deployment = types.deferredModuleWith { - staticModules = [ - inputs.nixops4.modules.nixops4Deployment.default - - { - _class = "nixops4Deployment"; - _module.args = { - resourceProviderSystem = builtins.currentSystem; - resources = { }; - }; - } - ]; - }; deployment = attrTag { - nixops4 = mkOption { - description = "A NixOps4 NixOS deployment. For an example, see https://github.com/nixops4/nixops4-nixos/blob/main/example/deployment.nix."; - type = nixops4Deployment; - }; }; in { -- 2.48.1 From b65a8daa8261980638918e82aecac049bd88832a Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 10 Aug 2025 13:18:44 +0200 Subject: [PATCH 13/76] add deployment method: ssh --- deployment/check/data-model/deployment.nix | 17 ++++++- deployment/data-model.nix | 53 ++++++++++++++++++++-- 2 files changed, 65 insertions(+), 5 deletions(-) diff --git a/deployment/check/data-model/deployment.nix b/deployment/check/data-model/deployment.nix index 2074edc0..135b9944 100644 --- a/deployment/check/data-model/deployment.nix +++ b/deployment/check/data-model/deployment.nix @@ -2,7 +2,7 @@ inputs, # sources, lib, - # providers, + config, ... }: @@ -28,7 +28,20 @@ let { implementation = requests: { input = requests; - output = { }; + output.ssh-host = { + ssh = { + host = "localhost"; + username = "root"; + authentication.password = "password"; + }; + nixos-configuration = + { ... }: + { + users.users = config.resources.shell.login-shell.apply ( + lib.filterAttrs (_name: value: value ? login-shell) requests + ); + }; + }; }; }; }; diff --git a/deployment/data-model.nix b/deployment/data-model.nix index dd7ada19..230e18f1 100644 --- a/deployment/data-model.nix +++ b/deployment/data-model.nix @@ -6,12 +6,15 @@ let inherit (lib) mkOption types; inherit (lib.types) - attrsOf attrTag + attrsOf deferredModuleWith - submodule - optionType functionTo + nullOr + optionType + raw + str + submodule ; functionType = import ./function.nix; @@ -25,7 +28,51 @@ let ); }; }; + nixos-configuration = mkOption { + description = "A NixOS configuration."; + type = raw; + }; + host-ssh = mkOption { + description = "SSH connection info to connect to a single host."; + type = submodule { + options = { + host = mkOption { + description = "the host to access by SSH"; + type = str; + }; + username = mkOption { + description = "the SSH user to use"; + type = nullOr str; + default = null; + }; + authentication = mkOption { + description = "authentication method"; + type = attrTag { + private-key = mkOption { + description = "path to the user's SSH private key"; + type = str; + example = "/root/.ssh/id_ed25519"; + }; + password = mkOption { + description = "SSH password"; + # TODO: mark as sensitive + type = str; + }; + }; + }; + }; + }; + }; deployment = attrTag { + ssh-host = { + description = "A Terraform deployment by SSH to update a single existing NixOS host."; + type = submodule { + options = { + inherit nixos-configuration; + ssh = host-ssh; + }; + }; + }; }; in { -- 2.48.1 From ca77181bae988ba689b357ca464945eb4eb86beb Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Thu, 14 Aug 2025 10:44:36 +0200 Subject: [PATCH 14/76] wip: use ssh in test --- deployment/check/data-model/nixosTest.nix | 83 ++++++++++++++++++++++- deployment/nixos.nix | 14 ++++ 2 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 deployment/nixos.nix diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index b07b376f..beaa9c20 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -1,8 +1,12 @@ { lib, + config, + sources, ... }: - +let + inherit (import ./constants.nix) targetMachines pathToRoot; +in { _class = "nixosTest"; @@ -19,6 +23,10 @@ { pkgs, ... }: { + environment.systemPackages = with pkgs; [ + jq + ]; + # FIXME: sad times system.extraDependencies = with pkgs; [ jq @@ -40,8 +48,77 @@ hello.fail("hello 1>&2") cowsay.fail("cowsay 1>&2") - with subtest("Run the deployment"): - deployer.succeed("nixops4 apply check-deployment-basic --show-trace --no-interactive 1>&2") + ${lib.concatStringsSep "\n" ( + lib.lists.map (nodeName: '' + with subtest("Run the deployment for ${nodeName}"): + deployer.succeed(""" + set -euo pipefail + + # INSTANTIATE + command=( + nix-instantiate + --expr + + ' + let + system = builtins.currentSystem; + configuration = { pkgs, config, ... }: { + imports = [ + ${pathToRoot}/deployment/check/common/sharedOptions.nix + ${pathToRoot}/deployment/check/common/targetNode.nix + ]; + + _module.args = builtins.fromJSON "${ + lib.replaceStrings [ "\"" ] [ "\\\\\"" ] ( + lib.strings.toJSON { + inherit sources; + } + ) + }"; + enableAcme = ${lib.strings.toJSON config.enableAcme}; + acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; + + # environment.systemPackages = [ pkgs.hello ]; + }; + os = import "${sources.nixpkgs}/nixos" { inherit system configuration; }; + in + # import "${pathToRoot}/deployment/nixos.nix" {} + { + substituters = builtins.concatStringsSep " " os.config.nix.settings.substituters; + trusted_public_keys = builtins.concatStringsSep " " os.config.nix.settings.trusted-public-keys; + drv_path = os.config.system.build.toplevel.drvPath; + out_path = os.config.system.build.toplevel; + } + ' + ) + # instantiate the config in /nix/store + "''${command[@]}" -A out_path + # get the other info + json="$("''${command[@]}" --eval --strict --json)" + + # DEPLOY + declare substituters trusted_public_keys drv_path + # set our variables using the json object + eval "export $(echo $json | jq -r 'to_entries | map("\(.key)=\(.value)") | @sh')" + host="root@${nodeName}" + buildArgs=( + --option extra-binary-caches https://cache.nixos.org/ + --option substituters $substituters + --option trusted-public-keys $trusted_public_keys + ) + sshOpts=( + -o BatchMode=yes + -o StrictHostKeyChecking=no + ) + # get the realized derivation to deploy + outPath=$(nix-store --realize "$drv_path" "''${buildArgs[@]}") + # deploy the config by nix-copy-closure + NIX_SSHOPTS="''${sshOpts[*]}" nix-copy-closure --to "$host" "$outPath" --gzip --use-substitutes + # switch the remote host to the config + ssh "''${sshOpts[@]}" "$host" "nix-env --profile /nix/var/nix/profiles/system --set $outPath; $outPath/bin/switch-to-configuration switch" + """) + '') targetMachines + )} with subtest("Check the deployment"): hello.succeed("hello 1>&2") diff --git a/deployment/nixos.nix b/deployment/nixos.nix new file mode 100644 index 00000000..f2f0d019 --- /dev/null +++ b/deployment/nixos.nix @@ -0,0 +1,14 @@ +{ + configuration, + system ? builtins.currentSystem, +}: +let + sources = import ../npins; + os = import "${sources.nixpkgs}/nixos" { inherit system configuration; }; +in +{ + substituters = builtins.concatStringsSep " " os.config.nix.settings.substituters; + trusted_public_keys = builtins.concatStringsSep " " os.config.nix.settings.trusted-public-keys; + drv_path = os.config.system.build.toplevel.drvPath; + out_path = os.config.system.build.toplevel; +} -- 2.48.1 From dea95b63c816775037e05123960975ce889f63d3 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Mon, 18 Aug 2025 12:47:05 +0200 Subject: [PATCH 15/76] add keys --- deployment/check/common/targetNode.nix | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/deployment/check/common/targetNode.nix b/deployment/check/common/targetNode.nix index a346d873..8e760888 100644 --- a/deployment/check/common/targetNode.nix +++ b/deployment/check/common/targetNode.nix @@ -42,6 +42,18 @@ in ## Test VMs don't have a bootloader by default. boot.loader.grub.enable = false; + + users.users.root.openssh.authorizedKeys.keys = + let + keys = import ../../../keys; + in + lib.attrValues keys.contributors + ++ [ + # allow our panel vm access to the test machines + keys.panel + # allow continuous deployment access + keys.cd + ]; } (mkIf config.enableAcme { -- 2.48.1 From 33b9936ab8438780ea68d4ba206a3077080f511a Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Tue, 19 Aug 2025 12:00:35 +0200 Subject: [PATCH 16/76] pasteable command for trying without rebuilding vm --- paste | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 paste diff --git a/paste b/paste new file mode 100644 index 00000000..4a115c24 --- /dev/null +++ b/paste @@ -0,0 +1,59 @@ +declare substituters trusted_public_keys drv_path + +host="root@fedi203.abundos.eu" + +sshOpts=( + -o BatchMode=yes + -o StrictHostKeyChecking=no +) + +command=(nix-instantiate --expr ' + let + sources = import ./npins; + configuration = { + imports = [ + ./infra/common/proxmox-qemu-vm.nix + ./infra/common/nixos/users.nix + ./deployment/check/common/sharedOptions.nix + ./deployment/check/common/targetNode.nix + "${sources.disko}/module.nix" + ]; + }; + eval = import "${sources.nixpkgs}/nixos/lib/eval-config.nix" { + system = builtins.currentSystem; + specialArgs = { + inherit sources; + }; + modules = [ configuration ]; + }; + os = { + inherit (eval) pkgs config options; + system = eval.config.system.build.toplevel; + inherit (eval.config.system.build) vm vmWithBootLoader; + }; + in + { + substituters = builtins.concatStringsSep " " os.config.nix.settings.substituters; + trusted_public_keys = builtins.concatStringsSep " " os.config.nix.settings.trusted-public-keys; + drv_path = os.config.system.build.toplevel.drvPath; + out_path = os.config.system.build.toplevel; + } +') + +buildArgs=( + --option extra-binary-caches https://cache.nixos.org/ + --option substituters "$substituters" + --option trusted-public-keys "$trusted_public_keys" +) + +"${command[@]}" -A out_path + +json="$("${command[@]}" --eval --strict --json)" + +eval "export $(echo $json | jq -r 'to_entries | map("\(.key)=\(.value)") | @sh')" + +outPath=$(nix-store --realize "$drv_path" "${buildArgs[@]}") + +NIX_SSHOPTS="${sshOpts[*]}" nix-copy-closure --to "$host" "$outPath" --gzip --use-substitutes + +ssh "${sshOpts[@]}" "$host" "nix-env --profile /nix/var/nix/profiles/system --set $outPath; $outPath/bin/switch-to-configuration switch" -- 2.48.1 From 099a82a7e2d167e53deed6e9a5dd9bdbb5716c2b Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sat, 23 Aug 2025 12:10:45 +0200 Subject: [PATCH 17/76] ditch superfluous substituters --- deployment/check/data-model/nixosTest.nix | 11 ++--------- paste | 21 ++------------------- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index beaa9c20..2a85a0a8 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -84,8 +84,6 @@ in in # import "${pathToRoot}/deployment/nixos.nix" {} { - substituters = builtins.concatStringsSep " " os.config.nix.settings.substituters; - trusted_public_keys = builtins.concatStringsSep " " os.config.nix.settings.trusted-public-keys; drv_path = os.config.system.build.toplevel.drvPath; out_path = os.config.system.build.toplevel; } @@ -97,21 +95,16 @@ in json="$("''${command[@]}" --eval --strict --json)" # DEPLOY - declare substituters trusted_public_keys drv_path + declare drv_path # set our variables using the json object eval "export $(echo $json | jq -r 'to_entries | map("\(.key)=\(.value)") | @sh')" host="root@${nodeName}" - buildArgs=( - --option extra-binary-caches https://cache.nixos.org/ - --option substituters $substituters - --option trusted-public-keys $trusted_public_keys - ) sshOpts=( -o BatchMode=yes -o StrictHostKeyChecking=no ) # get the realized derivation to deploy - outPath=$(nix-store --realize "$drv_path" "''${buildArgs[@]}") + outPath=$(nix-store --realize "$drv_path") # deploy the config by nix-copy-closure NIX_SSHOPTS="''${sshOpts[*]}" nix-copy-closure --to "$host" "$outPath" --gzip --use-substitutes # switch the remote host to the config diff --git a/paste b/paste index 4a115c24..25e6190f 100644 --- a/paste +++ b/paste @@ -1,12 +1,9 @@ -declare substituters trusted_public_keys drv_path - +declare drv_path host="root@fedi203.abundos.eu" - sshOpts=( -o BatchMode=yes -o StrictHostKeyChecking=no ) - command=(nix-instantiate --expr ' let sources = import ./npins; @@ -33,27 +30,13 @@ command=(nix-instantiate --expr ' }; in { - substituters = builtins.concatStringsSep " " os.config.nix.settings.substituters; - trusted_public_keys = builtins.concatStringsSep " " os.config.nix.settings.trusted-public-keys; drv_path = os.config.system.build.toplevel.drvPath; out_path = os.config.system.build.toplevel; } ') - -buildArgs=( - --option extra-binary-caches https://cache.nixos.org/ - --option substituters "$substituters" - --option trusted-public-keys "$trusted_public_keys" -) - "${command[@]}" -A out_path - json="$("${command[@]}" --eval --strict --json)" - eval "export $(echo $json | jq -r 'to_entries | map("\(.key)=\(.value)") | @sh')" - -outPath=$(nix-store --realize "$drv_path" "${buildArgs[@]}") - +outPath=$(nix-store --realize "$drv_path") NIX_SSHOPTS="${sshOpts[*]}" nix-copy-closure --to "$host" "$outPath" --gzip --use-substitutes - ssh "${sshOpts[@]}" "$host" "nix-env --profile /nix/var/nix/profiles/system --set $outPath; $outPath/bin/switch-to-configuration switch" -- 2.48.1 From 5c0735d54516e97b2b59075079600f820705bb2b Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sat, 23 Aug 2025 12:54:56 +0200 Subject: [PATCH 18/76] move imports from paste to targetNode to increase parity between paste and nixosTest --- deployment/check/common/targetNode.nix | 4 ++++ paste | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deployment/check/common/targetNode.nix b/deployment/check/common/targetNode.nix index 8e760888..7c24aa1e 100644 --- a/deployment/check/common/targetNode.nix +++ b/deployment/check/common/targetNode.nix @@ -3,6 +3,7 @@ config, lib, modulesPath, + sources, ... }: @@ -17,7 +18,10 @@ in imports = [ (modulesPath + "/profiles/qemu-guest.nix") (modulesPath + "/../lib/testing/nixos-test-base.nix") + "${sources.disko}/module.nix" + ../../../infra/common/proxmox-qemu-vm.nix ./sharedOptions.nix + ../../../infra/common/nixos/users.nix ]; config = mkMerge [ diff --git a/paste b/paste index 25e6190f..89b3ed52 100644 --- a/paste +++ b/paste @@ -9,11 +9,7 @@ command=(nix-instantiate --expr ' sources = import ./npins; configuration = { imports = [ - ./infra/common/proxmox-qemu-vm.nix - ./infra/common/nixos/users.nix - ./deployment/check/common/sharedOptions.nix ./deployment/check/common/targetNode.nix - "${sources.disko}/module.nix" ]; }; eval = import "${sources.nixpkgs}/nixos/lib/eval-config.nix" { -- 2.48.1 From cb4bf9101a6e47133fb7be816743b07e7a37b1db Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 18:13:22 +0200 Subject: [PATCH 19/76] settle for hello, ditching cowsay --- deployment/check/data-model/constants.nix | 1 - deployment/check/data-model/nixosTest.nix | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/deployment/check/data-model/constants.nix b/deployment/check/data-model/constants.nix index 3cf28d8f..0c8576a7 100644 --- a/deployment/check/data-model/constants.nix +++ b/deployment/check/data-model/constants.nix @@ -1,7 +1,6 @@ { targetMachines = [ "hello" - "cowsay" ]; pathToRoot = ../../..; pathFromRoot = ./.; diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 2a85a0a8..127b2e2d 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -46,7 +46,6 @@ in extraTestScript = '' with subtest("Check the status before deployment"): hello.fail("hello 1>&2") - cowsay.fail("cowsay 1>&2") ${lib.concatStringsSep "\n" ( lib.lists.map (nodeName: '' @@ -110,11 +109,8 @@ in # switch the remote host to the config ssh "''${sshOpts[@]}" "$host" "nix-env --profile /nix/var/nix/profiles/system --set $outPath; $outPath/bin/switch-to-configuration switch" """) + ${nodeName}.succeed("${nodeName} 1>&2") '') targetMachines )} - - with subtest("Check the deployment"): - hello.succeed("hello 1>&2") - cowsay.succeed("cowsay hi 1>&2") ''; } -- 2.48.1 From de505fb430dddbc8b4687d0aa2ddfc8e04ba41b3 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 18:14:26 +0200 Subject: [PATCH 20/76] reduce download attempts in test --- deployment/check/common/deployerNode.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/deployment/check/common/deployerNode.nix b/deployment/check/common/deployerNode.nix index 4e0dd2de..fbf04332 100644 --- a/deployment/check/common/deployerNode.nix +++ b/deployment/check/common/deployerNode.nix @@ -49,6 +49,7 @@ in substituters = mkForce [ ]; hashed-mirrors = null; connect-timeout = 1; + download-attempts = 1; extra-experimental-features = "flakes"; }; -- 2.48.1 From fb29c3d07edd9d0dae42839aaa9a50df5933fd2b Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 18:15:36 +0200 Subject: [PATCH 21/76] ensure availability of needed inputs --- deployment/check/common/deployerNode.nix | 32 +++++++++++++++++++++++ deployment/check/data-model/nixosTest.nix | 6 +++++ 2 files changed, 38 insertions(+) diff --git a/deployment/check/common/deployerNode.nix b/deployment/check/common/deployerNode.nix index fbf04332..8c584fcf 100644 --- a/deployment/check/common/deployerNode.nix +++ b/deployment/check/common/deployerNode.nix @@ -62,6 +62,10 @@ in pkgs.stdenv pkgs.stdenvNoCC + pkgs.automake + pkgs.autoconf + pkgs.binutils + pkgs.bison ] ++ ( let @@ -93,7 +97,35 @@ in machine.system.build.vm.inputDerivation machine.system.build.bootStage1.inputDerivation machine.system.build.bootStage2.inputDerivation + pkgs.automake.inputDerivation + pkgs.autoconf.inputDerivation + pkgs.bash.inputDerivation + pkgs.binutils.inputDerivation + pkgs.bison.inputDerivation ] + ++ concatLists ( + lib.lists.map ( + pkg: + if + pkg ? inputDerivation + # error: output '/nix/store/dki9d3vldafg9ydrfm7x0g0rr0qljk98-sudo-1.9.16p2' is not allowed to refer to the following paths: + # /nix/store/2xdmps65ryklmbf025bm4pxv16gb8ajv-sudo-1.9.16p2.tar.gz + # /nix/store/58br4vk3q5akf4g8lx0pqzfhn47k3j8d-bash-5.2p37 + # /nix/store/8v6k283dpbc0qkdq81nb6mrxrgcb10i1-gcc-wrapper-14-20241116 + # /nix/store/9r1nl9ksiyszy4qzzg6y2gcdkca0xmhy-stdenv-linux + # /nix/store/a4rmp6in7igbl1wbz9pli5nq0wiclq0y-groff-1.23.0 + # /nix/store/dki9d3vldafg9ydrfm7x0g0rr0qljk98-sudo-1.9.16p2 + # /nix/store/f5y58qz2fzpzgkhp0nizixi10x04ppyy-linux-pam-1.6.1 + # /nix/store/shkw4qm9qcw5sc5n1k5jznc83ny02r39-default-builder.sh + # /nix/store/vj1c3wf9c11a0qs6p3ymfvrnsdgsdcbq-source-stdenv.sh + # /nix/store/yh6qg1nsi5h2xblcr67030pz58fsaxx3-coreutils-9.6 + && !(lib.strings.hasInfix "sudo" (builtins.toString pkg)) + then + [ pkg.inputDerivation ] + else + [ ] + ) machine.environment.systemPackages + ) ++ concatLists ( lib.mapAttrsToList ( _k: v: if v ? source.inputDerivation then [ v.source.inputDerivation ] else [ ] diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 127b2e2d..ffc49ab4 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -25,12 +25,16 @@ in environment.systemPackages = with pkgs; [ jq + automake + autoconf ]; # FIXME: sad times system.extraDependencies = with pkgs; [ jq jq.inputDerivation + automake + autoconf ]; system.extraDependenciesFromModule = @@ -39,6 +43,8 @@ in environment.systemPackages = with pkgs; [ hello cowsay + automake + autoconf ]; }; }; -- 2.48.1 From b5d42bb64c5dce51e0d5d4c0dc5c5143663d580f Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 18:18:26 +0200 Subject: [PATCH 22/76] move stuff not needed in test out --- deployment/check/common/targetNode.nix | 3 --- paste | 34 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/deployment/check/common/targetNode.nix b/deployment/check/common/targetNode.nix index 7c24aa1e..55320160 100644 --- a/deployment/check/common/targetNode.nix +++ b/deployment/check/common/targetNode.nix @@ -3,7 +3,6 @@ config, lib, modulesPath, - sources, ... }: @@ -18,8 +17,6 @@ in imports = [ (modulesPath + "/profiles/qemu-guest.nix") (modulesPath + "/../lib/testing/nixos-test-base.nix") - "${sources.disko}/module.nix" - ../../../infra/common/proxmox-qemu-vm.nix ./sharedOptions.nix ../../../infra/common/nixos/users.nix ]; diff --git a/paste b/paste index 89b3ed52..9fdae1b9 100644 --- a/paste +++ b/paste @@ -10,7 +10,41 @@ command=(nix-instantiate --expr ' configuration = { imports = [ ./deployment/check/common/targetNode.nix + "${sources.nixpkgs}/nixos/modules/profiles/qemu-guest.nix" + "${sources.disko}/module.nix" ]; + disabledModules = [ "virtualisation/qemu-vm.nix" ]; + config = { + nix.nixPath = lib.mapAttrsToList (k: v: k + "=" + v) sources; + networking = { + nameservers = [ + "95.215.185.6" + "95.215.185.7" + "2a00:51c0::5fd7:b906" + "2a00:51c0::5fd7:b907" + ]; + interfaces.eth0.ipv4.addresses = [ + { + address = "95.215.187.203"; + prefixLength = 24; + } + ]; + interfaces.eth0.ipv6.addresses = [ + { + address = "2a00:51c0:13:1305::203"; + prefixLength = 64; + } + ]; + defaultGateway = { + address = "95.215.187.1"; + interface = "eth0"; + }; + defaultGateway6 = { + address = "2a00:51c0:13:1305::1"; + interface = "eth0"; + }; + }; + }; }; eval = import "${sources.nixpkgs}/nixos/lib/eval-config.nix" { system = builtins.currentSystem; -- 2.48.1 From 7eeb0fbdf6bb0edffc4e1be1da889aa5f0a8707e Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 18:18:49 +0200 Subject: [PATCH 23/76] rm paste --- paste | 72 ----------------------------------------------------------- 1 file changed, 72 deletions(-) delete mode 100644 paste diff --git a/paste b/paste deleted file mode 100644 index 9fdae1b9..00000000 --- a/paste +++ /dev/null @@ -1,72 +0,0 @@ -declare drv_path -host="root@fedi203.abundos.eu" -sshOpts=( - -o BatchMode=yes - -o StrictHostKeyChecking=no -) -command=(nix-instantiate --expr ' - let - sources = import ./npins; - configuration = { - imports = [ - ./deployment/check/common/targetNode.nix - "${sources.nixpkgs}/nixos/modules/profiles/qemu-guest.nix" - "${sources.disko}/module.nix" - ]; - disabledModules = [ "virtualisation/qemu-vm.nix" ]; - config = { - nix.nixPath = lib.mapAttrsToList (k: v: k + "=" + v) sources; - networking = { - nameservers = [ - "95.215.185.6" - "95.215.185.7" - "2a00:51c0::5fd7:b906" - "2a00:51c0::5fd7:b907" - ]; - interfaces.eth0.ipv4.addresses = [ - { - address = "95.215.187.203"; - prefixLength = 24; - } - ]; - interfaces.eth0.ipv6.addresses = [ - { - address = "2a00:51c0:13:1305::203"; - prefixLength = 64; - } - ]; - defaultGateway = { - address = "95.215.187.1"; - interface = "eth0"; - }; - defaultGateway6 = { - address = "2a00:51c0:13:1305::1"; - interface = "eth0"; - }; - }; - }; - }; - eval = import "${sources.nixpkgs}/nixos/lib/eval-config.nix" { - system = builtins.currentSystem; - specialArgs = { - inherit sources; - }; - modules = [ configuration ]; - }; - os = { - inherit (eval) pkgs config options; - system = eval.config.system.build.toplevel; - inherit (eval.config.system.build) vm vmWithBootLoader; - }; - in - { - drv_path = os.config.system.build.toplevel.drvPath; - out_path = os.config.system.build.toplevel; - } -') -"${command[@]}" -A out_path -json="$("${command[@]}" --eval --strict --json)" -eval "export $(echo $json | jq -r 'to_entries | map("\(.key)=\(.value)") | @sh')" -outPath=$(nix-store --realize "$drv_path") -NIX_SSHOPTS="${sshOpts[*]}" nix-copy-closure --to "$host" "$outPath" --gzip --use-substitutes -ssh "${sshOpts[@]}" "$host" "nix-env --profile /nix/var/nix/profiles/system --set $outPath; $outPath/bin/switch-to-configuration switch" -- 2.48.1 From f7b7e919604e0110a69eaa16e2c89d8cde3efcb5 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 18:19:53 +0200 Subject: [PATCH 24/76] ensure inputs --- deployment/check/data-model/nixosTest.nix | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index ffc49ab4..45c7201f 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -83,7 +83,11 @@ in enableAcme = ${lib.strings.toJSON config.enableAcme}; acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; - # environment.systemPackages = [ pkgs.hello ]; + environment.systemPackages = with pkgs; [ + hello + automake + autoconf + ]; }; os = import "${sources.nixpkgs}/nixos" { inherit system configuration; }; in -- 2.48.1 From 7d1f0c61af8fcdc7afbd24c9f453bcc9546a8ebd Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 18:21:38 +0200 Subject: [PATCH 25/76] nix in tests: download-attempts = 1 --- deployment/check/common/targetNode.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deployment/check/common/targetNode.nix b/deployment/check/common/targetNode.nix index 55320160..5ec8f47d 100644 --- a/deployment/check/common/targetNode.nix +++ b/deployment/check/common/targetNode.nix @@ -32,6 +32,9 @@ in ## Not used; save a large copy operation channel.enable = false; registry = lib.mkForce { }; + settings = { + download-attempts = 1; + }; }; services.openssh = { -- 2.48.1 From ff8d29177f15982d0229c8a41c723164b30dc999 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 18:25:00 +0200 Subject: [PATCH 26/76] specialArgs: sources --- deployment/check/data-model/nixosTest.nix | 29 ++++++++++++++++------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 45c7201f..0e5b3a6a 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -66,20 +66,20 @@ in ' let - system = builtins.currentSystem; + args = builtins.fromJSON "${ + lib.replaceStrings [ "\"" ] [ "\\\\\"" ] ( + lib.strings.toJSON { + inherit sources; + } + ) + }"; + inherit (args) sources; configuration = { pkgs, config, ... }: { imports = [ ${pathToRoot}/deployment/check/common/sharedOptions.nix ${pathToRoot}/deployment/check/common/targetNode.nix ]; - _module.args = builtins.fromJSON "${ - lib.replaceStrings [ "\"" ] [ "\\\\\"" ] ( - lib.strings.toJSON { - inherit sources; - } - ) - }"; enableAcme = ${lib.strings.toJSON config.enableAcme}; acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; @@ -89,7 +89,18 @@ in autoconf ]; }; - os = import "${sources.nixpkgs}/nixos" { inherit system configuration; }; + eval = import "${sources.nixpkgs}/nixos/lib/eval-config.nix" { + system = builtins.currentSystem; + specialArgs = { + inherit sources; + }; + modules = [ configuration ]; + }; + os = { + inherit (eval) pkgs config options; + system = eval.config.system.build.toplevel; + inherit (eval.config.system.build) vm vmWithBootLoader; + }; in # import "${pathToRoot}/deployment/nixos.nix" {} { -- 2.48.1 From 9bb52618fa597ddf7f81a376e7782abed05146ea Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 18:26:42 +0200 Subject: [PATCH 27/76] handle test outcome --- deployment/check/data-model/nixosTest.nix | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 0e5b3a6a..050fb0ad 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -122,14 +122,27 @@ in sshOpts=( -o BatchMode=yes -o StrictHostKeyChecking=no + -o "ConnectionAttempts=1" + -o "ConnectTimeout=1" + -o "ServerAliveCountMax=1" + -o "ServerAliveInterval=1" ) # get the realized derivation to deploy outPath=$(nix-store --realize "$drv_path") # deploy the config by nix-copy-closure NIX_SSHOPTS="''${sshOpts[*]}" nix-copy-closure --to "$host" "$outPath" --gzip --use-substitutes # switch the remote host to the config - ssh "''${sshOpts[@]}" "$host" "nix-env --profile /nix/var/nix/profiles/system --set $outPath; $outPath/bin/switch-to-configuration switch" + output=$(ssh "''${sshOpts[@]}" "$host" "nix-env --profile /nix/var/nix/profiles/system --set $outPath; nohup $outPath/bin/switch-to-configuration switch &" 2>&1) || echo "status code: $?" + echo "output: $output" + if [[ $output != *"Timeout, server ${nodeName} not responding"* ]]; then + echo "non-timeout error: $output" + exit 1 + else + exit 0 + fi """) + ${nodeName}.wait_for_unit("multi-user.target") + ${nodeName}.succeed("systemctl is-active sshd") ${nodeName}.succeed("${nodeName} 1>&2") '') targetMachines )} -- 2.48.1 From 81eef5498206bf9aa45b82d4ef00e489c696585f Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 18:27:19 +0200 Subject: [PATCH 28/76] users --- deployment/check/common/targetNode.nix | 28 ++++++++++++++++---------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/deployment/check/common/targetNode.nix b/deployment/check/common/targetNode.nix index 5ec8f47d..30299b0d 100644 --- a/deployment/check/common/targetNode.nix +++ b/deployment/check/common/targetNode.nix @@ -47,17 +47,23 @@ in ## Test VMs don't have a bootloader by default. boot.loader.grub.enable = false; - users.users.root.openssh.authorizedKeys.keys = - let - keys = import ../../../keys; - in - lib.attrValues keys.contributors - ++ [ - # allow our panel vm access to the test machines - keys.panel - # allow continuous deployment access - keys.cd - ]; + users.mutableUsers = false; + users.users.root = { + password = "password"; + hashedPassword = null; + hashedPasswordFile = null; + openssh.authorizedKeys.keys = + let + keys = import ../../../keys; + in + lib.attrValues keys.contributors + ++ [ + # allow our panel vm access to the test machines + keys.panel + # allow continuous deployment access + keys.cd + ]; + }; } (mkIf config.enableAcme { -- 2.48.1 From 75716ed6c35edfb5c09c51b8168188ee40bea117 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 18:30:43 +0200 Subject: [PATCH 29/76] networking --- deployment/check/common/targetNode.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/deployment/check/common/targetNode.nix b/deployment/check/common/targetNode.nix index 30299b0d..e3d0c40b 100644 --- a/deployment/check/common/targetNode.nix +++ b/deployment/check/common/targetNode.nix @@ -42,7 +42,10 @@ in settings.PermitRootLogin = "yes"; }; - networking.firewall.allowedTCPPorts = [ 22 ]; + networking = { + firewall.enable = false; + enableIPv6 = false; + }; ## Test VMs don't have a bootloader by default. boot.loader.grub.enable = false; -- 2.48.1 From 133b0a0bb0b4ade650e669db192cd9a71130b116 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 18:30:52 +0200 Subject: [PATCH 30/76] auto login --- deployment/check/common/targetNode.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deployment/check/common/targetNode.nix b/deployment/check/common/targetNode.nix index e3d0c40b..3db097b8 100644 --- a/deployment/check/common/targetNode.nix +++ b/deployment/check/common/targetNode.nix @@ -47,6 +47,8 @@ in enableIPv6 = false; }; + services.getty.autologinUser = lib.mkForce "root"; + ## Test VMs don't have a bootloader by default. boot.loader.grub.enable = false; -- 2.48.1 From 60160071432f8dee3bf36321df4183b19231f621 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 18:31:34 +0200 Subject: [PATCH 31/76] grub --- deployment/check/common/targetNode.nix | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/deployment/check/common/targetNode.nix b/deployment/check/common/targetNode.nix index 3db097b8..c5986a28 100644 --- a/deployment/check/common/targetNode.nix +++ b/deployment/check/common/targetNode.nix @@ -50,7 +50,20 @@ in services.getty.autologinUser = lib.mkForce "root"; ## Test VMs don't have a bootloader by default. - boot.loader.grub.enable = false; + # boot.loader = { + # # GRUB enabled: installation of GRUB on /dev/disk/by-id/virtio-root failed: No such file or directory + # grub.enable = false; + # # systemd boot enabled: '/boot' is not a mounted partition. Is the path configured correctly? + # systemd-boot.enable = true; + # efi.canTouchEfiVariables = true; + # }; + # # same issue as no bootloader + # boot.loader.generic-extlinux-compatible.enable = false; + # builds but won't boot back up + boot.loader.grub.forceInstall = true; + # # builds but won't boot back up + # # to be used with --no-bootloader, which i could only find for flakes + # boot.loader.grub.enable = false; users.mutableUsers = false; users.users.root = { -- 2.48.1 From 35ee0c638535bcfc61089a2e0570c00f0f549ba5 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 18:32:12 +0200 Subject: [PATCH 32/76] qemu guest --- deployment/check/data-model/nixosTest.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 050fb0ad..5a7de78b 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -78,6 +78,7 @@ in imports = [ ${pathToRoot}/deployment/check/common/sharedOptions.nix ${pathToRoot}/deployment/check/common/targetNode.nix + ${sources.nixpkgs}/nixos/modules/profiles/qemu-guest.nix ]; enableAcme = ${lib.strings.toJSON config.enableAcme}; -- 2.48.1 From f24347c7d0cf1339105b6276ef3948f050a05292 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 19:26:16 +0200 Subject: [PATCH 33/76] dedupe nixosTest.nix --- deployment/check/basic/constants.nix | 1 + deployment/check/basic/nixosTest.nix | 8 +- deployment/check/cli/constants.nix | 1 + deployment/check/cli/nixosTest.nix | 2 + deployment/check/common/nixosTest.nix | 59 +++--- deployment/check/common/sharedOptions.nix | 2 + .../check/data-model/common-nixosTest.nix | 168 ------------------ deployment/check/data-model/default.nix | 2 +- deployment/check/panel/constants.nix | 1 + deployment/check/panel/nixosTest.nix | 1 + 10 files changed, 48 insertions(+), 197 deletions(-) delete mode 100644 deployment/check/data-model/common-nixosTest.nix diff --git a/deployment/check/basic/constants.nix b/deployment/check/basic/constants.nix index 3cf28d8f..f2573fb8 100644 --- a/deployment/check/basic/constants.nix +++ b/deployment/check/basic/constants.nix @@ -5,4 +5,5 @@ ]; pathToRoot = ../../..; pathFromRoot = ./.; + useFlake = true; } diff --git a/deployment/check/basic/nixosTest.nix b/deployment/check/basic/nixosTest.nix index 93c8ad23..10ed196f 100644 --- a/deployment/check/basic/nixosTest.nix +++ b/deployment/check/basic/nixosTest.nix @@ -1,4 +1,9 @@ -{ inputs, lib, ... }: +{ + inputs, + lib, + config, + ... +}: { _class = "nixosTest"; @@ -8,6 +13,7 @@ sourceFileset = lib.fileset.unions [ ./constants.nix ./deployment.nix + (config.pathToCwd + "/flake-under-test.nix") ]; nodes.deployer = diff --git a/deployment/check/cli/constants.nix b/deployment/check/cli/constants.nix index ce3b5e4d..a48d9076 100644 --- a/deployment/check/cli/constants.nix +++ b/deployment/check/cli/constants.nix @@ -8,4 +8,5 @@ pathToRoot = ../../..; pathFromRoot = ./.; enableAcme = true; + useFlake = true; } diff --git a/deployment/check/cli/nixosTest.nix b/deployment/check/cli/nixosTest.nix index d171a182..f7f1cf2d 100644 --- a/deployment/check/cli/nixosTest.nix +++ b/deployment/check/cli/nixosTest.nix @@ -1,6 +1,7 @@ { inputs, hostPkgs, + config, lib, ... }: @@ -19,6 +20,7 @@ in sourceFileset = lib.fileset.unions [ ./constants.nix ./deployments.nix + (config.pathToCwd + "/flake-under-test.nix") # REVIEW: I would like to be able to grab all of `/deployment` minus # `/deployment/check`, but I can't because there is a bunch of other files diff --git a/deployment/check/common/nixosTest.nix b/deployment/check/common/nixosTest.nix index bc30edba..93bd3fef 100644 --- a/deployment/check/common/nixosTest.nix +++ b/deployment/check/common/nixosTest.nix @@ -76,8 +76,6 @@ in ./sharedOptions.nix ./targetNode.nix ./targetResource.nix - - (config.pathToCwd + "/flake-under-test.nix") ]; acmeNodeIP = config.nodes.acme.networking.primaryIPAddress; @@ -164,31 +162,38 @@ in deployer.succeed(f"echo '{host_key}' > ${config.pathFromRoot}/${tm}_host_key.pub") '')} - ## NOTE: This is super slow. It could probably be optimised in Nix, for - ## instance by allowing to grab things directly from the host's store. - ## - ## NOTE: We use the repository as-is (cf `src` above), overriding only - ## `flake.nix` by our `flake-under-test.nix`. We also override the flake - ## lock file to use locally available inputs, as we cannot download them. - ## - with subtest("Override the flake and its lock"): - deployer.succeed("cp ${config.pathFromRoot}/flake-under-test.nix flake.nix") - deployer.succeed(""" - nix flake lock --extra-experimental-features 'flakes nix-command' \ - --offline -v \ - --override-input nixops4 ${inputs.nixops4.packages.${system}.flake-in-a-bottle} \ - \ - --override-input nixops4-nixos ${inputs.nixops4-nixos} \ - --override-input nixops4-nixos/flake-parts ${inputs.nixops4-nixos.inputs.flake-parts} \ - --override-input nixops4-nixos/flake-parts/nixpkgs-lib ${inputs.nixops4-nixos.inputs.flake-parts.inputs.nixpkgs-lib} \ - --override-input nixops4-nixos/nixops4-nixos ${emptyFlake} \ - --override-input nixops4-nixos/nixpkgs ${inputs.nixops4-nixos.inputs.nixpkgs} \ - --override-input nixops4-nixos/nixops4 ${ - inputs.nixops4-nixos.inputs.nixops4.packages.${system}.flake-in-a-bottle - } \ - --override-input nixops4-nixos/git-hooks-nix ${emptyFlake} \ - ; - """) + ${ + if config.useFlake then + '' + ## NOTE: This is super slow. It could probably be optimised in Nix, for + ## instance by allowing to grab things directly from the host's store. + ## + ## NOTE: We use the repository as-is (cf `src` above), overriding only + ## `flake.nix` by our `flake-under-test.nix`. We also override the flake + ## lock file to use locally available inputs, as we cannot download them. + ## + with subtest("Override the flake and its lock"): + deployer.succeed("cp ${config.pathFromRoot}/flake-under-test.nix flake.nix") + deployer.succeed(""" + nix flake lock --extra-experimental-features 'flakes nix-command' \ + --offline -v \ + --override-input nixops4 ${inputs.nixops4.packages.${system}.flake-in-a-bottle} \ + \ + --override-input nixops4-nixos ${inputs.nixops4-nixos} \ + --override-input nixops4-nixos/flake-parts ${inputs.nixops4-nixos.inputs.flake-parts} \ + --override-input nixops4-nixos/flake-parts/nixpkgs-lib ${inputs.nixops4-nixos.inputs.flake-parts.inputs.nixpkgs-lib} \ + --override-input nixops4-nixos/nixops4-nixos ${emptyFlake} \ + --override-input nixops4-nixos/nixpkgs ${inputs.nixops4-nixos.inputs.nixpkgs} \ + --override-input nixops4-nixos/nixops4 ${ + inputs.nixops4-nixos.inputs.nixops4.packages.${system}.flake-in-a-bottle + } \ + --override-input nixops4-nixos/git-hooks-nix ${emptyFlake} \ + ; + """) + '' + else + "" + } ${optionalString config.enableAcme '' with subtest("Set up handmade DNS"): diff --git a/deployment/check/common/sharedOptions.nix b/deployment/check/common/sharedOptions.nix index 7201a8f5..c0efc6cf 100644 --- a/deployment/check/common/sharedOptions.nix +++ b/deployment/check/common/sharedOptions.nix @@ -64,5 +64,7 @@ in during the test to the correct value. ''; }; + + useFlake = lib.mkEnableOption "Use a flake in the test."; }; } diff --git a/deployment/check/data-model/common-nixosTest.nix b/deployment/check/data-model/common-nixosTest.nix deleted file mode 100644 index aa912dc3..00000000 --- a/deployment/check/data-model/common-nixosTest.nix +++ /dev/null @@ -1,168 +0,0 @@ -{ - inputs, - lib, - config, - hostPkgs, - sources, - ... -}: - -let - inherit (builtins) - concatStringsSep - toJSON - ; - inherit (lib) - types - fileset - mkOption - genAttrs - attrNames - optionalString - ; - inherit (hostPkgs) - writeText - ; - - forConcat = xs: f: concatStringsSep "\n" (map f xs); - -in -{ - _class = "nixosTest"; - - imports = [ - ../common/sharedOptions.nix - ]; - - options = { - ## FIXME: I wish I could just use `testScript` but with something like - ## `mkOrder` to put this module's string before something else. - extraTestScript = mkOption { }; - - sourceFileset = mkOption { - ## REVIEW: Upstream to nixpkgs? - type = types.mkOptionType { - name = "fileset"; - description = "fileset"; - descriptionClass = "noun"; - check = (x: (builtins.tryEval (fileset.unions [ x ])).success); - merge = (_: defs: fileset.unions (map (x: x.value) defs)); - }; - description = '' - A fileset that will be copied to the deployer node in the current - working directory. This should contain all the files that are - necessary to run that particular test, such as the NixOS - modules necessary to evaluate a deployment. - ''; - }; - }; - - config = { - sourceFileset = fileset.unions [ - ../../../mkFlake.nix - ../../../flake.lock - ../../../npins - ../../data-model.nix - ../../function.nix - - ../common/sharedOptions.nix - ../common/targetNode.nix - ../common/targetResource.nix - ]; - - acmeNodeIP = config.nodes.acme.networking.primaryIPAddress; - - nodes = - { - deployer = { - imports = [ ../common/deployerNode.nix ]; - _module.args = { inherit inputs sources; }; - enableAcme = config.enableAcme; - acmeNodeIP = config.nodes.acme.networking.primaryIPAddress; - }; - } - - // - - ( - if config.enableAcme then - { - acme = { - ## FIXME: This makes `nodes.acme` into a local resolver. Maybe this will - ## break things once we play with DNS? - imports = [ "${inputs.nixpkgs}/nixos/tests/common/acme/server" ]; - ## We aren't testing ACME - we just want certificates. - systemd.services.pebble.environment.PEBBLE_VA_ALWAYS_VALID = "1"; - }; - } - else - { } - ) - - // - - genAttrs config.targetMachines (_: { - imports = [ ../common/targetNode.nix ]; - _module.args = { inherit inputs sources; }; - enableAcme = config.enableAcme; - acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; - }); - - testScript = '' - ${forConcat (attrNames config.nodes) (n: '' - ${n}.start() - '')} - - ${forConcat (attrNames config.nodes) (n: '' - ${n}.wait_for_unit("multi-user.target") - '')} - - ## A subset of the repository that is necessary for this test. It will be - ## copied inside the test. The smaller this set, the faster our CI, because we - ## won't need to re-run when things change outside of it. - with subtest("Unpacking"): - deployer.succeed("cp -r --no-preserve=mode ${ - fileset.toSource { - root = ../../..; - fileset = config.sourceFileset; - } - }/* .") - - with subtest("Configure the network"): - ${forConcat config.targetMachines ( - tm: - let - targetNetworkJSON = writeText "target-network.json" ( - toJSON config.nodes.${tm}.system.build.networkConfig - ); - in - '' - deployer.copy_from_host("${targetNetworkJSON}", "${config.pathFromRoot}/${tm}-network.json") - '' - )} - - with subtest("Configure the deployer key"): - deployer.succeed("""mkdir -p ~/.ssh && ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa""") - deployer_key = deployer.succeed("cat ~/.ssh/id_rsa.pub").strip() - ${forConcat config.targetMachines (tm: '' - ${tm}.succeed(f"mkdir -p /root/.ssh && echo '{deployer_key}' >> /root/.ssh/authorized_keys") - '')} - - with subtest("Configure the target host key"): - ${forConcat config.targetMachines (tm: '' - host_key = ${tm}.succeed("ssh-keyscan ${tm} | grep -v '^#' | cut -f 2- -d ' ' | head -n 1") - deployer.succeed(f"echo '{host_key}' > ${config.pathFromRoot}/${tm}_host_key.pub") - '')} - - # with subtest("Override the flake and its lock"): - # deployer.succeed("cp ${config.pathFromRoot}/flake-under-test.nix flake.nix") - - ${optionalString config.enableAcme '' - with subtest("Set up handmade DNS"): - deployer.succeed("echo '${config.nodes.acme.networking.primaryIPAddress}' > ${config.pathFromRoot}/acme_server_ip") - ''} - - ${config.extraTestScript} - ''; - }; -} diff --git a/deployment/check/data-model/default.nix b/deployment/check/data-model/default.nix index 5eafbbbc..11c1fb37 100644 --- a/deployment/check/data-model/default.nix +++ b/deployment/check/data-model/default.nix @@ -8,7 +8,7 @@ runNixOSTest { imports = [ ../../data-model.nix ../../function.nix - ./common-nixosTest.nix + ../common/nixosTest.nix ./nixosTest.nix ]; _module.args = { inherit inputs sources; }; diff --git a/deployment/check/panel/constants.nix b/deployment/check/panel/constants.nix index ce3b5e4d..a48d9076 100644 --- a/deployment/check/panel/constants.nix +++ b/deployment/check/panel/constants.nix @@ -8,4 +8,5 @@ pathToRoot = ../../..; pathFromRoot = ./.; enableAcme = true; + useFlake = true; } diff --git a/deployment/check/panel/nixosTest.nix b/deployment/check/panel/nixosTest.nix index ffcb8e53..fddad457 100644 --- a/deployment/check/panel/nixosTest.nix +++ b/deployment/check/panel/nixosTest.nix @@ -128,6 +128,7 @@ in sourceFileset = lib.fileset.unions [ ./constants.nix ./deployment.nix + (config.pathToCwd + "/flake-under-test.nix") # REVIEW: I would like to be able to grab all of `/deployment` minus # `/deployment/check`, but I can't because there is a bunch of other files -- 2.48.1 From 35841f172c3073c1f43c8b07cc29d99cd31fcb14 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 19:45:40 +0200 Subject: [PATCH 34/76] restore imports --- deployment/check/common/deployerNode.nix | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/deployment/check/common/deployerNode.nix b/deployment/check/common/deployerNode.nix index 8c584fcf..3debfe3b 100644 --- a/deployment/check/common/deployerNode.nix +++ b/deployment/check/common/deployerNode.nix @@ -55,6 +55,11 @@ in system.extraDependencies = [ + inputs.nixops4 + inputs.nixops4-nixos + inputs.nixpkgs + + sources.flake-parts sources.nixpkgs sources.flake-inputs -- 2.48.1 From 239f556aa974ea69c1d0a9e0b6c6a33ebd609bd8 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 19:45:45 +0200 Subject: [PATCH 35/76] rm comment --- deployment/check/data-model/nixosTest.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 5a7de78b..238a8b1e 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -103,7 +103,6 @@ in inherit (eval.config.system.build) vm vmWithBootLoader; }; in - # import "${pathToRoot}/deployment/nixos.nix" {} { drv_path = os.config.system.build.toplevel.drvPath; out_path = os.config.system.build.toplevel; -- 2.48.1 From af4b04cf0580cf43679a20943d930c45fe85e246 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 19:53:30 +0200 Subject: [PATCH 36/76] download-attempts: settle for just targetNode --- deployment/check/common/deployerNode.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/deployment/check/common/deployerNode.nix b/deployment/check/common/deployerNode.nix index 3debfe3b..c15ebe83 100644 --- a/deployment/check/common/deployerNode.nix +++ b/deployment/check/common/deployerNode.nix @@ -49,7 +49,6 @@ in substituters = mkForce [ ]; hashed-mirrors = null; connect-timeout = 1; - download-attempts = 1; extra-experimental-features = "flakes"; }; -- 2.48.1 From 6cdf038d544702d922b7dfbff611b25917f0bead Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 20:00:09 +0200 Subject: [PATCH 37/76] mv attempts --- deployment/check/common/targetNode.nix | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/deployment/check/common/targetNode.nix b/deployment/check/common/targetNode.nix index c5986a28..1a0c221d 100644 --- a/deployment/check/common/targetNode.nix +++ b/deployment/check/common/targetNode.nix @@ -29,12 +29,11 @@ in system.switch.enable = true; nix = { + # short-cut network time-outs + settings.download-attempts = 1; ## Not used; save a large copy operation channel.enable = false; registry = lib.mkForce { }; - settings = { - download-attempts = 1; - }; }; services.openssh = { -- 2.48.1 From f65c82503c97662a5a725d4d2b9627bc57622aaa Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 20:00:13 +0200 Subject: [PATCH 38/76] rm getty --- deployment/check/common/targetNode.nix | 2 -- 1 file changed, 2 deletions(-) diff --git a/deployment/check/common/targetNode.nix b/deployment/check/common/targetNode.nix index 1a0c221d..609a3366 100644 --- a/deployment/check/common/targetNode.nix +++ b/deployment/check/common/targetNode.nix @@ -46,8 +46,6 @@ in enableIPv6 = false; }; - services.getty.autologinUser = lib.mkForce "root"; - ## Test VMs don't have a bootloader by default. # boot.loader = { # # GRUB enabled: installation of GRUB on /dev/disk/by-id/virtio-root failed: No such file or directory -- 2.48.1 From c2c8091b29c0ffd429b1392d2dcbfa1f2be5badb Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 20:00:23 +0200 Subject: [PATCH 39/76] rm comments --- deployment/check/data-model/deployment.nix | 2 -- 1 file changed, 2 deletions(-) diff --git a/deployment/check/data-model/deployment.nix b/deployment/check/data-model/deployment.nix index 135b9944..a6cf51f8 100644 --- a/deployment/check/data-model/deployment.nix +++ b/deployment/check/data-model/deployment.nix @@ -1,13 +1,11 @@ { inputs, - # sources, lib, config, ... }: let - # inherit (import ./constants.nix) targetMachines pathToRoot pathFromRoot; eval = module: (lib.evalModules { -- 2.48.1 From 0eb14ba64fe023b179465e1c22085b11b1af8a14 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 20:04:02 +0200 Subject: [PATCH 40/76] reenable firewall --- deployment/check/common/targetNode.nix | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/deployment/check/common/targetNode.nix b/deployment/check/common/targetNode.nix index 609a3366..689fe08d 100644 --- a/deployment/check/common/targetNode.nix +++ b/deployment/check/common/targetNode.nix @@ -41,10 +41,8 @@ in settings.PermitRootLogin = "yes"; }; - networking = { - firewall.enable = false; - enableIPv6 = false; - }; + networking.firewall.allowedTCPPorts = [ 22 ]; + networking.enableIPv6 = false; ## Test VMs don't have a bootloader by default. # boot.loader = { -- 2.48.1 From 6a56954e82fc3e82b4d76bad05801e064df6f27d Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 20:07:08 +0200 Subject: [PATCH 41/76] reenable ipv6 --- deployment/check/common/targetNode.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/deployment/check/common/targetNode.nix b/deployment/check/common/targetNode.nix index 689fe08d..01dd1f3a 100644 --- a/deployment/check/common/targetNode.nix +++ b/deployment/check/common/targetNode.nix @@ -42,7 +42,6 @@ in }; networking.firewall.allowedTCPPorts = [ 22 ]; - networking.enableIPv6 = false; ## Test VMs don't have a bootloader by default. # boot.loader = { -- 2.48.1 From 8304b892d7066bd9142e93fb58ca3524ac0a9901 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 20:10:44 +0200 Subject: [PATCH 42/76] rm users --- deployment/check/common/targetNode.nix | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/deployment/check/common/targetNode.nix b/deployment/check/common/targetNode.nix index 01dd1f3a..35c17345 100644 --- a/deployment/check/common/targetNode.nix +++ b/deployment/check/common/targetNode.nix @@ -58,24 +58,6 @@ in # # builds but won't boot back up # # to be used with --no-bootloader, which i could only find for flakes # boot.loader.grub.enable = false; - - users.mutableUsers = false; - users.users.root = { - password = "password"; - hashedPassword = null; - hashedPasswordFile = null; - openssh.authorizedKeys.keys = - let - keys = import ../../../keys; - in - lib.attrValues keys.contributors - ++ [ - # allow our panel vm access to the test machines - keys.panel - # allow continuous deployment access - keys.cd - ]; - }; } (mkIf config.enableAcme { -- 2.48.1 From 45ef1609fcc772b2a9b7ac2b7b6f177302a59a2b Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 20:14:54 +0200 Subject: [PATCH 43/76] simplify grub --- deployment/check/common/targetNode.nix | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/deployment/check/common/targetNode.nix b/deployment/check/common/targetNode.nix index 35c17345..d6497fd0 100644 --- a/deployment/check/common/targetNode.nix +++ b/deployment/check/common/targetNode.nix @@ -44,20 +44,7 @@ in networking.firewall.allowedTCPPorts = [ 22 ]; ## Test VMs don't have a bootloader by default. - # boot.loader = { - # # GRUB enabled: installation of GRUB on /dev/disk/by-id/virtio-root failed: No such file or directory - # grub.enable = false; - # # systemd boot enabled: '/boot' is not a mounted partition. Is the path configured correctly? - # systemd-boot.enable = true; - # efi.canTouchEfiVariables = true; - # }; - # # same issue as no bootloader - # boot.loader.generic-extlinux-compatible.enable = false; - # builds but won't boot back up - boot.loader.grub.forceInstall = true; - # # builds but won't boot back up - # # to be used with --no-bootloader, which i could only find for flakes - # boot.loader.grub.enable = false; + boot.loader.grub.enable = false; } (mkIf config.enableAcme { -- 2.48.1 From a6d91b6c0275a902eda57f78825d6fc360a9eeef Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 20:15:22 +0200 Subject: [PATCH 44/76] rm users --- deployment/check/common/targetNode.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/deployment/check/common/targetNode.nix b/deployment/check/common/targetNode.nix index d6497fd0..e88be811 100644 --- a/deployment/check/common/targetNode.nix +++ b/deployment/check/common/targetNode.nix @@ -18,7 +18,6 @@ in (modulesPath + "/profiles/qemu-guest.nix") (modulesPath + "/../lib/testing/nixos-test-base.nix") ./sharedOptions.nix - ../../../infra/common/nixos/users.nix ]; config = mkMerge [ -- 2.48.1 From eaa203fa9af14e6ce7c9139c928be7a099a0ef4b Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 20:27:54 +0200 Subject: [PATCH 45/76] factor out to nixos.nix --- deployment/check/data-model/nixosTest.nix | 17 +---------------- deployment/nixos.nix | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 238a8b1e..52d6f70e 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -90,23 +90,8 @@ in autoconf ]; }; - eval = import "${sources.nixpkgs}/nixos/lib/eval-config.nix" { - system = builtins.currentSystem; - specialArgs = { - inherit sources; - }; - modules = [ configuration ]; - }; - os = { - inherit (eval) pkgs config options; - system = eval.config.system.build.toplevel; - inherit (eval.config.system.build) vm vmWithBootLoader; - }; in - { - drv_path = os.config.system.build.toplevel.drvPath; - out_path = os.config.system.build.toplevel; - } + import ${pathToRoot}/deployment/nixos.nix { inherit configuration; } ' ) # instantiate the config in /nix/store diff --git a/deployment/nixos.nix b/deployment/nixos.nix index f2f0d019..65c5fe4b 100644 --- a/deployment/nixos.nix +++ b/deployment/nixos.nix @@ -4,11 +4,20 @@ }: let sources = import ../npins; - os = import "${sources.nixpkgs}/nixos" { inherit system configuration; }; + eval = import "${sources.nixpkgs}/nixos/lib/eval-config.nix" { + inherit system; + specialArgs = { + inherit sources; + }; + modules = [ configuration ]; + }; + os = { + inherit (eval) pkgs config options; + system = eval.config.system.build.toplevel; + inherit (eval.config.system.build) vm vmWithBootLoader; + }; in { - substituters = builtins.concatStringsSep " " os.config.nix.settings.substituters; - trusted_public_keys = builtins.concatStringsSep " " os.config.nix.settings.trusted-public-keys; drv_path = os.config.system.build.toplevel.drvPath; out_path = os.config.system.build.toplevel; } -- 2.48.1 From e0378992ac0f1bb1139d38665a81b1e980c10f36 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 20:30:06 +0200 Subject: [PATCH 46/76] remove unused JSON-serialized args (sources) --- deployment/check/data-model/nixosTest.nix | 8 -------- 1 file changed, 8 deletions(-) diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 52d6f70e..13b8ad25 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -66,14 +66,6 @@ in ' let - args = builtins.fromJSON "${ - lib.replaceStrings [ "\"" ] [ "\\\\\"" ] ( - lib.strings.toJSON { - inherit sources; - } - ) - }"; - inherit (args) sources; configuration = { pkgs, config, ... }: { imports = [ ${pathToRoot}/deployment/check/common/sharedOptions.nix -- 2.48.1 From 1e16c613dc9c03e3df39009f5047d0194e2191e2 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 20:31:47 +0200 Subject: [PATCH 47/76] rm cowsay --- deployment/check/data-model/nixosTest.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 13b8ad25..27bba96e 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -42,7 +42,6 @@ in { environment.systemPackages = with pkgs; [ hello - cowsay automake autoconf ]; -- 2.48.1 From 989d04a4180218d1ada70641715399a5b49a7d1f Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 21:46:30 +0200 Subject: [PATCH 48/76] dedupe inputDerivations --- deployment/check/common/deployerNode.nix | 5 ----- 1 file changed, 5 deletions(-) diff --git a/deployment/check/common/deployerNode.nix b/deployment/check/common/deployerNode.nix index c15ebe83..694a9eee 100644 --- a/deployment/check/common/deployerNode.nix +++ b/deployment/check/common/deployerNode.nix @@ -60,16 +60,11 @@ in sources.flake-parts sources.nixpkgs - sources.flake-inputs sources.git-hooks pkgs.stdenv pkgs.stdenvNoCC - pkgs.automake - pkgs.autoconf - pkgs.binutils - pkgs.bison ] ++ ( let -- 2.48.1 From 4133421e5bf87fa1d69aa4594ab97ce159bebb44 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 22:05:27 +0200 Subject: [PATCH 49/76] rm unused ssh settings --- deployment/check/data-model/nixosTest.nix | 2 -- 1 file changed, 2 deletions(-) diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 27bba96e..5f45174e 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -98,9 +98,7 @@ in sshOpts=( -o BatchMode=yes -o StrictHostKeyChecking=no - -o "ConnectionAttempts=1" -o "ConnectTimeout=1" - -o "ServerAliveCountMax=1" -o "ServerAliveInterval=1" ) # get the realized derivation to deploy -- 2.48.1 From c5d4d8e1e6e230cffd37231a4140a6a9ea78073d Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 22:14:46 +0200 Subject: [PATCH 50/76] - BatchMode --- deployment/check/data-model/nixosTest.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 5f45174e..cf6692f3 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -96,7 +96,6 @@ in eval "export $(echo $json | jq -r 'to_entries | map("\(.key)=\(.value)") | @sh')" host="root@${nodeName}" sshOpts=( - -o BatchMode=yes -o StrictHostKeyChecking=no -o "ConnectTimeout=1" -o "ServerAliveInterval=1" -- 2.48.1 From 6d56de3d57e03ac43d2389d84fd41f7f62bc48b5 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 22:17:24 +0200 Subject: [PATCH 51/76] move fail in --- deployment/check/data-model/nixosTest.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index cf6692f3..830ce714 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -49,11 +49,11 @@ in }; extraTestScript = '' - with subtest("Check the status before deployment"): - hello.fail("hello 1>&2") - ${lib.concatStringsSep "\n" ( lib.lists.map (nodeName: '' + with subtest("Check the status before deployment"): + ${nodeName}.fail("${nodeName} 1>&2") + with subtest("Run the deployment for ${nodeName}"): deployer.succeed(""" set -euo pipefail -- 2.48.1 From 01a55b13e982813526670c3a6b9ee0c2941e97d6 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 22:26:17 +0200 Subject: [PATCH 52/76] - auto --- deployment/check/data-model/nixosTest.nix | 8 -------- 1 file changed, 8 deletions(-) diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 830ce714..98656a08 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -25,16 +25,12 @@ in environment.systemPackages = with pkgs; [ jq - automake - autoconf ]; # FIXME: sad times system.extraDependencies = with pkgs; [ jq jq.inputDerivation - automake - autoconf ]; system.extraDependenciesFromModule = @@ -42,8 +38,6 @@ in { environment.systemPackages = with pkgs; [ hello - automake - autoconf ]; }; }; @@ -77,8 +71,6 @@ in environment.systemPackages = with pkgs; [ hello - automake - autoconf ]; }; in -- 2.48.1 From 199bcca285c4bf1720c52d506afff28caaa4103c Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 22:41:12 +0200 Subject: [PATCH 53/76] skip is-active sshd --- deployment/check/data-model/nixosTest.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 98656a08..b58af9b7 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -107,7 +107,6 @@ in fi """) ${nodeName}.wait_for_unit("multi-user.target") - ${nodeName}.succeed("systemctl is-active sshd") ${nodeName}.succeed("${nodeName} 1>&2") '') targetMachines )} -- 2.48.1 From c900b7a9c531808e8fc4a59abb8957f0a7c4a686 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 24 Aug 2025 22:58:17 +0200 Subject: [PATCH 54/76] simplify deployment --- deployment/check/data-model/nixosTest.nix | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index b58af9b7..e9f57e2d 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -77,23 +77,18 @@ in import ${pathToRoot}/deployment/nixos.nix { inherit configuration; } ' ) - # instantiate the config in /nix/store - "''${command[@]}" -A out_path - # get the other info - json="$("''${command[@]}" --eval --strict --json)" # DEPLOY - declare drv_path - # set our variables using the json object - eval "export $(echo $json | jq -r 'to_entries | map("\(.key)=\(.value)") | @sh')" host="root@${nodeName}" sshOpts=( -o StrictHostKeyChecking=no -o "ConnectTimeout=1" -o "ServerAliveInterval=1" ) + # instantiate the config in /nix/store + "''${command[@]}" -A out_path # get the realized derivation to deploy - outPath=$(nix-store --realize "$drv_path") + outPath=$(nix-store --realize "$("''${command[@]}" --eval --strict --json | jq -r '.drv_path')") # deploy the config by nix-copy-closure NIX_SSHOPTS="''${sshOpts[*]}" nix-copy-closure --to "$host" "$outPath" --gzip --use-substitutes # switch the remote host to the config -- 2.48.1 From d51943af4222fc672f9d7095dfaa87bcfe90d858 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Mon, 25 Aug 2025 00:21:54 +0200 Subject: [PATCH 55/76] simplify inputDerivations --- deployment/check/common/deployerNode.nix | 28 ----------------------- deployment/check/data-model/nixosTest.nix | 5 ++++ 2 files changed, 5 insertions(+), 28 deletions(-) diff --git a/deployment/check/common/deployerNode.nix b/deployment/check/common/deployerNode.nix index 694a9eee..dcb5deef 100644 --- a/deployment/check/common/deployerNode.nix +++ b/deployment/check/common/deployerNode.nix @@ -96,35 +96,7 @@ in machine.system.build.vm.inputDerivation machine.system.build.bootStage1.inputDerivation machine.system.build.bootStage2.inputDerivation - pkgs.automake.inputDerivation - pkgs.autoconf.inputDerivation - pkgs.bash.inputDerivation - pkgs.binutils.inputDerivation - pkgs.bison.inputDerivation ] - ++ concatLists ( - lib.lists.map ( - pkg: - if - pkg ? inputDerivation - # error: output '/nix/store/dki9d3vldafg9ydrfm7x0g0rr0qljk98-sudo-1.9.16p2' is not allowed to refer to the following paths: - # /nix/store/2xdmps65ryklmbf025bm4pxv16gb8ajv-sudo-1.9.16p2.tar.gz - # /nix/store/58br4vk3q5akf4g8lx0pqzfhn47k3j8d-bash-5.2p37 - # /nix/store/8v6k283dpbc0qkdq81nb6mrxrgcb10i1-gcc-wrapper-14-20241116 - # /nix/store/9r1nl9ksiyszy4qzzg6y2gcdkca0xmhy-stdenv-linux - # /nix/store/a4rmp6in7igbl1wbz9pli5nq0wiclq0y-groff-1.23.0 - # /nix/store/dki9d3vldafg9ydrfm7x0g0rr0qljk98-sudo-1.9.16p2 - # /nix/store/f5y58qz2fzpzgkhp0nizixi10x04ppyy-linux-pam-1.6.1 - # /nix/store/shkw4qm9qcw5sc5n1k5jznc83ny02r39-default-builder.sh - # /nix/store/vj1c3wf9c11a0qs6p3ymfvrnsdgsdcbq-source-stdenv.sh - # /nix/store/yh6qg1nsi5h2xblcr67030pz58fsaxx3-coreutils-9.6 - && !(lib.strings.hasInfix "sudo" (builtins.toString pkg)) - then - [ pkg.inputDerivation ] - else - [ ] - ) machine.environment.systemPackages - ) ++ concatLists ( lib.mapAttrsToList ( _k: v: if v ? source.inputDerivation then [ v.source.inputDerivation ] else [ ] diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index e9f57e2d..6f4dd3cf 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -31,6 +31,11 @@ in system.extraDependencies = with pkgs; [ jq jq.inputDerivation + automake.inputDerivation + autoconf.inputDerivation + bash.inputDerivation + binutils.inputDerivation + bison.inputDerivation ]; system.extraDependenciesFromModule = -- 2.48.1 From 520fa63612882465c0b8d2cab3ef1cbc34dd7cf2 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Mon, 25 Aug 2025 00:28:41 +0200 Subject: [PATCH 56/76] unimport qemu-guest --- deployment/check/data-model/nixosTest.nix | 2 -- 1 file changed, 2 deletions(-) diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 6f4dd3cf..0facf0c7 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -1,7 +1,6 @@ { lib, config, - sources, ... }: let @@ -68,7 +67,6 @@ in imports = [ ${pathToRoot}/deployment/check/common/sharedOptions.nix ${pathToRoot}/deployment/check/common/targetNode.nix - ${sources.nixpkgs}/nixos/modules/profiles/qemu-guest.nix ]; enableAcme = ${lib.strings.toJSON config.enableAcme}; -- 2.48.1 From 33cfb9f2110ec24ee4d6393ba7620aef29dfad4f Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Mon, 25 Aug 2025 00:31:38 +0200 Subject: [PATCH 57/76] simplify imputDerivations --- deployment/check/data-model/nixosTest.nix | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 0facf0c7..3d260ab2 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -26,17 +26,6 @@ in jq ]; - # FIXME: sad times - system.extraDependencies = with pkgs; [ - jq - jq.inputDerivation - automake.inputDerivation - autoconf.inputDerivation - bash.inputDerivation - binutils.inputDerivation - bison.inputDerivation - ]; - system.extraDependenciesFromModule = { pkgs, ... }: { -- 2.48.1 From 053fb3d3bfe4b218156447153cf85719aa354c79 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Mon, 25 Aug 2025 00:45:30 +0200 Subject: [PATCH 58/76] spacing --- deployment/check/data-model/nixosTest.nix | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 3d260ab2..7c7f1ee0 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -8,9 +8,7 @@ let in { _class = "nixosTest"; - name = "deployment-model"; - sourceFileset = lib.fileset.unions [ ../../data-model.nix ../../function.nix @@ -21,11 +19,9 @@ in nodes.deployer = { pkgs, ... }: { - environment.systemPackages = with pkgs; [ jq ]; - system.extraDependenciesFromModule = { pkgs, ... }: { @@ -46,30 +42,22 @@ in set -euo pipefail # INSTANTIATE - command=( - nix-instantiate - --expr - - ' + command=(nix-instantiate --expr ' let configuration = { pkgs, config, ... }: { imports = [ ${pathToRoot}/deployment/check/common/sharedOptions.nix ${pathToRoot}/deployment/check/common/targetNode.nix ]; - enableAcme = ${lib.strings.toJSON config.enableAcme}; acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; - environment.systemPackages = with pkgs; [ hello ]; }; in import ${pathToRoot}/deployment/nixos.nix { inherit configuration; } - ' - ) - + ') # DEPLOY host="root@${nodeName}" sshOpts=( -- 2.48.1 From 72ba1726b71fd2811b7b06d455d23e36a989c39d Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Mon, 25 Aug 2025 11:20:38 +0200 Subject: [PATCH 59/76] try and use deployment --- deployment/check/data-model/deployment.nix | 21 +++++++++++++++++--- deployment/check/data-model/nixosTest.nix | 23 ++++++++++------------ 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/deployment/check/data-model/deployment.nix b/deployment/check/data-model/deployment.nix index a6cf51f8..f843dbb0 100644 --- a/deployment/check/data-model/deployment.nix +++ b/deployment/check/data-model/deployment.nix @@ -1,11 +1,13 @@ { - inputs, - lib, config, + inputs ? (import ../../../default.nix { }).inputs, + sources ? import ../../../npins, ... }: let + inherit (sources) nixpkgs; + lib = import "${nixpkgs}/lib"; eval = module: (lib.evalModules { @@ -33,8 +35,21 @@ let authentication.password = "password"; }; nixos-configuration = - { ... }: + { pkgs, ... }: { + imports = [ + ../common/sharedOptions.nix + ../common/targetNode.nix + "${nixpkgs}/nixos/modules/profiles/qemu-guest.nix" + ]; + + inherit (config) enableAcme; + acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; + + environment.systemPackages = with pkgs; [ + hello + ]; + users.users = config.resources.shell.login-shell.apply ( lib.filterAttrs (_name: value: value ? login-shell) requests ); diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 7c7f1ee0..3b6991eb 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -5,6 +5,7 @@ }: let inherit (import ./constants.nix) targetMachines pathToRoot; + escapedJson = v: lib.replaceStrings [ "\"" ] [ "\\\\\"" ] (lib.strings.toJSON v); in { _class = "nixosTest"; @@ -43,20 +44,16 @@ in # INSTANTIATE command=(nix-instantiate --expr ' - let - configuration = { pkgs, config, ... }: { - imports = [ - ${pathToRoot}/deployment/check/common/sharedOptions.nix - ${pathToRoot}/deployment/check/common/targetNode.nix - ]; - enableAcme = ${lib.strings.toJSON config.enableAcme}; - acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; - environment.systemPackages = with pkgs; [ - hello - ]; + import ${pathToRoot}/deployment/nixos.nix { + configuration = import ${pathToRoot}/deployment/check/data-model/deployment.nix { + config = builtins.fromJSON "${ + escapedJson { + inherit (config) enableAcme; + acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; + } + }"; }; - in - import ${pathToRoot}/deployment/nixos.nix { inherit configuration; } + } ') # DEPLOY host="root@${nodeName}" -- 2.48.1 From 54b3b9ea50556a9881b27b0c695e08f31e5eaaa1 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Mon, 25 Aug 2025 17:14:45 +0200 Subject: [PATCH 60/76] fix a bug of mismatching names in data model test matches the name of `shell` to `operator-environment`. --- deployment/data-model-test.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/data-model-test.nix b/deployment/data-model-test.nix index c65e37ae..f3a6c7a4 100644 --- a/deployment/data-model-test.nix +++ b/deployment/data-model-test.nix @@ -125,7 +125,7 @@ in nixos.module = { ... }: { - users.users = config.resources.shell.login-shell.apply ( + users.users = config.resources.operator-environment.login-shell.apply ( lib.filterAttrs (_name: value: value ? login-shell) requests ); }; -- 2.48.1 From 7ef43bf3d6e09682696718a829116eda97cebc5e Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Mon, 25 Aug 2025 17:16:59 +0200 Subject: [PATCH 61/76] stylize user-specified names by quotes to clarify their status --- deployment/data-model-test.nix | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/deployment/data-model-test.nix b/deployment/data-model-test.nix index f3a6c7a4..8757939e 100644 --- a/deployment/data-model-test.nix +++ b/deployment/data-model-test.nix @@ -108,7 +108,7 @@ in environments.single-nixos-vm = { config, ... }: { - resources.operator-environment.login-shell.username = "operator"; + resources."operator-environment".login-shell.username = "operator"; implementation = requests: { input = requests; output.nixops4 = @@ -125,7 +125,7 @@ in nixos.module = { ... }: { - users.users = config.resources.operator-environment.login-shell.apply ( + users.users = config.resources."operator-environment".login-shell.apply ( lib.filterAttrs (_name: value: value ? login-shell) requests ); }; @@ -135,7 +135,7 @@ in }; }; options = { - example-configuration = mkOption { + "example-configuration" = mkOption { type = config.configuration; readOnly = true; default = { @@ -143,20 +143,22 @@ in applications.hello.enable = true; }; }; - example-deployment = mkOption { + "example-deployment" = mkOption { type = options.deployments.nestedType; readOnly = true; - default = config.environments.single-nixos-vm.deployment config.example-configuration; + default = config.environments.single-nixos-vm.deployment config."example-configuration"; }; }; } ); - resources = fediversity.applications.hello.resources fediversity.example-configuration.applications.hello; + resources = + fediversity.applications.hello.resources + fediversity."example-configuration".applications.hello; hello-shell = resources.resources.hello.login-shell; - environment = fediversity.environments.single-nixos-vm.resources.operator-environment.login-shell; + environment = fediversity.environments.single-nixos-vm.resources."operator-environment".login-shell; result = mkDeployment { modules = [ - (fediversity.environments.single-nixos-vm.deployment fediversity.example-configuration) + (fediversity.environments.single-nixos-vm.deployment fediversity."example-configuration") ]; }; -- 2.48.1 From a88445f3ee5869f05e6f0889a801d62be5dded0d Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Mon, 25 Aug 2025 17:18:22 +0200 Subject: [PATCH 62/76] wrap application resources to match the input of `apply` --- deployment/data-model-test.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deployment/data-model-test.nix b/deployment/data-model-test.nix index 8757939e..b875a324 100644 --- a/deployment/data-model-test.nix +++ b/deployment/data-model-test.nix @@ -125,9 +125,9 @@ in nixos.module = { ... }: { - users.users = config.resources."operator-environment".login-shell.apply ( - lib.filterAttrs (_name: value: value ? login-shell) requests - ); + users.users = config.resources."operator-environment".login-shell.apply { + resources = lib.filterAttrs (_name: value: value ? login-shell) requests; + }; }; }; }; -- 2.48.1 From 1ab79ab2da66221d5c26215b4133acdebd2c1153 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Mon, 25 Aug 2025 18:12:02 +0200 Subject: [PATCH 63/76] fix attrTag by adding mkOption --- deployment/data-model.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/data-model.nix b/deployment/data-model.nix index 230e18f1..73d67f32 100644 --- a/deployment/data-model.nix +++ b/deployment/data-model.nix @@ -64,7 +64,7 @@ let }; }; deployment = attrTag { - ssh-host = { + ssh-host = mkOption { description = "A Terraform deployment by SSH to update a single existing NixOS host."; type = submodule { options = { -- 2.48.1 From 737c6d868c9f7d26772c4cc4192bdb0098971faa Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Mon, 25 Aug 2025 18:12:35 +0200 Subject: [PATCH 64/76] rename deployment to deployment-type, disambiguating from environments' deployment --- deployment/data-model.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deployment/data-model.nix b/deployment/data-model.nix index 73d67f32..e41c4030 100644 --- a/deployment/data-model.nix +++ b/deployment/data-model.nix @@ -63,7 +63,7 @@ let }; }; }; - deployment = attrTag { + deployment-type = attrTag { ssh-host = mkOption { description = "A Terraform deployment by SSH to update a single existing NixOS host."; type = submodule { @@ -185,7 +185,7 @@ in readOnly = true; default = { input-type = application-resources; - output-type = deployment; + output-type = deployment-type; }; }; # TODO(@fricklerhandwerk): maybe this should be a separate thing such as `fediversity-setup`, -- 2.48.1 From 3102be50ead3314d0d3081131c3f80ef94d11586 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Mon, 25 Aug 2025 19:40:48 +0200 Subject: [PATCH 65/76] simplify auth to not accept password --- deployment/check/data-model/deployment.nix | 2 +- deployment/data-model.nix | 18 ++++-------------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/deployment/check/data-model/deployment.nix b/deployment/check/data-model/deployment.nix index f843dbb0..78893e97 100644 --- a/deployment/check/data-model/deployment.nix +++ b/deployment/check/data-model/deployment.nix @@ -32,7 +32,7 @@ let ssh = { host = "localhost"; username = "root"; - authentication.password = "password"; + key-file = null; }; nixos-configuration = { pkgs, ... }: diff --git a/deployment/data-model.nix b/deployment/data-model.nix index e41c4030..581daa8a 100644 --- a/deployment/data-model.nix +++ b/deployment/data-model.nix @@ -45,20 +45,10 @@ let type = nullOr str; default = null; }; - authentication = mkOption { - description = "authentication method"; - type = attrTag { - private-key = mkOption { - description = "path to the user's SSH private key"; - type = str; - example = "/root/.ssh/id_ed25519"; - }; - password = mkOption { - description = "SSH password"; - # TODO: mark as sensitive - type = str; - }; - }; + key-file = mkOption { + description = "path to the user's SSH private key"; + type = nullOr str; + example = "/root/.ssh/id_ed25519"; }; }; }; -- 2.48.1 From d8196d54db5b68b09561d7bce0d46d16e69cac5a Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Mon, 25 Aug 2025 19:42:38 +0200 Subject: [PATCH 66/76] update deployment --- deployment/check/data-model/deployment.nix | 153 ++++++++++++++++----- deployment/check/data-model/options.nix | 15 ++ 2 files changed, 135 insertions(+), 33 deletions(-) create mode 100644 deployment/check/data-model/options.nix diff --git a/deployment/check/data-model/deployment.nix b/deployment/check/data-model/deployment.nix index 78893e97..8c6b1f47 100644 --- a/deployment/check/data-model/deployment.nix +++ b/deployment/check/data-model/deployment.nix @@ -8,6 +8,8 @@ let inherit (sources) nixpkgs; lib = import "${nixpkgs}/lib"; + deployment-config = config; + inherit (lib) mkOption types; eval = module: (lib.evalModules { @@ -20,47 +22,132 @@ let ]; }).config; fediversity = eval ( - { ... }: + { config, ... }: { config = { - environments.single-nixos-vm = - { ... }: - { - implementation = requests: { - input = requests; - output.ssh-host = { - ssh = { - host = "localhost"; - username = "root"; - key-file = null; + resources.login-shell = { + description = "The operator needs to be able to log into the shell"; + request = + { ... }: + { + _class = "fediversity-resource-request"; + options = { + wheel = mkOption { + description = "Whether the login user needs root permissions"; + type = types.bool; + default = false; }; - nixos-configuration = - { pkgs, ... }: - { - imports = [ - ../common/sharedOptions.nix - ../common/targetNode.nix - "${nixpkgs}/nixos/modules/profiles/qemu-guest.nix" - ]; - - inherit (config) enableAcme; - acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; - - environment.systemPackages = with pkgs; [ - hello - ]; - - users.users = config.resources.shell.login-shell.apply ( - lib.filterAttrs (_name: value: value ? login-shell) requests - ); + packages = mkOption { + description = "Packages that need to be available in the user environment"; + type = with types; attrsOf package; + }; + }; + }; + policy = + { config, ... }: + { + _class = "fediversity-resource-policy"; + options = { + username = mkOption { + description = "Username for the operator"; + type = types.str; # TODO: use the proper constraints from NixOS + }; + wheel = mkOption { + description = "Whether to allow login with root permissions"; + type = types.bool; + default = false; + }; + }; + config = { + resource-type = types.raw; # TODO: splice out the user type from NixOS + apply = + requests: + let + # Filter out requests that need wheel if policy doesn't allow it + validRequests = lib.filterAttrs ( + _name: req: !req.login-shell.wheel || config.wheel + ) requests.resources; + in + lib.optionalAttrs (validRequests != { }) { + ${config.username} = { + isNormalUser = true; + packages = + with lib; + attrValues (concatMapAttrs (_name: request: request.login-shell.packages) validRequests); + extraGroups = lib.optional config.wheel "wheel"; + }; }; }; }; + }; + applications.hello = + { pkgs, ... }: + { + description = ''Command-line tool that will print "Hello, world!" on the terminal''; + module = + { ... }: + { + options.enable = lib.mkEnableOption "Hello in the shell"; + }; + implementation = cfg: { + input = cfg; + output = lib.optionalAttrs cfg.enable { + resources.hello.login-shell.packages.hello = pkgs.hello; + }; + }; }; + environments.single-nixos-vm = environment: { + resources."operator-environment".login-shell.username = "operator"; + implementation = requests: { + input = requests; + output.ssh-host = { + ssh = { + username = "root"; + inherit (deployment-config) host; + key-file = null; + }; + nixos-configuration = + { pkgs, ... }: + { + imports = [ + ./options.nix + ../common/sharedOptions.nix + ../common/targetNode.nix + "${nixpkgs}/nixos/modules/profiles/qemu-guest.nix" + ]; + + inherit (deployment-config) enableAcme; + acmeNodeIP = + if deployment-config.enableAcme then + deployment-config.nodes.acme.networking.primaryIPAddress + else + null; + + environment.systemPackages = with pkgs; [ + hello + ]; + + users.users = environment.config.resources."operator-environment".login-shell.apply { + resources = lib.filterAttrs (_name: value: value ? login-shell) requests; + }; + }; + }; + }; + }; + }; + options = { + "example-configuration" = mkOption { + type = config.configuration; + default = { + enable = true; + applications.hello.enable = true; + }; + }; + "example-deployment" = mkOption { + default = config.environments.single-nixos-vm.deployment config."example-configuration"; + }; }; } ); in -fediversity.environments.single-nixos-vm.deployment { - enable = true; -} +fediversity."example-deployment" diff --git a/deployment/check/data-model/options.nix b/deployment/check/data-model/options.nix new file mode 100644 index 00000000..8492bee3 --- /dev/null +++ b/deployment/check/data-model/options.nix @@ -0,0 +1,15 @@ +{ + lib, + ... +}: +let + inherit (lib) types; +in +{ + options = { + host = lib.mkOption { + type = types.str; + description = "name of the host to deploy to"; + }; + }; +} -- 2.48.1 From 6ec6f1677d1109e747dd5cab32bdabf8d51f26b7 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Mon, 25 Aug 2025 19:45:04 +0200 Subject: [PATCH 67/76] update test --- deployment/check/data-model/nixosTest.nix | 113 +++++++++++++--------- 1 file changed, 67 insertions(+), 46 deletions(-) diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 3b6991eb..ead89210 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -9,6 +9,10 @@ let in { _class = "nixosTest"; + imports = [ + ./options.nix + ]; + name = "deployment-model"; sourceFileset = lib.fileset.unions [ ../../data-model.nix @@ -34,53 +38,70 @@ in extraTestScript = '' ${lib.concatStringsSep "\n" ( - lib.lists.map (nodeName: '' - with subtest("Check the status before deployment"): - ${nodeName}.fail("${nodeName} 1>&2") - - with subtest("Run the deployment for ${nodeName}"): - deployer.succeed(""" - set -euo pipefail - - # INSTANTIATE - command=(nix-instantiate --expr ' - import ${pathToRoot}/deployment/nixos.nix { - configuration = import ${pathToRoot}/deployment/check/data-model/deployment.nix { - config = builtins.fromJSON "${ - escapedJson { - inherit (config) enableAcme; - acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; - } - }"; - }; - } - ') - # DEPLOY - host="root@${nodeName}" - sshOpts=( - -o StrictHostKeyChecking=no - -o "ConnectTimeout=1" - -o "ServerAliveInterval=1" + lib.lists.map ( + nodeName: + let + deployment-config = { + inherit (config) enableAcme; + acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; + host = nodeName; + }; + inherit + ((import ./deployment.nix { + config = deployment-config; + }).ssh-host.ssh ) - # instantiate the config in /nix/store - "''${command[@]}" -A out_path - # get the realized derivation to deploy - outPath=$(nix-store --realize "$("''${command[@]}" --eval --strict --json | jq -r '.drv_path')") - # deploy the config by nix-copy-closure - NIX_SSHOPTS="''${sshOpts[*]}" nix-copy-closure --to "$host" "$outPath" --gzip --use-substitutes - # switch the remote host to the config - output=$(ssh "''${sshOpts[@]}" "$host" "nix-env --profile /nix/var/nix/profiles/system --set $outPath; nohup $outPath/bin/switch-to-configuration switch &" 2>&1) || echo "status code: $?" - echo "output: $output" - if [[ $output != *"Timeout, server ${nodeName} not responding"* ]]; then - echo "non-timeout error: $output" - exit 1 - else - exit 0 - fi - """) - ${nodeName}.wait_for_unit("multi-user.target") - ${nodeName}.succeed("${nodeName} 1>&2") - '') targetMachines + host + username + key-file + ; + in + '' + with subtest("Check the status before deployment"): + ${nodeName}.fail("${nodeName} 1>&2") + + with subtest("Run the deployment for ${nodeName}"): + deployer.succeed(""" + set -euo pipefail + + # INSTANTIATE + command=(nix-instantiate --show-trace --expr ' + import ${pathToRoot}/deployment/nixos.nix { + configuration = ( + import ${pathToRoot}/deployment/check/data-model/deployment.nix { + config = builtins.fromJSON "${escapedJson deployment-config}"; + } + ).ssh-host.nixos-configuration; + } + ') + # DEPLOY + host="${lib.defaultTo "root" username}@${host}" + sshOpts=( + ${if key-file == null then "" else "-i ${key-file}"} + -o StrictHostKeyChecking=no + -o "ConnectTimeout=1" + -o "ServerAliveInterval=1" + ) + # instantiate the config in /nix/store + "''${command[@]}" --show-trace -A out_path + # get the realized derivation to deploy + outPath=$(nix-store --realize "$("''${command[@]}" --show-trace --eval --strict --json | jq -r '.drv_path')") + # deploy the config by nix-copy-closure + NIX_SSHOPTS="''${sshOpts[*]}" nix-copy-closure --to "$host" "$outPath" --gzip --use-substitutes + # switch the remote host to the config + output=$(ssh "''${sshOpts[@]}" "$host" "nix-env --profile /nix/var/nix/profiles/system --set $outPath; nohup $outPath/bin/switch-to-configuration switch &" 2>&1) || echo "status code: $?" + echo "output: $output" + if [[ $output != *"Timeout, server ${nodeName} not responding"* ]]; then + echo "non-timeout error: $output" + exit 1 + else + exit 0 + fi + """) + ${nodeName}.wait_for_unit("multi-user.target") + ${nodeName}.succeed("${nodeName} 1>&2") + '' + ) targetMachines )} ''; } -- 2.48.1 From cac3a80f37de8a0c7f293c9ef598678663e2969a Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Tue, 26 Aug 2025 17:15:23 +0200 Subject: [PATCH 68/76] pass system --- deployment/check/data-model/deployment.nix | 8 +++++--- deployment/check/data-model/nixosTest.nix | 7 +++++++ deployment/nixos.nix | 4 ++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/deployment/check/data-model/deployment.nix b/deployment/check/data-model/deployment.nix index 8c6b1f47..b7fbfdd2 100644 --- a/deployment/check/data-model/deployment.nix +++ b/deployment/check/data-model/deployment.nix @@ -1,5 +1,6 @@ { config, + system, inputs ? (import ../../../default.nix { }).inputs, sources ? import ../../../npins, ... @@ -7,7 +8,8 @@ let inherit (sources) nixpkgs; - lib = import "${nixpkgs}/lib"; + pkgs = import nixpkgs { inherit system; }; + inherit (pkgs) lib; deployment-config = config; inherit (lib) mkOption types; eval = @@ -81,7 +83,7 @@ let }; }; applications.hello = - { pkgs, ... }: + { ... }: { description = ''Command-line tool that will print "Hello, world!" on the terminal''; module = @@ -107,7 +109,7 @@ let key-file = null; }; nixos-configuration = - { pkgs, ... }: + { ... }: { imports = [ ./options.nix diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index ead89210..a0b95cdc 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -1,6 +1,7 @@ { lib, config, + pkgs, ... }: let @@ -48,6 +49,7 @@ in }; inherit ((import ./deployment.nix { + inherit (pkgs) system; config = deployment-config; }).ssh-host.ssh ) @@ -66,9 +68,14 @@ in # INSTANTIATE command=(nix-instantiate --show-trace --expr ' + let + system = "${pkgs.system}"; # FIXME: what system are we deploying to? + in import ${pathToRoot}/deployment/nixos.nix { + inherit system; configuration = ( import ${pathToRoot}/deployment/check/data-model/deployment.nix { + inherit system; config = builtins.fromJSON "${escapedJson deployment-config}"; } ).ssh-host.nixos-configuration; diff --git a/deployment/nixos.nix b/deployment/nixos.nix index 65c5fe4b..c5228106 100644 --- a/deployment/nixos.nix +++ b/deployment/nixos.nix @@ -1,9 +1,9 @@ { configuration, - system ? builtins.currentSystem, + system, + sources ? import ../npins, }: let - sources = import ../npins; eval = import "${sources.nixpkgs}/nixos/lib/eval-config.nix" { inherit system; specialArgs = { -- 2.48.1 From f30c47c8ec73eef63b84d14de452e69a85ebf714 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Tue, 26 Aug 2025 17:29:32 +0200 Subject: [PATCH 69/76] actually rely on user package from data model --- deployment/check/data-model/deployment.nix | 4 ---- deployment/check/data-model/nixosTest.nix | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/deployment/check/data-model/deployment.nix b/deployment/check/data-model/deployment.nix index b7fbfdd2..27de537b 100644 --- a/deployment/check/data-model/deployment.nix +++ b/deployment/check/data-model/deployment.nix @@ -125,10 +125,6 @@ let else null; - environment.systemPackages = with pkgs; [ - hello - ]; - users.users = environment.config.resources."operator-environment".login-shell.apply { resources = lib.filterAttrs (_name: value: value ? login-shell) requests; }; diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index a0b95cdc..11c0e3a6 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -106,7 +106,7 @@ in fi """) ${nodeName}.wait_for_unit("multi-user.target") - ${nodeName}.succeed("${nodeName} 1>&2") + ${nodeName}.succeed("su - operator -c ${nodeName} 1>&2") '' ) targetMachines )} -- 2.48.1 From 078a49f54a723644b13d07d29e95c5354a0694fb Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Tue, 26 Aug 2025 17:31:53 +0200 Subject: [PATCH 70/76] simpler data model, not sure it's desirable but at least it's consistent --- deployment/check/data-model/deployment.nix | 14 +++++++------- deployment/data-model-test.nix | 16 ++++++++-------- deployment/data-model.nix | 20 ++++++++------------ 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/deployment/check/data-model/deployment.nix b/deployment/check/data-model/deployment.nix index 27de537b..76abe204 100644 --- a/deployment/check/data-model/deployment.nix +++ b/deployment/check/data-model/deployment.nix @@ -66,9 +66,7 @@ let requests: let # Filter out requests that need wheel if policy doesn't allow it - validRequests = lib.filterAttrs ( - _name: req: !req.login-shell.wheel || config.wheel - ) requests.resources; + validRequests = lib.filterAttrs (_name: req: !req.login-shell.wheel || config.wheel) requests; in lib.optionalAttrs (validRequests != { }) { ${config.username} = { @@ -94,7 +92,7 @@ let implementation = cfg: { input = cfg; output = lib.optionalAttrs cfg.enable { - resources.hello.login-shell.packages.hello = pkgs.hello; + "my".login-shell.packages.hello = pkgs.hello; }; }; }; @@ -125,9 +123,11 @@ let else null; - users.users = environment.config.resources."operator-environment".login-shell.apply { - resources = lib.filterAttrs (_name: value: value ? login-shell) requests; - }; + users.users = environment.config.resources."operator-environment".login-shell.apply ( + lib.filterAttrs (_name: value: value ? login-shell) ( + lib.concatMapAttrs (k': lib.mapAttrs' (k: v: lib.nameValuePair "${k'}.${k}" v)) requests + ) + ); }; }; }; diff --git a/deployment/data-model-test.nix b/deployment/data-model-test.nix index b875a324..69a4408c 100644 --- a/deployment/data-model-test.nix +++ b/deployment/data-model-test.nix @@ -73,9 +73,7 @@ in requests: let # Filter out requests that need wheel if policy doesn't allow it - validRequests = lib.filterAttrs ( - _name: req: !req.login-shell.wheel || config.wheel - ) requests.resources; + validRequests = lib.filterAttrs (_name: req: !req.login-shell.wheel || config.wheel) requests; in lib.optionalAttrs (validRequests != { }) { ${config.username} = { @@ -101,7 +99,7 @@ in implementation = cfg: { input = cfg; output = lib.optionalAttrs cfg.enable { - resources.hello.login-shell.packages.hello = pkgs.hello; + "my".login-shell.packages.hello = pkgs.hello; }; }; }; @@ -125,9 +123,11 @@ in nixos.module = { ... }: { - users.users = config.resources."operator-environment".login-shell.apply { - resources = lib.filterAttrs (_name: value: value ? login-shell) requests; - }; + users.users = config.resources."operator-environment".login-shell.apply ( + lib.filterAttrs (_name: value: value ? login-shell) ( + lib.concatMapAttrs (k': lib.mapAttrs' (k: v: lib.nameValuePair "${k'}.${k}" v)) requests + ) + ); }; }; }; @@ -154,7 +154,7 @@ in resources = fediversity.applications.hello.resources fediversity."example-configuration".applications.hello; - hello-shell = resources.resources.hello.login-shell; + hello-shell = resources."my".login-shell; environment = fediversity.environments.single-nixos-vm.resources."operator-environment".login-shell; result = mkDeployment { modules = [ diff --git a/deployment/data-model.nix b/deployment/data-model.nix index 581daa8a..21ad803e 100644 --- a/deployment/data-model.nix +++ b/deployment/data-model.nix @@ -18,16 +18,12 @@ let ; functionType = import ./function.nix; - application-resources = submodule { - options.resources = mkOption { - # TODO: maybe transpose, and group the resources by type instead - type = attrsOf ( - attrTag ( - lib.mapAttrs (_name: resource: mkOption { type = submodule resource.request; }) config.resources - ) - ); - }; - }; + # TODO: maybe transpose, and group the resources by type instead + application-resources = attrsOf ( + attrTag ( + lib.mapAttrs (_name: resource: mkOption { type = submodule resource.request; }) config.resources + ) + ); nixos-configuration = mkOption { description = "A NixOS configuration."; type = raw; @@ -93,7 +89,7 @@ in description = "The type of resource this policy configures"; type = types.optionType; }; - # TODO(@fricklerhandwerk): we may want to make the function type explict here: `request -> resource-type` + # TODO(@fricklerhandwerk): we may want to make the function type explicit here: `attrsOf request -> resource-type` # and then also rename this to be consistent with the application's resource mapping options.apply = mkOption { description = "Apply the policy to a request"; @@ -174,7 +170,7 @@ in type = submodule functionType; readOnly = true; default = { - input-type = application-resources; + input-type = attrsOf application-resources; output-type = deployment-type; }; }; -- 2.48.1 From 64905f0b1c9f19b3142ee964f64e901768f42ba3 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Tue, 26 Aug 2025 23:31:48 +0200 Subject: [PATCH 71/76] restore data model with { resources } wrappers, this time working --- deployment/check/data-model/deployment.nix | 20 ++++++++++++-------- deployment/data-model-test.nix | 22 +++++++++++++--------- deployment/data-model.nix | 19 +++++++++++-------- 3 files changed, 36 insertions(+), 25 deletions(-) diff --git a/deployment/check/data-model/deployment.nix b/deployment/check/data-model/deployment.nix index 76abe204..36f9013b 100644 --- a/deployment/check/data-model/deployment.nix +++ b/deployment/check/data-model/deployment.nix @@ -66,7 +66,9 @@ let requests: let # Filter out requests that need wheel if policy doesn't allow it - validRequests = lib.filterAttrs (_name: req: !req.login-shell.wheel || config.wheel) requests; + validRequests = lib.filterAttrs ( + _name: req: !req.login-shell.wheel || config.wheel + ) requests.resources; in lib.optionalAttrs (validRequests != { }) { ${config.username} = { @@ -91,8 +93,8 @@ let }; implementation = cfg: { input = cfg; - output = lib.optionalAttrs cfg.enable { - "my".login-shell.packages.hello = pkgs.hello; + output.resources = lib.optionalAttrs cfg.enable { + hello.login-shell.packages.hello = pkgs.hello; }; }; }; @@ -123,11 +125,13 @@ let else null; - users.users = environment.config.resources."operator-environment".login-shell.apply ( - lib.filterAttrs (_name: value: value ? login-shell) ( - lib.concatMapAttrs (k': lib.mapAttrs' (k: v: lib.nameValuePair "${k'}.${k}" v)) requests - ) - ); + users.users = environment.config.resources."operator-environment".login-shell.apply { + resources = lib.filterAttrs (_name: value: value ? login-shell) ( + lib.concatMapAttrs ( + k': req: lib.mapAttrs' (k: lib.nameValuePair "${k'}.${k}") req.resources + ) requests + ); + }; }; }; }; diff --git a/deployment/data-model-test.nix b/deployment/data-model-test.nix index 69a4408c..d72baf11 100644 --- a/deployment/data-model-test.nix +++ b/deployment/data-model-test.nix @@ -73,7 +73,9 @@ in requests: let # Filter out requests that need wheel if policy doesn't allow it - validRequests = lib.filterAttrs (_name: req: !req.login-shell.wheel || config.wheel) requests; + validRequests = lib.filterAttrs ( + _name: req: !req.login-shell.wheel || config.wheel + ) requests.resources; in lib.optionalAttrs (validRequests != { }) { ${config.username} = { @@ -98,8 +100,8 @@ in }; implementation = cfg: { input = cfg; - output = lib.optionalAttrs cfg.enable { - "my".login-shell.packages.hello = pkgs.hello; + output.resources = lib.optionalAttrs cfg.enable { + hello.login-shell.packages.hello = pkgs.hello; }; }; }; @@ -123,11 +125,13 @@ in nixos.module = { ... }: { - users.users = config.resources."operator-environment".login-shell.apply ( - lib.filterAttrs (_name: value: value ? login-shell) ( - lib.concatMapAttrs (k': lib.mapAttrs' (k: v: lib.nameValuePair "${k'}.${k}" v)) requests - ) - ); + users.users = config.resources."operator-environment".login-shell.apply { + resources = lib.filterAttrs (_name: value: value ? login-shell) ( + lib.concatMapAttrs ( + k': req: lib.mapAttrs' (k: lib.nameValuePair "${k'}.${k}") req.resources + ) requests + ); + }; }; }; }; @@ -154,7 +158,7 @@ in resources = fediversity.applications.hello.resources fediversity."example-configuration".applications.hello; - hello-shell = resources."my".login-shell; + hello-shell = resources.resources.hello.login-shell; environment = fediversity.environments.single-nixos-vm.resources."operator-environment".login-shell; result = mkDeployment { modules = [ diff --git a/deployment/data-model.nix b/deployment/data-model.nix index 21ad803e..90d3b0d2 100644 --- a/deployment/data-model.nix +++ b/deployment/data-model.nix @@ -18,12 +18,16 @@ let ; functionType = import ./function.nix; - # TODO: maybe transpose, and group the resources by type instead - application-resources = attrsOf ( - attrTag ( - lib.mapAttrs (_name: resource: mkOption { type = submodule resource.request; }) config.resources - ) - ); + application-resources = submodule { + options.resources = mkOption { + # TODO: maybe transpose, and group the resources by type instead + type = attrsOf ( + attrTag ( + lib.mapAttrs (_name: resource: mkOption { type = submodule resource.request; }) config.resources + ) + ); + }; + }; nixos-configuration = mkOption { description = "A NixOS configuration."; type = raw; @@ -89,8 +93,7 @@ in description = "The type of resource this policy configures"; type = types.optionType; }; - # TODO(@fricklerhandwerk): we may want to make the function type explicit here: `attrsOf request -> resource-type` - # and then also rename this to be consistent with the application's resource mapping + # TODO(@fricklerhandwerk): we may want to make the function type explicit here: `application-resources -> resource-type` options.apply = mkOption { description = "Apply the policy to a request"; type = functionTo policy.config.resource-type; -- 2.48.1 From fbb6a45292e5298144e85222c11ca95f4677b867 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Tue, 26 Aug 2025 23:47:03 +0200 Subject: [PATCH 72/76] adjust deployment type this is a cop-out possible until https://git.fediversity.eu/fricklerhandwerk/Fediversity/pulls/15. after that, this will require actually figuring out how to get `options` for `deployment.nix` - which may need `evalModules` with `data-model.nix`. --- deployment/check/data-model/deployment.nix | 1 + deployment/data-model-test.nix | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/deployment/check/data-model/deployment.nix b/deployment/check/data-model/deployment.nix index 36f9013b..69380519 100644 --- a/deployment/check/data-model/deployment.nix +++ b/deployment/check/data-model/deployment.nix @@ -146,6 +146,7 @@ let }; }; "example-deployment" = mkOption { + type = config.environments.single-nixos-vm.resource-mapping.output-type; default = config.environments.single-nixos-vm.deployment config."example-configuration"; }; }; diff --git a/deployment/data-model-test.nix b/deployment/data-model-test.nix index d72baf11..e754bc03 100644 --- a/deployment/data-model-test.nix +++ b/deployment/data-model-test.nix @@ -31,7 +31,7 @@ in expr = let fediversity = eval ( - { config, options, ... }: + { config, ... }: { config = { resources.login-shell = { @@ -148,7 +148,7 @@ in }; }; "example-deployment" = mkOption { - type = options.deployments.nestedType; + type = config.environments.single-nixos-vm.resource-mapping.output-type; readOnly = true; default = config.environments.single-nixos-vm.deployment config."example-configuration"; }; -- 2.48.1 From b5a96a70b8d19d9ebf5c42baf53d13327a29b170 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Wed, 27 Aug 2025 20:40:49 +0200 Subject: [PATCH 73/76] add nixops4 data model test --- deployment/check/common/nixosTest.nix | 2 +- deployment/check/data-model/constants.nix | 4 +- deployment/check/data-model/default.nix | 7 +- deployment/check/data-model/deployment.nix | 114 ++++++++----- .../check/data-model/flake-under-test.nix | 26 +++ deployment/check/data-model/nixosTest.nix | 150 +++++++++--------- deployment/data-model-test.nix | 2 +- deployment/data-model.nix | 19 +++ 8 files changed, 210 insertions(+), 114 deletions(-) create mode 100644 deployment/check/data-model/flake-under-test.nix diff --git a/deployment/check/common/nixosTest.nix b/deployment/check/common/nixosTest.nix index 93bd3fef..574621c4 100644 --- a/deployment/check/common/nixosTest.nix +++ b/deployment/check/common/nixosTest.nix @@ -163,7 +163,7 @@ in '')} ${ - if config.useFlake then + if true then '' ## NOTE: This is super slow. It could probably be optimised in Nix, for ## instance by allowing to grab things directly from the host's store. diff --git a/deployment/check/data-model/constants.nix b/deployment/check/data-model/constants.nix index 0c8576a7..9cc81ea9 100644 --- a/deployment/check/data-model/constants.nix +++ b/deployment/check/data-model/constants.nix @@ -1,7 +1,9 @@ { targetMachines = [ - "hello" + "ssh" + "nixops4" ]; pathToRoot = ../../..; pathFromRoot = ./.; + enableAcme = true; } diff --git a/deployment/check/data-model/default.nix b/deployment/check/data-model/default.nix index 11c1fb37..1815f19a 100644 --- a/deployment/check/data-model/default.nix +++ b/deployment/check/data-model/default.nix @@ -12,5 +12,10 @@ runNixOSTest { ./nixosTest.nix ]; _module.args = { inherit inputs sources; }; - inherit (import ./constants.nix) targetMachines pathToRoot pathFromRoot; + inherit (import ./constants.nix) + targetMachines + pathToRoot + pathFromRoot + enableAcme + ; } diff --git a/deployment/check/data-model/deployment.nix b/deployment/check/data-model/deployment.nix index 69380519..769fea74 100644 --- a/deployment/check/data-model/deployment.nix +++ b/deployment/check/data-model/deployment.nix @@ -12,11 +12,12 @@ let inherit (pkgs) lib; deployment-config = config; inherit (lib) mkOption types; + inherit (import ./constants.nix) targetMachines pathToRoot pathFromRoot; eval = module: (lib.evalModules { specialArgs = { - inherit inputs; + inherit pkgs inputs; }; modules = [ module @@ -98,44 +99,67 @@ let }; }; }; - environments.single-nixos-vm = environment: { - resources."operator-environment".login-shell.username = "operator"; - implementation = requests: { - input = requests; - output.ssh-host = { - ssh = { - username = "root"; - inherit (deployment-config) host; - key-file = null; + environments = + let + mkNixosConfiguration = + environment: requests: + { ... }: + { + imports = [ + ./options.nix + ../common/sharedOptions.nix + ../common/targetNode.nix + "${nixpkgs}/nixos/modules/profiles/qemu-guest.nix" + ]; + + users.users = environment.config.resources."operator-environment".login-shell.apply { + resources = lib.filterAttrs (_name: value: value ? login-shell) ( + lib.concatMapAttrs ( + k': req: lib.mapAttrs' (k: lib.nameValuePair "${k'}.${k}") req.resources + ) requests + ); + }; }; - nixos-configuration = - { ... }: - { - imports = [ - ./options.nix - ../common/sharedOptions.nix - ../common/targetNode.nix - "${nixpkgs}/nixos/modules/profiles/qemu-guest.nix" - ]; - - inherit (deployment-config) enableAcme; - acmeNodeIP = - if deployment-config.enableAcme then - deployment-config.nodes.acme.networking.primaryIPAddress - else - null; - - users.users = environment.config.resources."operator-environment".login-shell.apply { - resources = lib.filterAttrs (_name: value: value ? login-shell) ( - lib.concatMapAttrs ( - k': req: lib.mapAttrs' (k: lib.nameValuePair "${k'}.${k}") req.resources - ) requests - ); + in + { + single-nixos-vm-ssh = environment: { + resources."operator-environment".login-shell.username = "operator"; + implementation = requests: { + input = requests; + output.ssh-host = { + nixos-configuration = mkNixosConfiguration environment requests; + ssh = { + username = "root"; + inherit (deployment-config) host; + key-file = null; }; }; + }; + }; + single-nixos-vm-nixops4 = environment: { + resources."operator-environment".login-shell.username = "operator"; + implementation = requests: { + input = requests; + output.nixops4 = + { providers, ... }: + { + providers = { + inherit (inputs.nixops4.modules.nixops4Provider) local; + }; + resources = lib.genAttrs targetMachines (nodeName: { + type = providers.local.exec; + imports = [ + inputs.nixops4-nixos.modules.nixops4Resource.nixos + ../common/targetResource.nix + ]; + nixos.module = mkNixosConfiguration environment requests; + _module.args = { inherit inputs sources; }; + inherit nodeName pathToRoot pathFromRoot; + }); + }; + }; }; }; - }; }; options = { "example-configuration" = mkOption { @@ -145,12 +169,24 @@ let applications.hello.enable = true; }; }; - "example-deployment" = mkOption { - type = config.environments.single-nixos-vm.resource-mapping.output-type; - default = config.environments.single-nixos-vm.deployment config."example-configuration"; - }; + "ssh-deployment" = + let + env = config.environments."single-nixos-vm-ssh"; + in + mkOption { + type = env.resource-mapping.output-type; + default = env.deployment config."example-configuration"; + }; + "nixops4-deployment" = + let + env = config.environments."single-nixos-vm-nixops4"; + in + mkOption { + type = env.resource-mapping.output-type; + default = env.deployment config."example-configuration"; + }; }; } ); in -fediversity."example-deployment" +fediversity diff --git a/deployment/check/data-model/flake-under-test.nix b/deployment/check/data-model/flake-under-test.nix new file mode 100644 index 00000000..d6f4482d --- /dev/null +++ b/deployment/check/data-model/flake-under-test.nix @@ -0,0 +1,26 @@ +{ + inputs = { + nixops4.follows = "nixops4-nixos/nixops4"; + nixops4-nixos.url = "github:nixops4/nixops4-nixos"; + }; + + outputs = + inputs: + import ./mkFlake.nix inputs ( + { inputs, ... }: + let + system = "x86_64-linux"; + in + { + imports = [ + inputs.nixops4.modules.flake.default + ]; + + nixops4Deployments.check-deployment-model = + (import ./deployment/check/data-model/deployment.nix { + inherit system inputs; + config.host = "nixops4"; + })."nixops4-deployment".nixops4; + } + ); +} diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 11c0e3a6..019203c8 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -2,11 +2,28 @@ lib, config, pkgs, + inputs, ... }: let - inherit (import ./constants.nix) targetMachines pathToRoot; + inherit (import ./constants.nix) pathToRoot; escapedJson = v: lib.replaceStrings [ "\"" ] [ "\\\\\"" ] (lib.strings.toJSON v); + deployment-config = { + inherit (config) enableAcme; + acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; + host = "ssh"; + }; + inherit + ((import ./deployment.nix { + inherit (pkgs) system; + inherit inputs; + config = deployment-config; + })."ssh-deployment".ssh-host.ssh + ) + host + username + key-file + ; in { _class = "nixosTest"; @@ -20,14 +37,24 @@ in ../../function.nix ./constants.nix ./deployment.nix + ./options.nix + (config.pathToCwd + "/flake-under-test.nix") ]; nodes.deployer = { pkgs, ... }: { environment.systemPackages = with pkgs; [ + inputs.nixops4.packages.${system}.default jq ]; + + # FIXME: sad times + system.extraDependencies = with pkgs; [ + jq + jq.inputDerivation + ]; + system.extraDependenciesFromModule = { pkgs, ... }: { @@ -38,77 +65,58 @@ in }; extraTestScript = '' - ${lib.concatStringsSep "\n" ( - lib.lists.map ( - nodeName: - let - deployment-config = { - inherit (config) enableAcme; - acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; - host = nodeName; - }; - inherit - ((import ./deployment.nix { - inherit (pkgs) system; - config = deployment-config; - }).ssh-host.ssh - ) - host - username - key-file - ; - in - '' - with subtest("Check the status before deployment"): - ${nodeName}.fail("${nodeName} 1>&2") + with subtest("nixops4"): + nixops4.fail("hello 1>&2") + deployer.succeed("nixops4 apply check-deployment-model --show-trace --verbose --no-interactive 1>&2") + nixops4.succeed("su - operator -c hello 1>&2") - with subtest("Run the deployment for ${nodeName}"): - deployer.succeed(""" - set -euo pipefail + with subtest("ssh: Check the status before deployment"): + ssh.fail("hello 1>&2") - # INSTANTIATE - command=(nix-instantiate --show-trace --expr ' - let - system = "${pkgs.system}"; # FIXME: what system are we deploying to? - in - import ${pathToRoot}/deployment/nixos.nix { - inherit system; - configuration = ( - import ${pathToRoot}/deployment/check/data-model/deployment.nix { - inherit system; - config = builtins.fromJSON "${escapedJson deployment-config}"; - } - ).ssh-host.nixos-configuration; - } - ') - # DEPLOY - host="${lib.defaultTo "root" username}@${host}" - sshOpts=( - ${if key-file == null then "" else "-i ${key-file}"} - -o StrictHostKeyChecking=no - -o "ConnectTimeout=1" - -o "ServerAliveInterval=1" - ) - # instantiate the config in /nix/store - "''${command[@]}" --show-trace -A out_path - # get the realized derivation to deploy - outPath=$(nix-store --realize "$("''${command[@]}" --show-trace --eval --strict --json | jq -r '.drv_path')") - # deploy the config by nix-copy-closure - NIX_SSHOPTS="''${sshOpts[*]}" nix-copy-closure --to "$host" "$outPath" --gzip --use-substitutes - # switch the remote host to the config - output=$(ssh "''${sshOpts[@]}" "$host" "nix-env --profile /nix/var/nix/profiles/system --set $outPath; nohup $outPath/bin/switch-to-configuration switch &" 2>&1) || echo "status code: $?" - echo "output: $output" - if [[ $output != *"Timeout, server ${nodeName} not responding"* ]]; then - echo "non-timeout error: $output" - exit 1 - else - exit 0 - fi - """) - ${nodeName}.wait_for_unit("multi-user.target") - ${nodeName}.succeed("su - operator -c ${nodeName} 1>&2") - '' - ) targetMachines - )} + with subtest("ssh: Run the deployment"): + deployer.succeed(""" + set -euo pipefail + + # INSTANTIATE + command=(nix-instantiate --show-trace --expr ' + let + system = "${pkgs.system}"; # FIXME: what system are we deploying to? + in + import ${pathToRoot}/deployment/nixos.nix { + inherit system; + configuration = ( + import ${pathToRoot}/deployment/check/data-model/deployment.nix { + inherit system; + config = builtins.fromJSON "${escapedJson deployment-config}"; + } + )."ssh-deployment".ssh-host.nixos-configuration; + } + ') + # DEPLOY + host="${lib.defaultTo "root" username}@${host}" + sshOpts=( + ${if key-file == null then "" else "-i ${key-file}"} + -o StrictHostKeyChecking=no + -o "ConnectTimeout=1" + -o "ServerAliveInterval=1" + ) + # instantiate the config in /nix/store + "''${command[@]}" --show-trace -A out_path + # get the realized derivation to deploy + outPath=$(nix-store --realize "$("''${command[@]}" --show-trace --eval --strict --json | jq -r '.drv_path')") + # deploy the config by nix-copy-closure + NIX_SSHOPTS="''${sshOpts[*]}" nix-copy-closure --to "$host" "$outPath" --gzip --use-substitutes + # switch the remote host to the config + output=$(ssh "''${sshOpts[@]}" "$host" "nix-env --profile /nix/var/nix/profiles/system --set $outPath; nohup $outPath/bin/switch-to-configuration switch &" 2>&1) || echo "status code: $?" + echo "output: $output" + if [[ $output != *"Timeout, server ssh not responding"* ]]; then + echo "non-timeout error: $output" + exit 1 + else + exit 0 + fi + """) + ssh.wait_for_unit("multi-user.target") + ssh.succeed("su - operator -c hello 1>&2") ''; } diff --git a/deployment/data-model-test.nix b/deployment/data-model-test.nix index e754bc03..aad95026 100644 --- a/deployment/data-model-test.nix +++ b/deployment/data-model-test.nix @@ -6,7 +6,7 @@ let module: (lib.evalModules { specialArgs = { - inherit inputs; + inherit pkgs inputs; }; modules = [ module diff --git a/deployment/data-model.nix b/deployment/data-model.nix index 90d3b0d2..89b7275e 100644 --- a/deployment/data-model.nix +++ b/deployment/data-model.nix @@ -1,6 +1,8 @@ { lib, config, + inputs, + pkgs, ... }: let @@ -28,6 +30,19 @@ let ); }; }; + nixops4Deployment = types.deferredModuleWith { + staticModules = [ + inputs.nixops4.modules.nixops4Deployment.default + + { + _class = "nixops4Deployment"; + _module.args = { + resourceProviderSystem = pkgs.system; + resources = { }; + }; + } + ]; + }; nixos-configuration = mkOption { description = "A NixOS configuration."; type = raw; @@ -63,6 +78,10 @@ let }; }; }; + nixops4 = mkOption { + description = "A NixOps4 NixOS deployment. For an example, see https://github.com/nixops4/nixops4-nixos/blob/main/example/deployment.nix."; + type = nixops4Deployment; + }; }; in { -- 2.48.1 From 80e2a9b90905e5b45345601e063cbc9c3fd90b2b Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Wed, 27 Aug 2025 21:02:53 +0200 Subject: [PATCH 74/76] factor out data model --- .../options.nix => common/data-model-options.nix} | 0 .../deployment.nix => common/data-model.nix} | 12 ++++++------ deployment/check/data-model/flake-under-test.nix | 7 +++++-- deployment/check/data-model/nixosTest.nix | 13 +++++++------ 4 files changed, 18 insertions(+), 14 deletions(-) rename deployment/check/{data-model/options.nix => common/data-model-options.nix} (100%) rename deployment/check/{data-model/deployment.nix => common/data-model.nix} (95%) diff --git a/deployment/check/data-model/options.nix b/deployment/check/common/data-model-options.nix similarity index 100% rename from deployment/check/data-model/options.nix rename to deployment/check/common/data-model-options.nix diff --git a/deployment/check/data-model/deployment.nix b/deployment/check/common/data-model.nix similarity index 95% rename from deployment/check/data-model/deployment.nix rename to deployment/check/common/data-model.nix index 769fea74..c4922f72 100644 --- a/deployment/check/data-model/deployment.nix +++ b/deployment/check/common/data-model.nix @@ -11,8 +11,8 @@ let pkgs = import nixpkgs { inherit system; }; inherit (pkgs) lib; deployment-config = config; + inherit (deployment-config) nodeName; inherit (lib) mkOption types; - inherit (import ./constants.nix) targetMachines pathToRoot pathFromRoot; eval = module: (lib.evalModules { @@ -106,7 +106,7 @@ let { ... }: { imports = [ - ./options.nix + ./data-model-options.nix ../common/sharedOptions.nix ../common/targetNode.nix "${nixpkgs}/nixos/modules/profiles/qemu-guest.nix" @@ -130,7 +130,7 @@ let nixos-configuration = mkNixosConfiguration environment requests; ssh = { username = "root"; - inherit (deployment-config) host; + host = nodeName; key-file = null; }; }; @@ -146,7 +146,7 @@ let providers = { inherit (inputs.nixops4.modules.nixops4Provider) local; }; - resources = lib.genAttrs targetMachines (nodeName: { + resources.${nodeName} = { type = providers.local.exec; imports = [ inputs.nixops4-nixos.modules.nixops4Resource.nixos @@ -154,8 +154,8 @@ let ]; nixos.module = mkNixosConfiguration environment requests; _module.args = { inherit inputs sources; }; - inherit nodeName pathToRoot pathFromRoot; - }); + inherit (deployment-config) nodeName pathToRoot pathFromRoot; + }; }; }; }; diff --git a/deployment/check/data-model/flake-under-test.nix b/deployment/check/data-model/flake-under-test.nix index d6f4482d..60b0ee38 100644 --- a/deployment/check/data-model/flake-under-test.nix +++ b/deployment/check/data-model/flake-under-test.nix @@ -17,9 +17,12 @@ ]; nixops4Deployments.check-deployment-model = - (import ./deployment/check/data-model/deployment.nix { + (import ./deployment/check/common/data-model.nix { inherit system inputs; - config.host = "nixops4"; + config = { + inherit (import ./deployment/check/data-model/constants.nix) pathToRoot pathFromRoot; + nodeName = "nixops4"; + }; })."nixops4-deployment".nixops4; } ); diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model/nixosTest.nix index 019203c8..a10c32e6 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model/nixosTest.nix @@ -9,12 +9,13 @@ let inherit (import ./constants.nix) pathToRoot; escapedJson = v: lib.replaceStrings [ "\"" ] [ "\\\\\"" ] (lib.strings.toJSON v); deployment-config = { + inherit (import ./constants.nix) pathToRoot pathFromRoot; inherit (config) enableAcme; acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; - host = "ssh"; + nodeName = "ssh"; }; inherit - ((import ./deployment.nix { + ((import ../common/data-model.nix { inherit (pkgs) system; inherit inputs; config = deployment-config; @@ -28,16 +29,16 @@ in { _class = "nixosTest"; imports = [ - ./options.nix + ../common/data-model-options.nix ]; name = "deployment-model"; sourceFileset = lib.fileset.unions [ ../../data-model.nix ../../function.nix + ../common/data-model.nix + ../common/data-model-options.nix ./constants.nix - ./deployment.nix - ./options.nix (config.pathToCwd + "/flake-under-test.nix") ]; @@ -85,7 +86,7 @@ in import ${pathToRoot}/deployment/nixos.nix { inherit system; configuration = ( - import ${pathToRoot}/deployment/check/data-model/deployment.nix { + import ${pathToRoot}/deployment/check/common/data-model.nix { inherit system; config = builtins.fromJSON "${escapedJson deployment-config}"; } -- 2.48.1 From cc6634844409f86302efd8c3d7e0e7bce0b2f74f Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Wed, 27 Aug 2025 22:00:04 +0200 Subject: [PATCH 75/76] split tests to allow running the faster ssh test separately --- .forgejo/workflows/ci.yaml | 10 +++- deployment/check/basic/default.nix | 7 ++- deployment/check/cli/default.nix | 1 + deployment/check/common/nixosTest.nix | 2 +- .../check/data-model-nixops4/constants.nix | 9 ++++ .../check/data-model-nixops4/default.nix | 22 ++++++++ .../flake-under-test.nix | 2 +- .../check/data-model-nixops4/nixosTest.nix | 52 +++++++++++++++++++ .../constants.nix | 1 - .../default.nix | 0 .../nixosTest.nix | 21 ++------ deployment/check/panel/default.nix | 1 + deployment/data-model.nix | 2 +- deployment/flake-part.nix | 7 ++- 14 files changed, 112 insertions(+), 25 deletions(-) create mode 100644 deployment/check/data-model-nixops4/constants.nix create mode 100644 deployment/check/data-model-nixops4/default.nix rename deployment/check/{data-model => data-model-nixops4}/flake-under-test.nix (85%) create mode 100644 deployment/check/data-model-nixops4/nixosTest.nix rename deployment/check/{data-model => data-model-ssh}/constants.nix (88%) rename deployment/check/{data-model => data-model-ssh}/default.nix (100%) rename deployment/check/{data-model => data-model-ssh}/nixosTest.nix (83%) diff --git a/.forgejo/workflows/ci.yaml b/.forgejo/workflows/ci.yaml index 629cce26..626c9d13 100644 --- a/.forgejo/workflows/ci.yaml +++ b/.forgejo/workflows/ci.yaml @@ -57,11 +57,17 @@ jobs: - uses: actions/checkout@v4 - run: nix build .#checks.x86_64-linux.deployment-panel -L - check-deployment-model: + check-deployment-model-ssh: runs-on: native steps: - uses: actions/checkout@v4 - - run: nix build .#checks.x86_64-linux.deployment-model -L + - run: nix build .#checks.x86_64-linux.deployment-model-ssh -L + + check-deployment-model-nixops4: + runs-on: native + steps: + - uses: actions/checkout@v4 + - run: nix build .#checks.x86_64-linux.deployment-model-nixops4 -L ## NOTE: NixOps4 does not provide a good “dry run” mode, so we instead check ## proxies for resources, namely whether their `.#vmOptions.` and diff --git a/deployment/check/basic/default.nix b/deployment/check/basic/default.nix index 6479b6be..176defce 100644 --- a/deployment/check/basic/default.nix +++ b/deployment/check/basic/default.nix @@ -10,5 +10,10 @@ runNixOSTest { ./nixosTest.nix ]; _module.args = { inherit inputs sources; }; - inherit (import ./constants.nix) targetMachines pathToRoot pathFromRoot; + inherit (import ./constants.nix) + targetMachines + pathToRoot + pathFromRoot + useFlake + ; } diff --git a/deployment/check/cli/default.nix b/deployment/check/cli/default.nix index 03c1379f..64448280 100644 --- a/deployment/check/cli/default.nix +++ b/deployment/check/cli/default.nix @@ -15,5 +15,6 @@ runNixOSTest { pathToRoot pathFromRoot enableAcme + useFlake ; } diff --git a/deployment/check/common/nixosTest.nix b/deployment/check/common/nixosTest.nix index 574621c4..93bd3fef 100644 --- a/deployment/check/common/nixosTest.nix +++ b/deployment/check/common/nixosTest.nix @@ -163,7 +163,7 @@ in '')} ${ - if true then + if config.useFlake then '' ## NOTE: This is super slow. It could probably be optimised in Nix, for ## instance by allowing to grab things directly from the host's store. diff --git a/deployment/check/data-model-nixops4/constants.nix b/deployment/check/data-model-nixops4/constants.nix new file mode 100644 index 00000000..548d6605 --- /dev/null +++ b/deployment/check/data-model-nixops4/constants.nix @@ -0,0 +1,9 @@ +{ + targetMachines = [ + "nixops4" + ]; + pathToRoot = ../../..; + pathFromRoot = ./.; + enableAcme = true; + useFlake = true; +} diff --git a/deployment/check/data-model-nixops4/default.nix b/deployment/check/data-model-nixops4/default.nix new file mode 100644 index 00000000..b735cd53 --- /dev/null +++ b/deployment/check/data-model-nixops4/default.nix @@ -0,0 +1,22 @@ +{ + runNixOSTest, + inputs, + sources, +}: + +runNixOSTest { + imports = [ + ../../data-model.nix + ../../function.nix + ../common/nixosTest.nix + ./nixosTest.nix + ]; + _module.args = { inherit inputs sources; }; + inherit (import ./constants.nix) + targetMachines + pathToRoot + pathFromRoot + enableAcme + useFlake + ; +} diff --git a/deployment/check/data-model/flake-under-test.nix b/deployment/check/data-model-nixops4/flake-under-test.nix similarity index 85% rename from deployment/check/data-model/flake-under-test.nix rename to deployment/check/data-model-nixops4/flake-under-test.nix index 60b0ee38..6a4ce06c 100644 --- a/deployment/check/data-model/flake-under-test.nix +++ b/deployment/check/data-model-nixops4/flake-under-test.nix @@ -20,7 +20,7 @@ (import ./deployment/check/common/data-model.nix { inherit system inputs; config = { - inherit (import ./deployment/check/data-model/constants.nix) pathToRoot pathFromRoot; + inherit (import ./deployment/check/data-model-nixops4/constants.nix) pathToRoot pathFromRoot; nodeName = "nixops4"; }; })."nixops4-deployment".nixops4; diff --git a/deployment/check/data-model-nixops4/nixosTest.nix b/deployment/check/data-model-nixops4/nixosTest.nix new file mode 100644 index 00000000..5a4499f3 --- /dev/null +++ b/deployment/check/data-model-nixops4/nixosTest.nix @@ -0,0 +1,52 @@ +{ + lib, + config, + inputs, + ... +}: +{ + _class = "nixosTest"; + imports = [ + ../common/data-model-options.nix + ]; + + name = "deployment-model"; + sourceFileset = lib.fileset.unions [ + ../../data-model.nix + ../../function.nix + ../common/data-model.nix + ../common/data-model-options.nix + ./constants.nix + (config.pathToCwd + "/flake-under-test.nix") + ]; + + nodes.deployer = + { pkgs, ... }: + { + environment.systemPackages = with pkgs; [ + inputs.nixops4.packages.${system}.default + jq + ]; + + # FIXME: sad times + system.extraDependencies = with pkgs; [ + jq + jq.inputDerivation + ]; + + system.extraDependenciesFromModule = + { pkgs, ... }: + { + environment.systemPackages = with pkgs; [ + hello + ]; + }; + }; + + extraTestScript = '' + with subtest("nixops4"): + nixops4.fail("hello 1>&2") + deployer.succeed("nixops4 apply check-deployment-model --show-trace --verbose --no-interactive 1>&2") + nixops4.succeed("su - operator -c hello 1>&2") + ''; +} diff --git a/deployment/check/data-model/constants.nix b/deployment/check/data-model-ssh/constants.nix similarity index 88% rename from deployment/check/data-model/constants.nix rename to deployment/check/data-model-ssh/constants.nix index 9cc81ea9..ef212896 100644 --- a/deployment/check/data-model/constants.nix +++ b/deployment/check/data-model-ssh/constants.nix @@ -1,7 +1,6 @@ { targetMachines = [ "ssh" - "nixops4" ]; pathToRoot = ../../..; pathFromRoot = ./.; diff --git a/deployment/check/data-model/default.nix b/deployment/check/data-model-ssh/default.nix similarity index 100% rename from deployment/check/data-model/default.nix rename to deployment/check/data-model-ssh/default.nix diff --git a/deployment/check/data-model/nixosTest.nix b/deployment/check/data-model-ssh/nixosTest.nix similarity index 83% rename from deployment/check/data-model/nixosTest.nix rename to deployment/check/data-model-ssh/nixosTest.nix index a10c32e6..dbc79fbf 100644 --- a/deployment/check/data-model/nixosTest.nix +++ b/deployment/check/data-model-ssh/nixosTest.nix @@ -6,18 +6,18 @@ ... }: let - inherit (import ./constants.nix) pathToRoot; + inherit (import ./constants.nix) pathToRoot pathFromRoot; + inherit (pkgs) system; escapedJson = v: lib.replaceStrings [ "\"" ] [ "\\\\\"" ] (lib.strings.toJSON v); deployment-config = { - inherit (import ./constants.nix) pathToRoot pathFromRoot; + inherit pathToRoot pathFromRoot; inherit (config) enableAcme; acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; nodeName = "ssh"; }; inherit ((import ../common/data-model.nix { - inherit (pkgs) system; - inherit inputs; + inherit system inputs; config = deployment-config; })."ssh-deployment".ssh-host.ssh ) @@ -39,23 +39,15 @@ in ../common/data-model.nix ../common/data-model-options.nix ./constants.nix - (config.pathToCwd + "/flake-under-test.nix") ]; nodes.deployer = { pkgs, ... }: { environment.systemPackages = with pkgs; [ - inputs.nixops4.packages.${system}.default jq ]; - # FIXME: sad times - system.extraDependencies = with pkgs; [ - jq - jq.inputDerivation - ]; - system.extraDependenciesFromModule = { pkgs, ... }: { @@ -66,11 +58,6 @@ in }; extraTestScript = '' - with subtest("nixops4"): - nixops4.fail("hello 1>&2") - deployer.succeed("nixops4 apply check-deployment-model --show-trace --verbose --no-interactive 1>&2") - nixops4.succeed("su - operator -c hello 1>&2") - with subtest("ssh: Check the status before deployment"): ssh.fail("hello 1>&2") diff --git a/deployment/check/panel/default.nix b/deployment/check/panel/default.nix index 03c1379f..64448280 100644 --- a/deployment/check/panel/default.nix +++ b/deployment/check/panel/default.nix @@ -15,5 +15,6 @@ runNixOSTest { pathToRoot pathFromRoot enableAcme + useFlake ; } diff --git a/deployment/data-model.nix b/deployment/data-model.nix index 89b7275e..c6fcc631 100644 --- a/deployment/data-model.nix +++ b/deployment/data-model.nix @@ -70,7 +70,7 @@ let }; deployment-type = attrTag { ssh-host = mkOption { - description = "A Terraform deployment by SSH to update a single existing NixOS host."; + description = "A deployment by SSH to update a single existing NixOS host."; type = submodule { options = { inherit nixos-configuration; diff --git a/deployment/flake-part.nix b/deployment/flake-part.nix index ca80f247..2d7c7e39 100644 --- a/deployment/flake-part.nix +++ b/deployment/flake-part.nix @@ -22,7 +22,12 @@ inherit inputs sources; }; - deployment-model = import ./check/data-model { + deployment-model-ssh = import ./check/data-model-ssh { + inherit (pkgs.testers) runNixOSTest; + inherit inputs sources; + }; + + deployment-model-nixops4 = import ./check/data-model-nixops4 { inherit (pkgs.testers) runNixOSTest; inherit inputs sources; }; -- 2.48.1 From cda46b5f659d237657767f5b2b77a749396e8412 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Thu, 28 Aug 2025 21:06:19 +0200 Subject: [PATCH 76/76] add data model test for TF --- .forgejo/workflows/ci.yaml | 6 ++ deployment/check/common/data-model.nix | 22 +++++ deployment/check/data-model-tf/constants.nix | 8 ++ deployment/check/data-model-tf/default.nix | 21 +++++ deployment/check/data-model-tf/deploy.sh | 35 ++++++++ deployment/check/data-model-tf/main.tf | 44 ++++++++++ deployment/check/data-model-tf/nixos.nix | 13 +++ deployment/check/data-model-tf/nixosTest.nix | 91 ++++++++++++++++++++ deployment/check/data-model-tf/setup.nix | 19 ++++ deployment/check/data-model-tf/tf-env.nix | 31 +++++++ deployment/check/data-model-tf/tf.nix | 11 +++ deployment/check/data-model-tf/variables.tf | 23 +++++ deployment/data-model.nix | 9 ++ deployment/flake-part.nix | 5 ++ 14 files changed, 338 insertions(+) create mode 100644 deployment/check/data-model-tf/constants.nix create mode 100644 deployment/check/data-model-tf/default.nix create mode 100644 deployment/check/data-model-tf/deploy.sh create mode 100644 deployment/check/data-model-tf/main.tf create mode 100644 deployment/check/data-model-tf/nixos.nix create mode 100644 deployment/check/data-model-tf/nixosTest.nix create mode 100644 deployment/check/data-model-tf/setup.nix create mode 100644 deployment/check/data-model-tf/tf-env.nix create mode 100644 deployment/check/data-model-tf/tf.nix create mode 100644 deployment/check/data-model-tf/variables.tf diff --git a/.forgejo/workflows/ci.yaml b/.forgejo/workflows/ci.yaml index 626c9d13..94f81623 100644 --- a/.forgejo/workflows/ci.yaml +++ b/.forgejo/workflows/ci.yaml @@ -69,6 +69,12 @@ jobs: - uses: actions/checkout@v4 - run: nix build .#checks.x86_64-linux.deployment-model-nixops4 -L + check-deployment-model-tf: + runs-on: native + steps: + - uses: actions/checkout@v4 + - run: nix build .#checks.x86_64-linux.deployment-model-tf -L + ## NOTE: NixOps4 does not provide a good “dry run” mode, so we instead check ## proxies for resources, namely whether their `.#vmOptions.` and ## `.#nixosConfigurations.` outputs evaluate and build correctly, and diff --git a/deployment/check/common/data-model.nix b/deployment/check/common/data-model.nix index c4922f72..8928e93b 100644 --- a/deployment/check/common/data-model.nix +++ b/deployment/check/common/data-model.nix @@ -159,6 +159,20 @@ let }; }; }; + single-nixos-vm-tf = environment: { + resources."operator-environment".login-shell.username = "operator"; + implementation = requests: { + input = requests; + output.tf-host = { + nixos-configuration = mkNixosConfiguration environment requests; + ssh = { + username = "root"; + host = nodeName; + key-file = null; + }; + }; + }; + }; }; }; options = { @@ -185,6 +199,14 @@ let type = env.resource-mapping.output-type; default = env.deployment config."example-configuration"; }; + "tf-deployment" = + let + env = config.environments."single-nixos-vm-tf"; + in + mkOption { + type = env.resource-mapping.output-type; + default = env.deployment config."example-configuration"; + }; }; } ); diff --git a/deployment/check/data-model-tf/constants.nix b/deployment/check/data-model-tf/constants.nix new file mode 100644 index 00000000..a3e4e33a --- /dev/null +++ b/deployment/check/data-model-tf/constants.nix @@ -0,0 +1,8 @@ +{ + targetMachines = [ + "target" + ]; + pathToRoot = ../../..; + pathFromRoot = ./.; + enableAcme = true; +} diff --git a/deployment/check/data-model-tf/default.nix b/deployment/check/data-model-tf/default.nix new file mode 100644 index 00000000..1815f19a --- /dev/null +++ b/deployment/check/data-model-tf/default.nix @@ -0,0 +1,21 @@ +{ + runNixOSTest, + inputs, + sources, +}: + +runNixOSTest { + imports = [ + ../../data-model.nix + ../../function.nix + ../common/nixosTest.nix + ./nixosTest.nix + ]; + _module.args = { inherit inputs sources; }; + inherit (import ./constants.nix) + targetMachines + pathToRoot + pathFromRoot + enableAcme + ; +} diff --git a/deployment/check/data-model-tf/deploy.sh b/deployment/check/data-model-tf/deploy.sh new file mode 100644 index 00000000..c6f6414f --- /dev/null +++ b/deployment/check/data-model-tf/deploy.sh @@ -0,0 +1,35 @@ +#! /usr/bin/env bash +set -xeuo pipefail +declare username host system config_nix config_tf + +# INSTANTIATE +command=(nix-instantiate --argstr system "$system" --argstr config_nix "$config_nix" --argstr config_tf "$config_tf" ./nixos.nix) +# instantiate the config in /nix/store +"${command[@]}" -A out_path + +# DEPLOY +sshOpts=( + -o BatchMode=yes + -o StrictHostKeyChecking=no + # TODO set key for production + # ${if key-file == null then "" else "-i ${key-file}"} + # NOTE the below options are for tests + -o ConnectTimeout=1 + -o ServerAliveInterval=1 +) +destination="$username@$host" +# get the realized derivation to deploy +outPath=$(nix-store --realize "$("${command[@]}" --show-trace --eval --strict --json | jq -r '.drv_path')") +# deploy the config by nix-copy-closure +NIX_SSHOPTS="${sshOpts[*]}" nix-copy-closure --to "$destination" "$outPath" --gzip --use-substitutes +# switch the remote host to the config +# NOTE checks here are for tests - in production time-outs could be a real thing, rather than indicator of success! +# shellcheck disable=SC2029 +output=$(ssh "${sshOpts[@]}" "$destination" "nix-env --profile /nix/var/nix/profiles/system --set $outPath; nohup $outPath/bin/switch-to-configuration switch &" 2>&1) || echo "status code: $?" +echo "output: $output" +if [[ $output != *"Timeout, server $host not responding"* ]]; then + echo "non-timeout error: $output" + exit 1 +else + exit 0 +fi diff --git a/deployment/check/data-model-tf/main.tf b/deployment/check/data-model-tf/main.tf new file mode 100644 index 00000000..a10dc19f --- /dev/null +++ b/deployment/check/data-model-tf/main.tf @@ -0,0 +1,44 @@ +# hash of our code directory, used to trigger re-deploy +# FIXME calculate separately to reduce false positives +data "external" "hash" { + program = ["sh", "-c", "echo \"{\\\"hash\\\":\\\"$(nix-hash ../../..)\\\"}\""] +} + +# TF resource to build and deploy NixOS instances. +resource "terraform_data" "nixos" { + + # trigger rebuild/deploy if (FIXME?) any potentially used config/code changed, + # preventing these (20+s, build being bottleneck) when nothing changed. + # terraform-nixos separates these to only deploy if instantiate changed, + # yet building even then - which may be not as bad using deploy on remote. + # having build/deploy one resource reflects wanting to prevent no-op rebuilds + # over preventing (with less false positives) no-op deployments, + # as i could not find a way to do prevent no-op rebuilds without merging them: + # - generic resources cannot have outputs, while we want info from the instantiation (unless built on host?). + # - `data` always runs, which is slow for deploy and especially build. + triggers_replace = [ + data.external.hash.result, + var.host, + var.config_nix, + var.config_tf, + ] + + provisioner "local-exec" { + # directory to run the script from. we use the TF project root dir, + # here as a path relative from where TF is run from, + # matching calling modules' expectations on config_nix locations. + # note that absolute paths can cause false positives in triggers, + # so are generally discouraged in TF. + working_dir = path.root + environment = { + system = var.system + username = var.username + host = var.host + config_nix = var.config_nix + config_tf = replace(jsonencode(var.config_tf), "\"", "\\\"") + } + # TODO: refactor back to command="ignoreme" interpreter=concat([]) to protect sensitive data from error logs? + # TODO: build on target? + command = "sh deploy.sh" + } +} diff --git a/deployment/check/data-model-tf/nixos.nix b/deployment/check/data-model-tf/nixos.nix new file mode 100644 index 00000000..e5c2d3ed --- /dev/null +++ b/deployment/check/data-model-tf/nixos.nix @@ -0,0 +1,13 @@ +{ + system, + config_nix, + config_tf, +}: +import ../../nixos.nix { + inherit system; + configuration = + (import ../common/data-model.nix { + inherit system; + config = config_nix // builtins.fromJSON config_tf; + })."tf-deployment".tf-host.nixos-configuration; +} diff --git a/deployment/check/data-model-tf/nixosTest.nix b/deployment/check/data-model-tf/nixosTest.nix new file mode 100644 index 00000000..11ec23e2 --- /dev/null +++ b/deployment/check/data-model-tf/nixosTest.nix @@ -0,0 +1,91 @@ +{ + lib, + config, + pkgs, + inputs, + ... +}: +let + inherit (import ./constants.nix) pathToRoot pathFromRoot; + inherit (pkgs) system; + # escapedJson = v: lib.replaceStrings [ "\"" ] [ "\\\\\"" ] (lib.strings.toJSON v); + deployment-config = { + inherit pathToRoot pathFromRoot; + inherit (config) enableAcme; + acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null; + nodeName = "target"; + }; + inherit + ((import ../common/data-model.nix { + inherit system inputs; + config = deployment-config; + })."tf-deployment".tf-host.ssh + ) + host + username + # key-file + ; + tf-vars = { + inherit host username system; + config_nix = lib.strings.toJSON deployment-config; + # config_nix = escapedJson deployment-config; + # config_tf = ; + }; + tf-env = pkgs.callPackage ./tf-env.nix { }; +in +{ + _class = "nixosTest"; + imports = [ + ../common/data-model-options.nix + ]; + + name = "deployment-model"; + sourceFileset = lib.fileset.unions [ + ../../data-model.nix + ../../function.nix + ../common/data-model.nix + ../common/data-model-options.nix + ./constants.nix + ./main.tf + ./variables.tf + ./deploy.sh + ]; + + nodes.deployer = + { pkgs, ... }: + { + # nixpkgs.config.allowUnfree = lib.mkForce true; + + environment.systemPackages = with pkgs; [ + (pkgs.callPackage ./tf.nix { }) + jq + ]; + + # needed only when building from deployer + system.extraDependenciesFromModule = + { pkgs, ... }: + { + environment.systemPackages = with pkgs; [ + hello + ]; + }; + }; + + extraTestScript = '' + with subtest("ssh: Check the status before deployment"): + target.fail("hello 1>&2") + + with subtest("ssh: Run the deployment"): + deployer.succeed(""" + set -xeuo pipefail + ${lib.concatStringsSep "\n" (lib.mapAttrsToList (k: v: ''export TF_VAR_${k}='${v}';'') tf-vars)} + export TF_LOG=info + + cd "${tf-env}/deployment/check/data-model-tf" + # parallelism=1: limit OOM risk + tofu apply --auto-approve -lock=false -parallelism=1 + """) + target.wait_for_unit("multi-user.target") + target.succeed("su - operator -c hello 1>&2") + ''; +} diff --git a/deployment/check/data-model-tf/setup.nix b/deployment/check/data-model-tf/setup.nix new file mode 100644 index 00000000..6a9cefc0 --- /dev/null +++ b/deployment/check/data-model-tf/setup.nix @@ -0,0 +1,19 @@ +{ + pkgs, + lib, + sources, +}: +pkgs.writeScriptBin "setup" '' + # calculated pins + echo '${lib.strings.toJSON sources}' > ./.npins.json + # generate TF lock for nix's TF providers + for category in deployment/check/data-model-tf; do + pushd "$category" + rm -rf .terraform/ + rm -f .terraform.lock.hcl + # suppress warning on architecture-specific generated lock file: + # `Warning: Incomplete lock file information for providers`. + tofu init -input=false 1>/dev/null + popd + done +'' diff --git a/deployment/check/data-model-tf/tf-env.nix b/deployment/check/data-model-tf/tf-env.nix new file mode 100644 index 00000000..cb22201d --- /dev/null +++ b/deployment/check/data-model-tf/tf-env.nix @@ -0,0 +1,31 @@ +{ + lib, + pkgs, + sources ? import ../../../npins, +}: +pkgs.stdenv.mkDerivation { + name = "tf-repo"; + src = + with lib.fileset; + toSource { + root = ../../../.; + # don't copy ignored files + fileset = intersection (gitTracked ../../../.) ../../../.; + }; + buildInputs = [ + (pkgs.callPackage ./tf.nix { }) + (pkgs.callPackage ./setup.nix { inherit sources; }) + ]; + buildPhase = '' + runHook preBuild + pushd deployment/check/data-model-tf + setup + popd + runHook postBuild + ''; + installPhase = '' + runHook preInstall + cp -r . $out + runHook postInstall + ''; +} diff --git a/deployment/check/data-model-tf/tf.nix b/deployment/check/data-model-tf/tf.nix new file mode 100644 index 00000000..8551cb82 --- /dev/null +++ b/deployment/check/data-model-tf/tf.nix @@ -0,0 +1,11 @@ +# FIXME: use overlays so this gets imported just once? +{ + pkgs, + ... +}: +let + tf = pkgs.opentofu; +in +tf.withPlugins (p: [ + p.external +]) diff --git a/deployment/check/data-model-tf/variables.tf b/deployment/check/data-model-tf/variables.tf new file mode 100644 index 00000000..c1fd0bf9 --- /dev/null +++ b/deployment/check/data-model-tf/variables.tf @@ -0,0 +1,23 @@ +variable "system" { + type = string + default = "x86_64-linux" +} + +variable "username" { + type = string + default = "root" +} + +variable "host" { + type = string +} + +variable "config_nix" { + type = string + default = "{}" +} + +variable "config_tf" { + type = map(any) + default = {} +} diff --git a/deployment/data-model.nix b/deployment/data-model.nix index c6fcc631..914a5e0f 100644 --- a/deployment/data-model.nix +++ b/deployment/data-model.nix @@ -82,6 +82,15 @@ let description = "A NixOps4 NixOS deployment. For an example, see https://github.com/nixops4/nixops4-nixos/blob/main/example/deployment.nix."; type = nixops4Deployment; }; + tf-host = mkOption { + description = "A Terraform deployment by SSH to update a single existing NixOS host."; + type = submodule { + options = { + inherit nixos-configuration; + ssh = host-ssh; + }; + }; + }; }; in { diff --git a/deployment/flake-part.nix b/deployment/flake-part.nix index 2d7c7e39..ece89360 100644 --- a/deployment/flake-part.nix +++ b/deployment/flake-part.nix @@ -31,6 +31,11 @@ inherit (pkgs.testers) runNixOSTest; inherit inputs sources; }; + + deployment-model-tf = import ./check/data-model-tf { + inherit (pkgs.testers) runNixOSTest; + inherit inputs sources; + }; }; }; } -- 2.48.1