Unverified Commit d7de3299 authored by Leona Maroni's avatar Leona Maroni
Browse files

nixos/vinyl-cache: init module

This module is essentialy the same as services.varnish, but adapts to the
changed project name. For the lifetime of NixOS 26.05, we still support
Varnish to simplify the migration.
parent a428a525
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1834,6 +1834,7 @@
  ./services/web-servers/unit/default.nix
  ./services/web-servers/uwsgi.nix
  ./services/web-servers/varnish/default.nix
  ./services/web-servers/vinyl-cache/default.nix
  ./services/x11/clight.nix
  ./services/x11/colord.nix
  ./services/x11/desktop-managers/default.nix
+240 −0
Original line number Diff line number Diff line
{
  config,
  lib,
  pkgs,
  ...
}:

let
  inherit (lib)
    types
    mkOption
    hasPrefix
    concatMapStringsSep
    optionalString
    concatMap
    ;

  cfg = config.services.vinyl-cache;

  # Vinyl Cache has very strong opinions and very complicated code around handling
  # the stateDir. After a lot of back and forth, we decided that we a)
  # do not want a configurable option here, as most of the handling depends
  # on the version and the compile time options. Putting everything into
  # /var/run (RAM backed) is absolutely recommended by Vinyl Cache anyways.
  # We do need to pay attention to the version-dependend variations, though!
  stateDir = "/var/run/vinyld";

  # from --help:
  #   -a [<name>=]address[:port][,proto] # HTTP listen address and port
  #      [,user=<u>][,group=<g>]   # Can be specified multiple times.
  #      [,mode=<m>]               #   default: ":80,HTTP"
  #                                # Proto can be "PROXY" or "HTTP" (default)
  #                                # user, group and mode set permissions for
  #                                #   a Unix domain socket.
  commandLineAddresses = (
    concatMapStringsSep " " (
      a:
      "-a "
      + optionalString (!isNull a.name) "${a.name}="
      + a.address
      + optionalString (!isNull a.port) ":${toString a.port}"
      + optionalString (!isNull a.proto) ",${a.proto}"
      + optionalString (!isNull a.user) ",user=${a.user}"
      + optionalString (!isNull a.group) ",group=${a.group}"
      + optionalString (!isNull a.mode) ",mode=${a.mode}"
    ) cfg.listen
  );

  addressSubmodule = types.submodule {
    options = {
      name = mkOption {
        description = "Name is referenced in logs. If name is not specified, 'a0', 'a1', etc. is used.";
        default = null;
        type = with types; nullOr str;
      };
      address = mkOption {
        description = ''
          If given an IP address, it can be a host name ("localhost"), an IPv4 dotted-quad
          ("127.0.0.1") or an IPv6  address enclosed in square brackets ("[::1]").

          (VCL4.1 and higher) If given an absolute Path ("/path/to/listen.sock") or "@"
          followed by the name of an abstract socket ("@myvinyld") accept connections
          on a Unix domain socket.

          The user, group and mode sub-arguments may be used to specify the permissions
          of the socket file. These sub-arguments do not apply to  abstract sockets.
        '';
        type = types.str;
      };
      port = mkOption {
        description = "The port to use for IP sockets. If port is not specified, port 80 (http) is used.";
        default = null;
        type = with types; nullOr port;
      };
      proto = mkOption {
        description = "PROTO can be 'HTTP' (the default) or 'PROXY'.  Both version 1 and 2 of the proxy protocol can be used.";
        type = types.enum [
          "HTTP"
          "PROXY"
        ];
        default = "HTTP";
      };
      user = mkOption {
        description = "User name who owns the socket file.";
        default = null;
        type = with lib.types; nullOr str;
      };
      group = mkOption {
        description = "Group name who owns the socket file.";
        default = null;
        type = with lib.types; nullOr str;
      };
      mode = mkOption {
        description = "Permission of the socket file (3-digit octal value).";
        default = null;
        type = with types; nullOr str;
      };
    };
  };
  checkedAddressModule = types.addCheck addressSubmodule (
    m:
    (
      if ((hasPrefix "@" m.address) || (hasPrefix "/" m.address)) then
        # this is a unix socket
        (m.port != null)
      else
      # this is not a path-based unix socket
      if !(hasPrefix "/" m.address) && (m.group != null) || (m.user != null) || (m.mode != null) then
        false
      else
        true
    )
  );
  commandLine =
    "-f ${pkgs.writeText "default.vcl" cfg.config}"
    +
      lib.optionalString (cfg.extraModules != [ ])
        " -p vmod_path='${
           lib.makeSearchPathOutput "lib" "lib/vinyl/vmods" ([ cfg.package ] ++ cfg.extraModules)
         }' -r vmod_path";
