forked from fediversity/fediversity
Compare commits
97 commits
ef4876239d
...
79f432f398
| Author | SHA1 | Date | |
|---|---|---|---|
| 79f432f398 | |||
| d86086d53a | |||
| bf4df5500a | |||
| 58c1999fd2 | |||
| 1f99a4c6c3 | |||
| 588bb77a94 | |||
| df3a070fa4 | |||
| be72b82875 | |||
| 1b66028f32 | |||
| 4509d277d3 | |||
| e488230d7b | |||
| 765183cd0d | |||
| 6cf1d87f0b | |||
| 8253288f8a | |||
| 67f50f08de | |||
| 5402178e7b | |||
| e627815399 | |||
| 354dba260a | |||
| b791bd515d | |||
| f2017aaeb4 | |||
| 980a994f83 | |||
| b9b13df04e | |||
| 159e4107b8 | |||
| 86305a6a2e | |||
| e62f14d9be | |||
| 82f83eea0d | |||
| aef414ffe8 | |||
| 6d74112518 | |||
| 2b2fb059fd | |||
| 66ceb66382 | |||
| ad9c61a3db | |||
| b4e1c5b5b3 | |||
| de38611572 | |||
| 1d40dcfc0e | |||
| c3bf158130 | |||
| 48c6a1f22b | |||
| 8a7984933d | |||
| 5520fa721b | |||
| eabfc228c5 | |||
| 3f923532a2 | |||
| 37d4fc5a42 | |||
| c7b05bb473 | |||
| 5345860006 | |||
| fbb0806072 | |||
| b0848727fd | |||
| a882de0b8e | |||
| 05572ff69e | |||
| 13c92280ab | |||
| 871672d447 | |||
| 6da42936e7 | |||
| 8df70a2ff0 | |||
| 5a92c2c0bc | |||
| 1c92009879 | |||
| a791ad41ec | |||
| c1dc0fef01 | |||
| 5a3cbe4d83 | |||
| fd1d55df5f | |||
| 0c23115cff | |||
| 3f1c8a9bb7 | |||
| 737aecaba6 | |||
| d7dbdd923c | |||
| 1c44004cfe | |||
| ae444d5352 | |||
| e77fdd9eec | |||
| 1f1cf0d516 | |||
| f94eac698a | |||
| 46182e7512 | |||
| 9a25a04bfa | |||
| c1b33121b6 | |||
| d073bd706d | |||
| 486b316885 | |||
| 611c961dcf | |||
| d67f533948 | |||
| bd1cfd7a7c | |||
| 939f9d961d | |||
| 4801433ae0 | |||
| 3a3a083793 | |||
| ace56e754e | |||
| dbb4ce67fc | |||
| 5a514b96e9 | |||
| 1b832c1f5b | |||
| 69b2e535fe | |||
| 09119803e8 | |||
| 4dd1491e71 | |||
| 2f55e1512a | |||
| b59f8a4183 | |||
| 56b953526b | |||
| 1f8677e83d | |||
| 2fae356d0a | |||
| 046f7c5998 | |||
| 69579fea1c | |||
| afc7ad2b88 | |||
| ac66b9d3c6 | |||
| ee5c2b90b7 | |||
| 5f66a034f3 | |||
| df9ce821de | |||
| f4ebf55a15 |
125 changed files with 3700 additions and 446 deletions
24
.forgejo/workflows/cd.yaml
Normal file
24
.forgejo/workflows/cd.yaml
Normal 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'
|
||||
|
|
@ -15,17 +15,29 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
- run: nix-build -A tests
|
||||
|
||||
check-data-model:
|
||||
runs-on: native
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- 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: cd services && nix-build -A tests.peertube
|
||||
- run: nix build .#checks.x86_64-linux.test-peertube-service -L
|
||||
|
||||
check-panel:
|
||||
runs-on: native
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: cd panel && nix-build -A tests
|
||||
- run: nix-build -A tests.panel
|
||||
|
||||
check-deployment-basic:
|
||||
runs-on: native
|
||||
|
|
@ -33,6 +45,50 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
- 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:
|
||||
|
|
|
|||
24
.forgejo/workflows/update.yaml
Normal file
24
.forgejo/workflows/update.yaml
Normal 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"
|
||||
30
README.md
30
README.md
|
|
@ -13,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.
|
||||
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).
|
||||
|
||||

