Compare commits

...

10 commits

Author SHA1 Message Date
4bff10c692
rename internal implementation to declare, mirroring apply 2025-08-27 12:44:30 +02:00
b13d00cff0
factor common logic back out
note that i worked around the question of how to delegate part of the
options to the consumer by creating aliases for different parts of the
function slot filling.
i've been pretty arbitrary about this so far tho, and mostly to preserve
existing interfaces, rather than out of conscious UX design per se.
so the interface can def change still, but at least the _user_'s side is
more DRY now!
2025-08-27 12:44:30 +02:00
24134c864d
combine UX with parameter type-check - DRYing TODO 2025-08-27 12:44:30 +02:00
3fb1889438
factor out function wrapper to module function
note that this does not yet contain the 'type check', as this does not
function yet.
this could be added in `data-model.nix` like:

```nix
function-type-check = mkOption {
  type = application.config.config-mapping.function-type;
  readOnly = true;
  default = input: {
    inherit input;
    output = application.config.config-mapping.implementation-type
input;
  };
};
```

... or even to `function.nix` itself, like:

```nix
function-type-check = mkOption {
  type = config.function-type;
  readOnly = true;
  default = input: {
    inherit input;
    output = config.implementation-type input;
  };
};
```
2025-08-27 12:44:30 +02:00
9d903f3ef7 Complete the data model with a runtime environment and end-to-end test (#481)
Closes #103

At last, a fully fledged data model for what Fediversity really is and does. This comes with a test that exercises a very simple but functionally complete arrangement with all ingredients fo the business logic: a dummy resource (login shell), a dummy application (`hello`, which needs a shell to live in), a dummy environment (a single NixOS VM that allows for one, the operator's, login shell), and a deployment of that environment given a dummy configuration (that enables `hello`).

The next step will be to lift this purely evaluation-level test into a VM test which verifies that the resulting VM indeed has `hello` deployed to the operator's user account.

Caveats:
- The exact naming has a bit of room for improvement, and may have diverged from the design document
- The test is not as pedantically type safe as it could be, since we simply use `types.raw` for resources such as NixOS users settings which *could* be more finely delineated

Co-authored-by: Kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: kiara Grouwstra <kiara@procolix.eu>
Reviewed-on: Fediversity/Fediversity#481
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-committed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2025-08-27 00:45:49 +02:00
1f99a4c6c3 listToAttrs o map o attrsToListmapAttrs' (#489)
Reviewed-on: Fediversity/Fediversity#489
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-08-01 13:09:26 +02:00
588bb77a94 Infra: expose and use checks for vmOptions and nixosConfigurations (#488)
Following Fediversity/Fediversity#478 (comment), here is a PR that plugs the infra's `vmOptions` and `nixosConfigurations` outputs into flake checks, instead of calling random Nix commands from the CI. There is still a bit of magic in the CI, but that's because we don't have yet a Nix-aware CI that exposes one job per flake check.

Reviewed-on: Fediversity/Fediversity#488
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-07-31 15:41:02 +02:00
df3a070fa4 Infra: get rid of makeResourceModule (#485)
Reviewed-on: Fediversity/Fediversity#485
Reviewed-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-07-31 00:49:00 +02:00
be72b82875 Link to upstreaming PR for lib.types.fileset (#487)
Reviewed-on: Fediversity/Fediversity#487
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-07-30 13:25:22 +02:00
1b66028f32 Fix infra and add more tests (#478)
This PR contains a bunch of small fixes having to do with infra code. The goal is not to fix everything as that would require a full rewrite. Instead, we fix just what is necessary to get some testing going on. Once that is available, we will be able to work on a full refactor with more guarantees. Something of note is that most of the difficulty was to find code that would make both `nixops4 apply` _and_ `nix build .#nixosConfigurations.<machine>` happy. The takeaway is that the tests that we are adding now will not catch a whole class of tests having to do with how NixOps4 wires up the resources. Still, this is probably less significant as we are supposed to use NixOps4 every now and then.

The commits should be read separately.

Reviewed-on: Fediversity/Fediversity#478
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-07-30 12:31:03 +02:00
28 changed files with 486 additions and 194 deletions

View file

@ -56,3 +56,29 @@ jobs:
steps:
- uses: actions/checkout@v4
- run: nix build .#checks.x86_64-linux.deployment-panel -L
## NOTE: NixOps4 does not provide a good “dry run” mode, so we instead check
## proxies for resources, namely whether their `.#vmOptions.<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

View file

@ -11,7 +11,8 @@ let
;
inherit (pkgs) lib;
inherit (import sources.flake-inputs) import-flake;
inherit ((import-flake { src = ./.; }).inputs) nixops4;
inputs = (import-flake { src = ./.; }).inputs;
inherit (inputs) nixops4;
panel = import ./panel { inherit sources system; };
pre-commit-check =
(import "${git-hooks}/nix" {
@ -78,6 +79,7 @@ in
# re-export inputs so they can be overridden granularly
# (they can't be accessed from the outside any other way)
inherit
inputs
sources
system
pkgs

View file

@ -48,7 +48,8 @@ in
extraTestScript = mkOption { };
sourceFileset = mkOption {
## REVIEW: Upstream to nixpkgs?
## FIXME: grab `lib.types.fileset` from NixOS, once upstreaming PR
## https://github.com/NixOS/nixpkgs/pull/428293 lands.
type = types.mkOptionType {
name = "fileset";
description = "fileset";

View file

@ -1,7 +1,7 @@
let
inherit (import ../default.nix { }) pkgs inputs;
inherit (pkgs) lib;
inherit (lib) mkOption;
inherit (lib) mkOption types;
eval =
module:
(lib.evalModules {
@ -13,17 +13,83 @@ let
./data-model.nix
];
}).config;
nixops4Deployment = inputs.nixops4.modules.nixops4Deployment.default;
inherit (inputs.nixops4.lib) mkDeployment;
in
{
_class = "nix-unit";
test-eval = {
/**
This tests a very simple arrangement that features all ingredients of the Fediversity business logic:
application, resource, environment, deployment; and wires it all up in one end-to-end exercise.
- The dummy resource is a login shell made available for some user.
- The dummy application is `hello` that requires a shell to be deployed.
- The dummy environment is a single NixOS VM that hosts one login shell, for the operator.
- The dummy configuration enables the `hello` application.
This will produce a NixOps4 deployment for a NixOS VM with a login shell for the operator and `hello` available.
*/
expr =
let
fediversity = eval (
{ config, ... }:
{
config = {
resources.login-shell = {
description = "The operator needs to be able to log into the shell";
request =
{ ... }:
{
_class = "fediversity-resource-request";
options = {
wheel = mkOption {
description = "Whether the login user needs root permissions";
type = types.bool;
default = false;
};
packages = mkOption {
description = "Packages that need to be available in the user environment";
type = with types; attrsOf package;
};
};
};
policy =
{ config, ... }:
{
_class = "fediversity-resource-policy";
options = {
username = mkOption {
description = "Username for the operator";
type = types.str; # TODO: use the proper constraints from NixOS
};
wheel = mkOption {
description = "Whether to allow login with root permissions";
type = types.bool;
default = false;
};
};
config = {
resource-type = types.raw; # TODO: splice out the user type from NixOS
apply =
requests:
let
# Filter out requests that need wheel if policy doesn't allow it
validRequests = lib.filterAttrs (
_name: req: !req.login-shell.wheel || config.wheel
) requests.resources;
in
lib.optionalAttrs (validRequests != { }) {
${config.username} = {
isNormalUser = true;
packages =
with lib;
attrValues (concatMapAttrs (_name: request: request.login-shell.packages) validRequests);
extraGroups = lib.optional config.wheel "wheel";
};
};
};
};
};
applications.hello =
{ ... }:
{
@ -31,14 +97,38 @@ in
module =
{ ... }:
{
options = {
enable = lib.mkEnableOption "Hello in the shell";
};
options.enable = lib.mkEnableOption "Hello in the shell";
};
implementation =
cfg:
lib.optionalAttrs cfg.enable {
dummy.login-shell.packages.hello = pkgs.hello;
resources.hello.login-shell.packages.hello = pkgs.hello;
};
};
environments.single-nixos-vm =
{ config, ... }:
{
resources.operator-environment.login-shell.username = "operator";
implementation =
requests:
{ providers, ... }:
{
providers = {
inherit (inputs.nixops4.modules.nixops4Provider) local;
};
resources.the-machine = {
type = providers.local.exec;
imports = [
inputs.nixops4-nixos.modules.nixops4Resource.nixos
];
nixos.module =
{ ... }:
{
users.users = config.resources.shell.login-shell.apply (
lib.filterAttrs (_name: value: value ? login-shell) requests
);
};
};
};
};
};
@ -51,20 +141,64 @@ in
applications.hello.enable = true;
};
};
example-deployment = mkOption {
type = types.submodule nixops4Deployment;
readOnly = true;
default = config.environments.single-nixos-vm.deployment config.example-configuration;
};
};
}
);
resources = fediversity.applications.hello.resources fediversity.example-configuration.applications.hello;
hello-shell = resources.resources.hello.login-shell;
environment = fediversity.environments.single-nixos-vm.resources.operator-environment.login-shell;
result = mkDeployment {
modules = [
(fediversity.environments.single-nixos-vm.deployment fediversity.example-configuration)
];
};
in
{
inherit (fediversity)
example-configuration
;
number-of-resources = with lib; length (attrNames fediversity.resources);
inherit (fediversity) example-configuration;
hello-package-exists = hello-shell.packages ? hello;
wheel-required = hello-shell.wheel;
wheel-allowed = environment.wheel;
operator-shell =
let
operator = (environment.apply resources).operator;
in
{
inherit (operator) isNormalUser;
packages = map (p: "${p.pname}") operator.packages;
extraGroups = operator.extraGroups;
};
deployment = {
inherit (result) _type;
deploymentFunction = lib.isFunction result.deploymentFunction;
getProviders = lib.isFunction result.getProviders;
};
};
expected = {
number-of-resources = 1;
example-configuration = {
enable = true;
applications.hello.enable = true;
};
hello-package-exists = true;
wheel-required = false;
wheel-allowed = false;
operator-shell = {
isNormalUser = true;
packages = [ "hello" ];
extraGroups = [ ];
};
deployment = {
_type = "nixops4Deployment";
deploymentFunction = true;
getProviders = true;
};
};
};
}

View file

@ -1,6 +1,7 @@
{
lib,
config,
inputs,
...
}:
let
@ -15,19 +16,73 @@ let
;
functionType = import ./function.nix;
application-resources = {
application-resources = submodule {
options.resources = mkOption {
# TODO: maybe transpose, and group the resources by type instead
type = attrsOf (
attrTag (lib.mapAttrs (_name: resource: mkOption { type = resource.request; }) config.resources)
attrTag (
lib.mapAttrs (_name: resource: mkOption { type = submodule resource.request; }) config.resources
)
);
};
};
nixops4Deployment = types.deferredModuleWith {
staticModules = [
inputs.nixops4.modules.nixops4Deployment.default
{
_class = "nixops4Deployment";
_module.args = {
resourceProviderSystem = builtins.currentSystem;
resources = { };
};
}
];
};
in
{
_class = "nixops4Deployment";
options = {
resources = mkOption {
description = "Collection of deployment resources that can be required by applications and policed by hosting providers";
type = attrsOf (
submodule (
{ ... }:
{
_class = "fediversity-resource";
options = {
description = mkOption {
description = "Description of the resource to help application module authors and hosting providers to work with it";
type = types.str;
};
request = mkOption {
description = "Options for declaring resource requirements by an application, a description of how the resource is consumed or accessed";
type = deferredModuleWith { staticModules = [ { _class = "fediversity-resource-request"; } ]; };
};
policy = mkOption {
description = "Options for configuring the resource policy for the hosting provider, a description of how the resource is made available";
type = deferredModuleWith {
staticModules = [
(policy: {
_class = "fediversity-resource-policy";
options.resource-type = mkOption {
description = "The type of resource this policy configures";
type = types.optionType;
};
# TODO(@fricklerhandwerk): we may want to make the function type explict here: `request -> resource-type`
# and then also rename this to be consistent with the application's resource mapping
options.apply = mkOption {
description = "Apply the policy to a request";
type = functionTo policy.config.resource-type;
};
})
];
};
};
};
}
)
);
};
applications = mkOption {
description = "Collection of Fediversity applications";
type = attrsOf (
@ -48,23 +103,89 @@ in
};
resources = mkOption {
description = "Compute resources required by an application";
type = functionTo application.config.config-mapping.output-type;
type = application.config.config-mapping.function-type;
readOnly = true;
default = input: (application.config.implementation input).output;
default = application.config.config-mapping.apply;
};
# TODO(@fricklerhandwerk): this needs a better name
config-mapping = mkOption {
description = "Function type for the mapping from application configuration to required resources";
description = "Compute resources required by an application";
type = submodule functionType;
readOnly = true;
default = {
input-type = application.config.module;
input-type = submodule application.config.module;
output-type = application-resources;
declare = application.config.implementation;
};
};
};
})
);
};
environments = mkOption {
description = "Run-time environments for Fediversity applications to be deployed to";
type = attrsOf (
submodule (environment: {
_class = "fediversity-environment";
options = {
resources = mkOption {
description = ''
Resources made available by the hosting provider, and their policies.
Setting this is optional, but provides a place to declare that information for programmatic use in the resource mapping.
'';
# TODO: maybe transpose, and group the resources by type instead
type = attrsOf (
attrTag (
lib.mapAttrs (_name: resource: mkOption { type = submodule resource.policy; }) config.resources
)
);
};
implementation = mkOption {
description = "Mapping of resources required by applications to available resources; the result can be deployed";
type = environment.config.resource-mapping.function-type;
};
resource-mapping = mkOption {
description = "Function type for the mapping from resources to a (NixOps4) deployment";
type = submodule functionType;
readOnly = true;
default = {
input-type = application-resources;
output-type = nixops4Deployment;
declare = environment.config.implementation;
};
};
config-mapping = mkOption {
description = "Mapping from a configuration to a deployment";
type = submodule functionType;
readOnly = true;
default = {
input-type = config.configuration;
output-type = nixops4Deployment;
declare =
cfg:
# TODO: check cfg.enable.true
let
required-resources = lib.mapAttrs (
name: application-settings: config.applications.${name}.resources application-settings
) cfg.applications;
in
environment.config.resource-mapping.apply required-resources;
};
};
# TODO(@fricklerhandwerk): maybe this should be a separate thing such as `fediversity-setup`,
# which makes explicit which applications and environments are available.
# then the deployments can simply be the result of the function application baked into this module.
deployment = mkOption {
description = "Generate a deployment from a configuration, by applying an environment's resource policies to the applications' resource mappings";
type = environment.config.config-mapping.function-type;
readOnly = true;
default = environment.config.config-mapping.apply;
};
};
})
);
};
configuration = mkOption {
description = "Configuration type declaring options to be set by operators";
type = optionType;

View file

@ -5,7 +5,6 @@
let
inherit (lib) mkOption types;
inherit (types)
deferredModule
submodule
functionTo
optionType
@ -14,24 +13,46 @@ in
{
options = {
input-type = mkOption {
type = deferredModule;
type = optionType;
};
output-type = mkOption {
type = deferredModule;
type = optionType;
};
function-type = mkOption {
type = optionType;
readOnly = true;
default = functionTo config.output-type;
};
wrapper-type = mkOption {
type = optionType;
readOnly = true;
default = functionTo (submodule {
options = {
input = mkOption {
type = submodule config.input-type;
type = config.input-type;
};
output = mkOption {
type = submodule config.output-type;
type = config.output-type;
};
};
});
};
declare = mkOption {
type = config.function-type;
default = _: { };
};
wrapper = mkOption {
type = config.wrapper-type;
readOnly = true;
default = input: fn: {
inherit input;
output = config.implementation fn.config.input;
};
};
apply = mkOption {
type = config.function-type;
readOnly = true;
default = input: (config.wrapper input).output;
};
};
}

View file

@ -20,16 +20,13 @@ in
'';
};
proxmox = mkOption {
type = types.nullOr (
types.enum [
"procolix"
"fediversity"
]
);
isFediversityVm = mkOption {
type = types.bool;
description = ''
The Proxmox instance. This is used for provisioning only and should be
set to `null` if the machine is not a VM.
Whether the machine is a Fediversity VM or not. This is used to
determine whether the machine should be provisioned via Proxmox or not.
Machines that are _not_ Fediversity VM could be physical machines, or
VMs that live outside Fediversity, eg. on Procolix's Proxmox.
'';
};

View file

@ -1,10 +1,14 @@
{ sources, ... }:
{ ... }:
{
_class = "nixos";
imports = [
"${sources.nixpkgs}/nixos/modules/profiles/qemu-guest.nix"
];
## FIXME: It would be nice, but the following leads to infinite recursion
## in the way we currently plug `sources` in.
##
# imports = [
# "${sources.nixpkgs}/nixos/modules/profiles/qemu-guest.nix"
# ];
boot = {
initrd = {

View file

@ -2,7 +2,6 @@
inputs,
lib,
config,
sources,
keys,
secrets,
...
@ -33,10 +32,9 @@ in
## should go into the `./nixos` subdirectory.
nixos.module = {
imports = [
"${sources.agenix}/modules/age.nix"
"${sources.disko}/module.nix"
./options.nix
./nixos
./proxmox-qemu-vm.nix
];
## Inject the shared options from the resource's `config` into the NixOS

View file

@ -14,90 +14,55 @@ let
mkOption
evalModules
filterAttrs
mapAttrs'
deepSeq
;
inherit (lib.attrsets) genAttrs;
## Given a machine's name and whether it is a test VM, make a resource module,
## except for its missing provider. (Depending on the use of that resource, we
## will provide a different one.)
makeResourceModule =
{ vmName, isTestVm }:
{
nixos.module.imports = [
./common/proxmox-qemu-vm.nix
];
nixos.specialArgs = {
inherit
sources
inputs
keys
secrets
;
};
imports =
[
./common/resource.nix
]
++ (
if isTestVm then
[
../machines/operator/${vmName}
{
nixos.module.users.users.root.openssh.authorizedKeys.keys = [
# allow our panel vm access to the test machines
keys.panel
];
}
]
else
[
../machines/dev/${vmName}
]
);
fediversityVm.name = vmName;
commonResourceModule = {
# TODO(@fricklerhandwerk): this is terrible but IMO we should just ditch
# flake-parts and have our own data model for how the project is organised
# internally
_module.args = {
inherit
inputs
keys
secrets
sources
;
};
## FIXME: It would be preferrable to have those `sources`-related imports in
## the modules that use them. However, doing so triggers infinite recursions
## because of the way we propagate `sources`. `sources` must be propagated by
## means of `specialArgs`, but this requires a bigger change.
nixos.module.imports = [
"${sources.nixpkgs}/nixos/modules/profiles/qemu-guest.nix"
"${sources.agenix}/modules/age.nix"
"${sources.disko}/module.nix"
"${sources.home-manager}/nixos"
];
imports = [
./common/resource.nix
];
};
## Given a list of machine names, make a deployment with those machines'
## configurations as resources.
makeDeployment =
vmNames:
{ providers, ... }:
{
# XXX: this type merge is for adding `specialArgs` to resource modules
options.resources = mkOption {
type =
with lib.types;
lazyAttrsOf (submoduleWith {
class = "nixops4Resource";
modules = [ ];
# TODO(@fricklerhandwerk): we may want to pass through all of `specialArgs`
# once we're sure it's sane. leaving it here for better control during refactoring.
specialArgs = {
inherit
sources
inputs
keys
secrets
;
};
});
};
config = {
providers.local = inputs.nixops4.modules.nixops4Provider.local;
resources = genAttrs vmNames (vmName: {
type = providers.local.exec;
imports = [
inputs.nixops4-nixos.modules.nixops4Resource.nixos
(makeResourceModule {
inherit vmName;
isTestVm = false;
})
];
});
};
providers.local = inputs.nixops4.modules.nixops4Provider.local;
resources = genAttrs vmNames (vmName: {
type = providers.local.exec;
imports = [
inputs.nixops4-nixos.modules.nixops4Resource.nixos
commonResourceModule
../machines/dev/${vmName}
];
});
};
makeDeployment' = vmName: makeDeployment [ vmName ];
@ -112,21 +77,29 @@ let
fediversity = import ../services/fediversity;
}
{
garageConfigurationResource = makeResourceModule {
vmName = "test01";
isTestVm = true;
garageConfigurationResource = {
imports = [
commonResourceModule
../machines/operator/test01
];
};
mastodonConfigurationResource = makeResourceModule {
vmName = "test06"; # somehow `test02` has a problem - use test06 instead
isTestVm = true;
mastodonConfigurationResource = {
imports = [
commonResourceModule
../machines/operator/test06 # somehow `test02` has a problem - use test06 instead
];
};
peertubeConfigurationResource = makeResourceModule {
vmName = "test05";
isTestVm = true;
peertubeConfigurationResource = {
imports = [
commonResourceModule
../machines/operator/test05
];
};
pixelfedConfigurationResource = makeResourceModule {
vmName = "test04";
isTestVm = true;
pixelfedConfigurationResource = {
imports = [
commonResourceModule
../machines/operator/test04
];
};
};
@ -139,54 +112,63 @@ let
## this is only needed to expose NixOS configurations for provisioning
## purposes, and eventually all of this should be handled by NixOps4.
options = {
nixos.module = mkOption { }; # NOTE: not just `nixos` otherwise merging will go wrong
nixos.module = mkOption { type = lib.types.deferredModule; }; # NOTE: not just `nixos` otherwise merging will go wrong
nixpkgs = mkOption { };
ssh = mkOption { };
};
};
makeResourceConfig =
vm:
{ vmName, isTestVm }:
(evalModules {
modules = [
nixops4ResourceNixosMockOptions
(makeResourceModule vm)
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:
let
inherit (sources) nixpkgs;
in
import "${nixpkgs}/nixos" {
modules = [
(makeResourceConfig { inherit vmName isTestVm; }).nixos.module
];
import "${sources.nixpkgs}/nixos" {
configuration = (makeResourceConfig { inherit vmName isTestVm; }).nixos.module;
system = "x86_64-linux";
};
makeVmOptions = isTestVm: vmName: {
inherit ((makeResourceConfig { inherit vmName isTestVm; }).fediversityVm)
proxmox
vmId
description
sockets
cores
memory
diskSize
hostPublicKey
unsafeHostPrivateKey
;
};
makeVmOptions =
isTestVm: vmName:
let
config = (makeResourceConfig { inherit vmName isTestVm; }).fediversityVm;
in
if config.isFediversityVm then
{
inherit (config)
vmId
description
sockets
cores
memory
diskSize
hostPublicKey
unsafeHostPrivateKey
;
}
else
null;
listSubdirectories = path: attrNames (filterAttrs (_: type: type == "directory") (readDir path));
machines = listSubdirectories ../machines/dev;
testMachines = listSubdirectories ../machines/operator;
nixosConfigurations =
genAttrs machines (makeConfiguration false)
// genAttrs testMachines (makeConfiguration true);
vmOptions =
filterAttrs (_: value: value != null) # Filter out non-Fediversity VMs
(genAttrs machines (makeVmOptions false) // genAttrs testMachines (makeVmOptions true));
in
{
_class = "flake";
@ -210,10 +192,23 @@ in
)
);
};
flake.nixosConfigurations =
genAttrs machines (makeConfiguration false)
// genAttrs testMachines (makeConfiguration true);
flake.vmOptions =
genAttrs machines (makeVmOptions false)
// genAttrs testMachines (makeVmOptions true);
flake = { inherit nixosConfigurations vmOptions; };
perSystem =
{ pkgs, ... }:
{
checks =
mapAttrs' (name: nixosConfiguration: {
name = "nixosConfigurations-${name}";
value = nixosConfiguration.config.system.build.toplevel;
}) nixosConfigurations
// mapAttrs' (name: vmOptions: {
name = "vmOptions-${name}";
## Check that VM options builds/evaluates correctly. `deepSeq e1
## e2` evaluates `e1` strictly in depth before returning `e2`. We
## use this trick because checks need to be derivations, which VM
## options are not.
value = deepSeq vmOptions pkgs.hello;
}) vmOptions;
};
}

View file

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

View file

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

View file

@ -2,8 +2,9 @@
_class = "nixops4Resource";
fediversityVm = {
name = "fedi200";
isFediversityVm = true;
vmId = 200;
proxmox = "fediversity";
description = "Testing machine for Hans";
domain = "abundos.eu";
@ -16,10 +17,4 @@
gateway = "2a00:51c0:13:1305::1";
};
};
nixos.module = {
imports = [
../../../infra/common/proxmox-qemu-vm.nix
];
};
}

View file

@ -2,8 +2,9 @@
_class = "nixops4Resource";
fediversityVm = {
name = "fedi201";
isFediversityVm = true;
vmId = 201;
proxmox = "fediversity";
description = "FediPanel";
domain = "abundos.eu";
@ -19,7 +20,6 @@
nixos.module = {
imports = [
../../../infra/common/proxmox-qemu-vm.nix
./fedipanel.nix
];
};

View file

@ -1,6 +1,5 @@
{
config,
sources,
...
}:
let
@ -11,7 +10,6 @@ in
imports = [
(import ../../../panel { }).module
"${sources.home-manager}/nixos"
];
security.acme = {

View file

@ -20,7 +20,9 @@ in
ssh.host = mkForce "forgejo-ci";
fediversityVm = {
name = "forgejo-ci";
domain = "procolix.com";
isFediversityVm = false;
ipv4 = {
interface = "enp1s0f0";

View file

@ -2,8 +2,9 @@
_class = "nixops4Resource";
fediversityVm = {
name = "vm02116";
isFediversityVm = false;
vmId = 2116;
proxmox = "procolix";
description = "Forgejo";
ipv4.address = "185.206.232.34";
@ -14,7 +15,6 @@
{ lib, ... }:
{
imports = [
../../../infra/common/proxmox-qemu-vm.nix
./forgejo.nix
];

View file

@ -2,8 +2,9 @@
_class = "nixops4Resource";
fediversityVm = {
name = "vm02187";
isFediversityVm = false;
vmId = 2187;
proxmox = "procolix";
description = "Wiki";
ipv4.address = "185.206.232.187";
@ -14,7 +15,6 @@
{ lib, ... }:
{
imports = [
../../../infra/common/proxmox-qemu-vm.nix
./wiki.nix
];

View file

@ -2,8 +2,9 @@
_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;

View file

@ -2,8 +2,9 @@
_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;

View file

@ -2,8 +2,9 @@
_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;

View file

@ -2,8 +2,9 @@
_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;

View file

@ -2,8 +2,9 @@
_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;

View file

@ -2,8 +2,9 @@
_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;

View file

@ -2,8 +2,9 @@
_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;

View file

@ -2,8 +2,9 @@
_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;

View file

@ -2,8 +2,9 @@
_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;

View file

@ -2,8 +2,9 @@
_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;