nixos-test-pixelfed-wip #22

Merged
taeer merged 11 commits from nixos-test-pixelfed-wip into main 2024-09-20 17:47:21 +02:00
9 changed files with 68 additions and 29 deletions
Showing only changes of commit 9d1f20fc1c - Show all commits

View file

@ -1,6 +1,7 @@
{ lib, ... }: { lib, config, ... }:
let let
inherit (builtins) toString;
inherit (lib) mkOption; inherit (lib) mkOption;
inherit (lib.types) types; inherit (lib.types) types;
@ -19,9 +20,38 @@ in {
default = false; default = false;
}; };
Niols marked this conversation as resolved Outdated
Outdated
Review

This can just be

fediversity = mkEnableOption "the collection of services bundled under fediversity";
This can just be ``` fediversity = mkEnableOption "the collection of services bundled under fediversity"; ```
Outdated
Review

Thanks! Done in 2ff8975b6b.

Thanks! Done in 2ff8975b6b2f19d5ffbd7574bd76494bf532d048.
garage = mkOption {
type = types.anything;
Niols marked this conversation as resolved Outdated
Outdated
Review

You almost never need types.anything. And certainly in this case the set of sub-options should be defined. This can be done with simple attrsets

garage = {
  api = {
    url = mkOption {
      ...
    };
  };
};

Also

We don't need to have a very clear sense of which options belong where yet, but eventually I think the top-level fediversity options should be the ones that should be set publicly (via NixPanel), and probably the ports being used in garage shouldn't be among them.

I'm not sure what the right namespace to use for those options is... maybe fediversity-private or fediversity.private...

You almost never need `types.anything`. And certainly in this case the set of sub-options should be defined. This can be done with simple attrsets ``` garage = { api = { url = mkOption { ... }; }; }; ``` --- Also We don't need to have a very clear sense of which options belong where **yet**, but eventually I think the top-level `fediversity` options should be the ones that should be set publicly (via NixPanel), and probably the ports being used in garage shouldn't be among them. I'm not sure what the right namespace to use for those options is... maybe `fediversity-private` or `fediversity.private`...
Outdated
Review

Done in 73939b9d87.

Done in 73939b9d8752ed4193ebae1b865c306d8eae4971.
};
domain = mkOption {
type = types.string;
Niols marked this conversation as resolved Outdated
Outdated
Review

should be types.str
types.string was being used for too many thing so it got deprecated and now there are several different string types. types.str is the one where you don't want to merge definitions if it's defined in more than one place

should be `types.str` `types.string` was being used for too many thing so it got deprecated and now there are several different string types. `types.str` is the one where you don't want to merge definitions if it's defined in more than one place
Outdated
Review

Done in d97772ccc4.

Done in d97772ccc4008417aec2117d62031e9123d8deba.
};
mastodon.enable = mkOption { type = types.bool; default = false; }; mastodon.enable = mkOption { type = types.bool; default = false; };
pixelfed.enable = mkOption { type = types.bool; default = false; }; pixelfed.enable = mkOption { type = types.bool; default = false; };
peertube.enable = mkOption { type = types.bool; default = false; }; peertube.enable = mkOption { type = types.bool; default = false; };
Niols marked this conversation as resolved Outdated
Outdated
Review

Similarly, mkEnableOption here

Similarly, `mkEnableOption` here
}; };
}; };
config.fediversity = {
Niols marked this conversation as resolved Outdated
Outdated
Review

This should probably be under a call to lib.mkDefault. Or simply put behind the default argument of mkOption. It seems to be the custom (where possible---sometimes it's not) to put it under the default argument.

This should probably be under a call to `lib.mkDefault`. Or simply put behind the `default` argument of `mkOption`. It seems to be the custom (where possible---sometimes it's not) to put it under the `default` argument.
Outdated
Review

Done in 73939b9d87.

Done in 73939b9d8752ed4193ebae1b865c306d8eae4971.
garage = {
api = rec {
domain = "s3.garage.${config.fediversity.domain}";
port = 3900;
url = "http://${domain}:${toString port}";
};
rpc = rec {
port = 3901;
};
web = rec {
rootDomain = "web.garage.${config.fediversity.domain}";
port = 3902;
rootDomainAndPort = "${rootDomain}:${toString port}";
urlFor = bucket: "http://${bucket}.${rootDomainAndPort}";
};
};
};
} }

View file