|
||||
|
||||
## Actors
|
||||
|
||||
- Fediversity project team
|
||||
|
|
@ -56,11 +62,11 @@ Not everyone has the expertise and time to run their own server.
|
|||
|
||||
- [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
|
||||
|
||||
|
|
@ -72,11 +78,11 @@ Not everyone has the expertise and time to run their own server.
|
|||
|
||||
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
|
||||
|
||||
Put software, such as services, onto computers.
|
||||
Put software, such as applications, onto computers.
|
||||
The software includes technical configuration that links software components.
|
||||
Most user-facing configuration remains untouched by the deployment process.
|
||||
|
||||
|
|
@ -84,12 +90,24 @@ Not everyone has the expertise and time to run their own server.
|
|||
|
||||
- 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.
|
||||
|
||||
- [OpenTofu](https://opentofu.org/)
|
||||
|
||||
An infrastructure-as-code tool, and open-source (MPL 2.0) fork of Terraform.
|
||||
|
||||
- Runtime backend
|
||||
|
||||
A type of digital environment one can run operating systems such as NixOS on, e.g. bare-metal, a hypervisor, or a container runtime.
|
||||
|
||||
- Runtime environment
|
||||
|
||||
The thing a deployment runs on, an interface against which the deployment is working. See runtime backend.
|
||||
|
||||
- Runtime config
|
||||
|
||||
Configuration logic specific to a runtime backend, e.g. how to deploy, how to access object storage.
|
||||
|
||||
## Development
|
||||
|
||||
All the code made for this project is freely licenced under [EUPL](https://en.m.wikipedia.org/wiki/European_Union_Public_Licence).
|
||||
|
|
|
|||
37
default.nix
37
default.nix
|
|
@ -4,8 +4,15 @@
|
|||
pkgs ? import sources.nixpkgs { inherit system; },
|
||||
}:
|
||||
let
|
||||
inherit (sources) nixpkgs git-hooks gitignore;
|
||||
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;
|
||||
|
|
@ -20,7 +27,7 @@ let
|
|||
## Add a directory here if pre-commit hooks shouldn't apply to it.
|
||||
optout = [
|
||||
"npins"
|
||||
".terraform"
|
||||
"**/.terraform"
|
||||
];
|
||||
excludes = map (dir: "^${dir}/") optout;
|
||||
addExcludes = lib.mapAttrs (_: c: c // { inherit excludes; });
|
||||
|
|
@ -38,13 +45,35 @@ in
|
|||
shell = pkgs.mkShellNoCC {
|
||||
inherit (pre-commit-check) shellHook;
|
||||
buildInputs = pre-commit-check.enabledPackages;
|
||||
packages = [
|
||||
pkgs.nixfmt-rfc-style
|
||||
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
|
||||
|
|
|
|||
123
deployment/README.md
Normal file
123
deployment/README.md
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
# Deployment
|
||||
|
||||
This directory contains work to generate a full Fediversity deployment from a minimal configuration.
|
||||
This is different from [`../services/`](../services) that 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.
|
||||
|
|
@ -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
|
||||
8
deployment/check/basic/constants.nix
Normal file
8
deployment/check/basic/constants.nix
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
targetMachines = [
|
||||
"hello"
|
||||
"cowsay"
|
||||
];
|
||||
pathToRoot = ../../..;
|
||||
pathFromRoot = ./.;
|
||||
}
|
||||
14
deployment/check/basic/default.nix
Normal file
14
deployment/check/basic/default.nix
Normal 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;
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
## This file is just a placeholder. It is overwritten by the test.
|
||||
|
|
@ -1,32 +1,36 @@
|
|||
{
|
||||
inputs,
|
||||
sources,
|
||||
lib,
|
||||
providers,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (import ./constants.nix) targetMachines pathToRoot pathFromRoot;
|
||||
in
|
||||
|
||||
{
|
||||
providers.local = inputs.nixops4.modules.nixops4Provider.local;
|
||||
|
||||
resources.target = {
|
||||
type = providers.local.exec;
|
||||
imports = [ inputs.nixops4-nixos.modules.nixops4Resource.nixos ];
|
||||
|
||||
ssh = {
|
||||
host = "target";
|
||||
hostPublicKey = builtins.readFile ./target_host_key.pub;
|
||||
providers = {
|
||||
inherit (inputs.nixops4.modules.nixops4Provider) local;
|
||||
};
|
||||
|
||||
nixpkgs = inputs.nixpkgs;
|
||||
resources = lib.genAttrs targetMachines (nodeName: {
|
||||
type = providers.local.exec;
|
||||
|
||||
imports = [
|
||||
inputs.nixops4-nixos.modules.nixops4Resource.nixos
|
||||
../common/targetResource.nix
|
||||
];
|
||||
|
||||
_module.args = { inherit inputs sources; };
|
||||
|
||||
inherit nodeName pathToRoot pathFromRoot;
|
||||
|
||||
nixos.module =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
imports = [
|
||||
./minimalTarget.nix
|
||||
(lib.modules.importJSON ./target-network.json)
|
||||
];
|
||||
nixpkgs.hostPlatform = "x86_64-linux";
|
||||
environment.systemPackages = [ pkgs.cowsay ];
|
||||
};
|
||||
environment.systemPackages = [ pkgs.${nodeName} ];
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
|||
22
deployment/check/basic/flake-under-test.nix
Normal file
22
deployment/check/basic/flake-under-test.nix
Normal 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; };
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -1,46 +1,28 @@
|
|||
{
|
||||
testers,
|
||||
inputs,
|
||||
lib,
|
||||
sources ? import ../npins,
|
||||
...
|
||||
}:
|
||||
{
|
||||
_class = "nixosTest";
|
||||
|
||||
testers.runNixOSTest (
|
||||
{
|
||||
lib,
|
||||
config,
|
||||
hostPkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
pathToRoot = ../../..;
|
||||
pathFromRoot = "deployment/check/basic";
|
||||
|
||||
## TODO: sanity check the existence of (pathToRoot + "/flake.nix")
|
||||
## TODO: sanity check that (pathToRoot + "/${pathFromRoot}" == ./.)
|
||||
|
||||
## The whole repository, with the flake at its root.
|
||||
src = lib.fileset.toSource {
|
||||
fileset = pathToRoot;
|
||||
root = pathToRoot;
|
||||
};
|
||||
|
||||
targetNetworkJSON = hostPkgs.writeText "target-network.json" (
|
||||
builtins.toJSON config.nodes.target.system.build.networkConfig
|
||||
);
|
||||
|
||||
in
|
||||
{
|
||||
name = "deployment-basic";
|
||||
# imports = [
|
||||
# ];
|
||||
|
||||
nodes = {
|
||||
deployer =
|
||||
{ pkgs, nodes, ... }:
|
||||
sourceFileset = lib.fileset.unions [
|
||||
./constants.nix
|
||||
./deployment.nix
|
||||
];
|
||||
|
||||
nodes.deployer =
|
||||
{ nodes, ... }:
|
||||
{
|
||||
# environment.systemPackages = [
|
||||
# ];
|
||||
system.extraDependenciesFromModule =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
environment.systemPackages = with pkgs; [
|
||||
hello
|
||||
cowsay
|
||||
];
|
||||
|
||||
virtualisation = {
|
||||
## Memory use is expected to be dominated by the NixOS evaluation,
|
||||
|
|
@ -57,7 +39,10 @@ testers.runNixOSTest (
|
|||
};
|
||||
|
||||
system.extraDependencies =
|
||||
with pkgs;
|
||||
[
|
||||
jq
|
||||
jq.inputDerivation
|
||||
sources.nixpkgs
|
||||
|
||||
pkgs.stdenv
|
||||
|
|
@ -82,44 +67,9 @@ testers.runNixOSTest (
|
|||
) nodes.target.environment.etc
|
||||
);
|
||||
};
|
||||
|
||||
target.imports = [ ./minimalTarget.nix ];
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
target.wait_for_unit("multi-user.target")
|
||||
deployer.wait_for_unit("multi-user.target")
|
||||
|
||||
with subtest("Unpacking"):
|
||||
deployer.succeed("cp -r --no-preserve=mode ${src} work")
|
||||
|
||||
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 nixpkgs ${inputs.nixpkgs} \
|
||||
;
|
||||
""")
|
||||
|
||||
extraTestScript = ''
|
||||
with subtest("Check the status before deployment"):
|
||||
target.fail("cowsay hi 1>&2")
|
||||
|
||||
|
|
@ -129,5 +79,4 @@ testers.runNixOSTest (
|
|||
with subtest("Check the deployment"):
|
||||
target.succeed("cowsay hi 1>&2")
|
||||
'';
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
{"comment": "This file is just a placeholder. It is overwritten by the test."}
|
||||
|
|
@ -1 +0,0 @@
|
|||
## This file is just a placeholder. It is overwritten by the test.
|
||||
11
deployment/check/cli/constants.nix
Normal file
11
deployment/check/cli/constants.nix
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
targetMachines = [
|
||||
"garage"
|
||||
"mastodon"
|
||||
"peertube"
|
||||
"pixelfed"
|
||||
];
|
||||
pathToRoot = ../../..;
|
||||
pathFromRoot = ./.;
|
||||
enableAcme = true;
|
||||
}
|
||||
19
deployment/check/cli/default.nix
Normal file
19
deployment/check/cli/default.nix
Normal 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
|
||||
;
|
||||
}
|
||||
59
deployment/check/cli/deployments.nix
Normal file
59
deployment/check/cli/deployments.nix
Normal 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;
|
||||
};
|
||||
}
|
||||
26
deployment/check/cli/flake-under-test.nix
Normal file
26
deployment/check/cli/flake-under-test.nix
Normal 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;
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
137
deployment/check/cli/nixosTest.nix
Normal file
137
deployment/check/cli/nixosTest.nix
Normal 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")
|
||||
'';
|
||||
}
|
||||
106
deployment/check/common/deployerNode.nix
Normal file
106
deployment/check/common/deployerNode.nix
Normal 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
|
||||
)
|
||||
);
|
||||
};
|
||||
}
|
||||
201
deployment/check/common/nixosTest.nix
Normal file
201
deployment/check/common/nixosTest.nix
Normal 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}
|
||||
'';
|
||||
};
|
||||
}
|
||||
68
deployment/check/common/sharedOptions.nix
Normal file
68
deployment/check/common/sharedOptions.nix
Normal 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.
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
67
deployment/check/common/targetNode.nix
Normal file
67
deployment/check/common/targetNode.nix
Normal 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";
|
||||
})
|
||||
];
|
||||
}
|
||||
51
deployment/check/common/targetResource.nix
Normal file
51
deployment/check/common/targetResource.nix
Normal 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";
|
||||
};
|
||||
};
|
||||
}
|
||||
11
deployment/check/panel/constants.nix
Normal file
11
deployment/check/panel/constants.nix
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
targetMachines = [
|
||||
"garage"
|
||||
"mastodon"
|
||||
"peertube"
|
||||
"pixelfed"
|
||||
];
|
||||
pathToRoot = ../../..;
|
||||
pathFromRoot = ./.;
|
||||
enableAcme = true;
|
||||
}
|
||||
19
deployment/check/panel/default.nix
Normal file
19
deployment/check/panel/default.nix
Normal 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
|
||||
;
|
||||
}
|
||||
58
deployment/check/panel/deployment.nix
Normal file
58
deployment/check/panel/deployment.nix
Normal 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
|
||||
)
|
||||
)
|
||||
26
deployment/check/panel/flake-under-test.nix
Normal file
26
deployment/check/panel/flake-under-test.nix
Normal 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;
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
377
deployment/check/panel/nixosTest.nix
Normal file
377
deployment/check/panel/nixosTest.nix
Normal 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")
|
||||
'';
|
||||
}
|
||||
12
deployment/configuration.sample.json
Normal file
12
deployment/configuration.sample.json
Normal 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"
|
||||
}
|
||||
}
|
||||
70
deployment/data-model-test.nix
Normal file
70
deployment/data-model-test.nix
Normal 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
89
deployment/data-model.nix
Normal 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;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,3 +1,221 @@
|
|||
<<<<<<< HEAD
|
||||
## `makeMakeDeployment` -- Function to help hosting providers make a
|
||||
## `makeDeployment` function.
|
||||
##
|
||||
## https://factoryfactoryfactory.net/
|
||||
|
||||
## Generic utilities used in this function, eg. nixpkgs, NixOps4 providers, etc.
|
||||
## REVIEW: We should maybe be more specific than just `inputs`.
|
||||
{
|
||||
lib,
|
||||
nixops4,
|
||||
nixops4-nixos,
|
||||
fediversity,
|
||||
}:
|
||||
|
||||
## Information on the hosting provider's infrastructure. This is where we inform
|
||||
## this function of where it can find eg. Proxmox.
|
||||
{
|
||||
## Four NixOS configuration resource modules for four services. Those are VMs
|
||||
## that are already deployed and on which we will push our configurations.
|
||||
##
|
||||
## - Ultimately, we just want a pool of VMs, or even just a Proxmox.
|
||||
## - Each machine is flagged for a certain use case until we control DNS.
|
||||
garageConfigurationResource,
|
||||
mastodonConfigurationResource,
|
||||
peertubeConfigurationResource,
|
||||
pixelfedConfigurationResource,
|
||||
}:
|
||||
|
||||
## From the hosting provider's perspective, the function is meant to be
|
||||
## partially applied only until here.
|
||||
|
||||
## Information on the specific deployment that we request. This is the
|
||||
## information coming from the FediPanel.
|
||||
##
|
||||
## FIXME: lock step the interface with the definitions in the FediPanel
|
||||
panelConfigNullable:
|
||||
|
||||
let
|
||||
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
|
||||
|
||||
## Regular arguments of a NixOps4 deployment module.
|
||||
{ config, providers, ... }:
|
||||
|
||||
let
|
||||
cfg = config.deployment;
|
||||
in
|
||||
{
|
||||
_class = "nixops4Deployment";
|
||||
|
||||
options = {
|
||||
deployment = lib.mkOption {
|
||||
description = ''
|
||||
Configuration to be deployed
|
||||
'';
|
||||
# XXX(@fricklerhandwerk):
|
||||
# misusing this will produce obscure errors that will be truncated by NixOps4
|
||||
type = lib.types.submodule ./options.nix;
|
||||
default = panelConfig;
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
providers = { inherit (nixops4.modules.nixops4Provider) local; };
|
||||
|
||||
resources =
|
||||
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 NixOps4's 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";
|
||||
};
|
||||
|
||||
makeConfigurationResource = resourceModule: config: {
|
||||
type = providers.local.exec;
|
||||
imports = [
|
||||
nixops4-nixos.modules.nixops4Resource.nixos
|
||||
resourceModule
|
||||
|
||||
{
|
||||
## NOTE: With NixOps4, there are several levels and all of them live
|
||||
## in the NixOS module system:
|
||||
##
|
||||
## 1. Each NixOps4 deployment is a module.
|
||||
## 2. Each NixOps4 resource is a module. This very comment is
|
||||
## inside an attrset imported as a module in a resource.
|
||||
## 3. Each NixOps4 'configuration' resource contains an attribute
|
||||
## 'nixos.module', itself a NixOS configuration module.
|
||||
nixos.module =
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
config
|
||||
fediversity
|
||||
];
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
garage-configuration = makeConfigurationResource garageConfigurationResource (
|
||||
{ pkgs, ... }:
|
||||
mkIf (cfg.mastodon.enable || cfg.peertube.enable || cfg.pixelfed.enable) {
|
||||
fediversity = {
|
||||
inherit (cfg) domain;
|
||||
garage.enable = true;
|
||||
pixelfed = pixelfedS3KeyConfig { inherit pkgs; };
|
||||
mastodon = mastodonS3KeyConfig { inherit pkgs; };
|
||||
peertube = peertubeS3KeyConfig { inherit pkgs; };
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
mastodon-configuration = makeConfigurationResource mastodonConfigurationResource (
|
||||
{ pkgs, ... }:
|
||||
mkIf cfg.mastodon.enable {
|
||||
fediversity = {
|
||||
inherit (cfg) domain;
|
||||
temp.initialUser = {
|
||||
inherit (cfg.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" cfg.initialUser.password;
|
||||
};
|
||||
|
||||
mastodon = mastodonS3KeyConfig { inherit pkgs; } // {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
temp.cores = 1; # FIXME: should come from NixOps4 eventually
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
peertube-configuration = makeConfigurationResource peertubeConfigurationResource (
|
||||
{ pkgs, ... }:
|
||||
mkIf cfg.peertube.enable {
|
||||
fediversity = {
|
||||
inherit (cfg) domain;
|
||||
temp.initialUser = {
|
||||
inherit (cfg.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" cfg.initialUser.password;
|
||||
};
|
||||
|
||||
peertube = peertubeS3KeyConfig { inherit pkgs; } // {
|
||||
enable = true;
|
||||
## NOTE: Only ever used for testing anyway.
|
||||
##
|
||||
## FIXME: Generate and store in NixOps4's state.
|
||||
secretsFile = pkgs.writeText "secret" "574e093907d1157ac0f8e760a6deb1035402003af5763135bae9cbd6abe32b24";
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
pixelfed-configuration = makeConfigurationResource pixelfedConfigurationResource (
|
||||
{ pkgs, ... }:
|
||||
mkIf cfg.pixelfed.enable {
|
||||
fediversity = {
|
||||
inherit (cfg) domain;
|
||||
temp.initialUser = {
|
||||
inherit (cfg.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" cfg.initialUser.password;
|
||||
};
|
||||
|
||||
pixelfed = pixelfedS3KeyConfig { inherit pkgs; } // {
|
||||
enable = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
=======
|
||||
{
|
||||
system ? builtins.currentSystem,
|
||||
sources ? import ../npins,
|
||||
|
|
@ -8,5 +226,6 @@
|
|||
{
|
||||
tests = {
|
||||
deployment-basic = import ./check/basic/nixosTest.nix { inherit pkgs; };
|
||||
>>>>>>> fac372b3 (strip test as a start to convert it)
|
||||
};
|
||||
}
|
||||
|
|
|
|||
26
deployment/flake-part.nix
Normal file
26
deployment/flake-part.nix
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{ inputs, sources, ... }:
|
||||
|
||||
{
|
||||
_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
37
deployment/function.nix
Normal 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;
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -17,6 +17,8 @@ let
|
|||
inherit (lib) types mkOption;
|
||||
in
|
||||
{
|
||||
_class = "nixops4Deployment";
|
||||
|
||||
options = {
|
||||
enable = lib.mkEnableOption "Fediversity configuration";
|
||||
domain = mkOption {
|
||||
|
|
|
|||
660
flake.lock
generated
Normal file
660
flake.lock
generated
Normal file
|
|
@ -0,0 +1,660 @@
|
|||
{
|
||||
"nodes": {
|
||||
"crane": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1727316705,
|
||||
"narHash": "sha256-/mumx8AQ5xFuCJqxCIOFCHTVlxHkMT21idpbgbm/TIE=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "5b03654ce046b5167e7b0bccbd8244cb56c16f0e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "ipetkov",
|
||||
"ref": "v0.19.0",
|
||||
"repo": "crane",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"dream2nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixops4-nixos",
|
||||
"nixops4",
|
||||
"nix-cargo-integration",
|
||||
"nixpkgs"
|
||||
],
|
||||
"purescript-overlay": "purescript-overlay",
|
||||
"pyproject-nix": "pyproject-nix"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1735160684,
|
||||
"narHash": "sha256-n5CwhmqKxifuD4Sq4WuRP/h5LO6f23cGnSAuJemnd/4=",
|
||||
"owner": "nix-community",
|
||||
"repo": "dream2nix",
|
||||
"rev": "8ce6284ff58208ed8961681276f82c2f8f978ef4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "dream2nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"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_2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1733328505,
|
||||
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat_3": {
|
||||
"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-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"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_2": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib_2"
|
||||
},
|
||||
"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_3": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"nixops4-nixos",
|
||||
"nixops4",
|
||||
"nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1733312601,
|
||||
"narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"git-hooks-nix": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"gitignore": "gitignore",
|
||||
"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_2": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"nixops4-nixos",
|
||||
"nixops4",
|
||||
"nix"
|
||||
],
|
||||
"gitignore": [
|
||||
"nixops4-nixos",
|
||||
"nixops4",
|
||||
"nix"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixops4-nixos",
|
||||
"nixops4",
|
||||
"nix",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-stable": [
|
||||
"nixops4-nixos",
|
||||
"nixops4",
|
||||
"nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1734279981,
|
||||
"narHash": "sha256-NdaCraHPp8iYMWzdXAt5Nv6sA3MUzlCiGiR586TCwo0=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "aa9f40c906904ebd83da78e7f328cd8aeaeae785",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixops4-nixos",
|
||||
"git-hooks-nix",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"mk-naked-shell": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1681286841,
|
||||
"narHash": "sha256-3XlJrwlR0nBiREnuogoa5i1b4+w/XPe0z8bbrJASw0g=",
|
||||
"owner": "yusdacra",
|
||||
"repo": "mk-naked-shell",
|
||||
"rev": "7612f828dd6f22b7fb332cc69440e839d7ffe6bd",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "yusdacra",
|
||||
"repo": "mk-naked-shell",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_2",
|
||||
"flake-parts": "flake-parts_3",
|
||||
"git-hooks-nix": "git-hooks-nix_2",
|
||||
"nixfmt": "nixfmt",
|
||||
"nixpkgs": [
|
||||
"nixops4-nixos",
|
||||
"nixops4",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-23-11": "nixpkgs-23-11",
|
||||
"nixpkgs-regression": "nixpkgs-regression"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1738382221,
|
||||
"narHash": "sha256-3+qJBts5RTAtjCExo6bkqrttL+skpYZzPOVEzPSwVtc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nix",
|
||||
"rev": "d949c8de7c7e84bb7537dc772609686c37033a3b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "master",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-cargo-integration": {
|
||||
"inputs": {
|
||||
"crane": "crane",
|
||||
"dream2nix": "dream2nix",
|
||||
"mk-naked-shell": "mk-naked-shell",
|
||||
"nixpkgs": [
|
||||
"nixops4-nixos",
|
||||
"nixops4",
|
||||
"nixpkgs"
|
||||
],
|
||||
"parts": "parts",
|
||||
"rust-overlay": "rust-overlay",
|
||||
"treefmt": "treefmt"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1738390452,
|
||||
"narHash": "sha256-o8kg4q1V2xV9ZlszAnizXJK2c+fbhAeLbGoTUa2u1Bw=",
|
||||
"owner": "yusdacra",
|
||||
"repo": "nix-cargo-integration",
|
||||
"rev": "718d577808e3e31f445b6564d58b755294e72f42",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "yusdacra",
|
||||
"repo": "nix-cargo-integration",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixfmt": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1736283758,
|
||||
"narHash": "sha256-hrKhUp2V2fk/dvzTTHFqvtOg000G1e+jyIam+D4XqhA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixfmt",
|
||||
"rev": "8d4bd690c247004d90d8554f0b746b1231fe2436",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixfmt",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixops4": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts_2",
|
||||
"nix": "nix",
|
||||
"nix-cargo-integration": "nix-cargo-integration",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"nixpkgs-old": "nixpkgs-old"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1739444468,
|
||||
"narHash": "sha256-brg21neEI7pUnSRksOx9IE8/Kcr8OmEg4NIxL0sSy+U=",
|
||||
"owner": "nixops4",
|
||||
"repo": "nixops4",
|
||||
"rev": "fb601ee79d8c9e3e7aca98dd2334ea91da3ea7e0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixops4",
|
||||
"repo": "nixops4",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixops4-nixos": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts",
|
||||
"git-hooks-nix": "git-hooks-nix",
|
||||
"nixops4": "nixops4",
|
||||
"nixops4-nixos": [
|
||||
"nixops4-nixos"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixops4-nixos",
|
||||
"nixops4",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1740082114,
|
||||
"narHash": "sha256-xNUnup8loWAMlP2+EnGX7HaO2vJlFakjvU0gTYi3F48=",
|
||||
"owner": "nixops4",
|
||||
"repo": "nixops4-nixos",
|
||||
"rev": "85ebdd8b4b182287e4c6ca0e888f2d3a4aa7453c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixops4",
|
||||
"repo": "nixops4-nixos",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"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-23-11": {
|
||||
"locked": {
|
||||
"lastModified": 1717159533,
|
||||
"narHash": "sha256-oamiKNfr2MS6yH64rUn99mIZjc45nGJlj9eGth/3Xuw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"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-lib_2": {
|
||||
"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": {
|
||||
"locked": {
|
||||
"lastModified": 1735563628,
|
||||
"narHash": "sha256-OnSAY7XDSx7CtDoqNh8jwVwh4xNL/2HaJxGjryLWzX8=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b134951a4c9f3c995fd7be05f3243f8ecd65d798",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-24.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-regression": {
|
||||
"locked": {
|
||||
"lastModified": 1643052045,
|
||||
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1738410390,
|
||||
"narHash": "sha256-xvTo0Aw0+veek7hvEVLzErmJyQkEcRk6PSR4zsRQFEc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "3a228057f5b619feb3186e986dbe76278d707b6e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"nixops4-nixos",
|
||||
"nixops4",
|
||||
"nix-cargo-integration",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1736143030,
|
||||
"narHash": "sha256-+hu54pAoLDEZT9pjHlqL9DNzWz0NbUn8NEAHP7PQPzU=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "b905f6fc23a9051a6e1b741e1438dbfc0634c6de",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"purescript-overlay": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_3",
|
||||
"nixpkgs": [
|
||||
"nixops4-nixos",
|
||||
"nixops4",
|
||||
"nix-cargo-integration",
|
||||
"dream2nix",
|
||||
"nixpkgs"
|
||||
],
|
||||
"slimlock": "slimlock"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1728546539,
|
||||
"narHash": "sha256-Sws7w0tlnjD+Bjck1nv29NjC5DbL6nH5auL9Ex9Iz2A=",
|
||||
"owner": "thomashoneyman",
|
||||
"repo": "purescript-overlay",
|
||||
"rev": "4ad4c15d07bd899d7346b331f377606631eb0ee4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "thomashoneyman",
|
||||
"repo": "purescript-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pyproject-nix": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1702448246,
|
||||
"narHash": "sha256-hFg5s/hoJFv7tDpiGvEvXP0UfFvFEDgTdyHIjDVHu1I=",
|
||||
"owner": "davhau",
|
||||
"repo": "pyproject.nix",
|
||||
"rev": "5a06a2697b228c04dd2f35659b4b659ca74f7aeb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "davhau",
|
||||
"ref": "dream2nix",
|
||||
"repo": "pyproject.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixops4": [
|
||||
"nixops4-nixos",
|
||||
"nixops4"
|
||||
],
|
||||
"nixops4-nixos": "nixops4-nixos"
|
||||
}
|
||||
},
|
||||
"rust-overlay": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixops4-nixos",
|
||||
"nixops4",
|
||||
"nix-cargo-integration",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1738376888,
|
||||
"narHash": "sha256-S6ErHxkSm0iA7ZMsjjDaASWxbELYcdfv8BhOkkj1rHw=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "83284068670d5ae4a43641c4afb150f3446be70d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"slimlock": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixops4-nixos",
|
||||
"nixops4",
|
||||
"nix-cargo-integration",
|
||||
"dream2nix",
|
||||
"purescript-overlay",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1688756706,
|
||||
"narHash": "sha256-xzkkMv3neJJJ89zo3o2ojp7nFeaZc2G0fYwNXNJRFlo=",
|
||||
"owner": "thomashoneyman",
|
||||
"repo": "slimlock",
|
||||
"rev": "cf72723f59e2340d24881fd7bf61cb113b4c407c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "thomashoneyman",
|
||||
"repo": "slimlock",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"treefmt": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixops4-nixos",
|
||||
"nixops4",
|
||||
"nix-cargo-integration",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1738070913,
|
||||
"narHash": "sha256-j6jC12vCFsTGDmY2u1H12lMr62fnclNjuCtAdF1a4Nk=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "bebf27d00f7d10ba75332a0541ac43676985dea3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
52
flake.nix
Normal file
52
flake.nix
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
inputs = {
|
||||
nixops4.follows = "nixops4-nixos/nixops4";
|
||||
nixops4-nixos.url = "github:nixops4/nixops4-nixos";
|
||||
};
|
||||
|
||||
outputs =
|
||||
inputs:
|
||||
import ./mkFlake.nix inputs (
|
||||
{ inputs, sources, ... }:
|
||||
{
|
||||
imports = [
|
||||
"${sources.git-hooks}/flake-module.nix"
|
||||
inputs.nixops4.modules.flake.default
|
||||
|
||||
./deployment/flake-part.nix
|
||||
./infra/flake-part.nix
|
||||
./keys/flake-part.nix
|
||||
./secrets/flake-part.nix
|
||||
./services/tests/flake-part.nix
|
||||
];
|
||||
|
||||
perSystem =
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
system,
|
||||
...
|
||||
}:
|
||||
{
|
||||
checks = {
|
||||
panel = (import ./. { inherit sources system; }).tests.panel.basic;
|
||||
};
|
||||
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;
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -2,10 +2,12 @@
|
|||
|
||||
let
|
||||
inherit (lib) mkDefault;
|
||||
|
||||
in
|
||||
{
|
||||
_class = "nixos";
|
||||
|
||||
imports = [
|
||||
./hardware.nix
|
||||
./networking.nix
|
||||
./users.nix
|
||||
];
|
||||
|
|
@ -21,4 +23,9 @@ in
|
|||
nix.extraOptions = ''
|
||||
experimental-features = nix-command flakes
|
||||
'';
|
||||
|
||||
boot.loader = {
|
||||
systemd-boot.enable = true;
|
||||
efi.canTouchEfiVariables = true;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,63 +1,64 @@
|
|||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
inherit (lib) mkDefault;
|
||||
inherit (lib) mkDefault mkIf mkMerge;
|
||||
|
||||
in
|
||||
{
|
||||
_class = "nixos";
|
||||
|
||||
config = {
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings.PasswordAuthentication = false;
|
||||
};
|
||||
|
||||
networking = {
|
||||
networking = mkMerge [
|
||||
{
|
||||
hostName = config.fediversityVm.name;
|
||||
domain = config.fediversityVm.domain;
|
||||
|
||||
## REVIEW: Do we actually need that, considering that we have static IPs?
|
||||
useDHCP = mkDefault true;
|
||||
|
||||
interfaces = {
|
||||
eth0 = {
|
||||
ipv4 = {
|
||||
addresses = [
|
||||
{
|
||||
inherit (config.fediversityVm.ipv4) address prefixLength;
|
||||
}
|
||||
];
|
||||
};
|
||||
ipv6 = {
|
||||
addresses = [
|
||||
{
|
||||
inherit (config.fediversityVm.ipv6) address prefixLength;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
defaultGateway = {
|
||||
address = config.fediversityVm.ipv4.gateway;
|
||||
interface = "eth0";
|
||||
};
|
||||
defaultGateway6 = {
|
||||
address = config.fediversityVm.ipv6.gateway;
|
||||
interface = "eth0";
|
||||
};
|
||||
|
||||
nameservers = [
|
||||
"95.215.185.6"
|
||||
"95.215.185.7"
|
||||
"2a00:51c0::5fd7:b906"
|
||||
"2a00:51c0::5fd7:b907"
|
||||
];
|
||||
|
||||
## Disable the default firewall and use nftables instead, with a custom
|
||||
## Procolix-made ruleset.
|
||||
firewall.enable = false;
|
||||
nftables = {
|
||||
enable = true;
|
||||
rulesetFile = ./nftables-ruleset.nft;
|
||||
};
|
||||
}
|
||||
|
||||
## IPv4
|
||||
(mkIf config.fediversityVm.ipv4.enable {
|
||||
interfaces.${config.fediversityVm.ipv4.interface}.ipv4.addresses = [
|
||||
{ inherit (config.fediversityVm.ipv4) address prefixLength; }
|
||||
];
|
||||
defaultGateway = {
|
||||
address = config.fediversityVm.ipv4.gateway;
|
||||
interface = config.fediversityVm.ipv4.interface;
|
||||
};
|
||||
nameservers = [
|
||||
"95.215.185.6"
|
||||
"95.215.185.7"
|
||||
];
|
||||
})
|
||||
|
||||
## IPv6
|
||||
(mkIf config.fediversityVm.ipv6.enable {
|
||||
interfaces.${config.fediversityVm.ipv6.interface}.ipv6.addresses = [
|
||||
{ inherit (config.fediversityVm.ipv6) address prefixLength; }
|
||||
];
|
||||
defaultGateway6 = {
|
||||
address = config.fediversityVm.ipv6.gateway;
|
||||
interface = config.fediversityVm.ipv6.interface;
|
||||
};
|
||||
nameservers = [
|
||||
"2a00:51c0::5fd7:b906"
|
||||
"2a00:51c0::5fd7:b907"
|
||||
];
|
||||
})
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,13 @@
|
|||
{
|
||||
config,
|
||||
...
|
||||
}:
|
||||
{
|
||||
_class = "nixos";
|
||||
|
||||
users.users = {
|
||||
root.openssh.authorizedKeys.keys = config.users.users.procolix.openssh.authorizedKeys.keys;
|
||||
|
||||
procolix = {
|
||||
isNormalUser = true;
|
||||
extraGroups = [ "wheel" ];
|
||||
|
|
|
|||
43
infra/common/node.nix
Normal file
43
infra/common/node.nix
Normal 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
|
||||
];
|
||||
}
|
||||
|
|
@ -6,6 +6,8 @@ let
|
|||
|
||||
in
|
||||
{
|
||||
# `config` not set and imported from multiple places: no fixed module class
|
||||
|
||||
options.fediversityVm = {
|
||||
|
||||
##########################################################################
|
||||
|
|
@ -18,16 +20,13 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
proxmox = mkOption {
|
||||
type = types.nullOr (
|
||||
types.enum [
|
||||
"procolix"
|
||||
"fediversity"
|
||||
]
|
||||
);
|
||||
isFediversityVm = mkOption {
|
||||
type = types.bool;
|
||||
description = ''
|
||||
The Proxmox instance. This is used for provisioning only and should be
|
||||
set to `null` if the machine is not a VM.
|
||||
Whether the machine is a Fediversity VM or not. This is used to
|
||||
determine whether the machine should be provisioned via Proxmox or not.
|
||||
Machines that are _not_ Fediversity VM could be physical machines, or
|
||||
VMs that live outside Fediversity, eg. on Procolix's Proxmox.
|
||||
'';
|
||||
};
|
||||
|
||||
|
|
@ -89,6 +88,17 @@ in
|
|||
};
|
||||
|
||||
ipv4 = {
|
||||
enable = mkOption {
|
||||
default = true;
|
||||
};
|
||||
|
||||
interface = mkOption {
|
||||
description = ''
|
||||
The interface that carries the machine's IPv4 network.
|
||||
'';
|
||||
default = "eth0";
|
||||
};
|
||||
|
||||
address = mkOption {
|
||||
description = ''
|
||||
The IP address of the machine, version 4. It will be injected as a
|
||||
|
|
@ -114,6 +124,17 @@ in
|
|||
};
|
||||
|
||||
ipv6 = {
|
||||
enable = mkOption {
|
||||
default = true;
|
||||
};
|
||||
|
||||
interface = mkOption {
|
||||
description = ''
|
||||
The interface that carries the machine's IPv6 network.
|
||||
'';
|
||||
default = "eth0";
|
||||
};
|
||||
|
||||
address = mkOption {
|
||||
description = ''
|
||||
The IP address of the machine, version 6. It will be injected as a
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
loader = {
|
||||
systemd-boot.enable = true;
|
||||
efi.canTouchEfiVariables = true;
|
||||
};
|
||||
|
||||
initrd = {
|
||||
availableKernelModules = [
|
||||
"ata_piix"
|
||||
"uhci_hcd"
|
||||
"virtio_pci"
|
||||
"virtio_scsi"
|
||||
"sd_mod"
|
||||
"sr_mod"
|
||||
];
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
keys,
|
||||
secrets,
|
||||
...
|
||||
}:
|
||||
|
||||
|
|
@ -9,10 +11,6 @@ let
|
|||
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};
|
||||
|
|
@ -23,6 +21,7 @@ in
|
|||
imports = [
|
||||
./options.nix
|
||||
./nixos
|
||||
./proxmox-qemu-vm.nix
|
||||
];
|
||||
|
||||
## Read all the secrets, filter the ones that are supposed to be readable
|
||||
|
|
@ -31,11 +30,16 @@ in
|
|||
age.secrets = concatMapAttrs (
|
||||
name: secret:
|
||||
optionalAttrs (elem config.fediversityVm.hostPublicKey secret.publicKeys) {
|
||||
${removeSuffix ".age" name}.file = secretsPrefix + "/${name}";
|
||||
${removeSuffix ".age" name}.file = secrets.rootPath + "/${name}";
|
||||
}
|
||||
) 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;
|
||||
users.users.root.openssh.authorizedKeys.keys = attrValues keys.contributors ++ [
|
||||
# allow our panel vm access to the test machines
|
||||
keys.panel
|
||||
# allow continuous deployment access
|
||||
keys.cd
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ in
|
|||
<agenix/modules/age.nix>
|
||||
../../services/fediversity
|
||||
./resource.nix
|
||||
# ./node.nix
|
||||
];
|
||||
fediversityVm.name = hostname;
|
||||
fediversity = {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,12 @@
|
|||
}:
|
||||
let
|
||||
inherit (pkgs) lib;
|
||||
setup = import ./setup.nix { inherit lib pkgs sources; };
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
|
||||
201
infra/flake-part.nix
Normal file
201
infra/flake-part.nix
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
{
|
||||
inputs,
|
||||
lib,
|
||||
sources,
|
||||
keys,
|
||||
secrets,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (builtins) readDir;
|
||||
inherit (lib)
|
||||
attrNames
|
||||
mkOption
|
||||
evalModules
|
||||
filterAttrs
|
||||
mapAttrs'
|
||||
deepSeq
|
||||
;
|
||||
inherit (lib.attrsets) genAttrs;
|
||||
|
||||
commonResourceModule = {
|
||||
# TODO(@fricklerhandwerk): this is terrible but IMO we should just ditch
|
||||
# flake-parts and have our own data model for how the project is organised
|
||||
# internally
|
||||
_module.args = {
|
||||
inherit
|
||||
inputs
|
||||
keys
|
||||
secrets
|
||||
sources
|
||||
;
|
||||
};
|
||||
|
||||
## FIXME: It would be preferrable to have those `sources`-related imports in
|
||||
## the modules that use them. However, doing so triggers infinite recursions
|
||||
## because of the way we propagate `sources`. `sources` must be propagated by
|
||||
## means of `specialArgs`, but this requires a bigger change.
|
||||
nixos.module.imports = [
|
||||
"${sources.nixpkgs}/nixos/modules/profiles/qemu-guest.nix"
|
||||
"${sources.agenix}/modules/age.nix"
|
||||
"${sources.disko}/module.nix"
|
||||
"${sources.home-manager}/nixos"
|
||||
];
|
||||
|
||||
imports = [
|
||||
./common/resource.nix
|
||||
];
|
||||
};
|
||||
|
||||
## Given a list of machine names, make a deployment with those machines'
|
||||
## configurations as resources.
|
||||
makeDeployment =
|
||||
vmNames:
|
||||
{ providers, ... }:
|
||||
{
|
||||
providers.local = inputs.nixops4.modules.nixops4Provider.local;
|
||||
resources = genAttrs vmNames (vmName: {
|
||||
type = providers.local.exec;
|
||||
imports = [
|
||||
inputs.nixops4-nixos.modules.nixops4Resource.nixos
|
||||
commonResourceModule
|
||||
../machines/dev/${vmName}
|
||||
];
|
||||
});
|
||||
};
|
||||
makeDeployment' = vmName: makeDeployment [ vmName ];
|
||||
|
||||
## Given an attrset of test configurations (key = test machine name, value =
|
||||
## NixOS configuration module), make a deployment with those machines'
|
||||
## configurations as resources.
|
||||
makeTestDeployment =
|
||||
(import ../deployment)
|
||||
{
|
||||
inherit lib;
|
||||
inherit (inputs) nixops4 nixops4-nixos;
|
||||
fediversity = import ../services/fediversity;
|
||||
}
|
||||
{
|
||||
garageConfigurationResource = {
|
||||
imports = [
|
||||
commonResourceModule
|
||||
../machines/operator/test01
|
||||
];
|
||||
};
|
||||
mastodonConfigurationResource = {
|
||||
imports = [
|
||||
commonResourceModule
|
||||
../machines/operator/test06 # somehow `test02` has a problem - use test06 instead
|
||||
];
|
||||
};
|
||||
peertubeConfigurationResource = {
|
||||
imports = [
|
||||
commonResourceModule
|
||||
../machines/operator/test05
|
||||
];
|
||||
};
|
||||
pixelfedConfigurationResource = {
|
||||
imports = [
|
||||
commonResourceModule
|
||||
../machines/operator/test04
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
nixops4ResourceNixosMockOptions = {
|
||||
## NOTE: We allow the use of a few options from
|
||||
## `nixops4-nixos.modules.nixops4Resource.nixos` such that we can
|
||||
## reuse modules that make use of them.
|
||||
##
|
||||
## REVIEW: We can probably do much better and cleaner. On the other hand,
|
||||
## this is only needed to expose NixOS configurations for provisioning
|
||||
## purposes, and eventually all of this should be handled by NixOps4.
|
||||
options = {
|
||||
nixos.module = mkOption { type = lib.types.deferredModule; }; # NOTE: not just `nixos` otherwise merging will go wrong
|
||||
nixpkgs = mkOption { };
|
||||
ssh = mkOption { };
|
||||
};
|
||||
};
|
||||
|
||||
makeResourceConfig =
|
||||
{ vmName, isTestVm }:
|
||||
(evalModules {
|
||||
modules = [
|
||||
nixops4ResourceNixosMockOptions
|
||||
commonResourceModule
|
||||
(if isTestVm then ../machines/operator/${vmName} else ../machines/dev/${vmName})
|
||||
];
|
||||
}).config;
|
||||
|
||||
## Given a VM name, make a NixOS configuration for this machine.
|
||||
makeConfiguration =
|
||||
isTestVm: vmName:
|
||||
import "${sources.nixpkgs}/nixos" {
|
||||
configuration = (makeResourceConfig { inherit vmName isTestVm; }).nixos.module;
|
||||
system = "x86_64-linux";
|
||||
};
|
||||
|
||||
makeVmOptions =
|
||||
isTestVm: vmName:
|
||||
let
|
||||
config = (makeResourceConfig { inherit vmName isTestVm; }).fediversityVm;
|
||||
in
|
||||
if config.isFediversityVm then
|
||||
{
|
||||
inherit (config)
|
||||
vmId
|
||||
description
|
||||
sockets
|
||||
cores
|
||||
memory
|
||||
diskSize
|
||||
hostPublicKey
|
||||
unsafeHostPrivateKey
|
||||
;
|
||||
}
|
||||
else
|
||||
null;
|
||||
|
||||
listSubdirectories = path: attrNames (filterAttrs (_: type: type == "directory") (readDir path));
|
||||
|
||||
machines = listSubdirectories ../machines/dev;
|
||||
|
||||
nixosConfigurations =
|
||||
genAttrs machines (makeConfiguration false);
|
||||
vmOptions =
|
||||
filterAttrs (_: value: value != null) # Filter out non-Fediversity VMs
|
||||
(genAttrs machines (makeVmOptions false));
|
||||
|
||||
in
|
||||
{
|
||||
_class = "flake";
|
||||
|
||||
## - Each normal or test machine gets a NixOS configuration.
|
||||
## - Each normal or test machine gets a VM options entry.
|
||||
## - Each normal machine gets a deployment.
|
||||
## - We add a “default” deployment with all normal machines.
|
||||
## - We add a “test” deployment with all test machines.
|
||||
nixops4Deployments = genAttrs machines makeDeployment' // {
|
||||
default = makeDeployment machines;
|
||||
};
|
||||
flake = { inherit nixosConfigurations vmOptions; };
|
||||
|
||||
perSystem =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
|
@ -10,14 +10,12 @@ let
|
|||
inherit lib pkgs;
|
||||
};
|
||||
tfEnv = pkgs.callPackage ./tf-env.nix { };
|
||||
nodes = {
|
||||
server = {
|
||||
nodes.server = {
|
||||
environment.systemPackages = [
|
||||
tf
|
||||
tfEnv
|
||||
];
|
||||
};
|
||||
};
|
||||
in
|
||||
lib.mapAttrs (name: test: pkgs.testers.runNixOSTest (test // { inherit name; })) {
|
||||
tf-validate-dev = {
|
||||
|
|
|
|||
1
keys/cd-ssh-key.pub
Normal file
1
keys/cd-ssh-key.pub
Normal file
|
|
@ -0,0 +1 @@
|
|||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMlsYTtMx3hFO8B5B8iHaXL2JKj9izHeC+/AMhIWXBPs cd-age
|
||||
|
|
@ -35,4 +35,5 @@ in
|
|||
contributors = collectKeys ./contributors;
|
||||
systems = collectKeys ./systems;
|
||||
panel = removeTrailingWhitespace (readFile ./panel-ssh-key.pub);
|
||||
cd = removeTrailingWhitespace (readFile ./cd-ssh-key.pub);
|
||||
}
|
||||
|
|
|
|||
5
keys/flake-part.nix
Normal file
5
keys/flake-part.nix
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
_class = "flake";
|
||||
|
||||
_module.args.keys = import ./.;
|
||||
}
|
||||
1
keys/systems/forgejo-ci.pub
Normal file
1
keys/systems/forgejo-ci.pub
Normal file
|
|
@ -0,0 +1 @@
|
|||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIFXQW5fxJoNY9wtTMsNExgbAbvyljIRGBLjY+USh/0A
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
{
|
||||
_class = "nixops4Resource";
|
||||
|
||||
fediversityVm = {
|
||||
name = "fedi200";
|
||||
isFediversityVm = true;
|
||||
vmId = 200;
|
||||
proxmox = "fediversity";
|
||||
description = "Testing machine for Hans";
|
||||
|
||||
domain = "abundos.eu";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
{
|
||||
_class = "nixops4Resource";
|
||||
|
||||
fediversityVm = {
|
||||
name = "fedi201";
|
||||
isFediversityVm = true;
|
||||
vmId = 201;
|
||||
proxmox = "fediversity";
|
||||
description = "FediPanel";
|
||||
|
||||
domain = "abundos.eu";
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ let
|
|||
name = "panel";
|
||||
in
|
||||
{
|
||||
_class = "nixos";
|
||||
|
||||
imports = [
|
||||
<home-manager/nixos>
|
||||
(import ../../../panel { }).module
|
||||
|
|
|
|||
72
machines/dev/forgejo-ci/default.nix
Normal file
72
machines/dev/forgejo-ci/default.nix
Normal 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"
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
47
machines/dev/forgejo-ci/forgejo-actions-runner.nix
Normal file
47
machines/dev/forgejo-ci/forgejo-actions-runner.nix
Normal 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;
|
||||
}
|
||||
|
|
@ -1,14 +1,21 @@
|
|||
{ lib, ... }:
|
||||
{
|
||||
_class = "nixops4Resource";
|
||||
|
||||
fediversityVm = {
|
||||
name = "vm02116";
|
||||
isFediversityVm = false;
|
||||
vmId = 2116;
|
||||
proxmox = "procolix";
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ let
|
|||
|
||||
in
|
||||
{
|
||||
_class = "nixos";
|
||||
|
||||
services.forgejo = {
|
||||
enable = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,21 @@
|
|||
{ lib, ... }:
|
||||
{
|
||||
_class = "nixops4Resource";
|
||||
|
||||
fediversityVm = {
|
||||
name = "vm02187";
|
||||
isFediversityVm = false;
|
||||
vmId = 2187;
|
||||
proxmox = "procolix";
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
{ config, ... }:
|
||||
|
||||
{
|
||||
_class = "nixos";
|
||||
|
||||
services.phpfpm.pools.mediawiki.phpOptions = ''
|
||||
upload_max_filesize = 1024M;
|
||||
post_max_size = 1024M;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"domain": "abundos.eu",
|
||||
"domain": "fediversity.net",
|
||||
"mastodon": { "enable": false },
|
||||
"peertube": { "enable": false },
|
||||
"pixelfed": { "enable": false },
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
{
|
||||
_class = "nixops4Resource";
|
||||
|
||||
fediversityVm = {
|
||||
name = "test01";
|
||||
isFediversityVm = true;
|
||||
vmId = 7001;
|
||||
proxmox = "fediversity";
|
||||
|
||||
hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub;
|
||||
unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
{
|
||||
_class = "nixops4Resource";
|
||||
|
||||
fediversityVm = {
|
||||
name = "test02";
|
||||
isFediversityVm = true;
|
||||
vmId = 7002;
|
||||
proxmox = "fediversity";
|
||||
|
||||
hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub;
|
||||
unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
{
|
||||
_class = "nixops4Resource";
|
||||
|
||||
fediversityVm = {
|
||||
name = "test03";
|
||||
isFediversityVm = true;
|
||||
vmId = 7003;
|
||||
proxmox = "fediversity";
|
||||
|
||||
hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub;
|
||||
unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
{
|
||||
_class = "nixops4Resource";
|
||||
|
||||
fediversityVm = {
|
||||
name = "test04";
|
||||
isFediversityVm = true;
|
||||
vmId = 7004;
|
||||
proxmox = "fediversity";
|
||||
|
||||
hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub;
|
||||
unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
{
|
||||
_class = "nixops4Resource";
|
||||
|
||||
fediversityVm = {
|
||||
name = "test05";
|
||||
isFediversityVm = true;
|
||||
vmId = 7005;
|
||||
proxmox = "fediversity";
|
||||
|
||||
hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub;
|
||||
unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
{
|
||||
_class = "nixops4Resource";
|
||||
|
||||
fediversityVm = {
|
||||
name = "test06";
|
||||
isFediversityVm = true;
|
||||
vmId = 7006;
|
||||
proxmox = "fediversity";
|
||||
|
||||
hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub;
|
||||
unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
{
|
||||
_class = "nixops4Resource";
|
||||
|
||||
fediversityVm = {
|
||||
name = "test11";
|
||||
isFediversityVm = true;
|
||||
vmId = 7011;
|
||||
proxmox = "fediversity";
|
||||
|
||||
hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub;
|
||||
unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
{
|
||||
_class = "nixops4Resource";
|
||||
|
||||
fediversityVm = {
|
||||
name = "test12";
|
||||
isFediversityVm = true;
|
||||
vmId = 7012;
|
||||
proxmox = "fediversity";
|
||||
|
||||
hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub;
|
||||
unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
{
|
||||
_class = "nixops4Resource";
|
||||
|
||||
fediversityVm = {
|
||||
name = "test13";
|
||||
isFediversityVm = true;
|
||||
vmId = 7013;
|
||||
proxmox = "fediversity";
|
||||
|
||||
hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub;
|
||||
unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
{
|
||||
_class = "nixops4Resource";
|
||||
|
||||
fediversityVm = {
|
||||
name = "test14";
|
||||
isFediversityVm = true;
|
||||
vmId = 7014;
|
||||
proxmox = "fediversity";
|
||||
|
||||
hostPublicKey = builtins.readFile ./ssh_host_ed25519_key.pub;
|
||||
unsafeHostPrivateKey = builtins.readFile ./ssh_host_ed25519_key;
|
||||
|
|
|
|||
54
mkFlake.nix
Normal file
54
mkFlake.nix
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
## This file contains a tweak of flake-parts's `mkFlake` function to splice in
|
||||
## sources taken from npins.
|
||||
|
||||
## NOTE: Much of the logic in this file feels like it should be not super
|
||||
## specific to fediversity. Could it make sense to extract the core of this to
|
||||
## another place it feels closer to in spirit, such as @fricklerhandwerk's
|
||||
## flake-inputs (which this code already depends on anyway, and which already
|
||||
## contained two distinct helpers for migrating away from flakes)? cf
|
||||
## https://git.fediversity.eu/Fediversity/Fediversity/pulls/447#issuecomment-8671
|
||||
|
||||
inputs@{ self, ... }:
|
||||
|
||||
let
|
||||
sources = import ./npins;
|
||||
inherit (import sources.flake-inputs) import-flake;
|
||||
|
||||
# XXX(@fricklerhandwerk): this atrocity is required to splice in a foreign Nixpkgs via flake-parts
|
||||
# XXX - this is just importing a flake
|
||||
nixpkgs = import-flake { src = sources.nixpkgs; };
|
||||
|
||||
# XXX - this overrides the inputs attached to `self`
|
||||
inputs' = self.inputs // {
|
||||
nixpkgs = nixpkgs;
|
||||
};
|
||||
self' = self // {
|
||||
inputs = inputs';
|
||||
};
|
||||
|
||||
flake-parts-lib = import "${sources.flake-parts}/lib.nix" { inherit (nixpkgs) lib; };
|
||||
in
|
||||
|
||||
flakeModule:
|
||||
|
||||
flake-parts-lib.mkFlake
|
||||
{
|
||||
# XXX - finally we override the overall set of `inputs` -- we need both:
|
||||
# `flake-parts obtains `nixpkgs` from `self.inputs` and not from `inputs`.
|
||||
inputs = inputs // {
|
||||
inherit nixpkgs;
|
||||
};
|
||||
self = self';
|
||||
specialArgs = {
|
||||
inherit sources;
|
||||
};
|
||||
}
|
||||
{
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
];
|
||||
imports = [ flakeModule ];
|
||||
}
|
||||
|
|
@ -36,10 +36,39 @@
|
|||
"version_upper_bound": null,
|
||||
"release_prefix": null,
|
||||
"submodules": false,
|
||||
"version": "v1.11.0",
|
||||
"revision": "cdf8deded8813edfa6e65544f69fdd3a59fa2bb4",
|
||||
"url": "https://api.github.com/repos/nix-community/disko/tarball/v1.11.0",
|
||||
"hash": "13brimg7z7k9y36n4jc1pssqyw94nd8qvgfjv53z66lv4xkhin92"
|
||||
"version": "v1.12.0",
|
||||
"revision": "7121f74b976481bc36877abaf52adab2a178fcbe",
|
||||
"url": "https://api.github.com/repos/nix-community/disko/tarball/v1.12.0",
|
||||
"hash": "0wbx518d2x54yn4xh98cgm65wvj0gpy6nia6ra7ns4j63hx14fkq"
|
||||
},
|
||||
"flake-inputs": {
|
||||
"type": "GitRelease",
|
||||
"repository": {
|
||||
"type": "GitHub",
|
||||
"owner": "fricklerhandwerk",
|
||||
"repo": "flake-inputs"
|
||||
},
|
||||
"pre_releases": false,
|
||||
"version_upper_bound": null,
|
||||
"release_prefix": null,
|
||||
"submodules": false,
|
||||
"version": "4.1",
|
||||
"revision": "ad02792f7543754569fe2fd3d5787ee00ef40be2",
|
||||
"url": "https://api.github.com/repos/fricklerhandwerk/flake-inputs/tarball/4.1",
|
||||
"hash": "1j57avx2mqjnhrsgq3xl7ih8v7bdhz1kj3min6364f486ys048bm"
|
||||
},
|
||||
"flake-parts": {
|
||||
"type": "Git",
|
||||
"repository": {
|
||||
"type": "GitHub",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts"
|
||||
},
|
||||
"branch": "main",
|
||||
"submodules": false,
|
||||
"revision": "c621e8422220273271f52058f618c94e405bb0f5",
|
||||
"url": "https://github.com/hercules-ci/flake-parts/archive/c621e8422220273271f52058f618c94e405bb0f5.tar.gz",
|
||||
"hash": "09j2dafd75ydlcw8v48vcpfm2mw0j6cs8286x2hha2lr08d232w4"
|
||||
},
|
||||
"git-hooks": {
|
||||
"type": "Git",
|
||||
|
|
@ -76,9 +105,9 @@
|
|||
},
|
||||
"branch": "master",
|
||||
"submodules": false,
|
||||
"revision": "22b326b42bf42973d5e4fe1044591fb459e6aeac",
|
||||
"url": "https://github.com/nix-community/home-manager/archive/22b326b42bf42973d5e4fe1044591fb459e6aeac.tar.gz",
|
||||
"hash": "0hwllnym5mrrxinjsq0p9zn39i110c1xixp4x64svl7jjm5zb4c4"
|
||||
"revision": "863842639722dd12ae9e37ca83bcb61a63b36f6c",
|
||||
"url": "https://github.com/nix-community/home-manager/archive/863842639722dd12ae9e37ca83bcb61a63b36f6c.tar.gz",
|
||||
"hash": "0rw9n8d4v87pzlmw7ws15f0sldb51fd9528skpbzmrzl4pinsgij"
|
||||
},
|
||||
"htmx": {
|
||||
"type": "GitRelease",
|
||||
|
|
|
|||
2
panel/.gitignore
vendored
2
panel/.gitignore
vendored
|
|
@ -11,5 +11,5 @@ db.sqlite3
|
|||
src/db.sqlite3
|
||||
src/static
|
||||
src/panel/static/htmx*
|
||||
src/panel/configuration/schema.py
|
||||
src/panel/configuration/schema.*
|
||||
.credentials
|
||||
|
|
|
|||
|
|
@ -20,6 +20,11 @@ in
|
|||
packages = [
|
||||
pkgs.npins
|
||||
manage
|
||||
|
||||
# NixOps4 and its dependencies
|
||||
pkgs.nixops4
|
||||
pkgs.nix
|
||||
pkgs.openssh
|
||||
];
|
||||
env =
|
||||
let
|
||||
|
|
@ -45,7 +50,7 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
module = import ./nix/configuration.nix;
|
||||
module = ./nix/configuration.nix;
|
||||
tests = pkgs.callPackage ./nix/tests.nix { };
|
||||
|
||||
# re-export inputs so they can be overridden granularly
|
||||
|
|
|
|||
|
|
@ -23,15 +23,13 @@ let
|
|||
cfg = config.services.${name};
|
||||
package = pkgs.callPackage ./package.nix { };
|
||||
|
||||
environment = import ../env.nix { inherit lib pkgs; } // {
|
||||
environment = {
|
||||
DATABASE_URL = "sqlite:////var/lib/${name}/db.sqlite3";
|
||||
USER_SETTINGS_FILE = pkgs.concatText "configuration.py" [
|
||||
((pkgs.formats.pythonVars { }).generate "settings.py" cfg.settings)
|
||||
(builtins.toFile "extra-settings.py" cfg.extra-settings)
|
||||
];
|
||||
REPO_DIR = import ../../infra/tf-env.nix {
|
||||
inherit lib pkgs;
|
||||
};
|
||||
REPO_DIR = import ../../infra/tf-env.nix { inherit lib pkgs; };
|
||||
};
|
||||
|
||||
python-environment = pkgs.python3.withPackages (
|
||||
|
|
@ -77,6 +75,8 @@ in
|
|||
# https://git.dgnum.eu/mdebray/djangonix/
|
||||
# unlicensed at the time of writing, but surely worth taking some inspiration from...
|
||||
{
|
||||
_class = "nixos";
|
||||
|
||||
options.services.${name} = {
|
||||
enable = mkEnableOption "Service configuration for `${name}`";
|
||||
production = mkOption {
|
||||
|
|
@ -136,6 +136,18 @@ in
|
|||
type = types.attrsOf types.path;
|
||||
default = { };
|
||||
};
|
||||
nixops4Package = mkOption {
|
||||
type = types.package;
|
||||
description = ''
|
||||
A package providing NixOps4.
|
||||
|
||||
TODO: This should not be at the level of the NixOS module, but instead
|
||||
at the level of the panel's package. Until one finds a way to grab
|
||||
NixOps4 from the package's npins-based code, we will have to do with
|
||||
this workaround.
|
||||
'';
|
||||
default = pkgs.nixops4;
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
|
@ -149,7 +161,19 @@ in
|
|||
${cfg.domain} =
|
||||
{
|
||||
locations = {
|
||||
"/".proxyPass = "http://localhost:${toString cfg.port}";
|
||||
"/" = {
|
||||
proxyPass = "http://localhost:${toString cfg.port}";
|
||||
extraConfig = ''
|
||||
## FIXME: The following is necessary because /deployment/status
|
||||
## can take aaaaages to respond. I think this is horrendous
|
||||
## design from the panel and should be changed there, but in the
|
||||
## meantime we bump nginx's timeouts to one hour.
|
||||
proxy_connect_timeout 3600;
|
||||
proxy_send_timeout 3600;
|
||||
proxy_read_timeout 3600;
|
||||
send_timeout 3600;
|
||||
'';
|
||||
};
|
||||
"/static/".alias = "/var/lib/${name}/static/";
|
||||
};
|
||||
}
|
||||
|
|
@ -160,6 +184,7 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
# needed to place a config file with home-manager
|
||||
users.users.${name}.isNormalUser = true;
|
||||
|
||||
users.groups.${name} = { };
|
||||
|
|
@ -171,6 +196,11 @@ in
|
|||
pkgs.openssh
|
||||
python-environment
|
||||
manage-service
|
||||
|
||||
## NixOps4 and its dependencies
|
||||
cfg.nixops4Package
|
||||
pkgs.nix
|
||||
pkgs.openssh
|
||||
];
|
||||
preStart = ''
|
||||
# Auto-migrate on first run or if the package has changed
|
||||
|
|
|
|||
|
|
@ -8,4 +8,17 @@ let
|
|||
in
|
||||
{
|
||||
python3 = prev.lib.attrsets.recursiveUpdate prev.python3 { pkgs = extraPython3Packages; };
|
||||
nixops4 =
|
||||
let
|
||||
sources = import ../../npins;
|
||||
inherit (import sources.flake-inputs) import-flake;
|
||||
inherit
|
||||
(import-flake {
|
||||
src = ../../.;
|
||||
})
|
||||
inputs
|
||||
;
|
||||
inherit (inputs) nixops4;
|
||||
in
|
||||
nixops4.packages.${prev.system}.default;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,17 +30,24 @@ let
|
|||
include-package-data = true
|
||||
'';
|
||||
|
||||
generated = [
|
||||
generated =
|
||||
let
|
||||
jsonschema = callPackage "${sources.clan-core}/lib/jsonschema" { } { };
|
||||
frontend-options = jsonschema.parseModule ../../deployment/options.nix;
|
||||
schema = with builtins; toFile "schema.json" (toJSON frontend-options);
|
||||
in
|
||||
[
|
||||
{
|
||||
from = "${sources.htmx}/dist/htmx.min.js";
|
||||
to = "./panel/static/htmx.min.js";
|
||||
}
|
||||
{
|
||||
from = schema;
|
||||
to = "./panel/configuration/schema.json";
|
||||
}
|
||||
{
|
||||
from =
|
||||
let
|
||||
jsonschema = callPackage "${sources.clan-core}/lib/jsonschema" { } { };
|
||||
frontend-options = jsonschema.parseModule ../../deployment/options.nix;
|
||||
schema = with builtins; toFile "schema.json" (toJSON frontend-options);
|
||||
codegen = "${python3Packages.datamodel-code-generator}/bin/datamodel-codegen";
|
||||
pydantic = runCommand "schema.py" { } ''
|
||||
# replace plain `pydantic` with `drf_pydantic` so we can create forms automatically
|
||||
|
|
@ -54,6 +61,8 @@ let
|
|||
];
|
||||
in
|
||||
python3.pkgs.buildPythonPackage {
|
||||
_class = "package";
|
||||
|
||||
pname = name;
|
||||
inherit (pyproject.project) version;
|
||||
pyproject = true;
|
||||
|
|
@ -91,11 +100,7 @@ python3.pkgs.buildPythonPackage {
|
|||
cp -v ${src}/manage.py $out/bin/manage.py
|
||||
chmod +x $out/bin/manage.py
|
||||
wrapProgram $out/bin/manage.py \
|
||||
--set REPO_DIR "${
|
||||
import ../../infra/tf-env.nix {
|
||||
inherit lib pkgs;
|
||||
}
|
||||
}" \
|
||||
--set REPO_DIR "${import ../../infra/tf-env.nix { inherit lib pkgs; }}" \
|
||||
--prefix PYTHONPATH : "$PYTHONPATH"
|
||||
${lib.concatStringsSep "\n" (
|
||||
map (file: "cp ${file.from} $out/${python3.sitePackages}/${file.to}") generated
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
}:
|
||||
|
||||
buildPythonPackage rec {
|
||||
_class = "package";
|
||||
|
||||
pname = "django-pydantic-field";
|
||||
version = "v0.3.12";
|
||||
pyproject = true;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
}:
|
||||
|
||||
buildPythonPackage rec {
|
||||
_class = "package";
|
||||
|
||||
pname = "drf-pydantic";
|
||||
version = "v2.7.1";
|
||||
pyproject = true;
|
||||
|
|
|
|||
|
|
@ -22,43 +22,30 @@ let
|
|||
};
|
||||
in
|
||||
lib.mapAttrs (name: test: pkgs.testers.runNixOSTest (test // { inherit name; })) {
|
||||
application-tests = {
|
||||
inherit defaults;
|
||||
nodes.server = _: { imports = [ ./configuration.nix ]; };
|
||||
# run all application-level tests managed by Django
|
||||
# https://docs.djangoproject.com/en/5.0/topics/testing/overview/
|
||||
testScript = ''
|
||||
server.wait_for_unit("${name}.service")
|
||||
server.succeed("manage test ${name}")
|
||||
'';
|
||||
};
|
||||
admin = {
|
||||
inherit defaults;
|
||||
nodes.server = _: { imports = [ ./configuration.nix ]; };
|
||||
# check that the admin interface is served
|
||||
testScript = ''
|
||||
server.wait_for_unit("${name}.service")
|
||||
server.wait_for_open_port(8000)
|
||||
server.succeed("curl --fail -L -H 'Host: example.org' http://localhost/admin")
|
||||
'';
|
||||
};
|
||||
|
||||
sass-processing = {
|
||||
basic = {
|
||||
inherit defaults;
|
||||
nodes.server = _: { imports = [ ./configuration.nix ]; };
|
||||
extraPythonPackages = ps: with ps; [ beautifulsoup4 ];
|
||||
# type checking on `beautifulsoup4` will error out
|
||||
skipTypeCheck = true;
|
||||
# check that stylesheets are pre-processed and served
|
||||
testScript = ''
|
||||
from bs4 import BeautifulSoup
|
||||
server.wait_for_unit("${name}.service")
|
||||
server.wait_for_open_port(8000)
|
||||
|
||||
with subtest("Django application tests"):
|
||||
# https://docs.djangoproject.com/en/5.0/topics/testing/overview/
|
||||
server.succeed("manage test ${name}")
|
||||
|
||||
with subtest("Check that admin interface is served"):
|
||||
server.succeed("curl --fail -L -H 'Host: example.org' http://localhost/admin")
|
||||
|
||||
with subtest("Check that stylesheets are pre-processed and served"):
|
||||
from bs4 import BeautifulSoup
|
||||
stdout = server.succeed("curl --fail -H 'Host: example.org' http://localhost")
|
||||
# the CSS is auto-generated with a hash in the file name
|
||||
html = BeautifulSoup(stdout, 'html.parser')
|
||||
css = html.find('link', type="text/css")['href']
|
||||
server.succeed(f"curl --fail -H 'Host: example.org' http://localhost/{css}")
|
||||
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
|
|
|||
1
panel/src/panel/configuration/schema.json
Symbolic link
1
panel/src/panel/configuration/schema.json
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
/nix/store/cgfzw0ddm69ypfmxpfzbifzswipa8pq9-schema.json
|
||||
|
|
@ -45,6 +45,7 @@ def get_secret(name: str, encoding: str = "utf-8") -> str:
|
|||
return secret
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
# This is used nowhere but is required by Django.
|
||||
SECRET_KEY = get_secret("SECRET_KEY")
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from django.contrib.auth.models import User
|
||||
from django.template import Template, Context
|
||||
from urllib.parse import unquote
|
||||
|
||||
from panel.templatetags.custom_tags import auth_url
|
||||
|
||||
class Login(TestCase):
|
||||
def setUp(self):
|
||||
self.username = 'testuser'
|
||||
|
|
@ -27,8 +28,7 @@ class Login(TestCase):
|
|||
|
||||
# check that the expected login URL is in the response
|
||||
context = response.context[0]
|
||||
template = Template("{% load custom_tags %}{% auth_url 'login' %}")
|
||||
login_url = template.render(context)
|
||||
login_url = auth_url(context, 'login')
|
||||
self.assertIn(login_url, response.content.decode('utf-8'))
|
||||
|
||||
# log in
|
||||
|
|
@ -49,8 +49,7 @@ class Login(TestCase):
|
|||
|
||||
# check that the expected logout URL is present
|
||||
context = response.context[0]
|
||||
template = Template("{% load custom_tags %}{% auth_url 'logout' %}")
|
||||
logout_url = template.render(context)
|
||||
logout_url = auth_url(context, 'logout')
|
||||
self.assertIn(logout_url, response.content.decode('utf-8'))
|
||||
|
||||
# log out again
|
||||
|
|
@ -88,8 +87,7 @@ class Login(TestCase):
|
|||
|
||||
# check that the expected logout URL is present
|
||||
context = response.context[0]
|
||||
template = Template("{% load custom_tags %}{% auth_url 'logout' %}")
|
||||
logout_url = template.render(context)
|
||||
logout_url = auth_url(context, 'logout')
|
||||
self.assertIn(logout_url, response.content.decode('utf-8'))
|
||||
|
||||
# log out
|
||||
|
|
@ -97,8 +95,7 @@ class Login(TestCase):
|
|||
|
||||
# check that we're at the expected location, logged out
|
||||
self.assertEqual(response.status_code, 200)
|
||||
template = Template("{% load custom_tags %}{% auth_url 'login' %}")
|
||||
login_url = template.render(context)
|
||||
login_url = auth_url(context, 'login')
|
||||
location, status = response.redirect_chain[-1]
|
||||
self.assertEqual(location, unquote(login_url))
|
||||
self.assertFalse(response.context['user'].is_authenticated)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ from panel.configuration import schema
|
|||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Index(TemplateView):
|
||||
template_name = 'index.html'
|
||||
|
||||
|
|
@ -70,9 +69,6 @@ class ConfigurationForm(LoginRequiredMixin, APIView):
|
|||
config.save()
|
||||
return redirect(self.success_url)
|
||||
|
||||
# TODO(@fricklerhandwerk):
|
||||
# this is broken after changing the form view.
|
||||
# but if there's no test for it, how do we know it ever worked in the first place?
|
||||
class DeploymentStatus(ConfigurationForm):
|
||||
|
||||
def post(self, request):
|
||||
|
|
@ -89,26 +85,13 @@ class DeploymentStatus(ConfigurationForm):
|
|||
config.save()
|
||||
|
||||
deployment_result, deployment_params = self.deployment(config.parsed_value)
|
||||
if deployment_result.returncode == 0:
|
||||
deployment_status = "Deployment Succeeded"
|
||||
else:
|
||||
deployment_status = "Deployment Failed"
|
||||
|
||||
return render(self.request, "partials/deployment_result.html", {
|
||||
"deployment_status": deployment_status,
|
||||
"deployment_succeeded": (deployment_result.returncode == 0),
|
||||
"services": deployment_params.json(),
|
||||
})
|
||||
|
||||
def deployment(self, config: BaseModel):
|
||||
# FIXME: let the user specify these from the form (#190)
|
||||
dummy_user = {
|
||||
"initialUser": {
|
||||
"displayName": "Testy McTestface",
|
||||
"username": "test",
|
||||
"password": "testtest",
|
||||
"email": "test@test.com",
|
||||
},
|
||||
}
|
||||
env = {
|
||||
"PATH": settings.bin_path,
|
||||
# "TF_LOG": "info",
|
||||
|
|
|
|||
|
|
@ -179,15 +179,9 @@ grab_vm_options () {
|
|||
--log-format raw --quiet
|
||||
)
|
||||
|
||||
proxmox=$(echo "$options" | jq -r .proxmox)
|
||||
vm_id=$(echo "$options" | jq -r .vmId)
|
||||
description=$(echo "$options" | jq -r .description)
|
||||
|
||||
if [ "$proxmox" != fediversity ]; then
|
||||
die "I do not know how to provision things that are not Fediversity VMs,
|
||||
but I got proxmox = '%s' for VM %s." "$proxmox" "$vm_name"
|
||||
fi
|
||||
|
||||
sockets=$(echo "$options" | jq -r .sockets)
|
||||
cores=$(echo "$options" | jq -r .cores)
|
||||
memory=$(echo "$options" | jq -r .memory)
|
||||
|
|
@ -228,6 +222,7 @@ build_iso () {
|
|||
|
||||
nix build \
|
||||
--impure --expr "
|
||||
let flake = builtins.getFlake (builtins.toString ./.); in
|
||||
import ./makeInstallerIso.nix {
|
||||
nixosConfiguration = flake.nixosConfigurations.$vm_name;
|
||||
nixpkgs = flake.inputs.nixpkgs;
|
||||
|
|
|
|||
|
|
@ -167,16 +167,10 @@ grab_vm_options () {
|
|||
--log-format raw --quiet
|
||||
)
|
||||
|
||||
proxmox=$(echo "$options" | jq -r .proxmox)
|
||||
vm_id=$(echo "$options" | jq -r .vmId)
|
||||
|
||||
if [ "$proxmox" != fediversity ]; then
|
||||
die "I do not know how to remove things that are not Fediversity VMs,
|
||||
but I got proxmox = '%s' for VM %s." "$proxmox" "$vm_name"
|
||||
fi
|
||||
|
||||
printf 'done grabing VM options for VM %s. Found VM %d on %s Proxmox.\n' \
|
||||
"$vm_name" "$vm_id" "$proxmox"
|
||||
printf 'done grabing VM options for VM %s. Got id: %d.\n' \
|
||||
"$vm_name" "$vm_id"
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ let
|
|||
inherit (sources) agenix;
|
||||
in
|
||||
{
|
||||
mapping = import ./secrets.nix;
|
||||
rootPath = ./.;
|
||||
# shell for testing TF directly
|
||||
shell = pkgs.mkShellNoCC {
|
||||
packages = [
|
||||
|
|
|
|||
5
secrets/flake-part.nix
Normal file
5
secrets/flake-part.nix
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
_class = "flake";
|
||||
|
||||
_module.args.secrets = import ./.;
|
||||
}
|
||||
|
|
@ -1,17 +1,19 @@
|
|||
age-encryption.org/v1
|
||||
-> ssh-ed25519 Jpc21A 9edPaA2tT4SeYNTPzF0E157daC2o+JH/WQQCT+vLbFg
|
||||
C48EtLdhB75TTzfEZTw1DypicHiVlSmFzjfbqfO9N/8
|
||||
-> ssh-ed25519 BAs8QA T+kXpZg1v0XRkub5DWir7vYwO7KaOJLZBNYxxXiBUCw
|
||||
zBRwMTDpyI7twEwUGsmJYyYPw9btBx5Kakj1yT+XY8U
|
||||
-> ssh-ed25519 ofQnlg 4UoEDY/tdKz8LrX1BkBU1/cn+vSaYLUl7xX9YmzANBY
|
||||
8CACq1n3AJgD9IyPN23iRvThqsfQFF5+jmkKnhun24U
|
||||
-> ssh-ed25519 COspvA HxcbkqHL+LpVmwb+Fo5JuUU+C+Pxzdxtb0yZHixwuzM
|
||||
7FIhxdbjHJlgQQgjrHHUK5cecqs5aT7X3I8TWf8c2gc
|
||||
-> ssh-ed25519 2XrTgw R6Ia8MVIZKPnNZ0rspZ34EqoY8fOLeB9H7vnvNBLg1g
|
||||
55NUqz5Yygt6FKJ3bR5iHxQp8G7S2gyFwrJNX1Pb/2Y
|
||||
-> ssh-ed25519 awJeHA hJdTuAScoewVMt7HWiisSkL0zSeClFzYzzKL84G893o
|
||||
ou780VLrW1s4d6L+lEVu3kXaGn4dvtFPA31supwEL50
|
||||
-> ssh-ed25519 Fa25Dw mJcqnXA3fQeoKrG7RJ7nVeLxPvrxqbj+lJdx6jQ9IR8
|
||||
f5Q7mrQSSDsm1Z/uSAnvx66mgnRC3XaBLQrVL9f/Ijs
|
||||
--- W/KmboXTLV12X6WtVQKHNe+ZHvS2q9EHUZwofSgJSE8
|
||||
^kûÚ h©0ÔkÇ ¢¸_Ç·ûQÞm‘’7\òÖ}÷Áë?½qø‚<ÿm
|
||||
-> ssh-ed25519 Jpc21A bBCQmvfRUwJuIXbpVJ092XUBVszGrb6gILGbgV9j9BY
|
||||
7DEGwhqdfqMs5cxXtlMkSTPjw4qhczBgW0dmoJ6dh6g
|
||||
-> ssh-ed25519 BAs8QA oiVedFC6UklEFCJUybGr93+XrddyCtV4r4TnE4nhpWI
|
||||
xasnkP4NCl9TuYSE1u0Xi0b/PiwcrfHCz2QMnpTjLcU
|
||||
-> ssh-ed25519 ofQnlg LrMcWdaEUVyIgd/KznwJW/2sucIu5MuxDEcEJAmf8mA
|
||||
p6pQoisuXre2J4r6ArV6C6lKO2J/aNdBFhqLPBoZ2wA
|
||||
-> ssh-ed25519 COspvA q2OGeVofPKyGCpr4Mf9VoaRvZCWTRl8n2mvkQOdTnyQ
|
||||
M+ffAGecJG/94k/Z5DdokltrZppS2IcxkZa8JKHwIMs
|
||||
-> ssh-ed25519 2XrTgw Bsz/G4QderToPSfMKOR6s5yWb0xCGUlsjGJxJYQNBRc
|
||||
JYrXZb8qj1Yi9u5bnI/WzuNxy7gyFLCTIUaGNmcOYnk
|
||||
-> ssh-ed25519 awJeHA KKJMQSt0PvC6P+T/kxQv96tSBdLQLiY2f8q35IwGm28
|
||||
p7Cf2HLlPl0qmsO6Hh5zwVgKkEs3A6fdSBndMKsacbk
|
||||
-> ssh-ed25519 Fa25Dw 3m/qyannP4gjXxkUuO0LQRU8Z8HXOg4WReMDd7786y8
|
||||
dNMyiBGeJDrBScE9TEyZZ7+MGMG6FLuoRTK82EVeX1w
|
||||
-> ssh-ed25519 i+ecmQ oCs4Ep2K75yjmUOh1ox4F25tGq+O/mZ2/c2E8+IRlEc
|
||||
0Wc9gDxhvHK5tEVM5kJ0mQXc3kp7tJ2JNHg54N0+tJ8
|
||||
--- mXrqbcHxjjkS5MrQaCVm4hTsAUEENAWlIYtiYx6rtas
|
||||
ž`€úì}öÙ7Ù>iŒbàéëÕè/&ɪŠwŽ„ì7àí[ã±Hˆc“
|
||||
Binary file not shown.
|
|
@ -1,15 +1,19 @@
|
|||
age-encryption.org/v1
|
||||
-> ssh-ed25519 Jpc21A N/T7HaInZ13IlJfzeli5nRz5pdBQETO6D1P8X42IHRw
|
||||
q431ZtsodQ9NgcWTjmS0Kx4ATwVFp2nkm+MHe7aXTZU
|
||||
-> ssh-ed25519 BAs8QA +VUHgmz2oNG6L1FgZy3uGVMs6qUGirFHK8Ts2ghNLHs
|
||||
sjQu78xqM6KLmRiYd2o2uK/PjYLnyZihzVoCV7qKBX4
|
||||
-> ssh-ed25519 ofQnlg cBfd95Ir33ggt1J1P2TkFRULr2uYPVuyrQ5XpjBxEW0
|
||||
TWFVHboXr95cFm5yjQ7gn7hjbSmVBfB/9dldsoga/9Q
|
||||
-> ssh-ed25519 COspvA RMW9FlDmiQUu7cg0fKir55VqrDRCoYVVZMOcMHyrMj8
|
||||
qeXkWdKFJN7APgYh7AjyJLeQI2CAEaGAcXiVaBaOJwY
|
||||
-> ssh-ed25519 2XrTgw BRobowRWZ9giVL2dFyGvzzF7gyWUQd1ounMQBtsM/lM
|
||||
dFyli2skTgzVWGVolLG2GuGNh/Xu3IaJsznOkcWqKGc
|
||||
-> ssh-ed25519 awJeHA Cu7fiv+SL71oho/xoJMw/Lztf4WkNKmImVS/8xyLiTo
|
||||
3sB/t0squi1crjHFBaN6btrvGUeWaKfmGa7yxREvy2o
|
||||
--- SqPDTJ/XV26nNG1ib5phNNRdQi5+Wk0cxhqUr1ygjGw
|
||||
Æt”OÊá<C3A1>âåYöª¶^´Iõ×U<C397>j†ë!k‹Y.<2E>^<5E>}Xúôæ¡3¿ÖŽ"kE×íšú¾‹s¥,0l+¾ýn‡;fW®‹
|
||||
-> ssh-ed25519 Jpc21A gkSA9BJIPUm4iQdvK8OzozIPomCha7BObc7PBpwSBhc
|
||||
QsxIoX8KXbTQPwHcANNotxje8eI51h4NmnMpcOBW2Rk
|
||||
-> ssh-ed25519 BAs8QA 909RuMasqJSYIDWxUf282xkp6vIrmouv/UbGFLw2WXw
|
||||
a7gid67fIVfuRfLsKJPb9f0oH0ZsZsfahJqD22z5Aco
|
||||
-> ssh-ed25519 ofQnlg Lx2N7tSXh6eOwcXWDiU27W4D2NEH6xj3W0t72hkNBG8
|
||||
O4/RVwxUSgXgLEMBpmaJ3H49qXulSB5EebpHakcN1rA
|
||||
-> ssh-ed25519 COspvA zQ855/8dQm+r2/GnoEFwy7ls3UDaVVaL988Rnsgs5Fs
|
||||
gl/sC2jLUCDQfsIOy6G67XObfW/io/JwCKBaqTgpzXk
|
||||
-> ssh-ed25519 2XrTgw ic13iHGBiNgco5PemRhzKNGdVILW0d6DpW1f/SvizSg
|
||||
D72w8Dgott/agkWJrybDbxBKJ3NKi7Xz2N6YO0nrTa8
|
||||
-> ssh-ed25519 awJeHA 143iH2pm6z9AF8fqdRbcw3c5+crLkk3HH0+wEZelu0A
|
||||
j6gK7R7AnI8JzWy7+3Wm00vmaU9/Th2BtNWs90q2r0k
|
||||
-> ssh-ed25519 8FIE3Q jL6tkwWOvL82zFr+kmyX3WNlFMOLsJav4Rpy7A66Hx4
|
||||
gE8+tgytH+y8HTKbeBsQeKKnqfmvl5O38diRsjipTDc
|
||||
-> ssh-ed25519 i+ecmQ AukyGGsUOnTj/h7sxxrldeskrMC2Wn4UL+E+HBIs9R0
|
||||
S2EwHq05mSOqTAih7nkj31NU9GitxMdSm+/BlLOQsis
|
||||
--- jW2fMlcUYlscw0dAkR5T+yfilTWOiODJmuqzFypcjUU
|
||||
õ˜›µ 0Ïfø‰œ]Á€âXøè:HœPZðGk<47>ÜþÊÕxmÿÀ©K‚P$z<>ŒŸð¯ÃRCš5Üå˜FH\A 2Z&ì—Óð±¯lü`p
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -7,11 +7,12 @@ let
|
|||
|
||||
keys = import ../keys;
|
||||
contributors = attrValues keys.contributors;
|
||||
cd = [ keys.cd ];
|
||||
in
|
||||
|
||||
concatMapAttrs
|
||||
(name: systems: {
|
||||
"${name}.age".publicKeys = contributors ++ systems;
|
||||
"${name}.age".publicKeys = contributors ++ systems ++ cd;
|
||||
})
|
||||
|
||||
(
|
||||
|
|
@ -26,7 +27,7 @@ concatMapAttrs
|
|||
{
|
||||
forgejo-database-password = [ vm02116 ];
|
||||
forgejo-email-password = [ vm02116 ];
|
||||
forgejo-runner-token = [ ];
|
||||
forgejo-runner-token = [ forgejo-ci ];
|
||||
panel-secret-key = [ fedi201 ];
|
||||
panel-ssh-key = [ fedi201 ];
|
||||
wiki-basicauth-htpasswd = [ vm02187 ];
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue