Compare commits

..

13 commits

110 changed files with 865 additions and 1183 deletions

View file

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

View file

@ -21,23 +21,17 @@ jobs:
- 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-peertube:
runs-on: native
steps:
- uses: actions/checkout@v4
- run: nix build .#checks.x86_64-linux.test-peertube-service -L
- run: cd services && nix-build -A tests.peertube
check-panel:
runs-on: native
steps:
- uses: actions/checkout@v4
- run: nix-build -A tests.panel
- run: cd panel && nix-build -A tests
check-deployment-basic:
runs-on: native

View file

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

View file

@ -10,9 +10,6 @@ let
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;
@ -58,21 +55,13 @@ in
};
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

View file

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

View file

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

View file

@ -1,36 +0,0 @@
{
inputs,
sources,
lib,
providers,
...
}:
let
inherit (import ./constants.nix) targetMachines pathToRoot pathFromRoot;
in
{
providers = {
inherit (inputs.nixops4.modules.nixops4Provider) local;
};
resources = lib.genAttrs targetMachines (nodeName: {
type = providers.local.exec;
imports = [
inputs.nixops4-nixos.modules.nixops4Resource.nixos
../common/targetResource.nix
];
_module.args = { inherit inputs sources; };
inherit nodeName pathToRoot pathFromRoot;
nixos.module =
{ pkgs, ... }:
{
environment.systemPackages = [ pkgs.${nodeName} ];
};
});
}

View file

@ -0,0 +1,54 @@
{
self,
inputs,
lib,
...
}:
let
inherit (lib) genAttrs;
targetMachines = [
"hello"
"cowsay"
];
pathToRoot = /. + (builtins.unsafeDiscardStringContext self);
pathFromRoot = ./.;
in
{
perSystem =
{ pkgs, ... }:
{
checks.deployment-basic = pkgs.testers.runNixOSTest {
imports = [
../common/nixosTest.nix
./nixosTest.nix
];
_module.args.inputs = inputs;
inherit targetMachines pathToRoot pathFromRoot;
};
};
nixops4Deployments.check-deployment-basic =
{ providers, ... }:
{
providers = {
inherit (inputs.nixops4.modules.nixops4Provider) local;
};
resources = genAttrs targetMachines (nodeName: {
type = providers.local.exec;
imports = [
inputs.nixops4-nixos.modules.nixops4Resource.nixos
../common/targetResource.nix
];
_module.args.inputs = inputs;
inherit nodeName pathToRoot pathFromRoot;
nixos.module =
{ pkgs, ... }:
{
environment.systemPackages = [ pkgs.${nodeName} ];
};
});
};
}

View file

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

View file

@ -1,15 +1,8 @@
{ inputs, lib, ... }:
{ inputs, ... }:
{
_class = "nixosTest";
name = "deployment-basic";
sourceFileset = lib.fileset.unions [
./constants.nix
./deployment.nix
];
nodes.deployer =
{ pkgs, ... }:
{

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,9 +1,4 @@
{
inputs,
hostPkgs,
lib,
...
}:
{ inputs, hostPkgs, ... }:
let
## Some places need a dummy file that will in fact never be used. We create
@ -12,25 +7,8 @@ let
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, ... }:
{

View file

@ -3,7 +3,6 @@
lib,
pkgs,
config,
sources,
...
}:
@ -15,10 +14,10 @@ let
types
;
sources = import ../../../npins;
in
{
_class = "nixos";
imports = [ ./sharedOptions.nix ];
options.system.extraDependenciesFromModule = mkOption {
@ -54,13 +53,13 @@ in
system.extraDependencies =
[
inputs.nixops4
inputs.nixops4-nixos
inputs.nixpkgs
"${inputs.flake-parts}"
"${inputs.flake-parts.inputs.nixpkgs-lib}"
"${inputs.nixops4}"
"${inputs.nixops4-nixos}"
"${inputs.nixpkgs}"
sources.flake-parts
sources.flake-inputs
sources.git-hooks
"${sources.flake-inputs}"
pkgs.stdenv
pkgs.stdenvNoCC
@ -77,7 +76,7 @@ in
config.system.extraDependenciesFromModule
{
nixpkgs.hostPlatform = "x86_64-linux";
_module.args = { inherit inputs sources; };
_module.args.inputs = inputs;
enableAcme = config.enableAcme;
acmeNodeIP = config.acmeNodeIP;
}

View file

@ -3,7 +3,6 @@
lib,
config,
hostPkgs,
sources,
...
}:
@ -13,7 +12,6 @@ let
toJSON
;
inherit (lib)
types
fileset
mkOption
genAttrs
@ -28,6 +26,14 @@ let
forConcat = xs: f: concatStringsSep "\n" (map f xs);
## The whole repository, with the flake at its root.
## FIXME: We could probably have fileset be the union of ./. with flake.nix
## and flake.lock - I doubt we need anything else.
src = fileset.toSource {
fileset = config.pathToRoot;
root = config.pathToRoot;
};
## We will need to override some inputs by the empty flake, so we make one.
emptyFlake = runCommandNoCC "empty-flake" { } ''
mkdir $out
@ -36,8 +42,6 @@ let
in
{
_class = "nixosTest";
imports = [
./sharedOptions.nix
];
@ -46,46 +50,16 @@ in
## FIXME: I wish I could just use `testScript` but with something like
## `mkOrder` to put this module's string before something else.
extraTestScript = mkOption { };
sourceFileset = mkOption {
## REVIEW: Upstream to nixpkgs?
type = types.mkOptionType {
name = "fileset";
description = "fileset";
descriptionClass = "noun";
check = (x: (builtins.tryEval (fileset.unions [ x ])).success);
merge = (_: defs: fileset.unions (map (x: x.value) defs));
};
description = ''
A fileset that will be copied to the deployer node in the current
working directory. This should contain all the files that are
necessary to run that particular test, such as the NixOS
modules necessary to evaluate a deployment.
'';
};
};
config = {
sourceFileset = fileset.unions [
# NOTE: not the flake itself; it will be overridden.
../../../mkFlake.nix
../../../flake.lock
../../../npins
./sharedOptions.nix
./targetNode.nix
./targetResource.nix
(config.pathToCwd + "/flake-under-test.nix")
];
acmeNodeIP = config.nodes.acme.networking.primaryIPAddress;
nodes =
{
deployer = {
imports = [ ./deployerNode.nix ];
_module.args = { inherit inputs sources; };
_module.args.inputs = inputs;
enableAcme = config.enableAcme;
acmeNodeIP = config.nodes.acme.networking.primaryIPAddress;
};
@ -112,7 +86,7 @@ in
genAttrs config.targetMachines (_: {
imports = [ ./targetNode.nix ];
_module.args = { inherit inputs sources; };
_module.args.inputs = inputs;
enableAcme = config.enableAcme;
acmeNodeIP = if config.enableAcme then config.nodes.acme.networking.primaryIPAddress else null;
});
@ -126,16 +100,8 @@ in
${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;
}
}/* .")
deployer.succeed("cp -r --no-preserve=mode ${src}/* .")
with subtest("Configure the network"):
${forConcat config.targetMachines (
@ -165,16 +131,11 @@ in
## 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")
with subtest("Override the lock"):
deployer.succeed("""
nix flake lock --extra-experimental-features 'flakes nix-command' \
--offline -v \
--override-input flake-parts ${inputs.flake-parts} \
--override-input nixops4 ${inputs.nixops4.packages.${system}.flake-in-a-bottle} \
\
--override-input nixops4-nixos ${inputs.nixops4-nixos} \
@ -186,6 +147,9 @@ in
inputs.nixops4-nixos.inputs.nixops4.packages.${system}.flake-in-a-bottle
} \
--override-input nixops4-nixos/git-hooks-nix ${emptyFlake} \
\
--override-input nixpkgs ${inputs.nixpkgs} \
--override-input git-hooks ${inputs.git-hooks} \
;
""")

View file

@ -11,7 +11,6 @@ let
inherit (lib) mkOption types;
in
# `config` not set and imported from multiple places: no fixed module class
{
options = {
targetMachines = mkOption {

View file

@ -12,8 +12,6 @@ let
in
{
_class = "nixos";
imports = [
(modulesPath + "/profiles/qemu-guest.nix")
(modulesPath + "/../lib/testing/nixos-test-base.nix")

View file

@ -2,7 +2,6 @@
inputs,
lib,
config,
sources,
...
}:
@ -13,8 +12,6 @@ let
in
{
_class = "nixops4Resource";
imports = [ ./sharedOptions.nix ];
options = {
@ -41,7 +38,7 @@ in
(lib.modules.importJSON (config.pathToCwd + "/${config.nodeName}-network.json"))
];
_module.args = { inherit inputs sources; };
_module.args.inputs = inputs;
enableAcme = config.enableAcme;
acmeNodeIP = trim (readFile (config.pathToCwd + "/acme_server_ip"));

View file

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

View file

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

View file

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

View file

@ -0,0 +1,91 @@
{
self,
inputs,
lib,
...
}:
let
inherit (builtins)
fromJSON
listToAttrs
;
targetMachines = [
"garage"
"mastodon"
"peertube"
"pixelfed"
];
pathToRoot = /. + (builtins.unsafeDiscardStringContext self);
pathFromRoot = ./.;
enableAcme = true;
in
{
perSystem =
{ pkgs, ... }:
{
checks.deployment-panel = pkgs.testers.runNixOSTest {
imports = [
../common/nixosTest.nix
./nixosTest.nix
];
_module.args.inputs = inputs;
inherit
targetMachines
pathToRoot
pathFromRoot
enableAcme
;
};
};
nixops4Deployments =
let
makeTargetResource = nodeName: {
imports = [ ../common/targetResource.nix ];
_module.args.inputs = inputs;
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
{
check-deployment-panel = makeTestDeployment (
fromJSON (
let
env = builtins.getEnv "DEPLOYMENT";
in
if env == "" then
throw "The DEPLOYMENT environment needs to be set. You do not want to use this deployment unless in the `deployment-panel` NixOS test."
else
env
)
);
};
}

View file

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

View file

@ -121,24 +121,8 @@ let
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;
@ -169,6 +153,7 @@ in
SECRET_KEY = dummyFile;
};
port = panelPort;
nixops4Package = inputs.nixops4.packages.${pkgs.system}.default;
deployment = {
flake = "/run/fedipanel/flake";

View file

@ -1,13 +1,9 @@
let
inherit (import ../default.nix { }) pkgs inputs;
inherit (import ../default.nix { }) pkgs;
inherit (pkgs) lib;
inherit (lib) mkOption;
eval =
module:
(lib.evalModules {
specialArgs = {
inherit inputs;
};
modules = [
module
./data-model.nix
@ -15,56 +11,35 @@ let
}).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;
};
example = eval {
runtime-environments.bar.nixos = {
module =
{ ... }:
{
system.stateVersion = "25.05";
};
};
}
);
};
applications.foo = {
module =
{ pkgs, ... }:
{
environment.systemPackages = [
pkgs.hello
];
};
};
};
in
{
inherit (fediversity)
example-configuration
;
has-runtime = lib.isAttrs example.runtime-environments.bar.nixos.module;
has-application = lib.isAttrs example.applications.foo.module;
};
expected = {
example-configuration = {
enable = true;
applications.hello.enable = true;
};
has-runtime = true;
has-application = true;
};
};
}

View file

@ -1,89 +1,43 @@
{
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)
);
};
};
inherit (lib) types mkOption;
in
with types;
{
_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;
runtime-environments = mkOption {
description = "Collection of runtime environments into which applications can be deployed";
type = attrsOf (attrTag {
nixos = mkOption {
description = "A single NixOS machine";
type = submodule {
options = {
module = mkOption {
description = "The NixOS module describing the base configuration for that machine";
type = deferredModule;
};
};
};
})
);
};
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;
};
};
});
};
applications = mkOption {
description = "Collection of Fediversity applications";
type = attrsOf (submoduleWith {
modules = [
{
options = {
module = mkOption {
description = "The NixOS module for that application, for configuring that application";
type = deferredModule;
};
};
}
];
});
};
};
}

View file

@ -65,8 +65,6 @@ let
cfg = config.deployment;
in
{
_class = "nixops4Deployment";
options = {
deployment = lib.mkOption {
description = ''

View file

@ -1,26 +1,7 @@
{ 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;
};
};
};
imports = [
./check/basic/flake-part.nix
./check/cli/flake-part.nix
./check/panel/flake-part.nix
];
}

View file

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

View file

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

121
flake.lock generated
View file

@ -59,6 +59,22 @@
}
},
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_3": {
"flake": false,
"locked": {
"lastModified": 1733328505,
@ -74,7 +90,7 @@
"type": "github"
}
},
"flake-compat_3": {
"flake-compat_4": {
"flake": false,
"locked": {
"lastModified": 1696426674,
@ -127,6 +143,24 @@
}
},
"flake-parts_3": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib_3"
},
"locked": {
"lastModified": 1738453229,
"narHash": "sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm+zmZ7vxbJdo=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "32ea77a06711b758da0ad9bd6a844c5740a87abd",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_4": {
"inputs": {
"nixpkgs-lib": [
"nixops4-nixos",
@ -167,12 +201,32 @@
"type": "github"
}
},
"git-hooks-nix": {
"git-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1742649964,
"narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"git-hooks-nix": {
"inputs": {
"flake-compat": "flake-compat_2",
"gitignore": "gitignore_2",
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1737465171,
"narHash": "sha256-R10v2hoJRLq8jcL4syVFag7nIGE7m13qO48wRIukWNg=",
@ -227,6 +281,27 @@
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"gitignore_2": {
"inputs": {
"nixpkgs": [
"nixops4-nixos",
@ -266,8 +341,8 @@
},
"nix": {
"inputs": {
"flake-compat": "flake-compat_2",
"flake-parts": "flake-parts_3",
"flake-compat": "flake-compat_3",
"flake-parts": "flake-parts_4",
"git-hooks-nix": "git-hooks-nix_2",
"nixfmt": "nixfmt",
"nixpkgs": [
@ -341,10 +416,10 @@
},
"nixops4": {
"inputs": {
"flake-parts": "flake-parts_2",
"flake-parts": "flake-parts_3",
"nix": "nix",
"nix-cargo-integration": "nix-cargo-integration",
"nixpkgs": "nixpkgs_2",
"nixpkgs": "nixpkgs_3",
"nixpkgs-old": "nixpkgs-old"
},
"locked": {
@ -363,7 +438,7 @@
},
"nixops4-nixos": {
"inputs": {
"flake-parts": "flake-parts",
"flake-parts": "flake-parts_2",
"git-hooks-nix": "git-hooks-nix",
"nixops4": "nixops4",
"nixops4-nixos": [
@ -445,6 +520,18 @@
"url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz"
}
},
"nixpkgs-lib_3": {
"locked": {
"lastModified": 1738452942,
"narHash": "sha256-vJzFZGaCpnmo7I6i416HaBLpC+hvcURh/BQwROcGIp8=",
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz"
}
},
"nixpkgs-old": {
"locked": {
"lastModified": 1735563628,
@ -478,6 +565,22 @@
}
},
"nixpkgs_2": {
"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_3": {
"locked": {
"lastModified": 1738410390,
"narHash": "sha256-xvTo0Aw0+veek7hvEVLzErmJyQkEcRk6PSR4zsRQFEc=",
@ -518,7 +621,7 @@
},
"purescript-overlay": {
"inputs": {
"flake-compat": "flake-compat_3",
"flake-compat": "flake-compat_4",
"nixpkgs": [
"nixops4-nixos",
"nixops4",
@ -561,6 +664,8 @@
},
"root": {
"inputs": {
"flake-parts": "flake-parts",
"git-hooks": "git-hooks",
"nixops4": [
"nixops4-nixos",
"nixops4"

126
flake.nix
View file

@ -1,52 +1,94 @@
{
inputs = {
flake-parts.url = "github:hercules-ci/flake-parts";
git-hooks.url = "github:cachix/git-hooks.nix";
nixops4.follows = "nixops4-nixos/nixops4";
nixops4-nixos.url = "github:nixops4/nixops4-nixos";
};
outputs =
inputs:
import ./mkFlake.nix inputs (
{ inputs, sources, ... }:
inputs@{ self, flake-parts, ... }:
let
sources = import ./npins;
inherit (import sources.flake-inputs) import-flake;
inherit (sources) git-hooks agenix;
# 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';
};
in
# XXX - finally we override the overall set of `inputs` -- we need both:
# `flake-parts obtains `nixpkgs` from `self.inputs` and not from `inputs`.
flake-parts.lib.mkFlake
{
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;
};
};
inputs = inputs // {
inherit nixpkgs;
};
self = self';
}
);
(
{ inputs, ... }:
{
systems = [
"x86_64-linux"
"aarch64-linux"
"x86_64-darwin"
"aarch64-darwin"
];
imports = [
(import "${git-hooks}/flake-module.nix")
inputs.nixops4.modules.flake.default
./deployment/flake-part.nix
./infra/flake-part.nix
];
perSystem =
{
pkgs,
lib,
inputs',
...
}:
{
formatter = pkgs.nixfmt-rfc-style;
pre-commit.settings.hooks =
let
## Add a directory here if pre-commit hooks shouldn't apply to it.
optout = [ "npins" ];
excludes = map (dir: "^${dir}/") optout;
addExcludes = lib.mapAttrs (_: c: c // { inherit excludes; });
in
addExcludes {
nixfmt-rfc-style.enable = true;
deadnix.enable = true;
trim-trailing-whitespace.enable = true;
shellcheck.enable = true;
};
devShells.default = pkgs.mkShell {
packages = [
pkgs.npins
pkgs.nil
(pkgs.callPackage "${agenix}/pkgs/agenix.nix" { })
pkgs.openssh
pkgs.httpie
pkgs.jq
# exposing this env var as a hack to pass info in from form
(inputs'.nixops4.packages.default.overrideAttrs {
impureEnvVars = [ "DEPLOYMENT" ];
})
];
};
};
}
);
}

View file

@ -1,13 +1,14 @@
# Infra
This directory contains the definition of [the VMs](../machines/machines.md) that host our
This directory contains the definition of [the VMs](machines.md) that host our
infrastructure.
## Provisioning VMs with an initial configuration
> NOTE[Niols]: This is still very manual and clunky. Two things will happen:
> 1. In the near future, I will improve the provisioning script to make this a bit less clunky.
> 2. In the far future, NixOps4 will be able to communicate with Proxmox directly and everything will become much cleaner.
NOTE[Niols]: This is very manual and clunky. Two things will happen. In the near
future, I will improve the provisioning script to make this a bit less clunky.
In the far future, NixOps4 will be able to communicate with Proxmox directly and
everything will become much cleaner.
1. Choose names for your VMs. It is recommended to choose `fediXXX`, with `XXX`
above 100. For instance, `fedi117`.
@ -24,7 +25,8 @@ infrastructure.
Those files need to exist during provisioning, but their content matters only
when updating the machines' configuration.
> FIXME: Remove this step by making the provisioning script not fail with the public key does not exist yet.
FIXME: Remove this step by making the provisioning script not fail with the
public key does not exist yet.
3. Run the provisioning script:
```
@ -42,7 +44,7 @@ infrastructure.
ssh fedi117.abundos.eu 'sudo cat /etc/ssh/ssh_host_ed25519_key.pub' > keys/systems/fedi117.pub
```
> FIXME: Make the provisioning script do that for us.
FIXME: Make the provisioning script do that for us.
7. Regenerate the list of machines:
```
@ -54,7 +56,7 @@ infrastructure.
just enough for it to boot and be reachable. Go on to the next section to
update the machine and put an actual configuration.
> FIXME: Figure out why the full configuration isn't on the machine at this
FIXME: Figure out why the full configuration isn't on the machine at this
point and fix it.
## Updating existing VM configurations

View file

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

View file

@ -0,0 +1,84 @@
{ config, lib, ... }:
let
inherit (lib) mkIf mkMerge;
in
{
config = mkMerge [
{
boot.loader = {
systemd-boot.enable = true;
efi.canTouchEfiVariables = true;
};
}
(mkIf config.fediversityVm.isQemuVm {
boot.initrd = {
availableKernelModules = [
"ata_piix"
"uhci_hcd"
"sd_mod"
"sr_mod"
# from `/profiles/qemu-guest.nix`
"virtio_net"
"virtio_pci"
"virtio_mmio"
"virtio_blk"
"virtio_scsi"
"9p"
"9pnet_virtio"
];
kernelModules = [
"dm-snapshot"
# from `/profiles/qemu-guest.nix`
"virtio_balloon"
"virtio_console"
"virtio_rng"
"virtio_gpu"
];
};
disko.devices.disk.main = {
device = "/dev/sda";
type = "disk";
content = {
type = "gpt";
partitions = {
MBR = {
priority = 0;
size = "1M";
type = "EF02";
};
ESP = {
priority = 1;
size = "500M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
root = {
priority = 2;
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
})
];
}

View file

@ -5,8 +5,6 @@ let
in
{
_class = "nixos";
config = {
services.openssh = {
enable = true;
@ -21,8 +19,13 @@ in
## REVIEW: Do we actually need that, considering that we have static IPs?
useDHCP = mkDefault true;
## Disable the default firewall and use nftables instead, with a custom
## Procolix-made ruleset.
nameservers = [
"95.215.185.6"
"95.215.185.7"
"2a00:51c0::5fd7:b906"
"2a00:51c0::5fd7:b907"
];
firewall.enable = false;
nftables = {
enable = true;
@ -39,10 +42,6 @@ in
address = config.fediversityVm.ipv4.gateway;
interface = config.fediversityVm.ipv4.interface;
};
nameservers = [
"95.215.185.6"
"95.215.185.7"
];
})
## IPv6
@ -54,10 +53,6 @@ in
address = config.fediversityVm.ipv6.gateway;
interface = config.fediversityVm.ipv6.interface;
};
nameservers = [
"2a00:51c0::5fd7:b906"
"2a00:51c0::5fd7:b907"
];
})
];
};

View file

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

View file

@ -6,8 +6,6 @@ let
in
{
# `config` not set and imported from multiple places: no fixed module class
options.fediversityVm = {
##########################################################################
@ -177,5 +175,13 @@ in
this for testing machines, as it is a security hole for so many reasons.
'';
};
isQemuVm = mkOption {
description = ''
Whether the machine is a QEMU VM. This will import all the necessary
things.
'';
default = true;
};
};
}

View file

@ -1,58 +0,0 @@
{ sources, ... }:
{
_class = "nixos";
imports = [
"${sources.nixpkgs}/nixos/modules/profiles/qemu-guest.nix"
];
boot = {
initrd = {
availableKernelModules = [
"ata_piix"
"uhci_hcd"
"sd_mod"
"sr_mod"
];
kernelModules = [ "dm-snapshot" ];
};
};
disko.devices.disk.main = {
device = "/dev/sda";
type = "disk";
content = {
type = "gpt";
partitions = {
MBR = {
priority = 0;
size = "1M";
type = "EF02";
};
ESP = {
priority = 1;
size = "500M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
root = {
priority = 2;
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
}

View file

@ -2,9 +2,6 @@
inputs,
lib,
config,
sources,
keys,
secrets,
...
}:
@ -12,11 +9,15 @@ let
inherit (lib) attrValues elem mkDefault;
inherit (lib.attrsets) concatMapAttrs optionalAttrs;
inherit (lib.strings) removeSuffix;
sources = import ../../npins;
inherit (sources) agenix disko;
secretsPrefix = ../../secrets;
secrets = import (secretsPrefix + "/secrets.nix");
keys = import ../../keys;
in
{
_class = "nixops4Resource";
imports = [ ./options.nix ];
fediversityVm.hostPublicKey = mkDefault keys.systems.${config.fediversityVm.name};
@ -33,8 +34,8 @@ in
## should go into the `./nixos` subdirectory.
nixos.module = {
imports = [
"${sources.agenix}/modules/age.nix"
"${sources.disko}/module.nix"
"${agenix}/modules/age.nix"
"${disko}/module.nix"
./options.nix
./nixos
];
@ -43,23 +44,21 @@ in
## configuration.
fediversityVm = config.fediversityVm;
## Read all the secrets, filter the ones that are supposed to be readable with
## public key, and create a mapping from `<name>.file` to the absolute path of
## the secret's file.
## 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 = secrets.rootPath + "/${name}";
}
) secrets.mapping;
optionalAttrs (elem config.fediversityVm.hostPublicKey secret.publicKeys) ({
${removeSuffix ".age" name}.file = secretsPrefix + "/${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 ++ [
# allow our panel vm access to the test machines
keys.panel
# allow continuous deployment access
keys.cd
];
};

View file

@ -1,9 +1,6 @@
{
inputs,
lib,
sources,
keys,
secrets,
...
}:
@ -16,6 +13,7 @@ let
filterAttrs
;
inherit (lib.attrsets) genAttrs;
sources = import ../../npins;
## Given a machine's name and whether it is a test VM, make a resource module,
## except for its missing provider. (Depending on the use of that resource, we
@ -24,21 +22,7 @@ let
{ vmName, isTestVm }:
{
# TODO(@fricklerhandwerk): this is terrible but IMO we should just ditch flake-parts and have our own data model for how the project is organised internally
_module.args = {
inherit
inputs
keys
secrets
;
};
nixos.module.imports = [
./common/proxmox-qemu-vm.nix
];
nixos.specialArgs = {
inherit sources;
};
_module.args = { inherit inputs; };
imports =
[
@ -51,7 +35,7 @@ let
{
nixos.module.users.users.root.openssh.authorizedKeys.keys = [
# allow our panel vm access to the test machines
keys.panel
(import ../keys).panel
];
}
]
@ -69,33 +53,17 @@ let
vmNames:
{ providers, ... }:
{
# XXX: this type merge is for adding `specialArgs` to resource modules
options.resources = mkOption {
type =
with lib.types;
lazyAttrsOf (submoduleWith {
class = "nixops4Resource";
modules = [ ];
# TODO(@fricklerhandwerk): we may want to pass through all of `specialArgs`
# once we're sure it's sane. leaving it here for better control during refactoring.
specialArgs = {
inherit sources;
};
});
};
config = {
providers.local = inputs.nixops4.modules.nixops4Provider.local;
resources = genAttrs vmNames (vmName: {
type = providers.local.exec;
imports = [
inputs.nixops4-nixos.modules.nixops4Resource.nixos
(makeResourceModule {
inherit vmName;
isTestVm = false;
})
];
});
};
providers.local = inputs.nixops4.modules.nixops4Provider.local;
resources = genAttrs vmNames (vmName: {
type = providers.local.exec;
imports = [
inputs.nixops4-nixos.modules.nixops4Resource.nixos
(makeResourceModule {
inherit vmName;
isTestVm = false;
})
];
});
};
makeDeployment' = vmName: makeDeployment [ vmName ];
@ -187,7 +155,9 @@ let
in
{
_class = "flake";
# NOTE: `forgejo-ci`, being a physical machine and not a Proxmox VM, gets
# custom treatment.
imports = [ ./forgejo-ci/flake-part.nix ];
## - Each normal or test machine gets a NixOS configuration.
## - Each normal or test machine gets a VM options entry.

View file

@ -0,0 +1,76 @@
{ config, lib, ... }:
let
inherit (lib) mkDefault mkForce;
in
{
imports = [
../common/options.nix
../common/nixos
./forgejo-actions-runner.nix
];
fediversityVm = {
name = "forgejo-ci";
domain = "procolix.com";
ipv4 = {
interface = "enp1s0f0";
address = "192.168.201.65";
prefixLength = 24;
gateway = "192.168.201.1";
};
ipv6.enable = false;
# Most Procolix machines are QEMU VMs so the options are tailored to them by
# default. `forgejo-ci` is not, so we need to explicitly disable them.
isQemuVm = false;
};
networking = {
nftables.enable = mkForce false;
hostId = "1d6ea552";
};
hardware.cpu.intel.updateMicrocode = mkDefault config.hardware.enableRedistributableFirmware;
boot = {
## In an initial version, we used `mkForce` to remove QEMU VM-specific
## kernel modules. This is a terrible idea as it will also remove other
## kernel modules, for instance the ones added for ZFS.
initrd = {
availableKernelModules = [
"ahci"
"xhci_pci"
"ehci_pci"
"nvme"
"megaraid_sas"
"usbhid"
"usb_storage"
"sd_mod"
];
kernelModules = [ ];
};
kernelModules = [ "kvm-intel" ];
};
fileSystems."/" = {
device = "rpool/root";
fsType = "zfs";
};
fileSystems."/home" = {
device = "rpool/home";
fsType = "zfs";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/50B2-DD3F";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
}

View file

@ -0,0 +1,55 @@
{ lib, inputs, ... }:
## NOTE: Hackish solution mostly taken from `../common/resource.nix`.
## Eventually, `forgejo-ci` should move to a datacentre somewhere and this code
## should be integrated with the code for other machines (in particular VMs).
let
inherit (lib) attrValues elem;
inherit (lib.attrsets) concatMapAttrs optionalAttrs;
inherit (lib.strings) removeSuffix;
secretsPrefix = ../../secrets;
secrets = import (secretsPrefix + "/secrets.nix");
keys = import ../../keys;
hostPublicKey = keys.systems.forgejo-ci;
sources = import ../../npins;
in
{
nixops4Deployments.forgejo-ci =
{ providers, ... }:
{
providers.local = inputs.nixops4.modules.nixops4Provider.local;
resources.forgejo-ci = {
type = providers.local.exec;
imports = [ inputs.nixops4-nixos.modules.nixops4Resource.nixos ];
ssh = {
host = "192.168.201.65";
opts = "-i ~/.ssh/procolix-id_rsa";
hostPublicKey = hostPublicKey;
};
nixpkgs = inputs.nixpkgs;
nixos.module = {
imports = with sources; [
"${agenix}/modules/age.nix"
"${disko}/module.nix"
./configuration.nix
];
age.secrets = concatMapAttrs (
name: secret:
optionalAttrs (elem hostPublicKey secret.publicKeys) ({
${removeSuffix ".age" name}.file = secretsPrefix + "/${name}";
})
) secrets;
users.users.root.openssh.authorizedKeys.keys = attrValues keys.contributors;
};
};
};
}

View file

@ -1,8 +1,6 @@
{ pkgs, config, ... }:
{
_class = "nixos";
services.gitea-actions-runner = {
package = pkgs.forgejo-actions-runner;
@ -17,7 +15,6 @@
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;

View file

@ -15,6 +15,7 @@ let
installer =
{
config,
pkgs,
lib,
...

View file

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

View file

@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAuT3C0f3nyQ7SwUvXcFmEYEgwL+crY6iK0Bhoi9yfn4soz3fhfMKyKSwc/0RIlRnrz3xnkyJiV0vFeU7AC1ixbGCS3T9uc0G1x0Yedd9n2yR8ZJmkdyfjZ5KE4YvqZ3f6UZn5Mtj+7tGmyp+ee+clLSHzsqeyDiX0FIgFmqiiAVJD6qeKPFAHeWz9b2MOXIBIw+fSLOpx0rosCgesOmPc8lgFvo+dMKpSlPkCuGLBPj2ObT4sLjc98NC5z8sNJMu3o5bMbiCDR9JWgx9nKj+NlALwk3Y/nzHSL/DNcnP5vz2zbX2CBKjx6ju0IXh6YKlJJVyMsH9QjwYkgDQVmy8amQ== procolix@sshnode2

View file

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

View file

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

View file

@ -1,6 +1,4 @@
{
_class = "nixops4Resource";
fediversityVm = {
vmId = 200;
proxmox = "fediversity";
@ -16,10 +14,4 @@
gateway = "2a00:51c0:13:1305::1";
};
};
nixos.module = {
imports = [
../../../infra/common/proxmox-qemu-vm.nix
];
};
}

View file

@ -1,6 +1,4 @@
{
_class = "nixops4Resource";
fediversityVm = {
vmId = 201;
proxmox = "fediversity";
@ -19,7 +17,6 @@
nixos.module = {
imports = [
../../../infra/common/proxmox-qemu-vm.nix
./fedipanel.nix
];
};

View file

@ -1,17 +1,15 @@
{
config,
sources,
...
}:
let
name = "panel";
sources = import ../../../npins;
in
{
_class = "nixos";
imports = [
(import ../../../panel { }).module
"${sources.home-manager}/nixos"
(import "${sources.home-manager}/nixos")
];
security.acme = {

View file

@ -1,70 +0,0 @@
{ 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 = {
domain = "procolix.com";
ipv4 = {
interface = "enp1s0f0";
address = "192.168.201.65";
prefixLength = 24;
gateway = "192.168.201.1";
};
ipv6.enable = false;
};
nixos.module =
{ config, ... }:
{
_class = "nixos";
imports = [
./forgejo-actions-runner.nix
];
hardware.cpu.intel.updateMicrocode = mkDefault config.hardware.enableRedistributableFirmware;
networking = {
nftables.enable = mkForce false;
hostId = "1d6ea552";
};
## NOTE: This is a physical machine, so is not covered by disko
fileSystems."/" = lib.mkForce {
device = "rpool/root";
fsType = "zfs";
};
fileSystems."/home" = {
device = "rpool/home";
fsType = "zfs";
};
fileSystems."/boot" = lib.mkForce {
device = "/dev/disk/by-uuid/50B2-DD3F";
fsType = "vfat";
options = [
"fmask=0077"
"dmask=0077"
];
};
};
}

View file

@ -1,6 +1,4 @@
{
_class = "nixops4Resource";
fediversityVm = {
vmId = 2116;
proxmox = "procolix";
@ -14,7 +12,6 @@
{ lib, ... }:
{
imports = [
../../../infra/common/proxmox-qemu-vm.nix
./forgejo.nix
];

View file

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

View file

@ -1,6 +1,4 @@
{
_class = "nixops4Resource";
fediversityVm = {
vmId = 2187;
proxmox = "procolix";
@ -14,7 +12,6 @@
{ lib, ... }:
{
imports = [
../../../infra/common/proxmox-qemu-vm.nix
./wiki.nix
];

View file

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

View file

@ -1,6 +1,4 @@
{
_class = "nixops4Resource";
fediversityVm = {
vmId = 7001;
proxmox = "fediversity";

View file

@ -1,6 +1,4 @@
{
_class = "nixops4Resource";
fediversityVm = {
vmId = 7002;
proxmox = "fediversity";

View file

@ -1,6 +1,4 @@
{
_class = "nixops4Resource";
fediversityVm = {
vmId = 7003;
proxmox = "fediversity";

View file

@ -1,6 +1,4 @@
{
_class = "nixops4Resource";
fediversityVm = {
vmId = 7004;
proxmox = "fediversity";

View file

@ -1,6 +1,4 @@
{
_class = "nixops4Resource";
fediversityVm = {
vmId = 7005;
proxmox = "fediversity";

View file

@ -1,6 +1,4 @@
{
_class = "nixops4Resource";
fediversityVm = {
vmId = 7006;
proxmox = "fediversity";

View file

@ -1,6 +1,4 @@
{
_class = "nixops4Resource";
fediversityVm = {
vmId = 7011;
proxmox = "fediversity";

View file

@ -1,6 +1,4 @@
{
_class = "nixops4Resource";
fediversityVm = {
vmId = 7012;
proxmox = "fediversity";

View file

@ -1,6 +1,4 @@
{
_class = "nixops4Resource";
fediversityVm = {
vmId = 7013;
proxmox = "fediversity";

View file

@ -1,6 +1,4 @@
{
_class = "nixops4Resource";
fediversityVm = {
vmId = 7014;
proxmox = "fediversity";

View file

@ -1,54 +0,0 @@
## 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 ];
}

View file

@ -22,12 +22,12 @@ in
manage
# NixOps4 and its dependencies
pkgs.nixops4
# FIXME: grab NixOps4 and add it here
pkgs.nix
pkgs.openssh
];
env = {
DEPLOYMENT_FLAKE = toString ../.;
DEPLOYMENT_FLAKE = ../.;
DEPLOYMENT_NAME = "test";
NPINS_DIRECTORY = toString ../npins;
CREDENTIALS_DIRECTORY = toString ./.credentials;
@ -45,7 +45,7 @@ in
'';
};
module = ./nix/configuration.nix;
module = import ./nix/configuration.nix;
tests = pkgs.callPackage ./nix/tests.nix { };
# re-export inputs so they can be overridden granularly

View file

@ -2,6 +2,7 @@
config,
pkgs,
lib,
inputs,
...
}:
let
@ -76,8 +77,6 @@ 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 {
@ -147,7 +146,19 @@ in
NixOps4 from the package's npins-based code, we will have to do with
this workaround.
'';
default = pkgs.nixops4;
default =
let
sources = import ../../npins;
inherit (import sources.flake-inputs) import-flake load-flake;
inherit
(import-flake {
src = ../../.;
})
inputs
;
inherit (inputs) nixops4;
in
(load-flake nixops4).packages.${pkgs.system}.default;
};
deployment = {
@ -202,8 +213,11 @@ in
};
};
# needed to place a config file with home-manager
users.users.${name}.isNormalUser = true;
users.users.${name} = {
# TODO[Niols]: change to system user or document why we specifically
# need a normal user.
isNormalUser = true;
};
users.groups.${name} = { };
systemd.services.${name} = {

View file

@ -8,17 +8,4 @@ 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;
}

View file

@ -60,8 +60,6 @@ let
];
in
python3.pkgs.buildPythonPackage {
_class = "package";
pname = name;
inherit (pyproject.project) version;
pyproject = true;

View file

@ -8,8 +8,6 @@
}:
buildPythonPackage rec {
_class = "package";
pname = "django-pydantic-field";
version = "v0.3.12";
pyproject = true;

View file

@ -10,8 +10,6 @@
}:
buildPythonPackage rec {
_class = "package";
pname = "drf-pydantic";
version = "v2.7.1";
pyproject = true;

View file

@ -13,6 +13,7 @@ let
secrets = {
SECRET_KEY = pkgs.writeText "SECRET_KEY" "secret";
};
nixops4Package = pkgs.hello; # FIXME: actually pass NixOps4
};
virtualisation = {

View file

@ -1,4 +0,0 @@
{
mapping = import ./secrets.nix;
rootPath = ./.;
}

View file

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

Binary file not shown.

View file

@ -1,19 +1,25 @@
age-encryption.org/v1
-> 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ÿÀ©KP$z<>ŒŸð¯ÃRCš5Üå˜FH\ A 2Z&ì—Óð±¯lü`p
-> ssh-ed25519 Jpc21A 5YVZwmmsBW1XCtm6gEVErkkNdLxoYtR/i7I4T79WQQI
v8JQJN1I3BDP+8XhezeVAVl/Cc6jvKPYlwiT3+O5x3w
-> ssh-ed25519 BAs8QA K9vO+kokOiKg5XHvTbIdSeC+WPTLHP/r5TQ2ySs3AHY
gSXsyZraT3pPxSURXohpkR44ZuUHP2aXfmfMNqbZnZA
-> ssh-ed25519 ofQnlg S+31bIhRpPG2ijrYd6RVDtPLoiH7FtREh+7upM1B6mU
7YILiVq3nZ5+XMmXi5KhsnzQRZdI2Fum21/v7tfGBhM
-> ssh-ed25519 COspvA kIiX95UPzbeOHBeLXPfahesfFpU2zlg0pAhsxJd1pWc
aIZp2xv+JpKMj0mSVy3kU98Pvu3IPns+Yvz4aq0yhFA
-> ssh-ed25519 2XrTgw joBq+DOUf944z/RpN3554eAb5EItlQbpEdpYMPtlVC0
qyUb1c0g9OlAOkTTj+oMpn8I3oI4D4czhdnJ4oA3d80
-> ssh-ed25519 awJeHA +bt39LTdJLhq4NG4T2wAS5PDSgYAYyNIpYgRO5lhvFk
LbJOrKxtnOuMHRxQTtTYGJSnwuNCooFOawJU/xhnquw
-> ssh-rsa pO5rrg
cbw33rxD0P5OyfBjH1PwqSdN6DmhKyDZ8+JbJjs6hqblx1M2DX754POfMmOkQNBX
hbCFc2jtLAU598rFwWBewfO4X4C/mT7JDHHN3WK1T8rq35QBN35rfIdNqjYQttt2
mZ1KDtBKg3+FRkc7F9VL1SwsYkhnW1dz1esb6J1bnAxXHFQEwAX3F30O7to4c7my
5t5bjJHPDuMZvNCWy8H5eA/AIuXZ5/OOA5z5lpWhtp+hNyVjWukrYr/PacdA5n9n
gAm8z6fsEhdGkM9AK+P29lYFvWOnkzOtQ6bzSEHCSvxZcZ922n+grLmuUtMvMPo7
bozXzZfCG7//SgxKdbE5TQ
-> ssh-ed25519 8FIE3Q COBr0dcOY+LseUEl8pe2CzGVHZ1+h21s/qlGwoSwIRU
Ea1XvW4QAXnd4L1t4TiAwb3m2wbLvcEcB8UowilQTVs
--- X2cdDTgAGKfqFu1oaaDJWCWzNBw0q/BTy9rIIxuryTg
5Ïo Õ²s<C2B2> W éœs,âEÎ=§kÂ-ˆ :œ»)Jq_¿)®™¶4ŽQ@Í6ôý<C3B4>ø­Âz)e´Ðµ.¹RYQo8ö:*8•

Binary file not shown.

Binary file not shown.

View file

@ -7,12 +7,11 @@ let
keys = import ../keys;
contributors = attrValues keys.contributors;
cd = [ keys.cd ];
in
concatMapAttrs
(name: systems: {
"${name}.age".publicKeys = contributors ++ systems ++ cd;
"${name}.age".publicKeys = contributors ++ systems;
})
(

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -21,11 +21,11 @@ For those that know it, we could say that the current module is an analogous of
## Content of this directory
- [fediversity](./fediversity) contains the definition of the services. Look in
- [fediversity][./fediversity] contains the definition of the services. Look in
particular at its `default.nix` that contains the definition of the options.
- [vm](./vm) contains options specific to making the service run in local QEMU
- [vm][./vm] contains options specific to making the service run in local QEMU
VMs. These modules will for instance override the defaults to disable SSL, and
they will add virtualisation options to forward ports, for instance.
- [tests](./tests) contain full NixOS tests of the services.
- [tests][./tests] contain full NixOS tests of the services.

13
services/default.nix Normal file
View file

@ -0,0 +1,13 @@
{
system ? builtins.currentSystem,
sources ? import ../npins,
pkgs ? import sources.nixpkgs { inherit system; },
...
}:
{
tests = {
mastodon = pkgs.nixosTest ./tests/mastodon.nix;
pixelfed-garage = pkgs.nixosTest ./tests/pixelfed-garage.nix;
peertube = pkgs.nixosTest ./tests/peertube.nix;
};
}

View file

@ -6,8 +6,6 @@ let
in
{
_class = "nixos";
imports = [
./garage
./mastodon
@ -49,7 +47,7 @@ in
displayName = mkOption {
type = types.str;
description = "Name of the initial user, for humans";
default = config.fediversity.temp.initialUser.username;
default = config.fediversity.temp.initialUser.name;
};
email = mkOption {
type = types.str;
@ -65,16 +63,4 @@ in
};
};
};
config = {
## FIXME: This should clearly go somewhere else; and we should have a
## `staging` vs. `production` setting somewhere.
security.acme = {
acceptTerms = true;
# use a priority more urgent than mkDefault for panel deployment to work,
# yet looser than default so this will not clash with the setting in tests.
defaults.email = lib.modules.mkOverride 200 "something@fediversity.net";
# defaults.server = "https://acme-staging-v02.api.letsencrypt.org/directory";
};
};
}

View file

@ -97,8 +97,6 @@ let
in
{
_class = "nixos";
imports = [ ./options.nix ];
config = mkIf config.fediversity.garage.enable {

View file

@ -5,8 +5,6 @@ let
in
{
_class = "nixos";
options.fediversity.garage = {
enable = mkEnableOption "Enable a Garage server on the machine";

View file

@ -11,8 +11,6 @@ let
in
{
_class = "nixos";
imports = [ ./options.nix ];
config = mkMerge [

View file

@ -1,8 +1,6 @@
{ config, lib, ... }:
{
_class = "nixos";
options.fediversity.mastodon =
(import ../sharedOptions.nix {
inherit config lib;

View file

@ -5,8 +5,6 @@ let
in
{
_class = "nixos";
imports = [ ./options.nix ];
config = mkMerge [

View file

@ -6,8 +6,6 @@ let
in
{
_class = "nixos";
options.fediversity.peertube =
(import ../sharedOptions.nix {
inherit config lib;

View file

@ -15,8 +15,6 @@ let
in
{
_class = "nixos";
imports = [ ./options.nix ];
config = mkMerge [

View file

@ -1,8 +1,6 @@
{ config, lib, ... }:
{
_class = "nixos";
options.fediversity.pixelfed =
(import ../sharedOptions.nix {
inherit config lib;

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