Commit 447eb14a authored by Wael Nasreddine's avatar Wael Nasreddine
Browse files

nixos/ncps: Migrate to a config file

parent 98e5404e
Loading
Loading
Loading
Loading
+101 −100
Original line number Diff line number Diff line
@@ -18,119 +18,110 @@ let
  ];

  ncpsWrapper = pkgs.writeShellScript "ncps-wrapper" ''
    ${lib.optionalString (cfg.cache.secretKeyPath != null) ''
      export CACHE_SECRET_KEY_PATH="$CREDENTIALS_DIRECTORY/secretKey"
    ''}

    ${lib.optionalString (cfg.cache.storage.s3 != null) ''
      export CACHE_STORAGE_S3_ACCESS_KEY_ID="$(cat "$CREDENTIALS_DIRECTORY/s3AccessKeyId")"
      export CACHE_STORAGE_S3_SECRET_ACCESS_KEY="$(cat "$CREDENTIALS_DIRECTORY/s3SecretAccessKey")"
    ''}

    ${lib.optionalString (cfg.cache.redis != null && cfg.cache.redis.passwordFile != null) ''
      export CACHE_REDIS_PASSWORD="$(cat "$CREDENTIALS_DIRECTORY/redisPassword")"
    ''}
    ${lib.optionalString (cfg.cache.redis != null) (
      if cfg.cache.redis.passwordFile != null then
        ''export CACHE_REDIS_PASSWORD="$(cat "$CREDENTIALS_DIRECTORY/redisPassword")"''
      else if cfg.cache.redis.password != null then
        ''export CACHE_REDIS_PASSWORD="${cfg.cache.redis.password}"''
      else
        ""
    )}

    ${lib.optionalString (cfg.cache.databaseURLFile != null) ''
      export CACHE_DATABASE_URL="$(cat "$CREDENTIALS_DIRECTORY/databaseURL")"
    ''}

    exec ${lib.getExe cfg.package} "$@"
  '';

  globalFlags = lib.concatStringsSep " " (
    [ "--log-level='${cfg.logLevel}'" ]
    ++ (lib.optionals cfg.openTelemetry.enable (
      [
        "--otel-enabled"
      ]
      ++ (lib.optional (
        cfg.openTelemetry.grpcURL != null
      ) "--otel-grpc-url='${cfg.openTelemetry.grpcURL}'")
    ))
    ++ (lib.optional cfg.prometheus.enable "--prometheus-enabled")
    ++ (lib.optional (!cfg.analytics.reporting.enable) "--analytics-reporting-enabled=false")
    ++ (lib.optional cfg.analytics.reporting.samples "--analytics-reporting-samples")
  );
    exec ${lib.getExe cfg.package} --config "${configFile}" "$@"
  '';

  serveFlags = lib.concatStringsSep " " (
    [
      "--cache-hostname='${cfg.cache.hostName}'"
      "--cache-lock-backend='${cfg.cache.lock.backend}'"
      "--cache-temp-path='${cfg.cache.tempPath}'"
      "--server-addr='${cfg.server.addr}'"
    ]
    ++ (lib.optional (cfg.cache.databaseURL != null) "--cache-database-url='${cfg.cache.databaseURL}'")
    ++ (lib.optional (cfg.cache.database.pool.maxOpenConns != 0)
      "--cache-database-pool-max-open-conns='${builtins.toString cfg.cache.database.pool.maxOpenConns}'"
    )
    ++ (lib.optional (cfg.cache.database.pool.maxIdleConns != 0)
      "--cache-database-pool-max-idle-conns='${builtins.toString cfg.cache.database.pool.maxIdleConns}'"
  settings = {
    log.level = cfg.logLevel;
    opentelemetry = lib.optionalAttrs cfg.openTelemetry.enable {
      enabled = true;
      grpc-url = cfg.openTelemetry.grpcURL;
    };
    prometheus = lib.optionalAttrs cfg.prometheus.enable {
      enabled = true;
    };
    analytics.reporting = {
      enabled = cfg.analytics.reporting.enable;
      samples = cfg.analytics.reporting.samples;
    };
    server.addr = cfg.server.addr;
    cache = {
      allow-delete-verb = cfg.cache.allowDeleteVerb;
      allow-put-verb = cfg.cache.allowPutVerb;
      hostname = cfg.cache.hostName;
      database-url = cfg.cache.databaseURL;
      database.pool = {
        max-open-conns = cfg.cache.database.pool.maxOpenConns;
        max-idle-conns = cfg.cache.database.pool.maxIdleConns;
      };
      max-size = cfg.cache.maxSize;
      lru = {
        schedule = cfg.cache.lru.schedule;
        timezone = cfg.cache.lru.scheduleTimeZone;
      };
      sign-narinfo = cfg.cache.signNarinfo;
      storage =
        if cfg.cache.storage.s3 != null then
          {
            s3 = {
              bucket = cfg.cache.storage.s3.bucket;
              endpoint = cfg.cache.storage.s3.endpoint;
              region = cfg.cache.storage.s3.region;
              force-path-style = cfg.cache.storage.s3.forcePathStyle;
            };
          }
        else
          {
            local = cfg.cache.storage.local;
          };
      temp-path = cfg.cache.tempPath;
      netrc-file = cfg.netrcFile;
      upstream = {
        urls = cfg.cache.upstream.urls;
        public-keys = cfg.cache.upstream.publicKeys;
        dialer-timeout = cfg.cache.upstream.dialerTimeout;
        response-header-timeout = cfg.cache.upstream.responseHeaderTimeout;
      };
      lock = {
        backend = cfg.cache.lock.backend;
        redis.key-prefix = cfg.cache.lock.redisKeyPrefix;
        postgres.key-prefix = cfg.cache.lock.postgresKeyPrefix;
        download-lock-ttl = cfg.cache.lock.downloadTTL;
        lru-lock-ttl = cfg.cache.lock.lruTTL;
        retry = {
          max-attempts = cfg.cache.lock.retry.maxAttempts;
          initial-delay = cfg.cache.lock.retry.initialDelay;
          max-delay = cfg.cache.lock.retry.maxDelay;
          jitter = cfg.cache.lock.retry.jitter;
        };
        allow-degraded-mode = cfg.cache.lock.allowDegradedMode;
      };
      redis = lib.optionalAttrs (cfg.cache.redis != null) {
        addrs = cfg.cache.redis.addresses;
        db = cfg.cache.redis.database;
        username = cfg.cache.redis.username;
        use-tls = cfg.cache.redis.useTLS;
        pool-size = cfg.cache.redis.poolSize;
      };
    };
  };

  configFile = pkgs.writeText "ncps-config.json" (
    builtins.toJSON (
      lib.filterAttrsRecursive (_: v: v != null && v != { } && v != "" && v != [ ]) settings
    )
    ++ (lib.optionals (cfg.cache.redis != null) (
      [
        "--cache-redis-db='${builtins.toString cfg.cache.redis.database}'"
        "--cache-redis-pool-size='${builtins.toString cfg.cache.redis.poolSize}'"
      ]
      ++ (lib.forEach cfg.cache.redis.addresses (addr: "--cache-redis-addrs='${addr}'"))
      ++ (lib.optional (
        cfg.cache.redis.username != null
      ) "--cache-redis-username='${cfg.cache.redis.username}'")
      ++ (lib.optional (
        cfg.cache.redis.password != null
      ) "--cache-redis-password='${cfg.cache.redis.password}'")
      ++ (lib.optional cfg.cache.redis.useTLS "--cache-redis-use-tls")
    ))
    ++ (lib.optional (
      cfg.cache.storage.s3 == null
    ) "--cache-storage-local='${cfg.cache.storage.local}'")
    ++ (lib.optionals (cfg.cache.storage.s3 != null) (
      [
        "--cache-storage-s3-bucket='${cfg.cache.storage.s3.bucket}'"
        "--cache-storage-s3-endpoint='${cfg.cache.storage.s3.endpoint}'"
      ]
      ++ (lib.optional cfg.cache.storage.s3.forcePathStyle "--cache-storage-s3-force-path-style")
      ++ (lib.optional (
        cfg.cache.storage.s3.region != null
      ) "--cache-storage-s3-region='${cfg.cache.storage.s3.region}'")
    ))
    ++ (lib.optional cfg.cache.allowDeleteVerb "--cache-allow-delete-verb")
    ++ (lib.optional cfg.cache.allowPutVerb "--cache-allow-put-verb")
    ++ (lib.optional (cfg.cache.maxSize != null) "--cache-max-size='${cfg.cache.maxSize}'")
    ++ (lib.optionals (cfg.cache.lru.schedule != null) [
      "--cache-lru-schedule='${cfg.cache.lru.schedule}'"
      "--cache-lru-schedule-timezone='${cfg.cache.lru.scheduleTimeZone}'"
    ])
    ++ (lib.optional (
      cfg.cache.lock.redisKeyPrefix != "ncps:lock:"
    ) "--cache-lock-redis-key-prefix='${cfg.cache.lock.redisKeyPrefix}'")
    ++ (lib.optional (
      cfg.cache.lock.postgresKeyPrefix != "ncps:lock:"
    ) "--cache-lock-postgres-key-prefix='${cfg.cache.lock.postgresKeyPrefix}'")
    ++ (lib.optional (
      cfg.cache.lock.downloadTTL != "5m0s"
    ) "--cache-lock-download-ttl='${cfg.cache.lock.downloadTTL}'")
    ++ (lib.optional (
      cfg.cache.lock.lruTTL != "30m0s"
    ) "--cache-lock-lru-ttl='${cfg.cache.lock.lruTTL}'")
    ++ (lib.optional (
      cfg.cache.lock.retry.maxAttempts != 3
    ) "--cache-lock-retry-max-attempts='${builtins.toString cfg.cache.lock.retry.maxAttempts}'")
    ++ (lib.optional (
      cfg.cache.lock.retry.initialDelay != "100ms"
    ) "--cache-lock-retry-initial-delay='${cfg.cache.lock.retry.initialDelay}'")
    ++ (lib.optional (
      cfg.cache.lock.retry.maxDelay != "2s"
    ) "--cache-lock-retry-max-delay='${cfg.cache.lock.retry.maxDelay}'")
    ++ (lib.optional (!cfg.cache.lock.retry.jitter) "--cache-lock-retry-jitter='false'")
    ++ (lib.optional cfg.cache.lock.allowDegradedMode "--cache-lock-allow-degraded-mode")
    ++ (lib.optional (cfg.cache.secretKeyPath != null) "--cache-secret-key-path='%d/secretKey'")
    ++ (lib.optional (!cfg.cache.signNarinfo) "--cache-sign-narinfo='false'")
    ++ (lib.optional (
      cfg.cache.upstream.dialerTimeout != null
    ) "--cache-upstream-dialer-timeout='${cfg.cache.upstream.dialerTimeout}'")
    ++ (lib.optional (
      cfg.cache.upstream.responseHeaderTimeout != null
    ) "--cache-upstream-response-header-timeout='${cfg.cache.upstream.responseHeaderTimeout}'")
    ++ (lib.forEach cfg.cache.upstream.publicKeys (pk: "--cache-upstream-public-key='${pk}'"))
    ++ (lib.forEach cfg.cache.upstream.urls (url: "--cache-upstream-url='${url}'"))
    ++ (lib.optional (cfg.netrcFile != null) "--netrc-file='${cfg.netrcFile}'")
  );

  isSqlite = cfg.cache.databaseURL != null && lib.strings.hasPrefix "sqlite:" cfg.cache.databaseURL;
@@ -300,6 +291,16 @@ in
            description = ''
              Lock backend to use: 'local' (single instance), 'redis'
              (distributed), 'postgres' (distributed, requires PostgreSQL).

              Advisory Locks and Connection Pools: If you use PostgreSQL as
              your distributed lock backend, each active lock consumes a
              dedicated connection from the pool. A single request can consume
              up to 3 connections simultaneously.

              To avoid deadlocks under concurrent load, ensure
              {option}`services.ncps.cache.database.pool.maxOpenConns` is
              significantly higher than your expected concurrency (at least
              50-100 is recommended).
            '';
          };

@@ -691,7 +692,7 @@ in

      serviceConfig = lib.mkMerge [
        {
          ExecStart = "${ncpsWrapper} ${globalFlags} serve ${serveFlags}";
          ExecStart = "${ncpsWrapper} serve";
          User = "ncps";
          Group = "ncps";
          Restart = "on-failure";