Compare commits

...

97 commits

Author SHA1 Message Date
79f432f398
strip test as a start to convert it 2025-08-07 14:04:59 +02:00
d86086d53a
switch out infra to terraform, remove flakes
account for 285
2025-08-07 13:29:20 +02:00
bf4df5500a
move files in line with tf-infra 2025-08-07 09:34:53 +02:00
58c1999fd2
button works deployed
late rebasing

account for 285
2025-08-07 09:30:36 +02:00
1f99a4c6c3 listToAttrs o map o attrsToListmapAttrs' (#489)
Reviewed-on: Fediversity/Fediversity#489
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-08-01 13:09:26 +02:00
588bb77a94 Infra: expose and use checks for vmOptions and nixosConfigurations (#488)
Following Fediversity/Fediversity#478 (comment), 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: Fediversity/Fediversity#488
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-07-31 15:41:02 +02:00
df3a070fa4 Infra: get rid of makeResourceModule (#485)
Reviewed-on: Fediversity/Fediversity#485
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-07-31 00:49:00 +02:00
be72b82875 Link to upstreaming PR for lib.types.fileset (#487)
Reviewed-on: Fediversity/Fediversity#487
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-07-30 13:25:22 +02:00
1b66028f32 Fix infra and add more tests (#478)
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.<machine>` 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: Fediversity/Fediversity#478
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-07-30 12:31:03 +02:00
4509d277d3 move arguments from _module.args to specialArgs (#469)
Reviewed-on: Fediversity/Fediversity#469
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-23 18:12:55 +02:00
e488230d7b updater: make npins command verbose (#477)
Reviewed-on: Fediversity/Fediversity#477
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-19 13:00:33 +02:00
765183cd0d fix typo in users (#475)
Reviewed-on: Fediversity/Fediversity#475
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-17 19:02:14 +02:00
6cf1d87f0b get parity in authorized keys between procolix and root for nixops4 ssh to non-VMs (#474)
Reviewed-on: Fediversity/Fediversity#474
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-17 18:59:31 +02:00
8253288f8a remove pixelfed from CI until fixed (#472)
this test is still borked as per #33.
the intent would be to get this test on a feature branch pertaining to that issue - the point being we should be able to rely on CI's boolean result for detecting (newly induced) regressions.

Reviewed-on: Fediversity/Fediversity#472
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-17 15:25:17 +02:00
67f50f08de enable continuous deployment (#471)
closes #177

Reviewed-on: Fediversity/Fediversity#471
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-17 10:01:51 +02:00
5402178e7b reinstate import statement for panel module, fixes error deploying fedi201 (#468)
resolves error on CI run https://git.fediversity.eu/Fediversity/Fediversity/actions/runs/1026:

```
123456       error: attempt to call something which is not a function but a path: /nix/store/93yyf22vw60l1j3l6h02c99p93lp55q5-source/panel
       at /nix/store/93yyf22vw60l1j3l6h02c99p93lp55q5-source/machines/dev/fedi201/fedipanel.nix:13:6:
           12|   imports = [
           13|     (../../../panel { }).module
             |      ^
           14|     "${sources.home-manager}/nixos"```
```

Reviewed-on: Fediversity/Fediversity#468
2025-07-16 20:51:23 +02:00
e627815399 pass SHELL env var in CD (#466)
see #177

Reviewed-on: Fediversity/Fediversity#466
2025-07-16 18:18:16 +02:00
354dba260a verbose CD (#465)
debugging effort part of #177

Reviewed-on: Fediversity/Fediversity#465
2025-07-16 14:14:06 +02:00
b791bd515d pass sources via specialArgs (#464)
this gets rid of ugly in-place imports and upward paths

Reviewed-on: Fediversity/Fediversity#464
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-committed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2025-07-16 10:53:36 +02:00
f2017aaeb4 CD: lump SSH commands into a single shell invocation (#462)
Reviewed-on: Fediversity/Fediversity#462
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-15 13:00:47 +02:00
980a994f83 run ssh commands thru the shell (which has openssh) (#461)
Reviewed-on: Fediversity/Fediversity#461
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-15 12:26:16 +02:00
b9b13df04e allow SSH access from continuous deployment (#460)
Reviewed-on: Fediversity/Fediversity#460
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-15 11:56:22 +02:00
159e4107b8 fix Pixelfed test eval failure (#458)
Reviewed-on: Fediversity/Fediversity#458
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-15 10:38:10 +02:00
86305a6a2e fix link; readability 2025-07-15 09:04:56 +02:00
e62f14d9be expose panel tests in flake 2025-07-15 08:54:48 +02:00
82f83eea0d fix mastodon test (#457)
closes #34.

Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Reviewed-on: Fediversity/Fediversity#457
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-15 08:54:20 +02:00
aef414ffe8 resolve regressions from recent qemu files (#432)
- move import to match module classes
- manually import sources to resolve infinite recursion

closes #431.

Reviewed-on: Fediversity/Fediversity#432
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-11 16:09:27 +02:00
6d74112518 ditch sources arg in fedi201, fixing infinite recursion error (#454)
c.f. #432.

closes #453.

Reviewed-on: Fediversity/Fediversity#454
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-11 16:06:15 +02:00
2b2fb059fd fix cd command (#455)
Reviewed-on: Fediversity/Fediversity#455
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-11 11:07:03 +02:00
66ceb66382 add deployment pipeline (#452)
part of #177

Reviewed-on: Fediversity/Fediversity#452
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-10 16:45:46 +02:00
ad9c61a3db docs: fix typos 2025-07-10 00:37:27 +02:00
b4e1c5b5b3 Restrict fileset necessary for deployment tests (#450)
Now that we won't depend on the flake.nix anymore, we won't depend on all the flake-part.nix files (necessary to evaluate flake.nix) and all the files they depend on etc., so the Nix dependencies of the tests will be drastically reduced, and I will be able to leverage that by introducing a more subtle src. This will make the test not need to re-run if only things outside that reduced src changed (and the previous run is in the Nix store).

Reviewed-on: Fediversity/Fediversity#450
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-07-09 22:57:52 +02:00
de38611572 Unflakify deployment tests (#449)
This PR builds on top of #447 and #448. Since these might be rejected, there will be some changes needed for this PR as well. Let's see how the discussions go in #447.

In the meantime, @fricklerhandwerk, would you mind (in)validating the core idea of this PR? You only need to look at 7cf43c4041, really.

Reviewed-on: Fediversity/Fediversity#449
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-07-09 15:07:02 +02:00
1d40dcfc0e Grab git-hooks from npins (#448)
This PR builds on top of #447 and will be subject to the same discussion. Let's discuss there whether it makes sense to get rid of the `flake-parts` and `git-hooks` flake inputs.

Reviewed-on: Fediversity/Fediversity#448
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-07-09 13:21:48 +02:00
c3bf158130 Note on extracting mkFlake to an external library (#451)
follow-up on Fediversity/Fediversity#447 (comment)

Reviewed-on: Fediversity/Fediversity#451
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-07-09 12:34:43 +02:00
48c6a1f22b Extract mkFlake to own file - get flake-parts from npins (#447)
The goal is to contain the “`mkFlake` hack” to a file that we can heavily document but otherwise ignore. This also will allow me to reuse it in the “flake under test” of the deployment tests.

Reviewed-on: Fediversity/Fediversity#447
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-07-09 10:12:47 +02:00
8a7984933d reinstate acme settings needed by applications (#434)
closes #417

Reviewed-on: Fediversity/Fediversity#434
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-08 10:02:13 +02:00
5520fa721b gitea PR unpruned (#445)
see #65

Reviewed-on: Fediversity/Fediversity#445
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-07 13:38:09 +02:00
eabfc228c5 updater: try the first upstream commit without git remote prune (#444)
Reviewed-on: Fediversity/Fediversity#444
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-07 13:23:08 +02:00
3f923532a2 updater: fully qualify github domain in uses (#443)
part of #65.
succeeds #442.

Reviewed-on: Fediversity/Fediversity#443
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-07 12:50:55 +02:00
37d4fc5a42 un-qualify github.com domain in updater uses, which resolved to data.forgejo.org/github.com (#442)
attempt to address https://git.fediversity.eu/Fediversity/Fediversity/actions/runs/920.
part of #65.

Reviewed-on: Fediversity/Fediversity#442
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-07 12:47:06 +02:00
c7b05bb473 deduplicate import-flake, fixing test regression from git merge (#441)
Reviewed-on: Fediversity/Fediversity#441
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-07 10:58:10 +02:00
5345860006 data model: add run-time configuration (#437)
based on @fricklerhandwerk's work at https://git.fediversity.eu/fricklerhandwerk/Fediversity/compare/main...data-model-as-diagram part of #103.
supersedes #402.

on the application model, feedback on a [sample implementation](#2) welcome as well.

Reviewed-on: Fediversity/Fediversity#437
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-06 21:42:25 +02:00
fbb0806072 in update workflow use PR action actually meant for gitea (#438)
see #65

Reviewed-on: Fediversity/Fediversity#438
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-06 21:39:26 +02:00
b0848727fd clarify the use of isNormalUser (#436)
Reviewed-on: Fediversity/Fediversity#436
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-06 09:09:43 +02:00
a882de0b8e address linter gripes in nix files (#430)
Reviewed-on: Fediversity/Fediversity#430
Reviewed-by: Nicolas Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-03 15:56:16 +02:00
05572ff69e remove string interpolation from imports (#429)
Reviewed-on: Fediversity/Fediversity#429
Reviewed-by: Nicolas Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-03 15:55:39 +02:00
13c92280ab Clean up lib in forgejo-ci and extend on the .ssh/config comment (#428)
Reviewed-on: Fediversity/Fediversity#428
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-07-02 17:49:44 +02:00
871672d447 Add forgejo-ci machine to our infrastructure (#389)
picked up from https://git.fediversity.eu/Fediversity/Fediversity/compare/main...niols:forgejo-ci.

closes #356.

Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Reviewed-on: Fediversity/Fediversity#389
Reviewed-by: Nicolas Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-02 17:43:09 +02:00
6da42936e7 add missing home-manager import to fedipanel VM (#425)
Reviewed-on: Fediversity/Fediversity#425
Reviewed-by: Nicolas Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-02 17:32:38 +02:00
8df70a2ff0 classify recent flake-parts files 2025-07-02 13:25:23 +02:00
5a92c2c0bc docs: fix links to machines (#426)
Reviewed-on: Fediversity/Fediversity#426
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-02 13:16:38 +02:00
1c92009879
Do not force QEMU options onto machines 2025-07-01 23:55:33 +02:00
a791ad41ec Inject sources, secrets and keys via module system - avoid import ../ (#421)
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Reviewed-on: Fediversity/Fediversity#421
Reviewed-by: Nicolas Jeannerod <nicolas.jeannerod@moduscreate.com>
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-01 21:08:15 +02:00
c1dc0fef01 Split nameservers between IPv4 and IPv6 (#420)
Reviewed-on: Fediversity/Fediversity#420
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-07-01 13:09:06 +02:00
5a3cbe4d83 fix agenix package in shell (#422)
as per Fediversity/Fediversity#419 (comment)

Reviewed-on: Fediversity/Fediversity#422
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-07-01 11:29:38 +02:00
fd1d55df5f
move shell from flake 2025-07-01 10:22:58 +02:00
0c23115cff allow configuring network interface (#413)
Reviewed-on: Fediversity/Fediversity#413
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-06-30 19:23:51 +02:00
3f1c8a9bb7
Document why Nix and OpenSSH
lost in #412. Alternatively, we could have a comment on both lines
saying eg. “for NixOps4”
2025-06-30 14:30:29 +02:00
737aecaba6 set default value for nixops4Package (#412)
Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Reviewed-on: Fediversity/Fediversity#412
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-06-30 12:44:53 +02:00
d7dbdd923c make CI test invocations idempotent to better facilitate manual use (#416)
Reviewed-on: Fediversity/Fediversity#416
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-06-30 12:42:19 +02:00
1c44004cfe update documentation for #375 (#406)
Reviewed-on: Fediversity/Fediversity#406
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-06-27 14:40:40 +02:00
ae444d5352 simplify imports (#415)
Reviewed-on: Fediversity/Fediversity#415
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-06-27 14:01:41 +02:00
e77fdd9eec expose nixops4 in nix-shell (#411)
Instead of Fediversity/Fediversity#406

Eventually we should merge `//panel/default.nix` with `//default.nix` of course.

Co-authored-by: Nicolas Jeannerod <nicolas.jeannerod@moduscreate.com>
Reviewed-on: Fediversity/Fediversity#411
Reviewed-by: Nicolas Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-committed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2025-06-27 12:00:47 +02:00
1f1cf0d516 unset class, fixing #408 (#410)
Reviewed-on: Fediversity/Fediversity#410
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-06-27 11:53:17 +02:00
f94eac698a
disable updater schedule while it hangs 2025-06-26 17:01:40 +02:00
46182e7512 fix workflow step title (#404)
split off from #399

Reviewed-on: Fediversity/Fediversity#404
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-06-24 08:58:01 +02:00
9a25a04bfa specify _class module attributes to explicitly declare module types (#398)
closes #93.

note that this includes classes:

- `nixos`
- `nixosTest`
- `nixops4Resource`
- `nixops4Deployment`

.. and my (made-up, as per the [docs](https://ryantm.github.io/nixpkgs/module-system/module-system/#module-system-lib-evalModules-param-class)):

- `nix-unit`
- `package`

.. while i did not manage to cover:

- service tests, given `pkgs.nixosTest` seemed to not actually like `_class = "nixosTest"` (?!)

... nor #93's mentioned destructured arguments for that matter, as per Fediversity/Fediversity#93 (comment) - let me know if that is still desired as well.

Reviewed-on: Fediversity/Fediversity#398
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-06-23 17:24:54 +02:00
c1b33121b6 expose npins in shell for CI (#403)
Should fix Fediversity/Fediversity#65 (comment)

Reviewed-on: Fediversity/Fediversity#403
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-committed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2025-06-23 17:21:32 +02:00
d073bd706d
cast DEPLOYMENT_FLAKE to string 2025-06-22 14:42:14 +02:00
486b316885 run updater natively (#394)
see Fediversity/Fediversity#65 (comment).

closes #65.

Reviewed-on: Fediversity/Fediversity#394
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-06-20 09:41:38 +02:00
611c961dcf separate test declarations from invocations (#396)
see Fediversity/Fediversity#395 (comment)

Reviewed-on: Fediversity/Fediversity#396
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-06-19 18:11:08 +02:00
d67f533948 fix running nixops4 apply test (#391)
Closes #390

Reviewed-on: Fediversity/Fediversity#391
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-committed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2025-06-19 08:26:20 +02:00
bd1cfd7a7c Introduce test for deploying all services via FediPanel (#361)
Closes #277

Same as #329 but where we run the FediPanel and interact with it via a browser
instead of running NixOps4 directly.

Reviewed-on: Fediversity/Fediversity#361
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-06-18 12:37:47 +02:00
939f9d961d add data model entity: application (#387)
part of #103.

Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Reviewed-on: Fediversity/Fediversity#387
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-06-17 17:11:52 +02:00
4801433ae0 Get rid of the need for deployer.pub (#385)
The tests still work because we manually write the deployer's public key in `/root/.ssh/authorized_keys` on the target machines. In itself, however, the configuration that we push does not allow the deployer to push anything on the target machines.

Context: Fediversity/Fediversity#361 (comment)
Reviewed-on: Fediversity/Fediversity#385
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-06-17 16:34:29 +02:00
3a3a083793 FediPanel: allow configuring flake and deployment (#376)
Last part of #361.

Builds on top of #375.

Reviewed-on: Fediversity/Fediversity#376
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-06-15 16:55:19 +02:00
ace56e754e FediPanel: do not call nix develop (#375)
Yet another piece of #361.

Reviewed-on: Fediversity/Fediversity#375
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-06-15 15:06:23 +02:00
dbb4ce67fc move machines to reflect a semantic structure (#367)
later we may want to distinguish dev vs host as well, tho eventually we expect not to have hard-coded machines anyway.

split off from #319.

Reviewed-on: Fediversity/Fediversity#367
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-06-15 15:01:56 +02:00
5a514b96e9 use deployed environment for launching nixops4 from the panel 2025-06-13 16:39:34 +02:00
1b832c1f5b bypass native flake input for Nixpkgs (#374)
@Niols the sheer amount of hassle and noise indicates that it may be better to first split out a `flake.nix` just for the tests. And all this clutter doesn't even explain yet *why* we thought it needs to be there.

closes #279.

Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Reviewed-on: Fediversity/Fediversity#374
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-committed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2025-06-12 13:05:11 +02:00
69b2e535fe Document nullable fields sanitation (#365)
Reviewed-on: Fediversity/Fediversity#365
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-06-10 11:57:01 +02:00
09119803e8 Deployment: handle nullable config fields
This is quite frustrating. In the meantime, it does get the deployment
working again.
2025-06-06 11:50:48 +02:00
4dd1491e71 FediPanel: fix deployment status
also remove unused `dummy_user`
2025-06-06 11:02:40 +02:00
2f55e1512a FediPanel: bump nginx timeout to an hour 2025-06-06 10:57:19 +02:00
b59f8a4183 simplify login tests (#352)
don't go through template generation but use underlying the tag
implementation directly

Co-authored-by: Nicolas Jeannerod <nicolas.jeannerod@moduscreate.com>
Reviewed-on: Fediversity/Fediversity#352
2025-06-06 10:56:34 +02:00
56b953526b Deployment tests: Check status of services before deploying 2025-06-06 10:54:06 +02:00
1f8677e83d FediPanel: better logging of NixOps4 2025-06-06 10:53:22 +02:00
2fae356d0a Deployment tests: also make acmeNodeIP available in NixOS test 2025-06-06 10:52:49 +02:00
046f7c5998 Deployment tests: comment on Pebble's certificate 2025-06-06 10:52:18 +02:00
69579fea1c expose JSON schema for inspection (#351)
intending to document more about how our schemas work, i found our code currently buries this somewhere in `/nix/store`.
this change exposes that generated file in the project structure as well, facilitating inspection of our data schema in this more generic format.

Reviewed-on: Fediversity/Fediversity#351
Reviewed-by: Nicolas Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-06-04 19:15:26 +02:00
afc7ad2b88 add to the glossary as per ZHF session (#340)
points of attention (compared to ZHF pad):

- nouns vs verbs (feel free to improve!)
- some phrasing differences
  - changed services to applications
  - unified host to runtime environment as per diagram
- distinguish actors using rounded corners

Reviewed-on: Fediversity/Fediversity#340
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2025-06-02 11:43:31 +02:00
ac66b9d3c6 automate dependency updates using update-npins (#343)
use [update-npins](https://github.com/getchoo/update-npins) to automate dependency updates.

alternative renovatebot seemed to mostly support flakes so far, while it mostly seemed better at showing what packages got updated to which version. i figured that seemed not worth fixing for now, so settled for update-npins.

not sure how to test before merge, but i added the deploy token (with write rights) and exposed it as the referenced secret.

closes #65.

Reviewed-on: Fediversity/Fediversity#343
Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-committed-by: Kiara Grouwstra <kiara@procolix.eu>
2025-06-01 16:00:10 +02:00
ee5c2b90b7 Introduce test for deploying all services with nixops4 apply (#329)
Closes Fediversity/Fediversity#276

This PR adds a CLI deployment test. It builds on top of Fediversity/Fediversity#323. This test features a deployer node and four target nodes. The deployer node runs `nixops4 apply` on a deployment built with our actual code in `deployment/default.nix`, which pushes onto the four target machines combinations of Garage/Mastodon/Peertube/Pixelfed depending on a JSON payload. We check that the expected services are indeed deployed on the machines. Getting there involved reworking the existing basic test to extract common patterns, and adding support for ACME certificates negotiation inside the NixOS test.

What works:
- deployer successfully runs `nixops4 apply` with various payloads
- target machines indeed get the right services pushed onto them and removed
- services on target machines successfully negotiate ACME certificates

What does not work: the services themselves depend a lot on DNS and that is not taken care of at all, so they are probably very broken. Still, this is a good milestone.

Test it yourself by running `nix build .#checks.x86_64-linux.deployment-basic -vL` and `nix build .#checks.x86_64-linux.deployment-cli -vL`. On the very beefy machine that I am using, the basic test runs in ~4 minutes and the CLI test in ~17 minutes. We know from Fediversity/Fediversity#323 that the basic test runs in ~12 minutes on the CI runner, so maybe about an hour for the CLI test?

Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Reviewed-on: Fediversity/Fediversity#329
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-05-19 02:18:54 +02:00
5f66a034f3 actually use the typed configuration in deployment
that change somehow slipped through the cracks previously
2025-05-09 12:46:11 +02:00
df9ce821de collapse VM tests into one, use subtests 2025-05-05 09:22:43 +02:00
f4ebf55a15
unify versioning (minus around nixops, for now) 2025-05-01 13:24:45 +02:00
186 changed files with 3893 additions and 1256 deletions

4
.envrc
View file

@ -3,8 +3,8 @@
# shellcheck shell=bash # shellcheck shell=bash
if type -P lorri &>/dev/null; then if type -P lorri &>/dev/null; then
eval "$(lorri direnv --flake .)" eval "$(lorri direnv)"
else else
echo 'while direnv evaluated .envrc, could not find the command "lorri" [https://github.com/nix-community/lorri]' echo 'while direnv evaluated .envrc, could not find the command "lorri" [https://github.com/nix-community/lorri]'
use flake use_nix
fi fi

View file

@ -0,0 +1,24 @@
name: deploy-infra
on:
workflow_dispatch: # allows manual triggering
push:
branches:
- main
jobs:
deploy:
runs-on: native
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up SSH key for age secrets and SSH
run: |
env
mkdir -p ~/.ssh
echo "${{ secrets.CD_SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
- name: Deploy
run: nix-shell --run 'eval "$(ssh-agent -s)" && ssh-add ~/.ssh/id_ed25519 && SHELL=$(which bash) nixops4 apply -v default'

View file

@ -13,22 +13,84 @@ jobs:
runs-on: native runs-on: native
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- run: nix build .#checks.x86_64-linux.pre-commit -L - run: nix-build -A tests
check-peertube: check-data-model:
runs-on: native runs-on: native
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- run: nix build .#checks.x86_64-linux.peertube -L - run: nix-shell --run 'nix-unit ./deployment/data-model-test.nix'
check-mastodon:
runs-on: native
steps:
- uses: actions/checkout@v4
- run: nix build .#checks.x86_64-linux.test-mastodon-service -L
check-services:
runs-on: native
steps:
- uses: actions/checkout@v4
- run: nix build .#checks.x86_64-linux.test-peertube-service -L
check-panel: check-panel:
runs-on: native runs-on: native
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- run: cd panel && nix-build -A tests - run: nix-build -A tests.panel
check-deployment-basic: check-deployment-basic:
runs-on: native runs-on: native
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- run: nix build .#checks.x86_64-linux.deployment-basic -L - run: cd deployment && nix-build -A tests
check-deployment-cli:
runs-on: native
steps:
- uses: actions/checkout@v4
- run: nix build .#checks.x86_64-linux.deployment-cli -L
check-deployment-panel:
runs-on: native
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.<machine>` and
## `.#nixosConfigurations.<machine>` 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
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 build .#checks.x86_64-linux.nixosConfigurations-$machine
done
check-launch:
runs-on: native
steps:
- uses: actions/checkout@v4
- run: cd launch && nix-build -A tests
check-infra:
runs-on: native
steps:
- uses: actions/checkout@v4
- run: cd infra && nix-build -A tests

View file

@ -0,0 +1,24 @@
name: update-dependencies
on:
workflow_dispatch: # allows manual triggering
# FIXME: re-enable when manual run works
# schedule:
# - cron: '0 0 1 * *' # monthly
jobs:
lockfile:
runs-on: native
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Update pins
run: nix-shell --run "npins --verbose update"
- name: Create PR
uses: https://github.com/KiaraGrouwstra/gitea-create-pull-request@f9f80aa5134bc5c03c38f5aaa95053492885b397
with:
remote-instance-api-version: v1
token: "${{ secrets.DEPLOY_KEY }}"
branch: npins-update
commit-message: "npins: update sources"
title: "npins: update sources"

6
.gitignore vendored
View file

@ -1,3 +1,9 @@
.npins.json
.terraform/
.terraform.lock.hcl
.terraform.tfstate.lock.info
terraform.tfstate*
.auto.tfvars.json
.DS_Store .DS_Store
.idea .idea
*.log *.log

View file

@ -1,8 +1,7 @@
# The Fediversity project # The Fediversity project
This repository contains all the code and code-related files having to do with This repository contains all the code and code-related files having to do with
[the Fediversity project](https://fediversity.eu/), with the notable exception [the Fediversity project](https://fediversity.eu/).
of [NixOps4 that is hosted on GitHub](https://github.com/nixops4/nixops4).
## Goals ## Goals
@ -14,6 +13,12 @@ There already exist solutions for self-hosting, but they're not suitable for wha
The ones we're aware of require substantial technical knowledge and time commitment by operators, especially for scaling to thousands of users. The ones we're aware of require substantial technical knowledge and time commitment by operators, especially for scaling to thousands of users.
Not everyone has the expertise and time to run their own server. Not everyone has the expertise and time to run their own server.
## Interactions
To reach these goals, we aim to implement the following interactions between [actors](#actors) (depicted with rounded corners) and system components (see the [glossary](#glossary), depicted with rectangles).
![](https://git.fediversity.eu/Fediversity/meta/raw/branch/main/architecture-docs/interactions.svg)
## Actors ## Actors
- Fediversity project team - Fediversity project team
@ -57,11 +62,11 @@ Not everyone has the expertise and time to run their own server.
- [Fediverse](https://en.wikipedia.org/wiki/Fediverse) - [Fediverse](https://en.wikipedia.org/wiki/Fediverse)
A collection of social networking services that can communicate with each other using a common protocol. A collection of social networking applications that can communicate with each other using a common protocol.
- Service - Application
A Fediverse application run by the hosting provider for an operator. User-facing software (e.g. from Fediverse) run by the hosting provider for an operator.
- Configuration - Configuration
@ -73,35 +78,35 @@ Not everyone has the expertise and time to run their own server.
Make a resource, such as a virtual machine, available for use. Make a resource, such as a virtual machine, available for use.
> Example: We use [Proxmox](https://www.proxmox.com) to provision VMs for services run by operators. > Example: We use [Proxmox](https://www.proxmox.com) to provision VMs for applications run by operators.
- Deploy - Deploy
Put software, such as services, onto computers. Put software, such as applications, onto computers.
The software includes technical configuration that links software components. The software includes technical configuration that links software components.
Most user-facing configuration remains untouched by the deployment process. Most user-facing configuration remains untouched by the deployment process.
> Example: NixOps4 is used to deploy [Pixelfed](https://pixelfed.org). > Example: OpenTofu is used to deploy [Pixelfed](https://pixelfed.org).
- Migrate - Migrate
Move service configurations and user data to a different hosting provider. Move service configurations and deployment state, including user data, from one hosting provider to another.
- [NixOps4](https://github.com/nixops4/nixops4) - [OpenTofu](https://opentofu.org/)
A tool for deploying and managing resources through the Nix language. An infrastructure-as-code tool, and open-source (MPL 2.0) fork of Terraform.
NixOps4 development is supported by the Fediversity project
- Resource - Runtime backend
A [resource for NixOps4](https://nixops.dev/manual/development/concept/resource.html) is any external entity that can be declared with NixOps4 expressions and manipulated with NixOps4, such as a virtual machine, an active NixOS configuration, a DNS entry, or customer database. A type of digital environment one can run operating systems such as NixOS on, e.g. bare-metal, a hypervisor, or a container runtime.
- Resource provider - Runtime environment
A resource provider for NixOps4 is an executable that communicates between a resource and NixOps4 using a standardised protocol, allowing [CRUD operations](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) on the resources to be performed by NixOps4. The thing a deployment runs on, an interface against which the deployment is working. See runtime backend.
Refer to the [NixOps4 manual](https://nixops.dev/manual/development/resource-provider/index.html) for details.
> Example: We need a resource provider for obtaining deployment secrets from a database. - Runtime config
Configuration logic specific to a runtime backend, e.g. how to deploy, how to access object storage.
## Development ## Development
@ -118,9 +123,6 @@ Contact the project team if you have questions or suggestions, or if you're inte
Most of the directories in this repository have their own README going into more Most of the directories in this repository have their own README going into more
details as to what they are for. As an overview: details as to what they are for. As an overview:
- [`deployment/`](./deployment) contains work to generate a full Fediversity
deployment from a minimal configuration.
- [`infra/`](./infra) contains the configurations for the various VMs that are - [`infra/`](./infra) contains the configurations for the various VMs that are
in production for the project, for instance the Git instances or the Wiki, as in production for the project, for instance the Git instances or the Wiki, as
well as means to provision and set up new ones. well as means to provision and set up new ones.
@ -128,14 +130,8 @@ details as to what they are for. As an overview:
- [`keys/`](./keys) contains the public keys of the contributors to this project - [`keys/`](./keys) contains the public keys of the contributors to this project
as well as the systems that we administrate. as well as the systems that we administrate.
- [`matrix/`](./matrix) contains everything having to do with setting up a
fully-featured Matrix server.
- [`secrets/`](./secrets) contains the secrets that need to get injected into - [`secrets/`](./secrets) contains the secrets that need to get injected into
machine configurations. machine configurations.
- [`services/`](./services) contains our effort to make Fediverse applications - [`services/`](./services) contains our effort to make Fediverse applications
work seemlessly together in our specific setting. work seemlessly together in our specific setting.
- [`website/`](./website) contains the framework and the content of [the
Fediversity website](https://fediversity.eu/)

86
default.nix Normal file
View file

@ -0,0 +1,86 @@
{
system ? builtins.currentSystem,
sources ? import ./npins,
pkgs ? import sources.nixpkgs { inherit system; },
}:
let
inherit (sources)
nixpkgs
git-hooks
gitignore
;
inherit (pkgs) lib;
inherit (import sources.flake-inputs) import-flake;
inherit ((import-flake { src = ./.; }).inputs) nixops4;
panel = import ./panel { inherit sources system; };
pre-commit-check =
(import "${git-hooks}/nix" {
inherit nixpkgs system;
gitignore-nix-src = {
lib = import gitignore { inherit lib; };
};
}).run
{
src = ./.;
hooks =
let
## Add a directory here if pre-commit hooks shouldn't apply to it.
optout = [
"npins"
"**/.terraform"
];
excludes = map (dir: "^${dir}/") optout;
addExcludes = lib.mapAttrs (_: c: c // { inherit excludes; });
in
addExcludes {
nixfmt-rfc-style.enable = true;
deadnix.enable = true;
trim-trailing-whitespace.enable = true;
shellcheck.enable = true;
};
};
in
{
# shell for testing TF directly
shell = pkgs.mkShellNoCC {
inherit (pre-commit-check) shellHook;
buildInputs = pre-commit-check.enabledPackages;
packages =
let
test-loop = pkgs.writeShellApplication {
name = "test-loop";
runtimeInputs = [
pkgs.watchexec
pkgs.nix-unit
];
text = ''
watchexec -w ${builtins.toString ./.} -- nix-unit ${builtins.toString ./deployment/data-model-test.nix} "$@"
'';
};
in
[
pkgs.npins
pkgs.nil
(pkgs.callPackage "${sources.agenix}/pkgs/agenix.nix" { })
pkgs.openssh
pkgs.httpie
pkgs.jq
pkgs.nix-unit
test-loop
nixops4.packages.${system}.default
];
};
tests = {
inherit pre-commit-check;
panel = panel.tests;
};
# re-export inputs so they can be overridden granularly
# (they can't be accessed from the outside any other way)
inherit
sources
system
pkgs
;
}

View file

@ -1,6 +1,123 @@
# Deployment # Deployment
This repository contains work to generate a full Fediversity deployment from a This directory contains work to generate a full Fediversity deployment from a minimal configuration.
minimal configuration. This is different from [`../services/`](../services) that This is different from [`../services/`](../services) that focuses on one machine, providing a polished and unified interface to different Fediverse services.
focuses on one machine, providing a polished and unified interface to different
Fediverse services. ## Data model
The core piece of the project is the [Fediversity data model](./data-model.nix), which describes all entities and their interactions.
What can be done with it is exemplified in the [evaluation tests](./data-model-test.nix).
Run `test-loop` in the development environment when hacking on the data model or adding tests.
## Checks
There are three levels of deployment checks: `basic`, `cli`, `panel`.
They can be found in subdirectories of [`check/`](./check).
They can be run as part of `nix flake check` or individually as:
``` console
$ nix build .#checks.<system>.deployment-<name> -vL
```
Since `nixops4 apply` operates on a flake, the tests take this repository's flake as a template.
This also why there are some dummy files that will be overwritten inside the test.
### Basic deployment check
The basic deployment check is here as a building block and sanity check.
It does not actually use any of the code in this directory but checks that our test strategy is sound and that basic NixOps4 functionalities are here.
It is a NixOS test featuring one deployer machine and two target machines.
The deployment simply adds `pkgs.hello` to one and `pkgs.cowsay` to the other.
It is heavily inspired by [a similar test in `nixops4-nixos`].
[a similar test in nixops4-nixos]: https://github.com/nixops4/nixops4-nixos/blob/main/test/default/nixosTest.nix
This test involves three nodes:
- `deployer` is the node that will perform the deployment using `nixops4 apply`.
Because the test runs in a sandboxed environment, `deployer` will not have access to internet, and therefore it must already have all store paths needed for the target nodes.
- “target machines” are two eponymous nodes on which the packages `hello` and `cowsay` will be deployed.
They start with a minimal configuration.
``` mermaid
flowchart LR
deployer["deployer<br><font size='1'>has store paths<br>runs nixops4</font>"]
subgraph target_machines["target machines"]
direction TB
hello
cowsay
end
deployer -->|deploys| target_machines
```
### Service deployment check using `nixops4 apply`
This check omits the panel by running a direct invocation of NixOps4.
It deploys some services and checks that they are indeed on the target machines, then cleans them up and checks whether that works, too.
It builds upon the basic deployment check.
This test involves seven nodes:
- `deployer` is the node that will perform the deployment using `nixops4 apply`.
Because the test runs in a sandboxed environment, `deployer` will not have access to internet, and therefore it must already have all store paths needed for the target nodes.
- “target machines” are four nodes — `garage`, `mastodon`, `peertube`, and `pixelfed` — on which the services will be deployed.
They start with a minimal configuration.
- `acme` is a node that runs [Pebble], a miniature ACME server to deliver the certificates that the services expect.
- [WIP] `client` is a node that runs a browser controlled by some Selenium scripts in order to check that the services are indeed running and are accessible.
[Pebble]: https://github.com/letsencrypt/pebble
``` mermaid
flowchart LR
classDef invisible fill:none,stroke:none
subgraph left [" "]
direction TB
deployer["deployer<br><font size='1'>has store paths<br>runs nixops4</font>"]
client["client<br><font size='1'>Selenium scripts</font>"]
end
subgraph middle [" "]
subgraph target_machines["target machines"]
direction TB
garage
mastodon
peertube
pixelfed
end
end
subgraph right [" "]
direction TB
acme["acme<br><font size='1'>runs Pebble</font>"]
end
left ~~~ middle ~~~ right
class left,middle,right invisible
deployer -->|deploys| target_machines
client -->|tests| mastodon
client -->|tests| peertube
client -->|tests| pixelfed
target_machines -->|get certs| acme
```
### Service deployment check from the FediPanel
This is a full deployment check running the [FediPanel](../panel) on the deployer machine, deploying some services through it and checking that they are indeed on the target machines, then cleans them up and checks whether that works, too.
It builds upon the basic and CLI deployment checks, the only difference being that `deployer` runs NixOps4 only indirectly via the panel, and the `client` node is the one that triggers the deployment via a browser, the way a human would.

View file

@ -1,9 +0,0 @@
# Basic deployment test
Basic deployment test with one deployer machine, one target machine, and a
simple target application, namely cowsay. The goal is to check that basic
functionalities are here.
It is heavily inspired by a similar test in nixops4-nixos:
https://github.com/nixops4/nixops4-nixos/blob/main/test/default/nixosTest.nix

View file

@ -0,0 +1,8 @@
{
targetMachines = [
"hello"
"cowsay"
];
pathToRoot = ../../..;
pathFromRoot = ./.;
}

View file

@ -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;
}

View file

@ -1 +0,0 @@
## This file is just a placeholder. It is overwritten by the test.

View file

@ -1,32 +1,36 @@
{ {
inputs, inputs,
sources,
lib, lib,
providers, providers,
... ...
}: }:
let
inherit (import ./constants.nix) targetMachines pathToRoot pathFromRoot;
in
{ {
providers.local = inputs.nixops4.modules.nixops4Provider.local; providers = {
inherit (inputs.nixops4.modules.nixops4Provider) local;
};
resources.target = { resources = lib.genAttrs targetMachines (nodeName: {
type = providers.local.exec; type = providers.local.exec;
imports = [ inputs.nixops4-nixos.modules.nixops4Resource.nixos ];
ssh = { imports = [
host = "target"; inputs.nixops4-nixos.modules.nixops4Resource.nixos
hostPublicKey = builtins.readFile ./target_host_key.pub; ../common/targetResource.nix
}; ];
_module.args = { inherit inputs sources; };
inherit nodeName pathToRoot pathFromRoot;
nixpkgs = inputs.nixpkgs;
nixos.module = nixos.module =
{ pkgs, ... }: { pkgs, ... }:
{ {
imports = [ environment.systemPackages = [ pkgs.${nodeName} ];
./minimalTarget.nix
(lib.modules.importJSON ./target-network.json)
];
nixpkgs.hostPlatform = "x86_64-linux";
environment.systemPackages = [ pkgs.cowsay ];
}; };
}; });
} }

View file

@ -1,21 +0,0 @@
{ inputs, ... }:
{
nixops4Deployments.check-deployment-basic =
{ ... }:
{
imports = [
./deployment.nix
];
_module.args.inputs = inputs;
};
perSystem =
{ inputs', pkgs, ... }:
{
checks.deployment-basic = pkgs.callPackage ./nixosTest.nix {
nixops4-flake-in-a-bottle = inputs'.nixops4.packages.flake-in-a-bottle;
inherit inputs;
};
};
}

View file

@ -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; };
};
}
);
}

View file

@ -1,35 +0,0 @@
{
lib,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/profiles/qemu-guest.nix")
(modulesPath + "/../lib/testing/nixos-test-base.nix")
];
## Test framework disables switching by default. That might be OK by itself,
## but we also use this config for getting the dependencies in
## `deployer.system.extraDependencies`.
system.switch.enable = true;
nix = {
## Not used; save a large copy operation
channel.enable = false;
registry = lib.mkForce { };
};
services.openssh = {
enable = true;
settings.PermitRootLogin = "yes";
};
networking.firewall.allowedTCPPorts = [ 22 ];
users.users.root.openssh.authorizedKeys.keyFiles = [ ./deployer.pub ];
## Test VMs don't have a bootloader by default.
boot.loader.grub.enable = false;
}

View file

@ -1,57 +1,27 @@
{ {
testers, lib,
inputs, sources ? import ../npins,
runCommandNoCC,
nixops4-flake-in-a-bottle,
... ...
}: }:
{
_class = "nixosTest";
testers.runNixOSTest ( name = "deployment-basic";
{
lib,
config,
hostPkgs,
...
}:
let
vmSystem = config.node.pkgs.hostPlatform.system;
pathToRoot = ../../..; sourceFileset = lib.fileset.unions [
pathFromRoot = "deployment/check/basic"; ./constants.nix
deploymentName = "check-deployment-basic"; ./deployment.nix
];
## TODO: sanity check the existence of (pathToRoot + "/flake.nix") nodes.deployer =
## TODO: sanity check that (pathToRoot + "/${pathFromRoot}" == ./.) { nodes, ... }:
{
## The whole repository, with the flake at its root. system.extraDependenciesFromModule =
src = lib.fileset.toSource { { pkgs, ... }:
fileset = pathToRoot;
root = pathToRoot;
};
## 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
'';
targetNetworkJSON = hostPkgs.writeText "target-network.json" (
builtins.toJSON config.nodes.target.system.build.networkConfig
);
in
{
name = "deployment-basic";
imports = [
inputs.nixops4-nixos.modules.nixosTest.static
];
nodes = {
deployer =
{ pkgs, nodes, ... }:
{ {
environment.systemPackages = [ environment.systemPackages = with pkgs; [
inputs.nixops4.packages.${vmSystem}.default hello
cowsay
]; ];
virtualisation = { virtualisation = {
@ -69,12 +39,11 @@ testers.runNixOSTest (
}; };
system.extraDependencies = system.extraDependencies =
with pkgs;
[ [
"${inputs.flake-parts}" jq
"${inputs.flake-parts.inputs.nixpkgs-lib}" jq.inputDerivation
"${inputs.nixops4}" sources.nixpkgs
"${inputs.nixops4-nixos}"
"${inputs.nixpkgs}"
pkgs.stdenv pkgs.stdenv
pkgs.stdenvNoCC pkgs.stdenvNoCC
@ -98,64 +67,16 @@ testers.runNixOSTest (
) nodes.target.environment.etc ) nodes.target.environment.etc
); );
}; };
target.imports = [ ./minimalTarget.nix ];
}; };
testScript = '' extraTestScript = ''
start_all() with subtest("Check the status before deployment"):
target.fail("cowsay hi 1>&2")
target.wait_for_unit("multi-user.target") with subtest("Run the deployment"):
deployer.wait_for_unit("multi-user.target") deployer.succeed("cd work && tofu apply --show-trace --no-interactive")
with subtest("Unpacking"): with subtest("Check the deployment"):
deployer.succeed("cp -r --no-preserve=mode ${src} work") target.succeed("cowsay hi 1>&2")
'';
with subtest("Configure the network"): }
deployer.copy_from_host("${targetNetworkJSON}", "/root/target-network.json")
deployer.succeed("mv /root/target-network.json work/${pathFromRoot}/target-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()
deployer.succeed(f"echo '{deployer_key}' > work/${pathFromRoot}/deployer.pub")
target.succeed(f"mkdir -p /root/.ssh && echo '{deployer_key}' >> /root/.ssh/authorized_keys")
with subtest("Configure the target host key"):
target_host_key = target.succeed("ssh-keyscan target | grep -v '^#' | cut -f 2- -d ' ' | head -n 1")
deployer.succeed(f"echo '{target_host_key}' > work/${pathFromRoot}/target_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.
with subtest("Override the lock"):
deployer.succeed("""
cd work
nix flake lock --extra-experimental-features 'flakes nix-command' \
--offline -v \
--override-input flake-parts ${inputs.flake-parts} \
--override-input nixops4 ${nixops4-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 ${nixops4-flake-in-a-bottle} \
--override-input nixops4-nixos/git-hooks-nix ${emptyFlake} \
\
--override-input nixpkgs ${inputs.nixpkgs} \
--override-input git-hooks ${inputs.git-hooks} \
;
""")
with subtest("Check the status before deployment"):
target.fail("cowsay hi 1>&2")
with subtest("Run the deployment"):
deployer.succeed("cd work && nixops4 apply ${deploymentName} --show-trace --no-interactive")
with subtest("Check the deployment"):
target.succeed("cowsay hi 1>&2")
'';
}
)

View file

@ -1 +0,0 @@
{"comment": "This file is just a placeholder. It is overwritten by the test."}

View file

@ -1 +0,0 @@
## This file is just a placeholder. It is overwritten by the test.

View file

@ -0,0 +1,11 @@
{
targetMachines = [
"garage"
"mastodon"
"peertube"
"pixelfed"
];
pathToRoot = ../../..;
pathFromRoot = ./.;
enableAcme = true;
}

View file

@ -0,0 +1,19 @@
{
runNixOSTest,
inputs,
sources,
}:
runNixOSTest {
imports = [
../common/nixosTest.nix
./nixosTest.nix
];
_module.args = { inherit inputs sources; };
inherit (import ./constants.nix)
targetMachines
pathToRoot
pathFromRoot
enableAcme
;
}

View file

@ -0,0 +1,59 @@
{
inputs,
sources,
lib,
}:
let
inherit (builtins) fromJSON readFile listToAttrs;
inherit (import ./constants.nix)
targetMachines
pathToRoot
pathFromRoot
enableAcme
;
makeTargetResource = nodeName: {
imports = [ ../common/targetResource.nix ];
_module.args = { inherit inputs sources; };
inherit
nodeName
pathToRoot
pathFromRoot
enableAcme
;
};
## The deployment function - what we are here to test!
##
## TODO: Modularise `deployment/default.nix` to get rid of the nested
## function calls.
makeTestDeployment =
args:
(import ../..)
{
inherit lib;
inherit (inputs) nixops4 nixops4-nixos;
fediversity = import ../../../services/fediversity;
}
(listToAttrs (
map (nodeName: {
name = "${nodeName}ConfigurationResource";
value = makeTargetResource nodeName;
}) targetMachines
))
(fromJSON (readFile ../../configuration.sample.json) // args);
in
{
check-deployment-cli-nothing = makeTestDeployment { };
check-deployment-cli-mastodon-pixelfed = makeTestDeployment {
mastodon.enable = true;
pixelfed.enable = true;
};
check-deployment-cli-peertube = makeTestDeployment {
peertube.enable = true;
};
}

View file

@ -0,0 +1,26 @@
{
inputs = {
nixops4.follows = "nixops4-nixos/nixops4";
nixops4-nixos.url = "github:nixops4/nixops4-nixos";
};
outputs =
inputs:
import ./mkFlake.nix inputs (
{
inputs,
sources,
lib,
...
}:
{
imports = [
inputs.nixops4.modules.flake.default
];
nixops4Deployments = import ./deployment/check/cli/deployments.nix {
inherit inputs sources lib;
};
}
);
}

View file

@ -0,0 +1,137 @@
{
inputs,
hostPkgs,
lib,
...
}:
let
## Some places need a dummy file that will in fact never be used. We create
## it here.
dummyFile = hostPkgs.writeText "dummy" "";
in
{
_class = "nixosTest";
name = "deployment-cli";
sourceFileset = lib.fileset.unions [
./constants.nix
./deployments.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
# in `/deployment`. Maybe we can think of a reorg making things more robust
# here? (comment also in panel test)
../../default.nix
../../options.nix
../../configuration.sample.json
../../../services/fediversity
];
nodes.deployer =
{ pkgs, ... }:
{
environment.systemPackages = [
inputs.nixops4.packages.${pkgs.system}.default
];
## FIXME: The following dependencies are necessary but I do not
## understand why they are not covered by the fake node.
system.extraDependencies = with pkgs; [
peertube
peertube.inputDerivation
gixy
gixy.inputDerivation
];
system.extraDependenciesFromModule = {
imports = [ ../../../services/fediversity ];
fediversity = {
domain = "fediversity.net"; # would write `dummy` but that would not type
garage.enable = true;
mastodon = {
enable = true;
s3AccessKeyFile = dummyFile;
s3SecretKeyFile = dummyFile;
};
peertube = {
enable = true;
secretsFile = dummyFile;
s3AccessKeyFile = dummyFile;
s3SecretKeyFile = dummyFile;
};
pixelfed = {
enable = true;
s3AccessKeyFile = dummyFile;
s3SecretKeyFile = dummyFile;
};
temp.cores = 1;
temp.initialUser = {
username = "dummy";
displayName = "dummy";
email = "dummy";
passwordFile = dummyFile;
};
};
};
};
## NOTE: The target machines may need more RAM than the default to handle
## being deployed to, otherwise we get something like:
##
## pixelfed # [ 616.785499 ] sshd-session[1167]: Conection closed by 2001:db8:1::2 port 45004
## deployer # error: writing to file: No space left on device
## pixelfed # [ 616.788538 ] sshd-session[1151]: pam_unix(sshd:session): session closed for user port
## pixelfed # [ 616.793929 ] systemd-logind[719]: Session 4 logged out. Waiting for processes to exit.
## deployer # Error: Could not create resource
##
## These values have been trimmed down to the gigabyte.
nodes.mastodon.virtualisation.memorySize = 4 * 1024;
nodes.pixelfed.virtualisation.memorySize = 4 * 1024;
nodes.peertube.virtualisation.memorySize = 5 * 1024;
## FIXME: The test of presence of the services are very simple: we only
## check that there is a systemd service of the expected name on the
## machine. This proves at least that NixOps4 did something, and we cannot
## really do more for now because the services aren't actually working
## properly, in particular because of DNS issues. We should fix the services
## and check that they are working properly.
extraTestScript = ''
with subtest("Check the status of the services - there should be none"):
garage.fail("systemctl status garage.service")
mastodon.fail("systemctl status mastodon-web.service")
peertube.fail("systemctl status peertube.service")
pixelfed.fail("systemctl status phpfpm-pixelfed.service")
with subtest("Run deployment with no services enabled"):
deployer.succeed("nixops4 apply check-deployment-cli-nothing --show-trace --no-interactive 1>&2")
with subtest("Check the status of the services - there should still be none"):
garage.fail("systemctl status garage.service")
mastodon.fail("systemctl status mastodon-web.service")
peertube.fail("systemctl status peertube.service")
pixelfed.fail("systemctl status phpfpm-pixelfed.service")
with subtest("Run deployment with Mastodon and Pixelfed enabled"):
deployer.succeed("nixops4 apply check-deployment-cli-mastodon-pixelfed --show-trace --no-interactive 1>&2")
with subtest("Check the status of the services - expecting Garage, Mastodon and Pixelfed"):
garage.succeed("systemctl status garage.service")
mastodon.succeed("systemctl status mastodon-web.service")
peertube.fail("systemctl status peertube.service")
pixelfed.succeed("systemctl status phpfpm-pixelfed.service")
with subtest("Run deployment with only Peertube enabled"):
deployer.succeed("nixops4 apply check-deployment-cli-peertube --show-trace --no-interactive 1>&2")
with subtest("Check the status of the services - expecting Garage and Peertube"):
garage.succeed("systemctl status garage.service")
mastodon.fail("systemctl status mastodon-web.service")
peertube.succeed("systemctl status peertube.service")
pixelfed.fail("systemctl status phpfpm-pixelfed.service")
'';
}

View file

@ -0,0 +1,106 @@
{
inputs,
lib,
pkgs,
config,
sources,
...
}:
let
inherit (lib)
mkOption
mkForce
concatLists
types
;
in
{
_class = "nixos";
imports = [ ./sharedOptions.nix ];
options.system.extraDependenciesFromModule = mkOption {
type = types.deferredModule;
description = ''
Grab the derivations needed to build the given module and dump them in
system.extraDependencies. You want to put in this module a superset of
all the things that you will need on your target machines.
NOTE: This will work as long as the union of all these configurations do
not have conflicts that would prevent evaluation.
'';
default = { };
};
config = {
virtualisation = {
## NOTE: The deployer machines needs more RAM and default than the
## default. These values have been trimmed down to the gigabyte.
## Memory use is expected to be dominated by the NixOS evaluation,
## which happens on the deployer.
memorySize = 4 * 1024;
diskSize = 4 * 1024;
cores = 2;
};
nix.settings = {
substituters = mkForce [ ];
hashed-mirrors = null;
connect-timeout = 1;
extra-experimental-features = "flakes";
};
system.extraDependencies =
[
inputs.nixops4
inputs.nixops4-nixos
inputs.nixpkgs
sources.flake-parts
sources.flake-inputs
sources.git-hooks
pkgs.stdenv
pkgs.stdenvNoCC
]
++ (
let
## We build a whole NixOS system that contains the module
## `system.extraDependenciesFromModule`, only to grab its
## configuration and the store paths needed to build it and
## dump them in `system.extraDependencies`.
machine =
(pkgs.nixos [
./targetNode.nix
config.system.extraDependenciesFromModule
{
nixpkgs.hostPlatform = "x86_64-linux";
_module.args = { inherit inputs sources; };
enableAcme = config.enableAcme;
acmeNodeIP = config.acmeNodeIP;
}
]).config;
in
[
machine.system.build.toplevel.inputDerivation
machine.system.build.etc.inputDerivation
machine.system.build.etcBasedir.inputDerivation
machine.system.build.etcMetadataImage.inputDerivation
machine.system.build.extraUtils.inputDerivation
machine.system.path.inputDerivation
machine.system.build.setEnvironment.inputDerivation
machine.system.build.vm.inputDerivation
machine.system.build.bootStage1.inputDerivation
machine.system.build.bootStage2.inputDerivation
]
++ concatLists (
lib.mapAttrsToList (
_k: v: if v ? source.inputDerivation then [ v.source.inputDerivation ] else [ ]
) machine.environment.etc
)
);
};
}

View file

@ -0,0 +1,201 @@
{
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 {
## 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";
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}
'';
};
}

View file

@ -0,0 +1,68 @@
/**
This file contains options shared by various components of the integration test, i.e. deployment resources, test nodes, target configurations, etc.
All these components are declared as modules, but are part of different evaluations, which is the options in this file can't be shared "directly".
Instead, each component imports this module and the same values are set for each of them from a common call site.
Not all components will use all the options, which allows not setting all the values.
*/
{ config, lib, ... }:
let
inherit (lib) mkOption types;
in
# `config` not set and imported from multiple places: no fixed module class
{
options = {
targetMachines = mkOption {
type = with types; listOf str;
description = ''
Names of the nodes in the NixOS test that are target machines. This is
used by the infrastructure to extract their network configuration, among
other things, and re-import it in the deployment.
'';
};
pathToRoot = mkOption {
type = types.path;
description = ''
Path from the location of the working directory to the root of the
repository.
'';
};
pathFromRoot = mkOption {
type = types.path;
description = ''
Path from the root of the repository to the working directory.
'';
apply = x: lib.path.removePrefix config.pathToRoot x;
};
pathToCwd = mkOption {
type = types.path;
description = ''
Path to the current working directory. This is a shortcut for
pathToRoot/pathFromRoot.
'';
default = config.pathToRoot + "/${config.pathFromRoot}";
};
enableAcme = mkOption {
type = types.bool;
description = ''
Whether to enable ACME in the NixOS test. This will add an ACME server
to the node and connect all the target machines to it.
'';
default = false;
};
acmeNodeIP = mkOption {
type = types.str;
description = ''
The IP of the ACME node in the NixOS test. This option will be set
during the test to the correct value.
'';
};
};
}

View file

@ -0,0 +1,67 @@
{
inputs,
config,
lib,
modulesPath,
...
}:
let
testCerts = import "${inputs.nixpkgs}/nixos/tests/common/acme/server/snakeoil-certs.nix";
inherit (lib) mkIf mkMerge;
in
{
_class = "nixos";
imports = [
(modulesPath + "/profiles/qemu-guest.nix")
(modulesPath + "/../lib/testing/nixos-test-base.nix")
./sharedOptions.nix
];
config = mkMerge [
{
## Test framework disables switching by default. That might be OK by itself,
## but we also use this config for getting the dependencies in
## `deployer.system.extraDependencies`.
system.switch.enable = true;
nix = {
## Not used; save a large copy operation
channel.enable = false;
registry = lib.mkForce { };
};
services.openssh = {
enable = true;
settings.PermitRootLogin = "yes";
};
networking.firewall.allowedTCPPorts = [ 22 ];
## Test VMs don't have a bootloader by default.
boot.loader.grub.enable = false;
}
(mkIf config.enableAcme {
security.acme = {
acceptTerms = true;
defaults.email = "test@test.com";
defaults.server = "https://acme.test/dir";
};
security.pki.certificateFiles = [
## NOTE: This certificate is the one used by the Pebble HTTPS server.
## This is NOT the root CA of the Pebble server. We do add it here so
## that Pebble clients can talk to its API, but this will not allow
## those machines to verify generated certificates.
testCerts.ca.cert
];
## FIXME: it is a bit sad that all this logistics is necessary. look into
## better DNS stuff
networking.extraHosts = "${config.acmeNodeIP} acme.test";
})
];
}

View file

@ -0,0 +1,51 @@
{
inputs,
lib,
config,
sources,
...
}:
let
inherit (builtins) readFile;
inherit (lib) trim mkOption types;
in
{
_class = "nixops4Resource";
imports = [ ./sharedOptions.nix ];
options = {
nodeName = mkOption {
type = types.str;
description = ''
The name of the node in the NixOS test;
needed for recovering the node configuration to prepare its deployment.
'';
};
};
config = {
ssh = {
host = config.nodeName;
hostPublicKey = readFile (config.pathToCwd + "/${config.nodeName}_host_key.pub");
};
nixpkgs = inputs.nixpkgs;
nixos.module = {
imports = [
./targetNode.nix
(lib.modules.importJSON (config.pathToCwd + "/${config.nodeName}-network.json"))
];
_module.args = { inherit inputs sources; };
enableAcme = config.enableAcme;
acmeNodeIP = trim (readFile (config.pathToCwd + "/acme_server_ip"));
nixpkgs.hostPlatform = "x86_64-linux";
};
};
}

View file

@ -0,0 +1,11 @@
{
targetMachines = [
"garage"
"mastodon"
"peertube"
"pixelfed"
];
pathToRoot = ../../..;
pathFromRoot = ./.;
enableAcme = true;
}

View file

@ -0,0 +1,19 @@
{
runNixOSTest,
inputs,
sources,
}:
runNixOSTest {
imports = [
../common/nixosTest.nix
./nixosTest.nix
];
_module.args = { inherit inputs sources; };
inherit (import ./constants.nix)
targetMachines
pathToRoot
pathFromRoot
enableAcme
;
}

View file

@ -0,0 +1,58 @@
{
inputs,
sources,
lib,
}:
let
inherit (builtins) fromJSON listToAttrs;
inherit (import ./constants.nix)
targetMachines
pathToRoot
pathFromRoot
enableAcme
;
makeTargetResource = nodeName: {
imports = [ ../common/targetResource.nix ];
_module.args = { inherit inputs sources; };
inherit
nodeName
pathToRoot
pathFromRoot
enableAcme
;
};
## The deployment function - what we are here to test!
##
## TODO: Modularise `deployment/default.nix` to get rid of the nested
## function calls.
makeTestDeployment =
args:
(import ../..)
{
inherit lib;
inherit (inputs) nixops4 nixops4-nixos;
fediversity = import ../../../services/fediversity;
}
(listToAttrs (
map (nodeName: {
name = "${nodeName}ConfigurationResource";
value = makeTargetResource nodeName;
}) targetMachines
))
args;
in
makeTestDeployment (
fromJSON (
let
env = builtins.getEnv "DEPLOYMENT";
in
if env == "" then
throw "The DEPLOYMENT environment needs to be set. You do not want to use this deployment unless in the `deployment-panel` NixOS test."
else
env
)
)

View file

@ -0,0 +1,26 @@
{
inputs = {
nixops4.follows = "nixops4-nixos/nixops4";
nixops4-nixos.url = "github:nixops4/nixops4-nixos";
};
outputs =
inputs:
import ./mkFlake.nix inputs (
{
inputs,
sources,
lib,
...
}:
{
imports = [
inputs.nixops4.modules.flake.default
];
nixops4Deployments.check-deployment-panel = import ./deployment/check/panel/deployment.nix {
inherit inputs sources lib;
};
}
);
}

View file

@ -0,0 +1,377 @@
{
inputs,
lib,
hostPkgs,
config,
...
}:
let
inherit (lib)
getExe
;
## Some places need a dummy file that will in fact never be used. We create
## it here.
dummyFile = hostPkgs.writeText "dummy" "dummy";
panelPort = 8000;
panelUser = "test";
panelEmail = "test@test.com";
panelPassword = "ouiprdaaa43"; # panel's manager complains if too close to username or email
fediUser = "test";
fediEmail = "test@test.com";
fediPassword = "testtest";
fediName = "Testy McTestface";
toPythonBool = b: if b then "True" else "False";
interactWithPanel =
{
baseUri,
enableMastodon,
enablePeertube,
enablePixelfed,
}:
hostPkgs.writers.writePython3Bin "interact-with-panel"
{
libraries = with hostPkgs.python3Packages; [ selenium ];
flakeIgnore = [
"E302" # expected 2 blank lines, found 0
"E303" # too many blank lines
"E305" # expected 2 blank lines after end of function or class
"E501" # line too long (> 79 characters)
"E731" # do not assign lambda expression, use a def
];
}
''
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.support.ui import WebDriverWait
print("Create and configure driver...")
options = Options()
options.add_argument("--headless")
options.binary_location = "${getExe hostPkgs.firefox-unwrapped}"
service = webdriver.FirefoxService(executable_path="${getExe hostPkgs.geckodriver}")
driver = webdriver.Firefox(options=options, service=service)
driver.set_window_size(1280, 960)
driver.implicitly_wait(360)
driver.command_executor.set_timeout(3600)
print("Open login page...")
driver.get("${baseUri}/login/")
print("Enter username...")
driver.find_element(By.XPATH, "//input[@name = 'username']").send_keys("${panelUser}")
print("Enter password...")
driver.find_element(By.XPATH, "//input[@name = 'password']").send_keys("${panelPassword}")
print("Click Login button...")
driver.find_element(By.XPATH, "//button[normalize-space() = 'Login']").click()
print("Open configuration page...")
driver.get("${baseUri}/configuration/")
# Helpers to actually set and not add or switch input values.
def input_set(elt, keys):
elt.clear()
elt.send_keys(keys)
def checkbox_set(elt, new_value):
if new_value != elt.is_selected():
elt.click()
print("Enable Fediversity...")
checkbox_set(driver.find_element(By.XPATH, "//input[@name = 'enable']"), True)
print("Fill in initialUser info...")
input_set(driver.find_element(By.XPATH, "//input[@name = 'initialUser.username']"), "${fediUser}")
input_set(driver.find_element(By.XPATH, "//input[@name = 'initialUser.password']"), "${fediPassword}")
input_set(driver.find_element(By.XPATH, "//input[@name = 'initialUser.email']"), "${fediEmail}")
input_set(driver.find_element(By.XPATH, "//input[@name = 'initialUser.displayName']"), "${fediName}")
print("Enable services...")
checkbox_set(driver.find_element(By.XPATH, "//input[@name = 'mastodon.enable']"), ${toPythonBool enableMastodon})
checkbox_set(driver.find_element(By.XPATH, "//input[@name = 'peertube.enable']"), ${toPythonBool enablePeertube})
checkbox_set(driver.find_element(By.XPATH, "//input[@name = 'pixelfed.enable']"), ${toPythonBool enablePixelfed})
print("Start deployment...")
driver.find_element(By.XPATH, "//button[@id = 'deploy-button']").click()
print("Wait for deployment status to show up...")
get_deployment_result = lambda d: d.find_element(By.XPATH, "//div[@id = 'deployment-result']//p")
WebDriverWait(driver, timeout=3660, poll_frequency=10).until(get_deployment_result)
deployment_result = get_deployment_result(driver).get_attribute('innerHTML')
print("Quit...")
driver.quit()
match deployment_result:
case 'Deployment Succeeded':
print("Deployment has succeeded; exiting normally")
exit(0)
case 'Deployment Failed':
print("Deployment has failed; exiting with return code `1`")
exit(1)
case _:
print(f"Unexpected deployment result: {deployment_result}; exiting with return code `2`")
exit(2)
'';
in
{
_class = "nixosTest";
name = "deployment-panel";
sourceFileset = lib.fileset.unions [
./constants.nix
./deployment.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
# in `/deployment`. Maybe we can think of a reorg making things more robust
# here? (comment also in CLI test)
../../default.nix
../../options.nix
../../../services/fediversity
];
## The panel's module sets `nixpkgs.overlays` which clashes with
## `pkgsReadOnly`. We disable it here.
node.pkgsReadOnly = false;
nodes.deployer =
{ pkgs, ... }:
{
imports = [
(import ../../../panel { }).module
];
## FIXME: This should be in the common stuff.
security.acme = {
acceptTerms = true;
defaults.email = "test@test.com";
defaults.server = "https://acme.test/dir";
};
security.pki.certificateFiles = [
(import "${inputs.nixpkgs}/nixos/tests/common/acme/server/snakeoil-certs.nix").ca.cert
];
networking.extraHosts = "${config.acmeNodeIP} acme.test";
services.panel = {
enable = true;
production = true;
domain = "deployer";
secrets = {
SECRET_KEY = dummyFile;
};
port = panelPort;
deployment = {
flake = "/run/fedipanel/flake";
name = "check-deployment-panel";
};
};
environment.systemPackages = [ pkgs.expect ];
## FIXME: The following dependencies are necessary but I do not
## understand why they are not covered by the fake node.
system.extraDependencies = with pkgs; [
peertube
peertube.inputDerivation
gixy # a configuration checker for nginx
gixy.inputDerivation
];
system.extraDependenciesFromModule = {
imports = [ ../../../services/fediversity ];
fediversity = {
domain = "fediversity.net"; # would write `dummy` but that would not type
garage.enable = true;
mastodon = {
enable = true;
s3AccessKeyFile = dummyFile;
s3SecretKeyFile = dummyFile;
};
peertube = {
enable = true;
secretsFile = dummyFile;
s3AccessKeyFile = dummyFile;
s3SecretKeyFile = dummyFile;
};
pixelfed = {
enable = true;
s3AccessKeyFile = dummyFile;
s3SecretKeyFile = dummyFile;
};
temp.cores = 1;
temp.initialUser = {
username = "dummy";
displayName = "dummy";
email = "dummy";
passwordFile = dummyFile;
};
};
};
};
nodes.client =
{ pkgs, ... }:
{
environment.systemPackages = with pkgs; [
httpie
dnsutils # for `dig`
openssl
cacert
wget
python3
python3Packages.selenium
firefox-unwrapped
geckodriver
];
security.pki.certificateFiles = [
config.nodes.acme.test-support.acme.caCert
];
networking.extraHosts = "${config.acmeNodeIP} acme.test";
};
## NOTE: The target machines may need more RAM than the default to handle
## being deployed to, otherwise we get something like:
##
## pixelfed # [ 616.785499 ] sshd-session[1167]: Conection closed by 2001:db8:1::2 port 45004
## deployer # error: writing to file: No space left on device
## pixelfed # [ 616.788538 ] sshd-session[1151]: pam_unix(sshd:session): session closed for user port
## pixelfed # [ 616.793929 ] systemd-logind[719]: Session 4 logged out. Waiting for processes to exit.
## deployer # Error: Could not create resource
##
## These values have been trimmed down to the gigabyte.
nodes.mastodon.virtualisation.memorySize = 4 * 1024;
nodes.pixelfed.virtualisation.memorySize = 4 * 1024;
nodes.peertube.virtualisation.memorySize = 5 * 1024;
## FIXME: The test of presence of the services are very simple: we only
## check that there is a systemd service of the expected name on the
## machine. This proves at least that NixOps4 did something, and we cannot
## really do more for now because the services aren't actually working
## properly, in particular because of DNS issues. We should fix the services
## and check that they are working properly.
extraTestScript = ''
## TODO: We want a nicer way to control where the FediPanel consumes its
## flake, which can default to the store but could also be somewhere else if
## someone wanted to change the code of the flake.
##
with subtest("Give the panel access to the flake"):
deployer.succeed("mkdir /run/fedipanel /run/fedipanel/flake >&2")
deployer.succeed("cp -R . /run/fedipanel/flake >&2")
deployer.succeed("chown -R panel:panel /run/fedipanel >&2")
## TODO: I want a programmatic way to provide an SSH key to the panel (and
## therefore NixOps4). This should happen either in the Python code, but
## maybe it is fair that that one picks up on the user's key? It could
## also be in the Nix packaging.
##
with subtest("Set up the panel's SSH keys"):
deployer.succeed("mkdir /home/panel/.ssh >&2")
deployer.succeed("cp -R /root/.ssh/* /home/panel/.ssh >&2")
deployer.succeed("chown -R panel:panel /home/panel/.ssh >&2")
deployer.succeed("chmod 600 /home/panel/.ssh/* >&2")
## TODO: This is a hack to accept the root CA used by Pebble on the client
## machine. Pebble randomizes everything, so the only way to get it is to
## call the /roots/0 endpoint at runtime, leaving not much margin for a nice
## Nixy way of adding the certificate. There is no way around it as this is
## by design in Pebble, showing in fact that Pebble was not the appropriate
## tool for our use and that nixpkgs does not in fact provide an easy way to
## generate _usable_ certificates in NixOS tests. I suggest we merge this,
## and track the task to set it up in a cleaner way. I would tackle this in
## a subsequent PR, and hopefully even contribute this BetterWay(tm) to
## nixpkgs. — Niols
##
with subtest("Set up ACME root CA on client"):
client.succeed("""
cd /etc/ssl/certs
curl -o pebble-root-ca.pem https://acme.test:15000/roots/0
curl -o pebble-intermediate-ca.pem https://acme.test:15000/intermediates/0
{ cat ca-bundle.crt
cat pebble-root-ca.pem
cat pebble-intermediate-ca.pem
} > new-ca-bundle.crt
rm ca-bundle.crt ca-certificates.crt
mv new-ca-bundle.crt ca-bundle.crt
ln -s ca-bundle.crt ca-certificates.crt
""")
## TODO: I would hope for a more declarative way to add users. This should
## be handled by the Nix packaging of the FediPanel. — Niols
##
with subtest("Create panel user"):
deployer.succeed("""
expect -c '
spawn manage createsuperuser --username ${panelUser} --email ${panelEmail}
expect "Password: "; send "${panelPassword}\\n";
expect "Password (again): "; send "${panelPassword}\\n"
interact
' >&2
""")
with subtest("Check the status of the services - there should be none"):
garage.fail("systemctl status garage.service")
mastodon.fail("systemctl status mastodon-web.service")
peertube.fail("systemctl status peertube.service")
pixelfed.fail("systemctl status phpfpm-pixelfed.service")
with subtest("Run deployment with no services enabled"):
client.succeed("${
interactWithPanel {
baseUri = "https://deployer";
enableMastodon = false;
enablePeertube = false;
enablePixelfed = false;
}
}/bin/interact-with-panel >&2")
with subtest("Check the status of the services - there should still be none"):
garage.fail("systemctl status garage.service")
mastodon.fail("systemctl status mastodon-web.service")
peertube.fail("systemctl status peertube.service")
pixelfed.fail("systemctl status phpfpm-pixelfed.service")
with subtest("Run deployment with Mastodon and Pixelfed enabled"):
client.succeed("${
interactWithPanel {
baseUri = "https://deployer";
enableMastodon = true;
enablePeertube = false;
enablePixelfed = true;
}
}/bin/interact-with-panel >&2")
with subtest("Check the status of the services - expecting Garage, Mastodon and Pixelfed"):
garage.succeed("systemctl status garage.service")
mastodon.succeed("systemctl status mastodon-web.service")
peertube.fail("systemctl status peertube.service")
pixelfed.succeed("systemctl status phpfpm-pixelfed.service")
with subtest("Run deployment with only Peertube enabled"):
client.succeed("${
interactWithPanel {
baseUri = "https://deployer";
enableMastodon = false;
enablePeertube = true;
enablePixelfed = false;
}
}/bin/interact-with-panel >&2")
with subtest("Check the status of the services - expecting Garage and Peertube"):
garage.succeed("systemctl status garage.service")
mastodon.fail("systemctl status mastodon-web.service")
peertube.succeed("systemctl status peertube.service")
pixelfed.fail("systemctl status phpfpm-pixelfed.service")
'';
}

View file

@ -0,0 +1,12 @@
{
"domain": "fediversity.net",
"mastodon": { "enable": false },
"peertube": { "enable": false },
"pixelfed": { "enable": false },
"initialUser": {
"displayName": "Testy McTestface",
"username": "test",
"password": "testtest",
"email": "test@test.com"
}
}

View file

@ -0,0 +1,70 @@
let
inherit (import ../default.nix { }) pkgs inputs;
inherit (pkgs) lib;
inherit (lib) mkOption;
eval =
module:
(lib.evalModules {
specialArgs = {
inherit inputs;
};
modules = [
module
./data-model.nix
];
}).config;
in
{
_class = "nix-unit";
test-eval = {
expr =
let
fediversity = eval (
{ config, ... }:
{
config = {
applications.hello =
{ ... }:
{
description = ''Command-line tool that will print "Hello, world!" on the terminal'';
module =
{ ... }:
{
options = {
enable = lib.mkEnableOption "Hello in the shell";
};
};
implementation =
cfg:
lib.optionalAttrs cfg.enable {
dummy.login-shell.packages.hello = pkgs.hello;
};
};
};
options = {
example-configuration = mkOption {
type = config.configuration;
readOnly = true;
default = {
enable = true;
applications.hello.enable = true;
};
};
};
}
);
in
{
inherit (fediversity)
example-configuration
;
};
expected = {
example-configuration = {
enable = true;
applications.hello.enable = true;
};
};
};
}

89
deployment/data-model.nix Normal file
View file

@ -0,0 +1,89 @@
{
lib,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (lib.types)
attrsOf
attrTag
deferredModuleWith
submodule
optionType
functionTo
;
functionType = import ./function.nix;
application-resources = {
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)
);
};
};
in
{
_class = "nixops4Deployment";
options = {
applications = mkOption {
description = "Collection of Fediversity applications";
type = attrsOf (
submodule (application: {
_class = "fediversity-application";
options = {
description = mkOption {
description = "Description to be shown in the application overview";
type = types.str;
};
module = mkOption {
description = "Operator-facing configuration options for the application";
type = deferredModuleWith { staticModules = [ { _class = "fediversity-application-config"; } ]; };
};
implementation = mkOption {
description = "Mapping of application configuration to deployment resources, a description of what an application needs to run";
type = application.config.config-mapping.function-type;
};
resources = mkOption {
description = "Compute resources required by an application";
type = functionTo application.config.config-mapping.output-type;
readOnly = true;
default = input: (application.config.implementation input).output;
};
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;
output-type = application-resources;
};
};
};
})
);
};
configuration = mkOption {
description = "Configuration type declaring options to be set by operators";
type = optionType;
readOnly = true;
default = submodule {
options = {
enable = lib.mkEnableOption {
description = "your Fediversity configuration";
};
applications = lib.mapAttrs (
_name: application:
mkOption {
description = application.description;
type = submodule application.module;
default = { };
}
) config.applications;
};
};
};
};
}

View file

@ -1,3 +1,4 @@
<<<<<<< HEAD
## `makeMakeDeployment` -- Function to help hosting providers make a ## `makeMakeDeployment` -- Function to help hosting providers make a
## `makeDeployment` function. ## `makeDeployment` function.
## ##
@ -33,17 +34,40 @@
## information coming from the FediPanel. ## information coming from the FediPanel.
## ##
## FIXME: lock step the interface with the definitions in the FediPanel ## FIXME: lock step the interface with the definitions in the FediPanel
panelConfig: panelConfigNullable:
let let
inherit (lib) mkIf; inherit (lib) mkIf;
## The convertor from module options to JSON schema does not generate proper
## JSON schema types, forcing us to use nullable fields for default values.
## However, working with those fields in the deployment code is annoying (and
## unusual for Nix programmers), so we sanitize the input here and add back
## the default value by hand.
nonNull = x: v: if x == null then v else x;
panelConfig = {
domain = nonNull panelConfigNullable.domain "fediversity.net";
initialUser = nonNull panelConfigNullable.initialUser {
displayName = "Testy McTestface";
username = "test";
password = "testtest";
email = "test@test.com";
};
mastodon = nonNull panelConfigNullable.mastodon { enable = false; };
peertube = nonNull panelConfigNullable.peertube { enable = false; };
pixelfed = nonNull panelConfigNullable.pixelfed { enable = false; };
};
in in
## Regular arguments of a NixOps4 deployment module. ## Regular arguments of a NixOps4 deployment module.
{ providers, ... }: { config, providers, ... }:
let
cfg = config.deployment;
in
{ {
_class = "nixops4Deployment";
options = { options = {
deployment = lib.mkOption { deployment = lib.mkOption {
description = '' description = ''
@ -52,6 +76,7 @@ in
# XXX(@fricklerhandwerk): # XXX(@fricklerhandwerk):
# misusing this will produce obscure errors that will be truncated by NixOps4 # misusing this will produce obscure errors that will be truncated by NixOps4
type = lib.types.submodule ./options.nix; type = lib.types.submodule ./options.nix;
default = panelConfig;
}; };
}; };
@ -116,9 +141,9 @@ in
{ {
garage-configuration = makeConfigurationResource garageConfigurationResource ( garage-configuration = makeConfigurationResource garageConfigurationResource (
{ pkgs, ... }: { pkgs, ... }:
mkIf (panelConfig.mastodon.enable || panelConfig.peertube.enable || panelConfig.pixelfed.enable) { mkIf (cfg.mastodon.enable || cfg.peertube.enable || cfg.pixelfed.enable) {
fediversity = { fediversity = {
inherit (panelConfig) domain; inherit (cfg) domain;
garage.enable = true; garage.enable = true;
pixelfed = pixelfedS3KeyConfig { inherit pkgs; }; pixelfed = pixelfedS3KeyConfig { inherit pkgs; };
mastodon = mastodonS3KeyConfig { inherit pkgs; }; mastodon = mastodonS3KeyConfig { inherit pkgs; };
@ -129,14 +154,14 @@ in
mastodon-configuration = makeConfigurationResource mastodonConfigurationResource ( mastodon-configuration = makeConfigurationResource mastodonConfigurationResource (
{ pkgs, ... }: { pkgs, ... }:
mkIf panelConfig.mastodon.enable { mkIf cfg.mastodon.enable {
fediversity = { fediversity = {
inherit (panelConfig) domain; inherit (cfg) domain;
temp.initialUser = { temp.initialUser = {
inherit (panelConfig.initialUser) username email displayName; inherit (cfg.initialUser) username email displayName;
# FIXME: disgusting, but nvm, this is going to be replaced by # FIXME: disgusting, but nvm, this is going to be replaced by
# proper central authentication at some point # proper central authentication at some point
passwordFile = pkgs.writeText "password" panelConfig.initialUser.password; passwordFile = pkgs.writeText "password" cfg.initialUser.password;
}; };
mastodon = mastodonS3KeyConfig { inherit pkgs; } // { mastodon = mastodonS3KeyConfig { inherit pkgs; } // {
@ -150,14 +175,14 @@ in
peertube-configuration = makeConfigurationResource peertubeConfigurationResource ( peertube-configuration = makeConfigurationResource peertubeConfigurationResource (
{ pkgs, ... }: { pkgs, ... }:
mkIf panelConfig.peertube.enable { mkIf cfg.peertube.enable {
fediversity = { fediversity = {
inherit (panelConfig) domain; inherit (cfg) domain;
temp.initialUser = { temp.initialUser = {
inherit (panelConfig.initialUser) username email displayName; inherit (cfg.initialUser) username email displayName;
# FIXME: disgusting, but nvm, this is going to be replaced by # FIXME: disgusting, but nvm, this is going to be replaced by
# proper central authentication at some point # proper central authentication at some point
passwordFile = pkgs.writeText "password" panelConfig.initialUser.password; passwordFile = pkgs.writeText "password" cfg.initialUser.password;
}; };
peertube = peertubeS3KeyConfig { inherit pkgs; } // { peertube = peertubeS3KeyConfig { inherit pkgs; } // {
@ -173,14 +198,14 @@ in
pixelfed-configuration = makeConfigurationResource pixelfedConfigurationResource ( pixelfed-configuration = makeConfigurationResource pixelfedConfigurationResource (
{ pkgs, ... }: { pkgs, ... }:
mkIf panelConfig.pixelfed.enable { mkIf cfg.pixelfed.enable {
fediversity = { fediversity = {
inherit (panelConfig) domain; inherit (cfg) domain;
temp.initialUser = { temp.initialUser = {
inherit (panelConfig.initialUser) username email displayName; inherit (cfg.initialUser) username email displayName;
# FIXME: disgusting, but nvm, this is going to be replaced by # FIXME: disgusting, but nvm, this is going to be replaced by
# proper central authentication at some point # proper central authentication at some point
passwordFile = pkgs.writeText "password" panelConfig.initialUser.password; passwordFile = pkgs.writeText "password" cfg.initialUser.password;
}; };
pixelfed = pixelfedS3KeyConfig { inherit pkgs; } // { pixelfed = pixelfedS3KeyConfig { inherit pkgs; } // {
@ -190,5 +215,17 @@ in
} }
); );
}; };
=======
{
system ? builtins.currentSystem,
sources ? import ../npins,
pkgs ? import sources.nixpkgs {
inherit system;
},
}:
{
tests = {
deployment-basic = import ./check/basic/nixosTest.nix { inherit pkgs; };
>>>>>>> fac372b3 (strip test as a start to convert it)
}; };
} }

View file

@ -1,3 +1,26 @@
{ inputs, sources, ... }:
{ {
imports = [ ./check/basic/flake-part.nix ]; _class = "flake";
perSystem =
{ pkgs, ... }:
{
checks = {
deployment-basic = import ./check/basic {
inherit (pkgs.testers) runNixOSTest;
inherit inputs sources;
};
deployment-cli = import ./check/cli {
inherit (pkgs.testers) runNixOSTest;
inherit inputs sources;
};
deployment-panel = import ./check/panel {
inherit (pkgs.testers) runNixOSTest;
inherit inputs sources;
};
};
};
} }

37
deployment/function.nix Normal file
View file

@ -0,0 +1,37 @@
/**
Modular function type
*/
{ config, lib, ... }:
let
inherit (lib) mkOption types;
inherit (types)
deferredModule
submodule
functionTo
optionType
;
in
{
options = {
input-type = mkOption {
type = deferredModule;
};
output-type = mkOption {
type = deferredModule;
};
function-type = mkOption {
type = optionType;
readOnly = true;
default = functionTo (submodule {
options = {
input = mkOption {
type = submodule config.input-type;
};
output = mkOption {
type = submodule config.output-type;
};
};
});
};
};
}

View file

@ -17,6 +17,8 @@ let
inherit (lib) types mkOption; inherit (lib) types mkOption;
in in
{ {
_class = "nixops4Deployment";
options = { options = {
enable = lib.mkEnableOption "Fediversity configuration"; enable = lib.mkEnableOption "Fediversity configuration";
domain = mkOption { domain = mkOption {

283
flake.lock generated
View file

@ -1,26 +1,5 @@
{ {
"nodes": { "nodes": {
"agenix": {
"inputs": {
"darwin": "darwin",
"home-manager": "home-manager",
"nixpkgs": "nixpkgs",
"systems": "systems"
},
"locked": {
"lastModified": 1736955230,
"narHash": "sha256-uenf8fv2eG5bKM8C/UvFaiJMZ4IpUFaQxk9OH5t/1gA=",
"owner": "ryantm",
"repo": "agenix",
"rev": "e600439ec4c273cf11e06fe4d9d906fb98fa097c",
"type": "github"
},
"original": {
"owner": "ryantm",
"repo": "agenix",
"type": "github"
}
},
"crane": { "crane": {
"flake": false, "flake": false,
"locked": { "locked": {
@ -38,46 +17,6 @@
"type": "github" "type": "github"
} }
}, },
"darwin": {
"inputs": {
"nixpkgs": [
"agenix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1700795494,
"narHash": "sha256-gzGLZSiOhf155FW7262kdHo2YDeugp3VuIFb4/GGng0=",
"owner": "lnl7",
"repo": "nix-darwin",
"rev": "4b9b83d5a92e8c1fbfd8eb27eda375908c11ec4d",
"type": "github"
},
"original": {
"owner": "lnl7",
"ref": "master",
"repo": "nix-darwin",
"type": "github"
}
},
"disko": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1740485968,
"narHash": "sha256-WK+PZHbfDjLyveXAxpnrfagiFgZWaTJglewBWniTn2Y=",
"owner": "nix-community",
"repo": "disko",
"rev": "19c1140419c4f1cdf88ad4c1cfb6605597628940",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "disko",
"type": "github"
}
},
"dream2nix": { "dream2nix": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@ -120,22 +59,6 @@
} }
}, },
"flake-compat_2": { "flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_3": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1733328505, "lastModified": 1733328505,
@ -151,7 +74,7 @@
"type": "github" "type": "github"
} }
}, },
"flake-compat_4": { "flake-compat_3": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1696426674, "lastModified": 1696426674,
@ -204,24 +127,6 @@
} }
}, },
"flake-parts_3": { "flake-parts_3": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib_3"
},
"locked": {
"lastModified": 1738453229,
"narHash": "sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm+zmZ7vxbJdo=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "32ea77a06711b758da0ad9bd6a844c5740a87abd",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_4": {
"inputs": { "inputs": {
"nixpkgs-lib": [ "nixpkgs-lib": [
"nixops4-nixos", "nixops4-nixos",
@ -246,7 +151,7 @@
}, },
"flake-utils": { "flake-utils": {
"inputs": { "inputs": {
"systems": "systems_2" "systems": "systems"
}, },
"locked": { "locked": {
"lastModified": 1710146030, "lastModified": 1710146030,
@ -262,31 +167,11 @@
"type": "github" "type": "github"
} }
}, },
"git-hooks": { "git-hooks-nix": {
"inputs": { "inputs": {
"flake-compat": "flake-compat", "flake-compat": "flake-compat",
"gitignore": "gitignore", "gitignore": "gitignore",
"nixpkgs": "nixpkgs_3" "nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1737465171,
"narHash": "sha256-R10v2hoJRLq8jcL4syVFag7nIGE7m13qO48wRIukWNg=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "9364dc02281ce2d37a1f55b6e51f7c0f65a75f17",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"git-hooks-nix": {
"inputs": {
"flake-compat": "flake-compat_2",
"gitignore": "gitignore_2",
"nixpkgs": "nixpkgs_4"
}, },
"locked": { "locked": {
"lastModified": 1737465171, "lastModified": 1737465171,
@ -342,27 +227,6 @@
} }
}, },
"gitignore": { "gitignore": {
"inputs": {
"nixpkgs": [
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"gitignore_2": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
"nixops4-nixos", "nixops4-nixos",
@ -384,27 +248,6 @@
"type": "github" "type": "github"
} }
}, },
"home-manager": {
"inputs": {
"nixpkgs": [
"agenix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1703113217,
"narHash": "sha256-7ulcXOk63TIT2lVDSExj7XzFx09LpdSAPtvgtM7yQPE=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "3bfaacf46133c037bb356193bd2f1765d9dc82c1",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "home-manager",
"type": "github"
}
},
"mk-naked-shell": { "mk-naked-shell": {
"flake": false, "flake": false,
"locked": { "locked": {
@ -423,8 +266,8 @@
}, },
"nix": { "nix": {
"inputs": { "inputs": {
"flake-compat": "flake-compat_3", "flake-compat": "flake-compat_2",
"flake-parts": "flake-parts_4", "flake-parts": "flake-parts_3",
"git-hooks-nix": "git-hooks-nix_2", "git-hooks-nix": "git-hooks-nix_2",
"nixfmt": "nixfmt", "nixfmt": "nixfmt",
"nixpkgs": [ "nixpkgs": [
@ -498,10 +341,10 @@
}, },
"nixops4": { "nixops4": {
"inputs": { "inputs": {
"flake-parts": "flake-parts_3", "flake-parts": "flake-parts_2",
"nix": "nix", "nix": "nix",
"nix-cargo-integration": "nix-cargo-integration", "nix-cargo-integration": "nix-cargo-integration",
"nixpkgs": "nixpkgs_5", "nixpkgs": "nixpkgs_2",
"nixpkgs-old": "nixpkgs-old" "nixpkgs-old": "nixpkgs-old"
}, },
"locked": { "locked": {
@ -520,7 +363,7 @@
}, },
"nixops4-nixos": { "nixops4-nixos": {
"inputs": { "inputs": {
"flake-parts": "flake-parts_2", "flake-parts": "flake-parts",
"git-hooks-nix": "git-hooks-nix", "git-hooks-nix": "git-hooks-nix",
"nixops4": "nixops4", "nixops4": "nixops4",
"nixops4-nixos": [ "nixops4-nixos": [
@ -548,16 +391,16 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1703013332, "lastModified": 1730768919,
"narHash": "sha256-+tFNwMvlXLbJZXiMHqYq77z/RfmpfpiI3yjL6o/Zo9M=", "narHash": "sha256-8AKquNnnSaJRXZxc5YmF/WfmxiHX6MMZZasRP6RRQkE=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "54aac082a4d9bb5bbc5c4e899603abfb76a3f6d6", "rev": "a04d33c0c3f1a59a2c1cb0c6e34cd24500e5a1dc",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixos-unstable", "ref": "nixpkgs-unstable",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
@ -602,18 +445,6 @@
"url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz" "url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz"
} }
}, },
"nixpkgs-lib_3": {
"locked": {
"lastModified": 1738452942,
"narHash": "sha256-vJzFZGaCpnmo7I6i416HaBLpC+hvcURh/BQwROcGIp8=",
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz"
}
},
"nixpkgs-old": { "nixpkgs-old": {
"locked": { "locked": {
"lastModified": 1735563628, "lastModified": 1735563628,
@ -647,54 +478,6 @@
} }
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": {
"lastModified": 1738136902,
"narHash": "sha256-pUvLijVGARw4u793APze3j6mU1Zwdtz7hGkGGkD87qw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9a5db3142ce450045840cc8d832b13b8a2018e0c",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1730768919,
"narHash": "sha256-8AKquNnnSaJRXZxc5YmF/WfmxiHX6MMZZasRP6RRQkE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a04d33c0c3f1a59a2c1cb0c6e34cd24500e5a1dc",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1730768919,
"narHash": "sha256-8AKquNnnSaJRXZxc5YmF/WfmxiHX6MMZZasRP6RRQkE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a04d33c0c3f1a59a2c1cb0c6e34cd24500e5a1dc",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_5": {
"locked": { "locked": {
"lastModified": 1738410390, "lastModified": 1738410390,
"narHash": "sha256-xvTo0Aw0+veek7hvEVLzErmJyQkEcRk6PSR4zsRQFEc=", "narHash": "sha256-xvTo0Aw0+veek7hvEVLzErmJyQkEcRk6PSR4zsRQFEc=",
@ -710,22 +493,6 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs_6": {
"locked": {
"lastModified": 1740463929,
"narHash": "sha256-4Xhu/3aUdCKeLfdteEHMegx5ooKQvwPHNkOgNCXQrvc=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "5d7db4668d7a0c6cc5fc8cf6ef33b008b2b1ed8b",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-24.11",
"repo": "nixpkgs",
"type": "github"
}
},
"parts": { "parts": {
"inputs": { "inputs": {
"nixpkgs-lib": [ "nixpkgs-lib": [
@ -751,7 +518,7 @@
}, },
"purescript-overlay": { "purescript-overlay": {
"inputs": { "inputs": {
"flake-compat": "flake-compat_4", "flake-compat": "flake-compat_3",
"nixpkgs": [ "nixpkgs": [
"nixops4-nixos", "nixops4-nixos",
"nixops4", "nixops4",
@ -794,16 +561,11 @@
}, },
"root": { "root": {
"inputs": { "inputs": {
"agenix": "agenix",
"disko": "disko",
"flake-parts": "flake-parts",
"git-hooks": "git-hooks",
"nixops4": [ "nixops4": [
"nixops4-nixos", "nixops4-nixos",
"nixops4" "nixops4"
], ],
"nixops4-nixos": "nixops4-nixos", "nixops4-nixos": "nixops4-nixos"
"nixpkgs": "nixpkgs_6"
} }
}, },
"rust-overlay": { "rust-overlay": {
@ -869,21 +631,6 @@
"type": "github" "type": "github"
} }
}, },
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"treefmt": { "treefmt": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [

100
flake.nix
View file

@ -1,74 +1,52 @@
{ {
inputs = { inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
flake-parts.url = "github:hercules-ci/flake-parts";
git-hooks.url = "github:cachix/git-hooks.nix";
agenix.url = "github:ryantm/agenix";
disko.url = "github:nix-community/disko";
nixops4.follows = "nixops4-nixos/nixops4"; nixops4.follows = "nixops4-nixos/nixops4";
nixops4-nixos.url = "github:nixops4/nixops4-nixos"; nixops4-nixos.url = "github:nixops4/nixops4-nixos";
}; };
outputs = outputs =
inputs@{ flake-parts, ... }: inputs:
flake-parts.lib.mkFlake { inherit inputs; } { import ./mkFlake.nix inputs (
systems = [ { inputs, sources, ... }:
"x86_64-linux" {
"aarch64-linux" imports = [
"x86_64-darwin" "${sources.git-hooks}/flake-module.nix"
"aarch64-darwin" inputs.nixops4.modules.flake.default
];
imports = [ ./deployment/flake-part.nix
inputs.git-hooks.flakeModule ./infra/flake-part.nix
inputs.nixops4.modules.flake.default ./keys/flake-part.nix
./secrets/flake-part.nix
./services/tests/flake-part.nix
];
./deployment/flake-part.nix perSystem =
./infra/flake-part.nix {
./services/flake-part.nix pkgs,
]; lib,
system,
perSystem = ...
{ }:
config, {
pkgs, checks = {
lib, panel = (import ./. { inherit sources system; }).tests.panel.basic;
inputs',
...
}:
{
formatter = pkgs.nixfmt-rfc-style;
pre-commit.settings.hooks =
let
## Add a directory here if pre-commit hooks shouldn't apply to it.
optout = [ "npins" ];
excludes = map (dir: "^${dir}/") optout;
addExcludes = lib.mapAttrs (_: c: c // { inherit excludes; });
in
addExcludes {
nixfmt-rfc-style.enable = true;
deadnix.enable = true;
trim-trailing-whitespace.enable = true;
shellcheck.enable = true;
}; };
formatter = pkgs.nixfmt-rfc-style;
devShells.default = pkgs.mkShell { pre-commit.settings.hooks =
packages = [ let
pkgs.nil ## Add a directory here if pre-commit hooks shouldn't apply to it.
inputs'.agenix.packages.default optout = [ "npins" ];
pkgs.openssh excludes = map (dir: "^${dir}/") optout;
pkgs.httpie addExcludes = lib.mapAttrs (_: c: c // { inherit excludes; });
pkgs.jq in
# exposing this env var as a hack to pass info in from form addExcludes {
(inputs'.nixops4.packages.default.overrideAttrs { nixfmt-rfc-style.enable = true;
impureEnvVars = [ "DEPLOYMENT" ]; deadnix.enable = true;
}) trim-trailing-whitespace.enable = true;
]; shellcheck.enable = true;
shellHook = config.pre-commit.installationScript; };
}; };
}; }
}; );
} }

10
infra/.envrc Normal file
View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
# the shebang is ignored, but nice for editors
# shellcheck shell=bash
if type -P lorri &>/dev/null; then
eval "$(lorri direnv)"
else
echo 'while direnv evaluated .envrc, could not find the command "lorri" [https://github.com/nix-community/lorri]'
use_nix
fi

View file

@ -1,98 +1,30 @@
# Infra # service deployment
This directory contains the definition of [the VMs](machines.md) that host our deploys [NixOS](https://nixos.org/) templates using [OpenTofu](https://opentofu.org/).
infrastructure.
## Provisioning VMs with an initial configuration ## requirements
NOTE[Niols]: This is very manual and clunky. Two things will happen. In the near - [nix](https://nix.dev/)
future, I will improve the provisioning script to make this a bit less clunky.
In the far future, NixOps4 will be able to communicate with Proxmox directly and
everything will become much cleaner.
1. Choose names for your VMs. It is recommended to choose `fediXXX`, with `XXX` ## usage
above 100. For instance, `fedi117`.
2. Add a basic configuration for the machine. These typically go in ### development
`infra/machines/<name>/default.nix`. You can look at other `fediXXX` VMs to
find inspiration. You probably do not need a `nixos.module` option at this
point.
2. Add a file for each of those VM's public keys, eg. before using other commands, if not using direnv:
```
touch keys/systems/fedi117.pub
```
Those files need to exist during provisioning, but their content matters only
when updating the machines' configuration.
FIXME: Remove this step by making the provisioning script not fail with the
public key does not exist yet.
3. Run the provisioning script:
```
sh infra/proxmox-provision.sh fedi117
```
The script can take several ids at the same time. It requires some
authentication options and provides several more. See `--help`.
4. (Optional) Add a DNS entry for the machine; for instance `fedi117.abundos.eu
A 95.215.187.117`.
5. Grab the public host keys for the machines in question, and add it to the
repository. For instance:
```
ssh fedi117.abundos.eu 'sudo cat /etc/ssh/ssh_host_ed25519_key.pub' > keys/systems/fedi117.pub
```
FIXME: Make the provisioning script do that for us.
7. Regenerate the list of machines:
```
sh infra/machines.md.sh
```
Commit it with the machine's configuration, public key, etc.
8. At this point, the machine contains a very basic configuration that contains
just enough for it to boot and be reachable. Go on to the next section to
update the machine and put an actual configuration.
FIXME: Figure out why the full configuration isn't on the machine at this
point and fix it.
## Updating existing VM configurations
Their configuration can be updated via NixOps4. Run
```sh ```sh
nixops4 deployments list nix-shell
``` ```
to see the available deployments. then to initialize, or after updating pins or TF providers:
This should be done from the root of the repository,
otherwise NixOps4 will fail with something like:
```
nixops4 error: evaluation: error:
… while calling the 'getFlake' builtin
error: path '/nix/store/05nn7krhvi8wkcyl6bsysznlv60g5rrf-source/flake.nix' does not exist, evaluation: error:
… while calling the 'getFlake' builtin
error: path '/nix/store/05nn7krhvi8wkcyl6bsysznlv60g5rrf-source/flake.nix' does not exist
```
Then, given a deployment (eg. `fedi200`), run
```sh ```sh
nixops4 apply <deployment> setup
``` ```
Alternatively, to run the `default` deployment, which contains all the VMs, run then, one can use the `tofu` CLI in the sub-folders.
```sh ## implementing
nixops4 apply
```
## Removing an existing VM proper documentation TODO.
until then, a reference implementation may be found in [`panel/`](https://git.fediversity.eu/Fediversity/Fediversity/src/branch/main/panel).
See `infra/proxmox-remove.sh --help`.

View file

@ -5,8 +5,9 @@ let
in in
{ {
_class = "nixos";
imports = [ imports = [
./hardware.nix
./networking.nix ./networking.nix
./users.nix ./users.nix
]; ];
@ -22,4 +23,9 @@ in
nix.extraOptions = '' nix.extraOptions = ''
experimental-features = nix-command flakes experimental-features = nix-command flakes
''; '';
boot.loader = {
systemd-boot.enable = true;
efi.canTouchEfiVariables = true;
};
} }

View file

@ -1,63 +1,64 @@
{ config, lib, ... }: { config, lib, ... }:
let let
inherit (lib) mkDefault; inherit (lib) mkDefault mkIf mkMerge;
in in
{ {
_class = "nixos";
config = { config = {
services.openssh = { services.openssh = {
enable = true; enable = true;
settings.PasswordAuthentication = false; settings.PasswordAuthentication = false;
}; };
networking = { networking = mkMerge [
hostName = config.fediversityVm.name; {
domain = config.fediversityVm.domain; hostName = config.fediversityVm.name;
domain = config.fediversityVm.domain;
## REVIEW: Do we actually need that, considering that we have static IPs? ## REVIEW: Do we actually need that, considering that we have static IPs?
useDHCP = mkDefault true; useDHCP = mkDefault true;
interfaces = { ## Disable the default firewall and use nftables instead, with a custom
eth0 = { ## Procolix-made ruleset.
ipv4 = { firewall.enable = false;
addresses = [ nftables = {
{ enable = true;
inherit (config.fediversityVm.ipv4) address prefixLength; rulesetFile = ./nftables-ruleset.nft;
}
];
};
ipv6 = {
addresses = [
{
inherit (config.fediversityVm.ipv6) address prefixLength;
}
];
};
}; };
}; }
defaultGateway = { ## IPv4
address = config.fediversityVm.ipv4.gateway; (mkIf config.fediversityVm.ipv4.enable {
interface = "eth0"; interfaces.${config.fediversityVm.ipv4.interface}.ipv4.addresses = [
}; { inherit (config.fediversityVm.ipv4) address prefixLength; }
defaultGateway6 = { ];
address = config.fediversityVm.ipv6.gateway; defaultGateway = {
interface = "eth0"; address = config.fediversityVm.ipv4.gateway;
}; interface = config.fediversityVm.ipv4.interface;
};
nameservers = [
"95.215.185.6"
"95.215.185.7"
];
})
nameservers = [ ## IPv6
"95.215.185.6" (mkIf config.fediversityVm.ipv6.enable {
"95.215.185.7" interfaces.${config.fediversityVm.ipv6.interface}.ipv6.addresses = [
"2a00:51c0::5fd7:b906" { inherit (config.fediversityVm.ipv6) address prefixLength; }
"2a00:51c0::5fd7:b907" ];
]; defaultGateway6 = {
address = config.fediversityVm.ipv6.gateway;
firewall.enable = false; interface = config.fediversityVm.ipv6.interface;
nftables = { };
enable = true; nameservers = [
rulesetFile = ./nftables-ruleset.nft; "2a00:51c0::5fd7:b906"
}; "2a00:51c0::5fd7:b907"
}; ];
})
];
}; };
} }

View file

@ -1,5 +1,13 @@
{ {
config,
...
}:
{
_class = "nixos";
users.users = { users.users = {
root.openssh.authorizedKeys.keys = config.users.users.procolix.openssh.authorizedKeys.keys;
procolix = { procolix = {
isNormalUser = true; isNormalUser = true;
extraGroups = [ "wheel" ]; extraGroups = [ "wheel" ];

43
infra/common/node.nix Normal file
View file

@ -0,0 +1,43 @@
{
lib,
config,
...
}:
let
inherit (lib) attrValues elem mkDefault;
inherit (lib.attrsets) concatMapAttrs optionalAttrs;
inherit (lib.strings) removeSuffix;
secretsPrefix = ../secrets;
secrets = import (secretsPrefix + "/secrets.nix");
keys = import ../keys;
in
{
fediversityVm.hostPublicKey = mkDefault keys.systems.${config.fediversityVm.name};
## The configuration of the machine. We strive to keep in this file only the
## options that really need to be injected from the resource. Everything else
## should go into the `./nixos` subdirectory.
imports = [
../infra/common/options.nix
../infra/common/nixos
];
## Read all the secrets, filter the ones that are supposed to be readable
## with this host's public key, and add them correctly to the configuration
## as `age.secrets.<name>.file`.
age.secrets = concatMapAttrs (
name: secret:
optionalAttrs (elem config.fediversityVm.hostPublicKey secret.publicKeys) {
${removeSuffix ".age" name}.file = secretsPrefix + "/${name}";
}
) secrets;
## FIXME: switch root authentication to users with password-less sudo, see #24
users.users.root.openssh.authorizedKeys.keys = attrValues keys.contributors ++ [
# allow our panel vm access to the test machines
keys.panel
];
}

View file

@ -6,6 +6,8 @@ let
in in
{ {
# `config` not set and imported from multiple places: no fixed module class
options.fediversityVm = { options.fediversityVm = {
########################################################################## ##########################################################################
@ -18,16 +20,13 @@ in
''; '';
}; };
proxmox = mkOption { isFediversityVm = mkOption {
type = types.nullOr ( type = types.bool;
types.enum [
"procolix"
"fediversity"
]
);
description = '' description = ''
The Proxmox instance. This is used for provisioning only and should be Whether the machine is a Fediversity VM or not. This is used to
set to `null` if the machine is not a VM. 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.
''; '';
}; };
@ -89,11 +88,22 @@ in
}; };
ipv4 = { ipv4 = {
enable = mkOption {
default = true;
};
interface = mkOption {
description = ''
The interface that carries the machine's IPv4 network.
'';
default = "eth0";
};
address = mkOption { address = mkOption {
description = '' description = ''
The IP address of the machine, version 4. It will be injected as a The IP address of the machine, version 4. It will be injected as a
value in `networking.interfaces.eth0`, but it will also be used to value in `networking.interfaces.eth0`, but it will also be used to
communicate with the machine via NixOps4. communicate with the machine.
''; '';
}; };
@ -114,11 +124,22 @@ in
}; };
ipv6 = { ipv6 = {
enable = mkOption {
default = true;
};
interface = mkOption {
description = ''
The interface that carries the machine's IPv6 network.
'';
default = "eth0";
};
address = mkOption { address = mkOption {
description = '' description = ''
The IP address of the machine, version 6. It will be injected as a The IP address of the machine, version 6. It will be injected as a
value in `networking.interfaces.eth0`, but it will also be used to value in `networking.interfaces.eth0`, but it will also be used to
communicate with the machine via NixOps4. communicate with the machine.
''; '';
}; };
@ -141,7 +162,7 @@ in
hostPublicKey = mkOption { hostPublicKey = mkOption {
description = '' description = ''
The ed25519 host public key of the machine. It is used to filter Age The ed25519 host public key of the machine. It is used to filter Age
secrets and only keep the relevant ones, and to feed to NixOps4. secrets and only keep the relevant ones, and to feed to TF.
''; '';
}; };

View file

@ -1,20 +1,20 @@
{ modulesPath, ... }: { ... }:
{ {
imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; _class = "nixos";
## 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 = { boot = {
loader = {
systemd-boot.enable = true;
efi.canTouchEfiVariables = true;
};
initrd = { initrd = {
availableKernelModules = [ availableKernelModules = [
"ata_piix" "ata_piix"
"uhci_hcd" "uhci_hcd"
"virtio_pci"
"virtio_scsi"
"sd_mod" "sd_mod"
"sr_mod" "sr_mod"
]; ];

View file

@ -1,7 +1,8 @@
{ {
inputs,
lib, lib,
config, config,
keys,
secrets,
... ...
}: }:
@ -10,54 +11,35 @@ let
inherit (lib.attrsets) concatMapAttrs optionalAttrs; inherit (lib.attrsets) concatMapAttrs optionalAttrs;
inherit (lib.strings) removeSuffix; inherit (lib.strings) removeSuffix;
secretsPrefix = ../../secrets;
secrets = import (secretsPrefix + "/secrets.nix");
keys = import ../../keys;
in in
{ {
imports = [ ./options.nix ];
fediversityVm.hostPublicKey = mkDefault keys.systems.${config.fediversityVm.name}; fediversityVm.hostPublicKey = mkDefault keys.systems.${config.fediversityVm.name};
ssh = {
host = config.fediversityVm.ipv4.address;
hostPublicKey = config.fediversityVm.hostPublicKey;
};
nixpkgs = inputs.nixpkgs;
## The configuration of the machine. We strive to keep in this file only the ## The configuration of the machine. We strive to keep in this file only the
## options that really need to be injected from the resource. Everything else ## options that really need to be injected from the resource. Everything else
## should go into the `./nixos` subdirectory. ## should go into the `./nixos` subdirectory.
nixos.module = { imports = [
imports = [ ./options.nix
inputs.agenix.nixosModules.default ./nixos
inputs.disko.nixosModules.default ./proxmox-qemu-vm.nix
./options.nix ];
./nixos
];
## Inject the shared options from the resource's `config` into the NixOS ## Read all the secrets, filter the ones that are supposed to be readable
## configuration. ## with this host's public key, and add them correctly to the configuration
fediversityVm = config.fediversityVm; ## as `age.secrets.<name>.file`.
age.secrets = concatMapAttrs (
name: secret:
optionalAttrs (elem config.fediversityVm.hostPublicKey secret.publicKeys) {
${removeSuffix ".age" name}.file = secrets.rootPath + "/${name}";
}
) secrets;
## Read all the secrets, filter the ones that are supposed to be readable ## FIXME: Remove direct root authentication once the NixOps4 NixOS provider
## with this host's public key, and add them correctly to the configuration ## supports users with password-less sudo.
## as `age.secrets.<name>.file`. users.users.root.openssh.authorizedKeys.keys = attrValues keys.contributors ++ [
age.secrets = concatMapAttrs ( # allow our panel vm access to the test machines
name: secret: keys.panel
optionalAttrs (elem config.fediversityVm.hostPublicKey secret.publicKeys) ({ # allow continuous deployment access
${removeSuffix ".age" name}.file = secretsPrefix + "/${name}"; keys.cd
}) ];
) secrets;
## FIXME: Remove direct root authentication once the NixOps4 NixOS provider
## supports users with password-less sudo.
users.users.root.openssh.authorizedKeys.keys = attrValues keys.contributors ++ [
# allow our panel vm access to the test machines
keys.panel
];
};
} }

27
infra/common/shared.nix Normal file
View file

@ -0,0 +1,27 @@
{
pkgs,
config,
...
}:
let
inherit (config.terraform) hostname domain initialUser;
in
{
imports = [
<disko/module.nix>
<agenix/modules/age.nix>
../../services/fediversity
./resource.nix
# ./node.nix
];
fediversityVm.name = hostname;
fediversity = {
inherit domain;
temp.initialUser = {
inherit (initialUser) username email displayName;
# FIXME: disgusting, but nvm, this is going to be replaced by
# proper central authentication at some point
passwordFile = pkgs.writeText "password" initialUser.password;
};
};
}

34
infra/default.nix Normal file
View file

@ -0,0 +1,34 @@
{
system ? builtins.currentSystem,
sources ? import ../npins,
pkgs ? import sources.nixpkgs { inherit system; },
}:
let
inherit (pkgs) lib;
setup = pkgs.writeScriptBin "setup" ''
echo '${lib.strings.toJSON sources}' > .npins.json
rm -f .terraform.lock.hcl
rm -rf .terraform/
tofu init
'';
in
{
# shell for testing TF directly
shell = pkgs.mkShellNoCC {
packages = [
(import ./tf.nix { inherit lib pkgs; })
pkgs.jaq
setup
];
};
tests = pkgs.callPackage ./tests.nix { };
# re-export inputs so they can be overridden granularly
# (they can't be accessed from the outside any other way)
inherit
sources
system
pkgs
;
}

44
infra/dev/main.tf Normal file
View file

@ -0,0 +1,44 @@
locals {
vm_domain = "abundos.eu"
}
module "nixos" {
source = "../sync-nix"
vm_domain = local.vm_domain
hostname = each.value.hostname
config_nix = each.value.config_nix
config_tf = each.value.config_tf
for_each = { for name, inst in {
# wiki = "vm02187" # does not resolve
# forgejo = "vm02116" # does not resolve
# TODO: move these to a separate `host` dir
dns = "fedi200"
fedipanel = "fedi201"
} : name => {
hostname = inst
config_tf = {
terraform = {
domain = local.vm_domain
hostname = inst
}
}
config_nix = <<-EOF
{
# note interpolations here TF ones
imports = [
# shared NixOS config
${path.root}/../common/shared.nix
# FIXME: separate template options by service
${path.root}/options.nix
# for service `forgejo` import `forgejo.nix`
${path.root}/../../machines/dev/${inst}/${name}.nix
# FIXME: get VM details from TF
${path.root}/../../machines/dev/${inst}
];
}
EOF
}
}
}

28
infra/dev/options.nix Normal file
View file

@ -0,0 +1,28 @@
# TODO: could (part of) this be generated somehow? c.f #275
{
lib,
...
}:
let
inherit (lib) types mkOption;
inherit (types) str enum;
in
{
options.terraform = {
domain = mkOption {
type = enum [
"fediversity.net"
];
description = ''
Apex domain under which the services will be deployed.
'';
default = "fediversity.net";
};
hostname = mkOption {
type = str;
description = ''
Internal name of the host, e.g. test01
'';
};
};
}

0
infra/dev/variables.tf Normal file
View file

View file

@ -1,50 +1,53 @@
{ {
self,
inputs, inputs,
lib, lib,
sources,
keys,
secrets,
... ...
}: }:
let let
inherit (builtins) readDir readFile fromJSON; inherit (builtins) readDir;
inherit (lib) inherit (lib)
attrNames attrNames
mkOption mkOption
evalModules evalModules
filterAttrs filterAttrs
mapAttrs'
deepSeq
; ;
inherit (lib.attrsets) genAttrs; inherit (lib.attrsets) genAttrs;
## Given a machine's name and whether it is a test VM, make a resource module, commonResourceModule = {
## except for its missing provider. (Depending on the use of that resource, we # TODO(@fricklerhandwerk): this is terrible but IMO we should just ditch
## will provide a different one.) # flake-parts and have our own data model for how the project is organised
makeResourceModule = # internally
{ vmName, isTestVm }: _module.args = {
{ inherit
_module.args = { inherit inputs; }; inputs
imports = keys
[ secrets
./common/resource.nix sources
] ;
++ (
if isTestVm then
[
./test-machines/${vmName}
{
nixos.module.users.users.root.openssh.authorizedKeys.keys = [
# allow our panel vm access to the test machines
(import ../keys).panel
];
}
]
else
[
./machines/${vmName}
]
);
fediversityVm.name = vmName;
}; };
## 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' ## Given a list of machine names, make a deployment with those machines'
## configurations as resources. ## configurations as resources.
makeDeployment = makeDeployment =
@ -56,10 +59,8 @@ let
type = providers.local.exec; type = providers.local.exec;
imports = [ imports = [
inputs.nixops4-nixos.modules.nixops4Resource.nixos inputs.nixops4-nixos.modules.nixops4Resource.nixos
(makeResourceModule { commonResourceModule
inherit vmName; ../machines/dev/${vmName}
isTestVm = false;
})
]; ];
}); });
}; };
@ -73,84 +74,102 @@ let
{ {
inherit lib; inherit lib;
inherit (inputs) nixops4 nixops4-nixos; inherit (inputs) nixops4 nixops4-nixos;
inherit (self.nixosModules) fediversity; fediversity = import ../services/fediversity;
} }
{ {
garageConfigurationResource = makeResourceModule { garageConfigurationResource = {
vmName = "test01"; imports = [
isTestVm = true; commonResourceModule
../machines/operator/test01
];
}; };
mastodonConfigurationResource = makeResourceModule { mastodonConfigurationResource = {
vmName = "test06"; # somehow `test02` has a problem - use test06 instead imports = [
isTestVm = true; commonResourceModule
../machines/operator/test06 # somehow `test02` has a problem - use test06 instead
];
}; };
peertubeConfigurationResource = makeResourceModule { peertubeConfigurationResource = {
vmName = "test05"; imports = [
isTestVm = true; commonResourceModule
../machines/operator/test05
];
}; };
pixelfedConfigurationResource = makeResourceModule { pixelfedConfigurationResource = {
vmName = "test04"; imports = [
isTestVm = true; commonResourceModule
../machines/operator/test04
];
}; };
}; };
nixops4ResourceNixosMockOptions = { nixops4ResourceNixosMockOptions = {
## NOTE: We allow the use of a few options from ## NOTE: We allow the use of a few options from
## `inputs.nixops4-nixos.modules.nixops4Resource.nixos` such that we can ## `nixops4-nixos.modules.nixops4Resource.nixos` such that we can
## reuse modules that make use of them. ## reuse modules that make use of them.
## ##
## REVIEW: We can probably do much better and cleaner. On the other hand, ## REVIEW: We can probably do much better and cleaner. On the other hand,
## this is only needed to expose NixOS configurations for provisioning ## this is only needed to expose NixOS configurations for provisioning
## purposes, and eventually all of this should be handled by NixOps4. ## purposes, and eventually all of this should be handled by NixOps4.
options = { 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 { }; nixpkgs = mkOption { };
ssh = mkOption { }; ssh = mkOption { };
}; };
}; };
makeResourceConfig = makeResourceConfig =
vm: { vmName, isTestVm }:
(evalModules { (evalModules {
modules = [ modules = [
nixops4ResourceNixosMockOptions nixops4ResourceNixosMockOptions
(makeResourceModule vm) commonResourceModule
(if isTestVm then ../machines/operator/${vmName} else ../machines/dev/${vmName})
]; ];
}).config; }).config;
## Given a VM name, make a NixOS configuration for this machine. ## Given a VM name, make a NixOS configuration for this machine.
makeConfiguration = makeConfiguration =
isTestVm: vmName: isTestVm: vmName:
inputs.nixpkgs.lib.nixosSystem { import "${sources.nixpkgs}/nixos" {
modules = [ configuration = (makeResourceConfig { inherit vmName isTestVm; }).nixos.module;
(makeResourceConfig { inherit vmName isTestVm; }).nixos.module system = "x86_64-linux";
];
}; };
makeVmOptions = isTestVm: vmName: { makeVmOptions =
inherit ((makeResourceConfig { inherit vmName isTestVm; }).fediversityVm) isTestVm: vmName:
proxmox let
vmId config = (makeResourceConfig { inherit vmName isTestVm; }).fediversityVm;
description in
if config.isFediversityVm then
sockets {
cores inherit (config)
memory vmId
diskSize description
sockets
hostPublicKey cores
unsafeHostPrivateKey memory
; diskSize
}; hostPublicKey
unsafeHostPrivateKey
;
}
else
null;
listSubdirectories = path: attrNames (filterAttrs (_: type: type == "directory") (readDir path)); listSubdirectories = path: attrNames (filterAttrs (_: type: type == "directory") (readDir path));
machines = listSubdirectories ./machines; machines = listSubdirectories ../machines/dev;
testMachines = listSubdirectories ./test-machines;
nixosConfigurations =
genAttrs machines (makeConfiguration false);
vmOptions =
filterAttrs (_: value: value != null) # Filter out non-Fediversity VMs
(genAttrs machines (makeVmOptions false));
in in
{ {
flake.lib.makeInstallerIso = import ./makeInstallerIso.nix; _class = "flake";
## - Each normal or test machine gets a NixOS configuration. ## - Each normal or test machine gets a NixOS configuration.
## - Each normal or test machine gets a VM options entry. ## - Each normal or test machine gets a VM options entry.
@ -159,22 +178,24 @@ in
## - We add a “test” deployment with all test machines. ## - We add a “test” deployment with all test machines.
nixops4Deployments = genAttrs machines makeDeployment' // { nixops4Deployments = genAttrs machines makeDeployment' // {
default = makeDeployment machines; default = makeDeployment machines;
test = makeTestDeployment (
fromJSON (
let
env = builtins.getEnv "DEPLOYMENT";
in
if env != "" then
env
else
builtins.trace "env var DEPLOYMENT not set, falling back to ./test-machines/configuration.json!" (readFile ./test-machines/configuration.json)
)
);
}; };
flake.nixosConfigurations = flake = { inherit nixosConfigurations vmOptions; };
genAttrs machines (makeConfiguration false)
// genAttrs testMachines (makeConfiguration true); perSystem =
flake.vmOptions = { pkgs, ... }:
genAttrs machines (makeVmOptions false) {
// genAttrs testMachines (makeVmOptions true); checks =
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;
};
} }

View file

@ -1,15 +0,0 @@
<!-- This file is auto-generated by `machines.md.sh` from the machines'
configuration. -->
# Machines
Currently, this repository keeps track of the following VMs:
Machine | Proxmox | Description
--------|---------|-------------
[`fedi200`](./fedi200) | fediversity | Testing machine for Hans
[`fedi201`](./fedi201) | fediversity | FediPanel
[`vm02116`](./vm02116) | procolix | Forgejo
[`vm02187`](./vm02187) | procolix | Wiki
This table excludes all machines with names starting with `test`.

View file

@ -1,43 +0,0 @@
#!/usr/bin/env sh
set -euC
cd "$(dirname "$0")"
{
cat <<\EOF
<!-- This file is auto-generated by `machines.md.sh` from the machines'
configuration. -->
# Machines
Currently, this repository keeps track of the following VMs:
Machine | Proxmox | Description
--------|---------|-------------
EOF
vmOptions=$(
cd ..
nix eval \
--impure --raw --expr "
builtins.toJSON (builtins.getFlake (builtins.toString ./.)).vmOptions
" \
--log-format raw --quiet
)
## NOTE: `jq`'s `keys` is alphabetically sorted, just what we want here.
for machine in $(echo "$vmOptions" | jq -r 'keys[]'); do
if [ "${machine#test}" = "$machine" ]; then
proxmox=$(echo "$vmOptions" | jq -r ".$machine.proxmox")
description=$(echo "$vmOptions" | jq -r ".$machine.description" | head -n 1)
# shellcheck disable=SC2016
printf '[`%s`](./%s) | %s | %s\n' "$machine" "$machine" "$proxmox" "$description"
fi
done
cat <<\EOF
This table excludes all machines with names starting with `test`.
EOF
} >| machines.md

View file

@ -1,38 +0,0 @@
{
fediversityVm = {
vmId = 2116;
proxmox = "procolix";
description = "Forgejo";
ipv4.address = "185.206.232.34";
ipv6.address = "2a00:51c0:12:1201::20";
};
nixos.module =
{ lib, ... }:
{
imports = [
./forgejo.nix
];
## vm02116 is running on old hardware based on a Xen VM environment, so it
## needs these extra options. Once the VM gets moved to a newer node, these
## two options can safely be removed.
boot.initrd.availableKernelModules = [ "xen_blkfront" ];
services.xe-guest-utilities.enable = true;
## NOTE: This VM was created manually, which requires us to override the
## default disko-based `fileSystems` definition.
fileSystems = lib.mkForce {
"/" = {
device = "/dev/disk/by-uuid/3802a66d-e31a-4650-86f3-b51b11918853";
fsType = "ext4";
};
"/boot" = {
device = "/dev/disk/by-uuid/2CE2-1173";
fsType = "vfat";
};
};
};
}

View file

@ -1,36 +0,0 @@
{
fediversityVm = {
vmId = 2187;
proxmox = "procolix";
description = "Wiki";
ipv4.address = "185.206.232.187";
ipv6.address = "2a00:51c0:12:1201::187";
};
nixos.module =
{ lib, ... }:
{
imports = [
./wiki.nix
];
## NOTE: This VM was created manually, which requires us to override the
## default disko-based `fileSystems` definition.
fileSystems = lib.mkForce {
"/" = {
device = "/dev/disk/by-uuid/a46a9c46-e32b-4216-a4aa-8819b2cd0d49";
fsType = "ext4";
};
"/boot" = {
device = "/dev/disk/by-uuid/6AB5-4FA8";
fsType = "vfat";
options = [
"fmask=0022"
"dmask=0022"
];
};
};
};
}

74
infra/operator/main.tf Normal file
View file

@ -0,0 +1,74 @@
locals {
vm_domain = "abundos.eu"
# user-facing applications
application_configs = {
# FIXME: wrap applications at the interface to grab them in one go?
mastodon = {
cfg = var.mastodon
hostname = "test06"
}
pixelfed = {
cfg = var.pixelfed
hostname = "test04"
}
peertube = {
cfg = var.peertube
hostname = "test05"
}
}
# services shared between applications
peripherals = { for name, inst in {
garage = "test01"
} : name => {
hostname = inst
cfg = {
# enable if any user applications are enabled
enable = anytrue([for _, app in local.application_configs: try(app.cfg.enable, false)])
}
}
}
}
module "nixos" {
source = "../sync-nix"
vm_domain = local.vm_domain
hostname = each.value.hostname
config_nix = each.value.config_nix
config_tf = each.value.config_tf
for_each = {for name, inst in merge(
local.peripherals,
local.application_configs,
) : name => merge(inst, {
config_tf = {
terraform = {
domain = var.domain
hostname = inst.hostname
initialUser = var.initialUser
}
}
config_nix = <<-EOF
{
# note interpolations here TF ones
imports = [
# shared NixOS config
${path.root}/../common/shared.nix
# FIXME: separate template options by service
${path.root}/options.nix
# for service `mastodon` import `mastodon.nix`
${path.root}/../../machines/operator/${inst.hostname}/${name}.nix
# FIXME: get VM details from TF
${path.root}/../../machines/operator/${inst.hostname}
];
## FIXME: switch root authentication to users with password-less sudo, see #24
users.users.root.openssh.authorizedKeys.keys = let
keys = import ../../keys;
in [
# allow our panel vm access to the test machines
keys.panel
];
}
EOF
}) if try(inst.cfg.enable, false)}
}

View file

@ -0,0 +1,54 @@
# TODO: could (part of) this be generated somehow? c.f #275
{
lib,
...
}:
let
inherit (lib) types mkOption;
inherit (types) str enum submodule;
in
{
options.terraform = {
domain = mkOption {
type = enum [
"fediversity.net"
];
description = ''
Apex domain under which the services will be deployed.
'';
default = "fediversity.net";
};
hostname = mkOption {
type = str;
description = ''
Internal name of the host, e.g. test01
'';
};
initialUser = mkOption {
description = ''
Some services require an initial user to access them.
This option sets the credentials for such an initial user.
'';
type = submodule {
options = {
displayName = mkOption {
type = str;
description = "Display name of the user";
};
username = mkOption {
type = str;
description = "Username for login";
};
email = mkOption {
type = str;
description = "User's email address";
};
password = mkOption {
type = str;
description = "Password for login";
};
};
};
};
};
}

View file

@ -0,0 +1,51 @@
# TODO: (partially) generate, say from nix modules, c.f. #275
variable "domain" {
type = string
default = "fediversity.net"
}
variable "mastodon" {
type = object({
enable = bool
})
default = {
enable = false
}
}
variable "pixelfed" {
type = object({
enable = bool
})
default = {
enable = false
}
}
variable "peertube" {
type = object({
enable = bool
})
default = {
enable = false
}
}
variable "initialUser" {
type = object({
displayName = string
username = string
email = string
# TODO: mark (nested) credentials as sensitive
# https://discuss.hashicorp.com/t/is-it-possible-to-mark-an-attribute-of-an-object-as-sensitive/24649/2
password = string
})
# FIXME: remove default when the form provides this value, see #285
default = {
displayName = "Testy McTestface"
username = "test"
email = "test@test.com"
password = "testtest"
}
}

15
infra/pass-ssh-key.sh Executable file
View file

@ -0,0 +1,15 @@
#!/usr/bin/env bash
export host="$host"
mkdir -p etc/ssh
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
for keyname in ssh_host_ed25519_key ssh_host_ed25519_key.pub; do
if [[ $keyname == *.pub ]]; then
umask 0133
else
umask 0177
fi
cp "$SCRIPT_DIR/../infra/test-machines/${host}/${keyname}" ./etc/ssh/${keyname}
done

20
infra/setup.nix Normal file
View file

@ -0,0 +1,20 @@
{
pkgs,
lib,
sources,
...
}:
pkgs.writeScriptBin "setup" ''
# calculated pins
echo '${lib.strings.toJSON sources}' > sync-nix/.npins.json
# generate TF lock for nix's TF providers
for category in dev operator sync-nix; 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
''

1
infra/shell.nix Normal file
View file

@ -0,0 +1 @@
(import ./. { }).shell

102
infra/sync-nix/main.tf Normal file
View file

@ -0,0 +1,102 @@
locals {
system = "x86_64-linux"
# dependency paths pre-calculated from npins
pins = jsondecode(file("${path.module}/.npins.json"))
# nix path: expose pins, use nixpkgs in flake commands (`nix run`)
nix_path = "${join(":", [for name, dir in local.pins : "${name}=${dir}"])}:flake=${local.pins["nixpkgs"]}:flake"
}
# 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.hostname,
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 = {
# nix path used on build, lets us refer to e.g. nixpkgs like `<nixpkgs>`
NIX_PATH = local.nix_path
}
# TODO: refactor back to command="ignoreme" interpreter=concat([]) to protect sensitive data from error logs?
# TODO: build on target?
command = <<-EOF
set -euo pipefail
# INSTANTIATE
command=(
nix-instantiate
--expr
'let
os = import <nixpkgs/nixos> {
system = "${local.system}";
configuration =
${var.config_nix} //
# template parameters passed in from TF thru json
builtins.fromJSON "${replace(jsonencode(var.config_tf), "\"", "\\\"")}" //
{
# nix path for debugging
nix.nixPath = [ "${local.nix_path}" ];
};
};
in
# info we want to get back out
{
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 | jaq -r 'to_entries | map("\(.key)=\(.value)") | @sh')"
host="root@${var.hostname}.${var.vm_domain}" # FIXME: #24
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"
EOF
}
}

View file

@ -0,0 +1,17 @@
variable "vm_domain" {
type = string
}
variable "hostname" {
type = string
}
variable "config_nix" {
type = string
default = "{}"
}
variable "config_tf" {
type = map(any)
default = {}
}

35
infra/tests.nix Normal file
View file

@ -0,0 +1,35 @@
{ lib, pkgs }:
let
defaults = {
virtualisation = {
memorySize = 2048;
cores = 2;
};
};
tf = pkgs.callPackage ./tf.nix {
inherit lib pkgs;
};
tfEnv = pkgs.callPackage ./tf-env.nix { };
nodes.server = {
environment.systemPackages = [
tf
tfEnv
];
};
in
lib.mapAttrs (name: test: pkgs.testers.runNixOSTest (test // { inherit name; })) {
tf-validate-dev = {
inherit defaults nodes;
testScript = ''
server.wait_for_unit("multi-user.target")
server.succeed("${lib.getExe tf} -chdir='${tfEnv}/infra/dev' validate")
'';
};
tf-validate-operator = {
inherit defaults nodes;
testScript = ''
server.wait_for_unit("multi-user.target")
server.succeed("${lib.getExe tf} -chdir='${tfEnv}/infra/operator' validate")
'';
};
}

32
infra/tf-env.nix Normal file
View file

@ -0,0 +1,32 @@
{
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 = [
(import ./tf.nix { inherit lib pkgs; })
(import ./setup.nix { inherit lib pkgs sources; })
];
buildPhase = ''
runHook preBuild
pushd infra
setup
popd
runHook postBuild
'';
installPhase = ''
runHook preInstall
cp -r . $out
runHook postInstall
'';
}

24
infra/tf.nix Normal file
View file

@ -0,0 +1,24 @@
# FIXME: use overlays so this gets imported just once?
{
lib,
pkgs,
...
}:
let
tofuProvider =
provider:
provider.override (oldArgs: {
provider-source-address =
lib.replaceStrings [ "https://registry.terraform.io/providers" ] [ "registry.opentofu.org" ]
oldArgs.homepage;
});
tf = pkgs.opentofu;
tfPlugins = (
p: [
p.external
]
);
in
# tf.withPlugins tfPlugins
# https://github.com/NixOS/nixpkgs/pull/358522
tf.withPlugins (p: pkgs.lib.lists.map tofuProvider (tfPlugins p))

View file

@ -14,7 +14,7 @@ overwrite a secret without knowing its contents.)
In infra management, the systems' keys are used for security reasons; they In infra management, the systems' keys are used for security reasons; they
identify the machine that we are talking to. The contributor keys are used to identify the machine that we are talking to. The contributor keys are used to
give access to the `root` user on these machines, which allows, among other give access to the `root` user on these machines, which allows, among other
things, to deploy their configurations with NixOps4. things, to deploy their configurations.
## Adding a contributor ## Adding a contributor

1
keys/cd-ssh-key.pub Normal file
View file

@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMlsYTtMx3hFO8B5B8iHaXL2JKj9izHeC+/AMhIWXBPs cd-age

View file

@ -35,4 +35,5 @@ in
contributors = collectKeys ./contributors; contributors = collectKeys ./contributors;
systems = collectKeys ./systems; systems = collectKeys ./systems;
panel = removeTrailingWhitespace (readFile ./panel-ssh-key.pub); panel = removeTrailingWhitespace (readFile ./panel-ssh-key.pub);
cd = removeTrailingWhitespace (readFile ./cd-ssh-key.pub);
} }

5
keys/flake-part.nix Normal file
View file

@ -0,0 +1,5 @@
{
_class = "flake";
_module.args.keys = import ./.;
}

View file

@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIFXQW5fxJoNY9wtTMsNExgbAbvyljIRGBLjY+USh/0A

4
machines/README.md Normal file
View file

@ -0,0 +1,4 @@
# Machines
This directory contains the definition of [the VMs](machines.md) that host our
infrastructure.

View file

@ -1,7 +1,10 @@
{ {
_class = "nixops4Resource";
fediversityVm = { fediversityVm = {
name = "fedi200";
isFediversityVm = true;
vmId = 200; vmId = 200;
proxmox = "fediversity";
description = "Testing machine for Hans"; description = "Testing machine for Hans";
domain = "abundos.eu"; domain = "abundos.eu";

View file

@ -0,0 +1,2 @@
_: {
}

View file

@ -1,7 +1,10 @@
{ {
_class = "nixops4Resource";
fediversityVm = { fediversityVm = {
name = "fedi201";
isFediversityVm = true;
vmId = 201; vmId = 201;
proxmox = "fediversity";
description = "FediPanel"; description = "FediPanel";
domain = "abundos.eu"; domain = "abundos.eu";
@ -14,10 +17,4 @@
gateway = "2a00:51c0:13:1305::1"; gateway = "2a00:51c0:13:1305::1";
}; };
}; };
nixos.module = {
imports = [
./fedipanel.nix
];
};
} }

View file

@ -6,13 +6,15 @@ let
name = "panel"; name = "panel";
in in
{ {
_class = "nixos";
imports = [ imports = [
<home-manager/nixos>
(import ../../../panel { }).module (import ../../../panel { }).module
]; ];
security.acme = { security.acme = {
acceptTerms = true; acceptTerms = true;
defaults.email = "beheer@procolix.com";
}; };
age.secrets.panel-ssh-key = { age.secrets.panel-ssh-key = {
@ -37,6 +39,24 @@ in
enable = true; enable = true;
production = true; production = true;
domain = "demo.fediversity.eu"; domain = "demo.fediversity.eu";
# FIXME: make it work without this duplication
settings =
let
cfg = config.services.${name};
in
{
STATIC_ROOT = "/var/lib/${name}/static";
DEBUG = false;
ALLOWED_HOSTS = [
cfg.domain
cfg.host
"localhost"
"[::1]"
];
CSRF_TRUSTED_ORIGINS = [ "https://${cfg.domain}" ];
COMPRESS_OFFLINE = true;
LIBSASS_OUTPUT_STYLE = "compressed";
};
secrets = { secrets = {
SECRET_KEY = config.age.secrets.panel-secret-key.path; SECRET_KEY = config.age.secrets.panel-secret-key.path;
}; };

View file

@ -0,0 +1,72 @@
{ lib, ... }:
let
inherit (lib) mkDefault mkForce;
in
{
_class = "nixops4Resource";
# NOTE: This needs an SSH config entry `forgejo-ci` to locate and access the
# machine. This is because different people access the machine in different
# way (eg. via a proxy vs. via Procolix's VPN). This might look like:
#
# Host forgejo-ci
# HostName 45.142.234.216
# HostKeyAlias forgejo-ci
#
# The `HostKeyAlias` statement is crucial. Without it, deployment will fail
# with the SSH error “Host key verification failed”.
ssh.host = mkForce "forgejo-ci";
fediversityVm = {
name = "forgejo-ci";
domain = "procolix.com";
isFediversityVm = false;
ipv4 = {
interface = "enp1s0f0";
address = "192.168.201.65";
prefixLength = 24;
gateway = "192.168.201.1";
};
ipv6.enable = false;
};
nixos.module =
{ config, ... }:
{
_class = "nixos";
imports = [
./forgejo-actions-runner.nix
];
hardware.cpu.intel.updateMicrocode = mkDefault config.hardware.enableRedistributableFirmware;
networking = {
nftables.enable = mkForce false;
hostId = "1d6ea552";
};
## NOTE: This is a physical machine, so is not covered by disko
fileSystems."/" = lib.mkForce {
device = "rpool/root";
fsType = "zfs";
};
fileSystems."/home" = {
device = "rpool/home";
fsType = "zfs";
};
fileSystems."/boot" = lib.mkForce {
device = "/dev/disk/by-uuid/50B2-DD3F";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
};
}

View file

@ -0,0 +1,47 @@
{ pkgs, config, ... }:
{
_class = "nixos";
services.gitea-actions-runner = {
package = pkgs.forgejo-actions-runner;
instances.default = {
enable = true;
name = config.networking.fqdn;
url = "https://git.fediversity.eu";
tokenFile = config.age.secrets.forgejo-runner-token.path;
settings = {
log.level = "info";
runner = {
file = ".runner";
# Take only 1 job at a time to avoid clashing NixOS tests, see #362
capacity = 1;
timeout = "3h";
insecure = false;
fetch_timeout = "5s";
fetch_interval = "2s";
};
};
## This runner supports Docker (with a default Ubuntu image) and native
## modes. In native mode, it contains a few default packages.
labels = [
"docker:docker://node:16-bullseye"
"native:host"
];
hostPackages = with pkgs; [
bash
git
nix
nodejs
];
};
};
## For the Docker mode of the runner.
virtualisation.docker.enable = true;
}

View file

@ -0,0 +1,38 @@
{ lib, ... }:
{
_class = "nixops4Resource";
fediversityVm = {
name = "vm02116";
isFediversityVm = false;
vmId = 2116;
description = "Forgejo";
ipv4.address = "185.206.232.34";
ipv6.address = "2a00:51c0:12:1201::20";
};
imports = [
./forgejo.nix
];
## vm02116 is running on old hardware based on a Xen VM environment, so it
## needs these extra options. Once the VM gets moved to a newer node, these
## two options can safely be removed.
boot.initrd.availableKernelModules = [ "xen_blkfront" ];
services.xe-guest-utilities.enable = true;
## NOTE: This VM was created manually, which requires us to override the
## default disko-based `fileSystems` definition.
fileSystems = lib.mkForce {
"/" = {
device = "/dev/disk/by-uuid/3802a66d-e31a-4650-86f3-b51b11918853";
fsType = "ext4";
};
"/boot" = {
device = "/dev/disk/by-uuid/2CE2-1173";
fsType = "vfat";
};
};
}

View file

@ -5,6 +5,8 @@ let
in in
{ {
_class = "nixos";
services.forgejo = { services.forgejo = {
enable = true; enable = true;

View file

@ -0,0 +1,36 @@
{ lib, ... }:
{
_class = "nixops4Resource";
fediversityVm = {
name = "vm02187";
isFediversityVm = false;
vmId = 2187;
description = "Wiki";
ipv4.address = "185.206.232.187";
ipv6.address = "2a00:51c0:12:1201::187";
};
imports = [
./wiki.nix
];
## NOTE: This VM was created manually, which requires us to override the
## default disko-based `fileSystems` definition.
fileSystems = lib.mkForce {
"/" = {
device = "/dev/disk/by-uuid/a46a9c46-e32b-4216-a4aa-8819b2cd0d49";
fsType = "ext4";
};
"/boot" = {
device = "/dev/disk/by-uuid/6AB5-4FA8";
fsType = "vfat";
options = [
"fmask=0022"
"dmask=0022"
];
};
};
}

View file

@ -1,6 +1,8 @@
{ config, ... }: { config, ... }:
{ {
_class = "nixos";
services.phpfpm.pools.mediawiki.phpOptions = '' services.phpfpm.pools.mediawiki.phpOptions = ''
upload_max_filesize = 1024M; upload_max_filesize = 1024M;
post_max_size = 1024M; post_max_size = 1024M;

View file

@ -1,5 +1,5 @@
{ {
"domain": "abundos.eu", "domain": "fediversity.net",
"mastodon": { "enable": false }, "mastodon": { "enable": false },
"peertube": { "enable": false }, "peertube": { "enable": false },
"pixelfed": { "enable": false }, "pixelfed": { "enable": false },

View file

@ -1,7 +1,10 @@
{ {
_class = "nixops4Resource";
fediversityVm = { fediversityVm = {
name = "test01";
isFediversityVm = true;
vmId = 7001; vmId = 7001;
proxmox = "fediversity";
hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub; hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub;
unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key; unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key;

View file

@ -0,0 +1,34 @@
{ pkgs, ... }:
let
## NOTE: All of these secrets are publicly available in this source file
## and will end up in the Nix store. We don't care as they are only ever
## used for testing anyway.
##
## FIXME: Generate and store in state.
mastodonS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK3515373e4c851ebaad366558";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7d37d093435a41f2aab8f13c19ba067d9776c90215f56614adad6ece597dbb34";
};
peertubeS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK1f9feea9960f6f95ff404c9b";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7295c4201966a02c2c3d25b5cea4a5ff782966a2415e3a196f91924631191395";
};
pixelfedS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GKb5615457d44214411e673b7b";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "5be6799a88ca9b9d813d1a806b64f15efa49482dbe15339ddfaf7f19cf434987";
};
in
{
fediversity = {
garage.enable = true;
pixelfed = pixelfedS3KeyConfig { inherit pkgs; };
mastodon = mastodonS3KeyConfig { inherit pkgs; };
peertube = peertubeS3KeyConfig { inherit pkgs; };
};
}

View file

@ -1,7 +1,10 @@
{ {
_class = "nixops4Resource";
fediversityVm = { fediversityVm = {
name = "test02";
isFediversityVm = true;
vmId = 7002; vmId = 7002;
proxmox = "fediversity";
hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub; hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub;
unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key; unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key;

View file

@ -1,7 +1,10 @@
{ {
_class = "nixops4Resource";
fediversityVm = { fediversityVm = {
name = "test03";
isFediversityVm = true;
vmId = 7003; vmId = 7003;
proxmox = "fediversity";
hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub; hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub;
unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key; unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key;

Some files were not shown because too many files have changed in this diff Show more