@ -11,6 +11,7 @@ in
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
let let
inherit (builtins) toString;
inherit (lib) types mkOption mkEnableOption optionalString concatStringsSep; inherit (lib) types mkOption mkEnableOption optionalString concatStringsSep;
inherit (lib.strings) escapeShellArg; inherit (lib.strings) escapeShellArg;
cfg = config.services.garage; cfg = config.services.garage;
@ -41,7 +42,7 @@ let
${optionalString corsRules.enable '' ${optionalString corsRules.enable ''
garage bucket allow --read --write --owner ${bucketArg} --key tmp garage bucket allow --read --write --owner ${bucketArg} --key tmp
# TODO: endpoin-url should not be hard-coded # TODO: endpoin-url should not be hard-coded
aws --region ${cfg.settings.s3_api.s3_region} --endpoint-url http://s3.garage.localhost:3900 s3api put-bucket-cors --bucket ${bucketArg} --cors-configuration ${corsRulesJSON} aws --region ${cfg.settings.s3_api.s3_region} --endpoint-url ${config.fediversity.garage.api.url} s3api put-bucket-cors --bucket ${bucketArg} --cors-configuration ${corsRulesJSON}
garage bucket deny --read --write --owner ${bucketArg} --key tmp garage bucket deny --read --write --owner ${bucketArg} --key tmp
''} ''}
''; '';
@ -135,19 +136,22 @@ in
virtualisation.forwardPorts = [ virtualisation.forwardPorts = [
{ {
from = "host"; from = "host";
host.port = 3901; host.port = config.fediversity.garage.rpc.port;
guest.port = 3901; guest.port = config.fediversity.garage.rpc.port;
} }
{ {
from = "host"; from = "host";
host.port = 3902; host.port = config.fediversity.garage.web.port;
guest.port = 3902; guest.port = config.fediversity.garage.web.port;
} }
]; ];
environment.systemPackages = [ pkgs.minio-client pkgs.awscli ]; environment.systemPackages = [ pkgs.minio-client pkgs.awscli ];
networking.firewall.allowedTCPPorts = [ 3901 3902 ]; networking.firewall.allowedTCPPorts = [
config.fediversity.garage.rpc.port
config.fediversity.garage.web.port
];
services.garage = { services.garage = {
enable = true; enable = true;
package = pkgs.garage_0_9; package = pkgs.garage_0_9;
@ -156,15 +160,15 @@ in
# TODO: use a secret file # TODO: use a secret file
rpc_secret = "d576c4478cc7d0d94cfc127138cbb82018b0155c037d1c827dfb6c36be5f6625"; rpc_secret = "d576c4478cc7d0d94cfc127138cbb82018b0155c037d1c827dfb6c36be5f6625";
# TODO: why does this have to be set? is there not a sensible default? # TODO: why does this have to be set? is there not a sensible default?
rpc_bind_addr = "[::]:3901"; rpc_bind_addr = "[::]:${toString config.fediversity.garage.rpc.port}";
rpc_public_addr = "[::1]:3901"; rpc_public_addr = "[::1]:${toString config.fediversity.garage.rpc.port}";
s3_api.api_bind_addr = "[::]:3900"; s3_api.api_bind_addr = "[::]:${toString config.fediversity.garage.api.port}";
s3_web.bind_addr = "[::]:3902"; s3_web.bind_addr = "[::]:${toString config.fediversity.garage.web.port}";
s3_web.root_domain = ".web.garage.localhost"; s3_web.root_domain = ".${config.fediversity.garage.web.rootDomain}";
index = "index.html"; index = "index.html";
s3_api.s3_region = "garage"; s3_api.s3_region = "garage";
s3_api.root_domain = ".s3.garage.localhost"; s3_api.root_domain = ".${config.fediversity.garage.api.domain}";
}; };
}; };
systemd.services.ensure-garage = { systemd.services.ensure-garage = {
@ -177,9 +181,9 @@ in
script = '' script = ''
set -xeuo pipefail set -xeuo pipefail
# Give garage time to start up by waiting until somethings speaks HTTP # Give Garage time to start up by waiting until somethings speaks HTTP
# behind localhost:3900. # behind Garage's API URL.
until ${pkgs.curl}/bin/curl -sio /dev/null http://localhost:3900/; do sleep 1; done until ${pkgs.curl}/bin/curl -sio /dev/null ${config.fediversity.garage.api.url}; do sleep 1; done
# XXX: this is very sensitive to being a single instance # XXX: this is very sensitive to being a single instance
# (doing the bare minimum to get garage up and running) # (doing the bare minimum to get garage up and running)

View file

@ -38,7 +38,7 @@ lib.mkIf (config.fediversity.enable && config.fediversity.mastodon.enable) {
extraConfig = rec { extraConfig = rec {
S3_ENABLED = "true"; S3_ENABLED = "true";
# TODO: this shouldn't be hard-coded, it should come from the garage configuration # TODO: this shouldn't be hard-coded, it should come from the garage configuration
S3_ENDPOINT = "http://s3.garage.localhost:3900"; S3_ENDPOINT = config.fediversity.garage.api.url;
S3_REGION = "garage"; S3_REGION = "garage";
S3_BUCKET = "mastodon"; S3_BUCKET = "mastodon";
# use <S3_BUCKET>.<S3_ENDPOINT> # use <S3_BUCKET>.<S3_ENDPOINT>
@ -46,7 +46,7 @@ lib.mkIf (config.fediversity.enable && config.fediversity.mastodon.enable) {
AWS_ACCESS_KEY_ID = snakeoil_key.id; AWS_ACCESS_KEY_ID = snakeoil_key.id;
AWS_SECRET_ACCESS_KEY = snakeoil_key.secret; AWS_SECRET_ACCESS_KEY = snakeoil_key.secret;
S3_PROTOCOL = "http"; S3_PROTOCOL = "http";
S3_HOSTNAME = "web.garage.localhost:3902"; S3_HOSTNAME = config.fediversity.garage.web.rootDomainAndPort;
# by default it tries to use "<S3_HOSTNAME>/<S3_BUCKET>" # by default it tries to use "<S3_HOSTNAME>/<S3_BUCKET>"
S3_ALIAS_HOST = "${S3_BUCKET}.${S3_HOSTNAME}"; S3_ALIAS_HOST = "${S3_BUCKET}.${S3_HOSTNAME}";
# SEE: the last section in https://docs.joinmastodon.org/admin/optional/object-storage/ # SEE: the last section in https://docs.joinmastodon.org/admin/optional/object-storage/

View file

@ -56,27 +56,27 @@ lib.mkIf (config.fediversity.enable && config.fediversity.peertube.enable) {
settings = { settings = {
object_storage = { object_storage = {
enabled = true; enabled = true;
endpoint = "http://s3.garage.localhost:3900"; endpoint = config.fediversity.garage.api.url;
region = "garage"; region = "garage";
# not supported by garage # not supported by garage
# SEE: https://garagehq.deuxfleurs.fr/documentation/connect/apps/#peertube # SEE: https://garagehq.deuxfleurs.fr/documentation/connect/apps/#peertube
proxy.proxyify_private_files = false; proxy.proxyify_private_files = false;
web_videos = { web_videos = rec {
bucket_name = "peertube-videos"; bucket_name = "peertube-videos";
prefix = ""; prefix = "";
base_url = "http://peertube-videos.web.garage.localhost:3902"; base_url = config.fediversity.garage.web.urlFor bucket_name;
}; };
videos = { videos = rec {
bucket_name = "peertube-videos"; bucket_name = "peertube-videos";
prefix = ""; prefix = "";
base_url = "http://peertube-videos.web.garage.localhost:3902"; base_url = config.fediversity.garage.web.urlFor bucket_name;
}; };
streaming_playlists = { streaming_playlists = rec {
bucket_name = "peertube-playlists"; bucket_name = "peertube-playlists";
prefix = ""; prefix = "";
base_url = "http://peertube-playlists.web.garage.localhost:3902"; base_url = config.fediversity.garage.web.urlFor bucket_name;
}; };
}; };
}; };

View file

@ -37,7 +37,6 @@ lib.mkIf (config.fediversity.enable && config.fediversity.pixelfed.enable) {
services.pixelfed.enable = true; services.pixelfed.enable = true;
# TODO: factor these out so we're only defining e.g. s3.garage.localhost and port 3900 in one place
services.pixelfed.settings = { services.pixelfed.settings = {
# DANGEROUSLY_SET_FILESYSTEM_DRIVER = "s3"; # DANGEROUSLY_SET_FILESYSTEM_DRIVER = "s3";
FILESYSTEM_CLOUD = "s3"; FILESYSTEM_CLOUD = "s3";
@ -45,9 +44,9 @@ lib.mkIf (config.fediversity.enable && config.fediversity.pixelfed.enable) {
AWS_ACCESS_KEY_ID = snakeoil_key.id; AWS_ACCESS_KEY_ID = snakeoil_key.id;
AWS_SECRET_ACCESS_KEY = snakeoil_key.secret; AWS_SECRET_ACCESS_KEY = snakeoil_key.secret;
AWS_DEFAULT_REGION = "garage"; AWS_DEFAULT_REGION = "garage";
AWS_URL = "http://pixelfed.web.garage.localhost:3902/"; AWS_URL = config.fediversity.garage.web.urlFor "pixelfed";
AWS_BUCKET = "pixelfed"; AWS_BUCKET = "pixelfed";
AWS_ENDPOINT = "http://s3.garage.localhost:3900"; AWS_ENDPOINT = config.fediversity.garage.api.url;
AWS_USE_PATH_STYLE_ENDPOINT = false; AWS_USE_PATH_STYLE_ENDPOINT = false;
}; };

View file

@ -95,6 +95,7 @@ pkgs.nixosTest {
server.succeed("toot post --media $POST_MEDIA") server.succeed("toot post --media $POST_MEDIA")
with subtest("access garage"): with subtest("access garage"):
## REVIEW: could we grab `config.fediversity.garage.api.url` here in some way?
server.succeed("mc alias set garage http://s3.garage.localhost:3900 --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") server.succeed("mc alias set garage http://s3.garage.localhost:3900 --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY")
Review

Yes! In typical nix dynamically-typed fashion, testScript can also be a function! And we can have

testScript = { nodes, ... }: ''
...
   ${nodes.server.fediversity.garage.api.url}
...
'';
Yes! In typical nix dynamically-typed fashion, `testScript` can also be a function! And we can have ``` testScript = { nodes, ... }: '' ... ${nodes.server.fediversity.garage.api.url} ... ''; ```
server.succeed("mc ls garage/mastodon") server.succeed("mc ls garage/mastodon")
@ -121,6 +122,7 @@ pkgs.nixosTest {
raise Exception("mastodon did not send a content security policy header") raise Exception("mastodon did not send a content security policy header")
csp = csp_match.group(1) csp = csp_match.group(1)
# the img-src content security policy should include the garage server # the img-src content security policy should include the garage server
## REVIEW: could we grab `config.fediversity.garage.web.url` here in some way?
Outdated
Review

Yes, though we need to be careful about escaping for the regex

Yes, though we need to be careful about escaping for the regex
garage_csp = re.match(".*; img-src[^;]*web\.garage\.localhost:3902.*", csp) garage_csp = re.match(".*; img-src[^;]*web\.garage\.localhost:3902.*", csp)
if garage_csp is None: if garage_csp is None:
raise Exception("Mastodon's content security policy does not include garage server. image will not be displayed properly on mastodon.") raise Exception("Mastodon's content security policy does not include garage server. image will not be displayed properly on mastodon.")

View file

@ -186,6 +186,7 @@ pkgs.nixosTest {
raise Exception("cannot detect the uploaded image on pixelfed page.") raise Exception("cannot detect the uploaded image on pixelfed page.")
with subtest("access garage"): with subtest("access garage"):
## REVIEW: could we grab `config.fediversity.garage.api.url` here in some way?
server.succeed("mc alias set garage http://s3.garage.localhost:3900 --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") server.succeed("mc alias set garage http://s3.garage.localhost:3900 --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY")
server.succeed("mc ls garage/pixelfed") server.succeed("mc ls garage/pixelfed")
@ -202,6 +203,7 @@ pkgs.nixosTest {
with subtest("Check that image comes from garage"): with subtest("Check that image comes from garage"):
src = server.succeed("su - selenium -c 'selenium-script-get-src ${email} ${password}'") src = server.succeed("su - selenium -c 'selenium-script-get-src ${email} ${password}'")
## REVIEW: could we grab `config.fediversity.garage.web.url` here in some way?
if not src.startswith("http://pixelfed.web.garage.localhost:3902/"): if not src.startswith("http://pixelfed.web.garage.localhost:3902/"):
raise Exception("image does not come from garage") raise Exception("image does not come from garage")
''; '';

View file

@ -9,6 +9,7 @@
{ {
fediversity = { fediversity = {
enable = true; enable = true;
domain = "localhost";
mastodon.enable = true; mastodon.enable = true;
}; };

View file

@ -7,6 +7,7 @@
fediversity = { fediversity = {
enable = true; enable = true;
domain = "localhost";
pixelfed.enable = true; pixelfed.enable = true;
}; };
@ -24,7 +25,7 @@
# I feel like this should have an `enable` option and be configured via `services.nginx` rather than mirroring those options in services.pixelfed.nginx # I feel like this should have an `enable` option and be configured via `services.nginx` rather than mirroring those options in services.pixelfed.nginx
# TODO: If that indeed makes sense, upstream it. # TODO: If that indeed makes sense, upstream it.
nginx = { nginx = {
# locations."/public/".proxyPass = "http://pixelfed.web.garage.localhost:3902/public/"; # locations."/public/".proxyPass = "${config.fediversity.garage.web.urlFor "pixelfed"}/public/";
}; };
}; };
virtualisation.memorySize = 2048; virtualisation.memorySize = 2048;