aboutsummaryrefslogblamecommitdiff
path: root/modules/private/databases/redis_replication.nix
blob: a3fe3bb775f0f412d846bc80f7b9049035fe38f6 (plain) (tree)
1
                           































































                                                           
                                                                      






































                                                                                
                                                       












                                                                           
                                                                        




















































                                                                                                                     
{ pkgs, config, lib, ... }:
let
  cfg = config.myServices.databasesReplication.redis;
in
{
  options.myServices.databasesReplication.redis = {
    enable = lib.mkEnableOption "Enable redis replication";
    base = lib.mkOption {
      type = lib.types.path;
      description = ''
        Base path to put the replications
        '';
    };
    hosts = lib.mkOption {
      default = {};
      description = ''
        Hosts to backup
        '';
      type = lib.types.attrsOf (lib.types.submodule {
        options = {
          package = lib.mkOption {
            type = lib.types.package;
            default = pkgs.redis;
            description = ''
              Redis package for this host
            '';
          };
          host = lib.mkOption {
            type = lib.types.str;
            description = ''
              Host to connect to
              '';
          };
          port = lib.mkOption {
            type = lib.types.str;
            description = ''
              Port to connect to
              '';
          };
          password = lib.mkOption {
            type = lib.types.nullOr lib.types.str;
            default = null;
            description = ''
              Password to use
              '';
          };
        };
      });
    };
  };

  config = lib.mkIf cfg.enable {
    users.users.redis = {
      description = "Redis database user";
      group = "redis";
      uid = config.ids.uids.redis;
      extraGroups = [ "keys" ];
    };
    users.groups.redis.gid = config.ids.gids.redis;

    services.spiped = { # sync from eldiron
      enable = true;
      config.redis = {
        encrypt = true;
        source = "127.0.0.1:16379";
        target = "${config.myEnv.servers.eldiron.ips.main.ip4}:16379";
        keyfile = "${config.secrets.location}/redis/spiped_eldiron_keyfile";
      };
    };

    secrets.keys = lib.flatten (lib.mapAttrsToList (name: hcfg: [
      {
        dest = "redis_replication/${name}/config";
        user = "redis";
        group = "redis";
        permissions = "0400";
        text = ''
          pidfile ${cfg.base}/${name}/redis/redis.pid
          port 0
          unixsocket /run/redis_${name}/redis.sock
          loglevel notice
          logfile /dev/null
          syslog-enabled yes
          databases 16
          save 900 1
          save 300 10
          save 60 10000
          dbfilename dump.rdb
          dir ${cfg.base}/${name}/redis/
          slaveof ${hcfg.host} ${hcfg.port}
          ${if hcfg.password != null then "masterauth ${hcfg.password}" else ""}
          appendOnly no
          appendfsync everysec
          slowlog-log-slower-than 10000
          slowlog-max-len 128
          unixsocketperm 777
          maxclients 1024
          '';
      }
    ]) cfg.hosts) ++ [
      { # For eldiron only
        dest = "redis/spiped_eldiron_keyfile";
        user = "spiped";
        group = "spiped";
        permissions = "0400";
        text = config.myEnv.databases.redis.spiped_key;
      }
    ];

    services.cron = {
      enable = true;
      systemCronJobs = lib.flatten (lib.mapAttrsToList (name: hcfg:
        let
          dataDir = "${cfg.base}/${name}/redis";
          backupDir = "${cfg.base}/${name}/redis_backup";
          backup_script = pkgs.writeScript "backup_redis_${name}" ''
              #!${pkgs.stdenv.shell}

              ${pkgs.coreutils}/bin/cp ${cfg.base}/${name}/redis/dump.rdb \
                ${backupDir}/$(${pkgs.coreutils}/bin/date -Iminutes).rdb
            '';
          u = pkgs.callPackage ./utils.nix {};
          cleanup_script = pkgs.writeScript "cleanup_redis_${name}" (u.exponentialDumps "rdb" backupDir);
        in [
          "0 22,4,10,16 * * * root ${backup_script}"
          "0 3 * * * root ${cleanup_script}"
        ]) cfg.hosts);
    };

    system.activationScripts = lib.attrsets.mapAttrs' (name: hcfg:
      lib.attrsets.nameValuePair "redis_replication_${name}" {
        deps = [ "users" "groups" ];
        text = ''
          install -m 0700 -o redis -g redis -d ${cfg.base}/${name}/redis
          install -m 0700 -o redis -g redis -d ${cfg.base}/${name}/redis_backup
          '';
      }) cfg.hosts;

    systemd.services = {
      spiped_redis = { # For eldiron
        description = "Secure pipe 'redis'";
        after = [ "network.target" ];
        wantedBy = [ "multi-user.target" ];

        serviceConfig = {
          Restart   = "always";
          User      = "spiped";
          PermissionsStartOnly = true;
          SupplementaryGroups = "keys";
        };

        script = "exec ${pkgs.spiped}/bin/spiped -F `cat /etc/spiped/redis.spec`";
      };
    } // lib.attrsets.mapAttrs' (name: hcfg:
      let
        dataDir = "${cfg.base}/${name}/redis";
      in
      lib.attrsets.nameValuePair "redis_backup_${name}" {
        description = "Redis replication for ${name}";
        wantedBy = [ "multi-user.target" ];
        after = [ "network.target" ];
        unitConfig.RequiresMountsFor = dataDir;

        serviceConfig = {
          ExecStart = "${hcfg.package}/bin/redis-server ${config.secrets.location}/redis_replication/${name}/config";
          User = "redis";
          RuntimeDirectory = "redis_${name}";
        };
    }) cfg.hosts;
  };
}