From a5875376b85eaec84065c486135c627dd7cd1ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 17 Dec 2024 17:14:27 +0100 Subject: [PATCH 1/5] Fix Peertube service --- services/fediversity/peertube.nix | 12 +++++++++++- services/vm/peertube-vm.nix | 18 +++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/services/fediversity/peertube.nix b/services/fediversity/peertube.nix index 1d1ea08..bb2b618 100644 --- a/services/fediversity/peertube.nix +++ b/services/fediversity/peertube.nix @@ -11,6 +11,10 @@ lib.mkIf (config.fediversity.enable && config.fediversity.peertube.enable) { networking.firewall.allowedTCPPorts = [ 80 443 + + ## For Live streaming and Live streaming when RTMPS is enabled. + 1935 + 1936 ]; services.garage = { @@ -70,6 +74,8 @@ lib.mkIf (config.fediversity.enable && config.fediversity.peertube.enable) { enabled = true; endpoint = config.fediversity.internal.garage.api.url; region = "garage"; + upload_acl.public = null; # Garage does not support ACL + upload_acl.private = null; # Garage does not support ACL # not supported by garage # SEE: https://garagehq.deuxfleurs.fr/documentation/connect/apps/#peertube @@ -101,7 +107,11 @@ lib.mkIf (config.fediversity.enable && config.fediversity.peertube.enable) { ## Proxying through Nginx - services.peertube.configureNginx = true; + services.peertube = { + configureNginx = true; + listenWeb = 443; + enableWebHttps = true; + }; services.nginx.virtualHosts.${config.services.peertube.localDomain} = { forceSSL = true; enableACME = true; diff --git a/services/vm/peertube-vm.nix b/services/vm/peertube-vm.nix index 9ba7c00..0e2c992 100644 --- a/services/vm/peertube-vm.nix +++ b/services/vm/peertube-vm.nix @@ -1,11 +1,23 @@ -{ modulesPath, ... }: +{ + modulesPath, + pkgs, + ... +}: { - imports = [ (modulesPath + "/virtualisation/qemu-vm.nix") ]; + fediversity = { + enable = true; + domain = "localhost"; + peertube.enable = true; + + temp.peertubeSecretsFile = pkgs.writeText "secret" '' + 574e093907d1157ac0f8e760a6deb1035402003af5763135bae9cbd6abe32b24 + ''; + }; + services.peertube = { - enableWebHttps = false; settings = { listen.hostname = "0.0.0.0"; instance.name = "PeerTube Test VM"; From f1440bc7353157f579e21155f2e3326e7e59bcfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 17 Dec 2024 17:14:58 +0100 Subject: [PATCH 2/5] Rename Mastodon test --- services/tests/mastodon.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/tests/mastodon.nix b/services/tests/mastodon.nix index 7bd36e1..ef6a667 100644 --- a/services/tests/mastodon.nix +++ b/services/tests/mastodon.nix @@ -43,7 +43,7 @@ let in pkgs.nixosTest { - name = "test-mastodon-garage"; + name = "mastodon"; nodes = { server = From 1864e20a8c0bd1ab7674bbb5d8c759ea3f3d064e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 17 Dec 2024 17:15:11 +0100 Subject: [PATCH 3/5] Add Peertube test --- services/flake-part.nix | 1 + services/tests/green.mp4 | Bin 0 -> 1884 bytes services/tests/peertube.nix | 233 ++++++++++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+) create mode 100644 services/tests/green.mp4 create mode 100644 services/tests/peertube.nix diff --git a/services/flake-part.nix b/services/flake-part.nix index 5563878..485e261 100644 --- a/services/flake-part.nix +++ b/services/flake-part.nix @@ -9,6 +9,7 @@ checks = { mastodon = import ./tests/mastodon.nix { inherit self pkgs; }; pixelfed-garage = import ./tests/pixelfed-garage.nix { inherit self pkgs; }; + peertube = import ./tests/peertube.nix { inherit self pkgs; }; }; }; } diff --git a/services/tests/green.mp4 b/services/tests/green.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..82ce4c110052a140fa8ec1044ca0ef0258d15e6a GIT binary patch literal 1884 zcmah~U1%It6uz5ijj?D7DHI#ws)4>_H#3toX@i5VNpOV=2*H55GTz302Ya4U3yvm&B98%`ucipEpTNLinx|!&1uS8^HmgrlcXw0;(UQS!WNFH^Gtf?40oj+CQi%L z7qkV9i&%u6T$);YWB~0HS8bqqp5`5dqPH^F)N2}o(D5i}OV^@|IwGu0 z#7$}HbKLQ>BoY=vb*?fQhCvQ=qJ6@36o9i5nHrWL+_On6O+%m3b?%8=S!v!1Qeu<| zR#GeRyj&`?YH;Oeh{#QYhe^^BJ_c6brt>@u9IT~p(0H5`b2vdH6s;qOmCQl`66|)k znu(6(BvC39#tx21Wg>`?0>n%Z$IC<{a~zg!r5)hF#oV?fT#?c)Y|9QrPLl+gEcUf7 zgQl-+_>xq{Rx?TAEsZHcTe5@Prg2>BK`RPkQ#W|-$XGh7%AD0`nVFy?&1CK)JaeoY z+a-VIm`>*L$i}(SwlFY_nVQB|Xfvi(n*&TKaiK-qoI43au4HP?@*qW#a3N4b`4b7uI@xBlXPeDv}w=U-}E{cPj>t3Uku-O-(Pv;*pB z{la8eziaosCx4{NUf+7SeXe`kbA&5fLM=WxGE!paS>W4&$LbgSN8hdcubmoszPo$z zjpMM5*0;_rADWuK5&1Sn5*u0Gp-9St$oCN$BC^@t-KzdxMt-?`cp&m>k3IZ(lq73l zgwdMsLJ!FB6?~9aScTDVPyRE2dEfYhLSi#hj5iVfbq4cNIJOeNw@S(xVWzr3?cu))hy&Cu6&a#I|TkTS9!l6 z#QBh3OK6h8oTbIUwi|%dNGgXSaI2CcZc2G zg#KWA>Db1`r6n8{yM?-E9%yWysjvh7FF$>C-=%-9U#IA?ROBf%Eri-QzOLRWAH@== zu_L)EDgan+l2Jl%u`#L)Nk86q_PBSH_K}`{yr=tP8d6-53;O^3W5ma;EBF(b+aUv_ zKli~NUV`sz;VC=VhrR~96OB0iMiUO&2FKW&lfX|x`RN*oRaXhg9!fh;mHih8#GzpT literal 0 HcmV?d00001 diff --git a/services/tests/peertube.nix b/services/tests/peertube.nix new file mode 100644 index 0000000..6a5161b --- /dev/null +++ b/services/tests/peertube.nix @@ -0,0 +1,233 @@ +## This file is a basic test of Peertube functionalities. + +{ pkgs, self }: + +let + lib = pkgs.lib; + + testVideo = pkgs.copyPathToStore ./green.mp4; + testVideoColour = "#00FF00"; + + postVideoInBrowser = + pkgs.writers.writePython3Bin "post-video-in-browser" + { + libraries = with pkgs.python3Packages; [ selenium ]; + flakeIgnore = [ "E501" ]; # welcome to the 21st century + } + '' + import sys + from urllib.parse import urlparse + + from selenium import webdriver + from selenium.webdriver.common.by import By + from selenium.webdriver.firefox.options import Options + from selenium.webdriver.support.ui import WebDriverWait + from selenium.common.exceptions import NoSuchElementException + + options = Options() + print("########################################", file=sys.stderr) + print("A", file=sys.stderr) + options.add_argument("--headless") + print("########################################", file=sys.stderr) + print("B", file=sys.stderr) + service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}") + print("########################################", file=sys.stderr) + print("C", file=sys.stderr) + driver = webdriver.Firefox(options=options, service=service) + print("########################################", file=sys.stderr) + print("D", file=sys.stderr) + driver.set_window_size(4096, 2160) + print("########################################", file=sys.stderr) + print("E", file=sys.stderr) + driver.implicitly_wait(360) + print("########################################", file=sys.stderr) + print("F", file=sys.stderr) + wait = WebDriverWait(driver, timeout=360, poll_frequency=10) + print("########################################", file=sys.stderr) + + ############################################################ + # Login + + + def load(driver, page): + print(f"Loading page {page}...", file=sys.stderr) + driver.get(page) + + print("Waiting until page is loaded...", file=sys.stderr) + wait.until(lambda d: d.execute_script("return document.readyState") == "complete") + + if urlparse(driver.current_url).path == "/login": + print("Hit a login page.", file=sys.stderr) + + print("Enter username...", file=sys.stderr) + driver.find_element(By.ID, "username").send_keys("root") + + print("Enter password...", file=sys.stderr) + driver.find_element(By.ID, "password").send_keys(sys.argv[1]) + + print("Click “Login” button...", file=sys.stderr) + driver.find_element(By.XPATH, "//input[@value='Login']").click() + + print("Waiting until we are logged-in...", file=sys.stderr) + wait.until(lambda d: urlparse(d.current_url).path == urlparse(page).path) + + print("Waiting until page is loaded...", file=sys.stderr) + wait.until(lambda d: d.execute_script("return document.readyState") == "complete") + + print("Clicking the annoying setup wizard away...", file=sys.stderr) + try: + driver.find_element(By.XPATH, "//input[@value='Remind me later']").click() + except NoSuchElementException: + # Somehow, sometimes, the wizard just does not show up; then we + # ignore the error and carry on like nothing happened. + print("Setup wizard did not show up.", file=sys.stderr) + + print(f"Done loading page {page}.", file=sys.stderr) + + + ############################################################ + # Upload video and take a screenshot + + print("Go to the upload page...", file=sys.stderr) + load(driver, "http://peertube.localhost/videos/upload") + + print("Submit video file...", file=sys.stderr) + driver.find_element(By.XPATH, "//input[@type='file']").send_keys("${testVideo}") + + print("Wait for file to upload, then publish it...", file=sys.stderr) + publish_button = driver.find_element(By.XPATH, "//button[.//span[normalize-space()='Publish']]") + wait.until(lambda _d: "disabled" not in publish_button.get_attribute("class")) + publish_button.click() + + print("Waiting until we are redirected...", file=sys.stderr) + wait.until(lambda d: urlparse(d.current_url).path != "/videos/upload") + wait.until(lambda d: d.execute_script("return document.readyState") == "complete") + + # FIXME: The video cannot play and we get “Failed to play video”. I + # believe it is a codec problem, and it possibly has to do with video + # codecs enabled in Firefox in headless mode -- after all, who would + # want to play a video? The following is a list of things that I have + # tried without success. Maybe one day we can manage? + # + # video = driver.find_element(By.XPATH, "//video") + # wait.until(lambda _d: not video.get_attribute("src").startswith("blob:")) + # wait.until(lambda d: d.execute_script("return arguments[0].readyState", video) == 4) + # driver.find_element(By.XPATH, "//*[contains(text(), 'Failed to play video')]") + # + # def detect_image_in_screen(d): + # print("Taking a screenshot...", file=sys.stderr) + # d.save_screenshot("/screenshot.png") + # print("Checking it...", file=sys.stderr) + # displayed_colours = subprocess.run( + # [ + # "magick", + # "/screenshot.png", + # "-define", + # "histogram:unique-colors=true", + # "-format", + # "%c", + # "histogram:info:", + # ], + # capture_output=True, + # text=True, + # check=True, + # ).stdout + # return bool(re.match(".*#${testVideoColour}.*", displayed_colours, re.S)) + # + # print("Wait until the image shows in screen...", file=sys.stderr) + # wait.until(detect_image_in_screen) + + ############################################################ + + print("Done; bye!", file=sys.stderr) + driver.close() + ''; + + acquireRootPassword = pkgs.writeShellScriptBin "acquire-root-password" '' + readonly retry=30 + readonly wait=60 + + for _ in $(seq $retry); do + password=$(journalctl -u peertube | perl -ne '/password: (.*)/ && print $1') + if [ -n "$password" ]; then + echo "$password" + exit 0 + fi + sleep $wait + done + + echo "Could not acquire root password in $((retry * wait))s." + exit 1 + ''; +in + +pkgs.nixosTest { + name = "peertube"; + + nodes = { + server = + { config, ... }: + { + imports = with self.nixosModules; [ + fediversity + ../vm/garage-vm.nix + ../vm/peertube-vm.nix + ../vm/interactive-vm.nix + ]; + + virtualisation = { + memorySize = lib.mkVMOverride 8192; + cores = 8; + }; + + environment.systemPackages = with pkgs; [ + python3 + firefox-unwrapped + geckodriver + toot + acquireRootPassword + postVideoInBrowser + imagemagick + ffmpeg # to identify videos + expect + ]; + + ## FIXME: The CI is very slow, so the default timeout of 120s is not + ## good enough. We bump it drastically. + systemd.services.postgresql.serviceConfig.TimeoutSec = lib.mkForce 3600; + + environment.variables = { + AWS_ACCESS_KEY_ID = config.services.garage.ensureKeys.peertube.id; + AWS_SECRET_ACCESS_KEY = config.services.garage.ensureKeys.peertube.secret; + PT_INITIAL_ROOT_PASSWORD = "testtest"; + }; + }; + }; + + testScript = + { nodes, ... }: + '' + server.start() + + # FIXME: I think this trick to look for a password can be replaced by + # services.peertube.serviceEnvironmentFile.PT_INITIAL_ROOT_PASSWORD=testtest + + with subtest("Peertube starts"): + server.wait_for_unit("peertube.service") + root_password = server.succeed("acquire-root-password").rstrip() + + with subtest("Post a video in the browser"): + server.succeed(f"post-video-in-browser {root_password}") + + with subtest("Find video in garage"): + server.succeed("mc alias set garage ${nodes.server.fediversity.internal.garage.api.url} --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY") + video = server.succeed("mc find garage --regex '\\.mp4'").rstrip() + if video == "": + raise Exception("Could not find any .mp4 video stored in Garage") + server.succeed(f"mc cat {video} > /video.mp4") + garage_hash = server.succeed("identify -quiet -format '%#' /video.mp4") + hash = server.succeed("identify -quiet -format '%#' ${testVideo}") + if garage_hash != hash: + raise Exception("The video stored in Garage does not correspond to the original one.") + ''; +} From d96c14270634fd23a572b24ba5943045cf731a11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 17 Dec 2024 17:15:16 +0100 Subject: [PATCH 4/5] Enable CI for Peertube test --- .forgejo/workflows/ci.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.forgejo/workflows/ci.yaml b/.forgejo/workflows/ci.yaml index 4ec5836..d1ff749 100644 --- a/.forgejo/workflows/ci.yaml +++ b/.forgejo/workflows/ci.yaml @@ -21,3 +21,9 @@ jobs: - uses: actions/checkout@v4 - run: cd website && nix-build -A tests - run: cd website && nix-build -A build + + check-peertube: + runs-on: native + steps: + - uses: actions/checkout@v4 + - run: nix build .#checks.x86_64-linux.peertube -L From 0e6f2c52c2a1a34816110a899e26e1210fe1e02c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?= Date: Tue, 17 Dec 2024 19:20:55 +0100 Subject: [PATCH 5/5] Bump nixpkgs This should contain the backport of Peertube 6.3.3, necessary to fix our Garage Content-Security-Policy issue. See https://github.com/NixOS/nixpkgs/pull/363680 for the PR backporting Peertube 6.3.3 to nixpkgs 24.11. --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 0bbdc67..e65b00c 100644 --- a/flake.lock +++ b/flake.lock @@ -836,11 +836,11 @@ }, "nixpkgs_6": { "locked": { - "lastModified": 1732350895, - "narHash": "sha256-GcOQbOgmwlsRhpLGSwZJwLbo3pu9ochMETuRSS1xpz4=", + "lastModified": 1734323986, + "narHash": "sha256-m/lh6hYMIWDYHCAsn81CDAiXoT3gmxXI9J987W5tZrE=", "owner": "nixos", "repo": "nixpkgs", - "rev": "0c582677378f2d9ffcb01490af2f2c678dcb29d3", + "rev": "394571358ce82dff7411395829aa6a3aad45b907", "type": "github" }, "original": {