in
{
  meta.maintainers = [
    lib.maintainers.leona
    lib.maintainers.osnyx
  ];
  options = {
    services.vinyl-cache = {
      enable = lib.mkEnableOption "Vinyl Cache";

      enableConfigCheck = lib.mkEnableOption "checking the config during build time" // {
        default = true;
      };

      package = lib.mkPackageOption pkgs "vinyl-cache" { };

      listen = lib.mkOption {
        description = "Accept for client requests on the specified listen addresses.";
        type = lib.types.listOf checkedAddressModule;
        defaultText = lib.literalExpression ''[ { address="*"; port=6081; } ]'';
        default = [
          {
            address = "*";
            port = 6081;
          }
        ];
      };

      config = lib.mkOption {
        type = lib.types.lines;
        description = ''
          Verbatim default.vcl configuration.
        '';
      };

      extraModules = lib.mkOption {
        type = lib.types.listOf lib.types.package;
        default = [ ];
        description = ''
          Vinyl Cache modules (except 'std').
        '';
      };

      extraCommandLine = lib.mkOption {
        type = lib.types.str;
        default = "";
        example = "-s malloc,256M";
        description = ''
          Command line switches for vinyld (run 'vinyld -?' to get list of options)
        '';
      };

      enableFileLogging = lib.mkEnableOption "file based logging";
    };

  };

  config = lib.mkMerge [
    (lib.mkIf cfg.enable {
      systemd.services.vinyl-cache = {
        description = "Vinyl Cache";
        wantedBy = [ "multi-user.target" ];
        after = [ "network.target" ];
        serviceConfig = {
          Type = "simple";
          ExecStart = "${cfg.package}/bin/vinyld ${commandLineAddresses} -n ${stateDir} -F ${cfg.extraCommandLine} ${commandLine}";
          Restart = "always";
          RestartSec = "5s";
          User = "vinyl-cache";
          Group = "vinyl-cache";
          DynamicUser = true;
          RuntimeDirectory = lib.removePrefix "/var/run/" stateDir;
          AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
          NoNewPrivileges = true;
          LimitNOFILE = 131072;
        };
      };

      environment.systemPackages = [ cfg.package ];

      # check .vcl syntax at compile time (e.g. before nixops deployment)
      system.checks = lib.mkIf cfg.enableConfigCheck [
        (pkgs.runCommand "check-vinyl-cache-syntax" { } ''
          ${cfg.package}/bin/vinyld -C ${commandLine} 2> $out || (cat $out; exit 1)
        '')
      ];

    (lib.mkIf (cfg.enable && cfg.enableFileLogging) {
      systemd.services = {
        vinylncsa = {
          after = [ "vinyl-cache.service" ];
          requires = [ "vinyl-cache.service" ];
          description = "Vinyl Cache logging daemon";
          wantedBy = [ "multi-user.target" ];
          # We want to reopen logs with HUP. vinylncsa must run in daemon mode for that.
          serviceConfig = {
            Type = "forking";
            Restart = "always";
            RuntimeDirectory = "vinylncsa";
            LogsDirectory = "vinyl-cache";
            PIDFile = "/run/vinylncsa/vinylncsa.pid";
            User = "vinyl-cache";
            Group = "vinyl-cache";
            ExecStart = "${cfg.package}/bin/vinylncsa -D -a -w /var/log/vinyl-cache/vinyl-cache.log -P /run/vinylncsa/vinylncsa.pid";
            ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
          };
        };
      };

      services.logrotate.settings.vinyl-cache = lib.mapAttrs (_: lib.mkDefault) {
        files = [ "/var/log/vinyl-cache/*.log" ];
        frequency = "daily";
        rotate = 14;
        compress = true;
        delaycompress = true;
        postrotate = "systemctl reload vinylncsa";
      };
    })
  ];
}
+4 −0
Original line number Diff line number Diff line
@@ -1743,6 +1743,10 @@ in
  victoriametrics = import ./victoriametrics { inherit runTest; };
  victoriatraces = import ./victoriatraces { inherit runTest; };
  vikunja = runTest ./vikunja.nix;
  vinyl-cache_9 = runTest {
    imports = [ ./vinyl-cache.nix ];
    _module.args.package = pkgs.vinyl-cache_9;
  };
  virtualbox = handleTestOn [ "x86_64-linux" ] ./virtualbox.nix { };
  vm-variant = handleTest ./vm-variant.nix { };
  vscode-remote-ssh = handleTestOn [ "x86_64-linux" ] ./vscode-remote-ssh.nix { };
+126 −0
Original line number Diff line number Diff line
{
  pkgs,
  package,
  lib,
  ...
}:
let
  testPath = pkgs.hello;
in
{
  name = "vinyl";
  meta = {
    maintainers = [
      lib.maintainers.leona
      lib.maintainers.osnyx
    ];
  };

  nodes = {
    vinyl =
      {
        config,
        pkgs,
        lib,
        ...
      }:
      {
        services.nix-serve = {
          enable = true;
        };

        services.vinyl-cache = {
          inherit package;
          enable = true;
          enableFileLogging = true;
          listen = [
            {
              address = "0.0.0.0";
              port = 80;
              proto = "HTTP";
            }
            {
              name = "proxyport";
              address = "0.0.0.0";
              port = 8080;
              proto = "PROXY";
            }
            {
              address = "/var/run/vinyld/client.http.sock";
              user = "vinyl-cache";
              group = "vinyl-cache";
              mode = "660";
            }
          ]
          ++ lib.optionals (lib.versionAtLeast package.version "7.3") [
            # Support added in 7.3.0
            { address = "@asdf"; }
          ];
          config = ''
            vcl 4.1;

            backend nix-serve {
              .host = "127.0.0.1";
              .port = "${toString config.services.nix-serve.port}";
            }
          '';
        };

        networking.firewall.allowedTCPPorts = [ 80 ];
        system.extraDependencies = [ testPath ];

        assertions =
          let
            cmdline = config.systemd.services.vinyl-cache.serviceConfig.ExecStart;
          in
          map
            (pattern: {
              assertion = lib.hasInfix pattern cmdline;
              message = "Address argument `${pattern}` missing in commandline `${cmdline}`.";
            })
            (
              [
                " -a 0.0.0.0:80,HTTP "
                " -a proxyport=0.0.0.0:8080,PROXY "
                " -a /var/run/vinyld/client.http.sock,HTTP,user=vinyl-cache,group=vinyl-cache,mode=660 "
              ]
              ++ lib.optionals (lib.versionAtLeast package.version "7.3") [
                " -a @asdf,HTTP "
              ]
            );
      };

    client =
      { lib, ... }:
      {
        nix.settings = {
          require-sigs = false;
          substituters = lib.mkForce [ "http://vinyl" ];
        };
      };
  };

  testScript = ''
    from pathlib import Path
    import os

    start_all()
    vinyl.wait_for_open_port(80)
    vinyl.wait_for_unit("vinylncsa")

    client.wait_until_succeeds("curl -f http://vinyl/nix-cache-info");

    client.wait_until_succeeds("nix-store -r ${testPath}")
    client.succeed("${testPath}/bin/hello")

    output = vinyl.succeed("vinyladm status")
    print(output)
    assert "Child in state running" in output, "Unexpected vinyladm response"

    vinyl.copy_from_machine("/var/log/vinyl-cache/vinyl-cache.log")

    out_dir = os.environ.get("out", os.getcwd())
    vinyl_log = (Path(out_dir) / "vinyl-cache.log").read_text()
    assert "http://vinyl/nix-cache-info" in vinyl_log
  '';
}
+1 −0
Original line number Diff line number Diff line
@@ -108,6 +108,7 @@ let

      passthru = {
        python = python3;
        tests = nixosTests."vinyl-cache_${lib.versions.major version}";
      };

      meta = {