From 58cf8505ce73d10175d4bbe03818eaf318bb7718 Mon Sep 17 00:00:00 2001
From: Malte Poll <1780588+malt3@users.noreply.github.com>
Date: Sat, 4 Nov 2023 00:13:35 +0100
Subject: [PATCH 1/2] athens: init at 0.12.1
Co-authored-by: Paul Meyer <49727155+katexochen@users.noreply.github.com>
---
pkgs/by-name/at/athens/package.nix | 37 ++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
create mode 100644 pkgs/by-name/at/athens/package.nix
diff --git a/pkgs/by-name/at/athens/package.nix b/pkgs/by-name/at/athens/package.nix
new file mode 100644
index 000000000000..0dfee342a3c2
--- /dev/null
+++ b/pkgs/by-name/at/athens/package.nix
@@ -0,0 +1,37 @@
+{ lib
+, fetchFromGitHub
+, buildGo121Module
+}:
+buildGo121Module rec {
+ pname = "athens";
+ version = "0.12.1";
+
+ src = fetchFromGitHub {
+ owner = "gomods";
+ repo = pname;
+ rev = "v${version}";
+ hash = "sha256-m75Ut1UVwz7uWneBwPxUL7aPOXIpy6YPqIXMwczHOpY=";
+ };
+
+ vendorHash = "sha256-zK4EE242Gbgew33oxAUNxylKdhRdPhqP0Hrpu4sYiFg=";
+
+ CGO_ENABLED = "0";
+ ldflags = [ "-s" "-w" "-buildid=" "-X github.com/gomods/athens/pkg/build.version=${version}" ];
+ flags = [ "-trimpath" ];
+
+ subPackages = [ "cmd/proxy" ];
+
+ postInstall = ''
+ mv $out/bin/proxy $out/bin/athens
+ '';
+
+ meta = with lib; {
+ description = "A Go module datastore and proxy";
+ homepage = "https://github.com/gomods/athens";
+ changelog = "https://github.com/gomods/athens/releases/tag/v${version}";
+ license = licenses.mit;
+ mainProgram = "athens";
+ maintainers = with maintainers; [ katexochen malt3 ];
+ platforms = platforms.unix;
+ };
+}
From 9c5825bd27f5684aed9d31ad31727c12dd982211 Mon Sep 17 00:00:00 2001
From: Malte Poll <1780588+malt3@users.noreply.github.com>
Date: Sat, 4 Nov 2023 02:46:35 +0100
Subject: [PATCH 2/2] nixos/athens: init at 0.12.1
Co-authored-by: Paul Meyer <49727155+katexochen@users.noreply.github.com>
---
.../manual/release-notes/rl-2311.section.md | 2 +
nixos/modules/module-list.nix | 8 +-
nixos/modules/services/development/athens.md | 52 +
nixos/modules/services/development/athens.nix | 936 ++++++++++++++++++
4 files changed, 995 insertions(+), 3 deletions(-)
create mode 100644 nixos/modules/services/development/athens.md
create mode 100644 nixos/modules/services/development/athens.nix
diff --git a/nixos/doc/manual/release-notes/rl-2311.section.md b/nixos/doc/manual/release-notes/rl-2311.section.md
index c5406e183c39..1a59672cb718 100644
--- a/nixos/doc/manual/release-notes/rl-2311.section.md
+++ b/nixos/doc/manual/release-notes/rl-2311.section.md
@@ -66,6 +66,8 @@
* [NS-USBLoader](https://github.com/developersu/ns-usbloader/), an all-in-one tool for managing Nintendo Switch homebrew. Available as [programs.ns-usbloader](#opt-programs.ns-usbloader.enable).
+- [athens](https://github.com/gomods/athens), a Go module datastore and proxy. Available as [services.athens](#opt-services.athens.enable).
+
- [Mobilizon](https://joinmobilizon.org/), a Fediverse platform for publishing events.
- [Anuko Time Tracker](https://github.com/anuko/timetracker), a simple, easy to use, open source time tracking system. Available as [services.anuko-time-tracker](#opt-services.anuko-time-tracker.enable).
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 44acc011ba19..e3fe34343071 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -479,6 +479,7 @@
./services/desktops/telepathy.nix
./services/desktops/tumbler.nix
./services/desktops/zeitgeist.nix
+ ./services/development/athens.nix
./services/development/blackfire.nix
./services/development/bloop.nix
./services/development/distccd.nix
@@ -1532,9 +1533,10 @@
./virtualisation/waydroid.nix
./virtualisation/xe-guest-utilities.nix
./virtualisation/xen-dom0.nix
- { documentation.nixos.extraModules = [
- ./virtualisation/qemu-vm.nix
- ./image/repart.nix
+ {
+ documentation.nixos.extraModules = [
+ ./virtualisation/qemu-vm.nix
+ ./image/repart.nix
];
}
]
diff --git a/nixos/modules/services/development/athens.md b/nixos/modules/services/development/athens.md
new file mode 100644
index 000000000000..77663db509d5
--- /dev/null
+++ b/nixos/modules/services/development/athens.md
@@ -0,0 +1,52 @@
+# Athens {#module-athens}
+
+*Source:* {file}`modules/services/development/athens.nix`
+
+*Upstream documentation:*
+
+[Athens](https://github.com/gomods/athens)
+is a Go module datastore and proxy
+
+The main goal of Athens is providing a Go proxy (`$GOPROXY`) in regions without access to `https://proxy.golang.org` or to
+improve the speed of Go module downloads for CI/CD systems.
+
+## Configuring {#module-services-development-athens-configuring}
+
+A complete list of options for the Athens module may be found
+[here](#opt-services.athens.enable).
+
+## Basic usage for a caching proxy configuration {#opt-services-development-athens-caching-proxy}
+
+A very basic configuration for Athens that acts as a caching and forwarding HTTP proxy is:
+```
+{
+ services.athens = {
+ enable = true;
+ };
+}
+```
+
+If you want to prevent Athens from writing to disk, you can instead configure it to cache modules only in memory:
+
+```
+{
+ services.athens = {
+ enable = true;
+ storageType = "memory";
+ };
+}
+```
+
+To use the local proxy in Go builds, you can set the proxy as environment variable:
+
+```
+{
+ environment.variables = {
+ GOPROXY = "http://localhost:3000"
+ };
+}
+```
+
+It is currently not possible to use the local proxy for builds done by the Nix daemon. This might be enabled
+by experimental features, specifically [`configurable-impure-env`](https://nixos.org/manual/nix/unstable/contributing/experimental-features#xp-feature-configurable-impure-env),
+in upcoming Nix versions.
diff --git a/nixos/modules/services/development/athens.nix b/nixos/modules/services/development/athens.nix
new file mode 100644
index 000000000000..34f8964a3bd5
--- /dev/null
+++ b/nixos/modules/services/development/athens.nix
@@ -0,0 +1,936 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.athens;
+
+ athensConfig = flip recursiveUpdate cfg.extraConfig (
+ {
+ GoBinary = "${cfg.goBinary}/bin/go";
+ GoEnv = cfg.goEnv;
+ GoBinaryEnvVars = lib.mapAttrsToList (k: v: "${k}=${v}") cfg.goBinaryEnvVars;
+ GoGetWorkers = cfg.goGetWorkers;
+ GoGetDir = cfg.goGetDir;
+ ProtocolWorkers = cfg.protocolWorkers;
+ LogLevel = cfg.logLevel;
+ CloudRuntime = cfg.cloudRuntime;
+ EnablePprof = cfg.enablePprof;
+ PprofPort = ":${toString cfg.pprofPort}";
+ FilterFile = cfg.filterFile;
+ RobotsFile = cfg.robotsFile;
+ Timeout = cfg.timeout;
+ StorageType = cfg.storageType;
+ TLSCertFile = cfg.tlsCertFile;
+ TLSKeyFile = cfg.tlsKeyFile;
+ Port = ":${toString cfg.port}";
+ UnixSocket = cfg.unixSocket;
+ GlobalEndpoint = cfg.globalEndpoint;
+ BasicAuthUser = cfg.basicAuthUser;
+ BasicAuthPass = cfg.basicAuthPass;
+ ForceSSL = cfg.forceSSL;
+ ValidatorHook = cfg.validatorHook;
+ PathPrefix = cfg.pathPrefix;
+ NETRCPath = cfg.netrcPath;
+ GithubToken = cfg.githubToken;
+ HGRCPath = cfg.hgrcPath;
+ TraceExporter = cfg.traceExporter;
+ StatsExporter = cfg.statsExporter;
+ SumDBs = cfg.sumDBs;
+ NoSumPatterns = cfg.noSumPatterns;
+ DownloadMode = cfg.downloadMode;
+ NetworkMode = cfg.networkMode;
+ DownloadURL = cfg.downloadURL;
+ SingleFlightType = cfg.singleFlightType;
+ IndexType = cfg.indexType;
+ ShutdownTimeout = cfg.shutdownTimeout;
+ SingleFlight = {
+ Etcd = {
+ Endpoints = builtins.concatStringsSep "," cfg.singleFlight.etcd.endpoints;
+ };
+ Redis = {
+ Endpoint = cfg.singleFlight.redis.endpoint;
+ Password = cfg.singleFlight.redis.password;
+ LockConfig = {
+ TTL = cfg.singleFlight.redis.lockConfig.ttl;
+ Timeout = cfg.singleFlight.redis.lockConfig.timeout;
+ MaxRetries = cfg.singleFlight.redis.lockConfig.maxRetries;
+ };
+ };
+ RedisSentinel = {
+ Endpoints = cfg.singleFlight.redisSentinel.endpoints;
+ MasterName = cfg.singleFlight.redisSentinel.masterName;
+ SentinelPassword = cfg.singleFlight.redisSentinel.sentinelPassword;
+ LockConfig = {
+ TTL = cfg.singleFlight.redisSentinel.lockConfig.ttl;
+ Timeout = cfg.singleFlight.redisSentinel.lockConfig.timeout;
+ MaxRetries = cfg.singleFlight.redisSentinel.lockConfig.maxRetries;
+ };
+ };
+ };
+ Storage = {
+ CDN = {
+ Endpoint = cfg.storage.cdn.endpoint;
+ };
+ Disk = {
+ RootPath = cfg.storage.disk.rootPath;
+ };
+ GCP = {
+ ProjectID = cfg.storage.gcp.projectID;
+ Bucket = cfg.storage.gcp.bucket;
+ JSONKey = cfg.storage.gcp.jsonKey;
+ };
+ Minio = {
+ Endpoint = cfg.storage.minio.endpoint;
+ Key = cfg.storage.minio.key;
+ Secret = cfg.storage.minio.secret;
+ EnableSSL = cfg.storage.minio.enableSSL;
+ Bucket = cfg.storage.minio.bucket;
+ region = cfg.storage.minio.region;
+ };
+ Mongo = {
+ URL = cfg.storage.mongo.url;
+ DefaultDBName = cfg.storage.mongo.defaultDBName;
+ CertPath = cfg.storage.mongo.certPath;
+ Insecure = cfg.storage.mongo.insecure;
+ };
+ S3 = {
+ Region = cfg.storage.s3.region;
+ Key = cfg.storage.s3.key;
+ Secret = cfg.storage.s3.secret;
+ Token = cfg.storage.s3.token;
+ Bucket = cfg.storage.s3.bucket;
+ ForcePathStyle = cfg.storage.s3.forcePathStyle;
+ UseDefaultConfiguration = cfg.storage.s3.useDefaultConfiguration;
+ CredentialsEndpoint = cfg.storage.s3.credentialsEndpoint;
+ AwsContainerCredentialsRelativeURI = cfg.storage.s3.awsContainerCredentialsRelativeURI;
+ Endpoint = cfg.storage.s3.endpoint;
+ };
+ AzureBlob = {
+ AccountName = cfg.storage.azureblob.accountName;
+ AccountKey = cfg.storage.azureblob.accountKey;
+ ContainerName = cfg.storage.azureblob.containerName;
+ };
+ External = {
+ URL = cfg.storage.external.url;
+ };
+ };
+ Index = {
+ MySQL = {
+ Protocol = cfg.index.mysql.protocol;
+ Host = cfg.index.mysql.host;
+ Port = cfg.index.mysql.port;
+ User = cfg.index.mysql.user;
+ Password = cfg.index.mysql.password;
+ Database = cfg.index.mysql.database;
+ Params = {
+ parseTime = cfg.index.mysql.params.parseTime;
+ timeout = cfg.index.mysql.params.timeout;
+ };
+ };
+ Postgres = {
+ Host = cfg.index.postgres.host;
+ Port = cfg.index.postgres.port;
+ User = cfg.index.postgres.user;
+ Password = cfg.index.postgres.password;
+ Database = cfg.index.postgres.database;
+ Params = {
+ connect_timeout = cfg.index.postgres.params.connect_timeout;
+ sslmode = cfg.index.postgres.params.sslmode;
+ };
+ };
+ };
+ }
+ );
+
+ configFile = pkgs.runCommandLocal "config.toml" { } ''
+ ${pkgs.buildPackages.jq}/bin/jq 'del(..|nulls)' \
+ < ${pkgs.writeText "config.json" (builtins.toJSON athensConfig)} | \
+ ${pkgs.buildPackages.remarshal}/bin/remarshal -if json -of toml \
+ > $out
+ '';
+in
+{
+ meta = {
+ maintainers = pkgs.athens.meta.maintainers;
+ doc = ./athens.md;
+ };
+
+ options.services.athens = {
+ enable = mkEnableOption (lib.mdDoc "Go module datastore and proxy");
+
+ package = mkOption {
+ default = pkgs.athens;
+ defaultText = literalExpression "pkgs.athens";
+ example = "pkgs.athens";
+ description = lib.mdDoc "Which athens derivation to use";
+ type = types.package;
+ };
+
+ goBinary = mkOption {
+ type = types.package;
+ default = pkgs.go;
+ defaultText = literalExpression "pkgs.go";
+ example = "pkgs.go_1_21";
+ description = lib.mdDoc ''
+ The Go package used by Athens at runtime.
+
+ Athens primarily runs two Go commands:
+ 1. `go mod download -json @`
+ 2. `go list -m -json @latest`
+ '';
+ };
+
+ goEnv = mkOption {
+ type = types.enum [ "development" "production" ];
+ description = lib.mdDoc "Specifies the type of environment to run. One of 'development' or 'production'.";
+ default = "development";
+ example = "production";
+ };
+
+ goBinaryEnvVars = mkOption {
+ type = types.attrs;
+ description = lib.mdDoc "Environment variables to pass to the Go binary.";
+ example = ''
+ { "GOPROXY" = "direct", "GODEBUG" = "true" }
+ '';
+ default = { };
+ };
+
+ goGetWorkers = mkOption {
+ type = types.int;
+ description = lib.mdDoc "Number of workers concurrently downloading modules.";
+ default = 10;
+ example = 32;
+ };
+
+ goGetDir = mkOption {
+ type = types.nullOr types.path;
+ description = lib.mdDoc ''
+ Temporary directory that Athens will use to
+ fetch modules from VCS prior to persisting
+ them to a storage backend.
+
+ If the value is empty, Athens will use the
+ default OS temp directory.
+ '';
+ default = null;
+ example = "/tmp/athens";
+ };
+
+ protocolWorkers = mkOption {
+ type = types.int;
+ description = lib.mdDoc "Number of workers concurrently serving protocol paths.";
+ default = 30;
+ };
+
+ logLevel = mkOption {
+ type = types.nullOr (types.enum [ "panic" "fatal" "error" "warning" "info" "debug" "trace" ]);
+ description = lib.mdDoc ''
+ Log level for Athens.
+ Supports all logrus log levels (https://github.com/Sirupsen/logrus#level-logging)".
+ '';
+ default = "warning";
+ example = "debug";
+ };
+
+ cloudRuntime = mkOption {
+ type = types.enum [ "GCP" "none" ];
+ description = lib.mdDoc ''
+ Specifies the Cloud Provider on which the Proxy/registry is running.
+ '';
+ default = "none";
+ example = "GCP";
+ };
+
+ enablePprof = mkOption {
+ type = types.bool;
+ description = lib.mdDoc "Enable pprof endpoints.";
+ default = false;
+ };
+
+ pprofPort = mkOption {
+ type = types.port;
+ description = lib.mdDoc "Port number for pprof endpoints.";
+ default = 3301;
+ example = 443;
+ };
+
+ filterFile = mkOption {
+ type = types.nullOr types.path;
+ description = lib.mdDoc ''Filename for the include exclude filter.'';
+ default = null;
+ example = literalExpression ''
+ pkgs.writeText "filterFile" '''
+ - github.com/azure
+ + github.com/azure/azure-sdk-for-go
+ D golang.org/x/tools
+ '''
+ '';
+ };
+
+ robotsFile = mkOption {
+ type = types.nullOr types.path;
+ description = lib.mdDoc ''Provides /robots.txt for net crawlers.'';
+ default = null;
+ example = literalExpression ''pkgs.writeText "robots.txt" "# my custom robots.txt ..."'';
+ };
+
+ timeout = mkOption {
+ type = types.int;
+ description = lib.mdDoc "Timeout for external network calls in seconds.";
+ default = 300;
+ example = 3;
+ };
+
+ storageType = mkOption {
+ type = types.enum [ "memory" "disk" "mongo" "gcp" "minio" "s3" "azureblob" "external" ];
+ description = lib.mdDoc "Specifies the type of storage backend to use.";
+ default = "disk";
+ };
+
+ tlsCertFile = mkOption {
+ type = types.nullOr types.path;
+ description = lib.mdDoc "Path to the TLS certificate file.";
+ default = null;
+ example = "/etc/ssl/certs/athens.crt";
+ };
+
+ tlsKeyFile = mkOption {
+ type = types.nullOr types.path;
+ description = lib.mdDoc "Path to the TLS key file.";
+ default = null;
+ example = "/etc/ssl/certs/athens.key";
+ };
+
+ port = mkOption {
+ type = types.port;
+ default = 3000;
+ description = lib.mdDoc ''
+ Port number Athens listens on.
+ '';
+ example = 443;
+ };
+
+ unixSocket = mkOption {
+ type = types.nullOr types.path;
+ description = lib.mdDoc ''
+ Path to the unix socket file.
+ If set, Athens will listen on the unix socket instead of TCP socket.
+ '';
+ default = null;
+ example = "/run/athens.sock";
+ };
+
+ globalEndpoint = mkOption {
+ type = types.str;
+ description = lib.mdDoc ''
+ Endpoint for a package registry in case of a proxy cache miss.
+ '';
+ default = "";
+ example = "http://upstream-athens.example.com:3000";
+ };
+
+ basicAuthUser = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc ''
+ Username for basic auth.
+ '';
+ default = null;
+ example = "user";
+ };
+
+ basicAuthPass = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc ''
+ Password for basic auth. Warning: this is stored in plain text in the config file.
+ '';
+ default = null;
+ example = "swordfish";
+ };
+
+ forceSSL = mkOption {
+ type = types.bool;
+ description = lib.mdDoc ''
+ Force SSL redirects for incoming requests.
+ '';
+ default = false;
+ };
+
+ validatorHook = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc ''
+ Endpoint to validate modules against.
+
+ Not used if empty.
+ '';
+ default = null;
+ example = "https://validation.example.com";
+ };
+
+ pathPrefix = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc ''
+ Sets basepath for all routes.
+ '';
+ default = null;
+ example = "/athens";
+ };
+
+ netrcPath = mkOption {
+ type = types.nullOr types.path;
+ description = lib.mdDoc ''
+ Path to the .netrc file.
+ '';
+ default = null;
+ example = "/home/user/.netrc";
+ };
+
+ githubToken = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc ''
+ Creates .netrc file with the given token to be used for GitHub.
+ Warning: this is stored in plain text in the config file.
+ '';
+ default = null;
+ example = "ghp_1234567890";
+ };
+
+ hgrcPath = mkOption {
+ type = types.nullOr types.path;
+ description = lib.mdDoc ''
+ Path to the .hgrc file.
+ '';
+ default = null;
+ example = "/home/user/.hgrc";
+ };
+
+ traceExporter = mkOption {
+ type = types.nullOr (types.enum [ "jaeger" "datadog" ]);
+ description = lib.mdDoc ''
+ Trace exporter to use.
+ '';
+ default = null;
+ };
+
+ traceExporterURL = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc ''
+ URL endpoint that traces will be sent to.
+ '';
+ default = null;
+ example = "http://localhost:14268";
+ };
+
+ statsExporter = mkOption {
+ type = types.nullOr (types.enum [ "prometheus" ]);
+ description = lib.mdDoc "Stats exporter to use.";
+ default = null;
+ };
+
+ sumDBs = mkOption {
+ type = types.listOf types.str;
+ description = lib.mdDoc ''
+ List of fully qualified URLs that Athens will proxy
+ that the go command can use a checksum verifier.
+ '';
+ default = [ "https://sum.golang.org" ];
+ };
+
+ noSumPatterns = mkOption {
+ type = types.listOf types.str;
+ description = lib.mdDoc ''
+ List of patterns that Athens sum db proxy will return a 403 for.
+ '';
+ default = [ ];
+ example = [ "github.com/mycompany/*" ];
+ };
+
+ downloadMode = mkOption {
+ type = types.oneOf [ (types.enum [ "sync" "async" "redirect" "async_redirect" "none" ]) (types.strMatching "^file:.*$|^custom:.*$") ];
+ description = lib.mdDoc ''
+ Defines how Athens behaves when a module@version
+ is not found in storage. There are 7 options:
+ 1. "sync": download the module synchronously and
+ return the results to the client.
+ 2. "async": return 404, but asynchronously store the module
+ in the storage backend.
+ 3. "redirect": return a 301 redirect status to the client
+ with the base URL as the DownloadRedirectURL from below.
+ 4. "async_redirect": same as option number 3 but it will
+ asynchronously store the module to the backend.
+ 5. "none": return 404 if a module is not found and do nothing.
+ 6. "file:": will point to an HCL file that specifies
+ any of the 5 options above based on different import paths.
+ 7. "custom:" is the same as option 6
+ but the file is fully encoded in the option. This is
+ useful for using an environment variable in serverless
+ deployments.
+ '';
+ default = "async_redirect";
+ };
+
+ networkMode = mkOption {
+ type = types.enum [ "strict" "offline" "fallback" ];
+ description = lib.mdDoc ''
+ Configures how Athens will return the results
+ of the /list endpoint as it can be assembled from both its own
+ storage and the upstream VCS.
+
+ Note, that for better error messaging, this would also affect how other
+ endpoints behave.
+
+ Modes:
+ 1. strict: merge VCS versions with storage versions, but fail if either of them fails.
+ 2. offline: only get storage versions, never reach out to VCS.
+ 3. fallback: only return storage versions, if VCS fails. Note this means that you may
+ see inconsistent results since fallback mode does a best effort of giving you what's
+ available at the time of requesting versions.
+ '';
+ default = "strict";
+ };
+
+ downloadURL = mkOption {
+ type = types.str;
+ description = lib.mdDoc "URL used if DownloadMode is set to redirect.";
+ default = "https://proxy.golang.org";
+ };
+
+ singleFlightType = mkOption {
+ type = types.enum [ "memory" "etcd" "redis" "redis-sentinel" "gcp" "azureblob" ];
+ description = lib.mdDoc ''
+ Determines what mechanism Athens uses to manage concurrency flowing into the Athens backend.
+ '';
+ default = "memory";
+ };
+
+ indexType = mkOption {
+ type = types.enum [ "none" "memory" "mysql" "postgres" ];
+ description = lib.mdDoc ''
+ Type of index backend Athens will use.
+ '';
+ default = "none";
+ };
+
+ shutdownTimeout = mkOption {
+ type = types.int;
+ description = lib.mdDoc ''
+ Number of seconds to wait for the server to shutdown gracefully.
+ '';
+ default = 60;
+ example = 1;
+ };
+
+ singleFlight = {
+ etcd = {
+ endpoints = mkOption {
+ type = types.listOf types.str;
+ description = lib.mdDoc "URLs that determine all distributed etcd servers.";
+ default = [ ];
+ example = [ "localhost:2379" ];
+ };
+ };
+ redis = {
+ endpoint = mkOption {
+ type = types.str;
+ description = lib.mdDoc "URL of the redis server.";
+ default = "";
+ example = "localhost:6379";
+ };
+ password = mkOption {
+ type = types.str;
+ description = lib.mdDoc "Password for the redis server. Warning: this is stored in plain text in the config file.";
+ default = "";
+ example = "swordfish";
+ };
+
+ lockConfig = {
+ ttl = mkOption {
+ type = types.int;
+ description = lib.mdDoc "TTL for the lock in seconds.";
+ default = 900;
+ example = 1;
+ };
+ timeout = mkOption {
+ type = types.int;
+ description = lib.mdDoc "Timeout for the lock in seconds.";
+ default = 15;
+ example = 1;
+ };
+ maxRetries = mkOption {
+ type = types.int;
+ description = lib.mdDoc "Maximum number of retries for the lock.";
+ default = 10;
+ example = 1;
+ };
+ };
+ };
+
+ redisSentinel = {
+ endpoints = mkOption {
+ type = types.listOf types.str;
+ description = lib.mdDoc "URLs that determine all distributed redis servers.";
+ default = [ ];
+ example = [ "localhost:26379" ];
+ };
+ masterName = mkOption {
+ type = types.str;
+ description = lib.mdDoc "Name of the sentinel master server.";
+ default = "";
+ example = "redis-1";
+ };
+ sentinelPassword = mkOption {
+ type = types.str;
+ description = lib.mdDoc "Password for the sentinel server. Warning: this is stored in plain text in the config file.";
+ default = "";
+ example = "swordfish";
+ };
+
+ lockConfig = {
+ ttl = mkOption {
+ type = types.int;
+ description = lib.mdDoc "TTL for the lock in seconds.";
+ default = 900;
+ example = 1;
+ };
+ timeout = mkOption {
+ type = types.int;
+ description = lib.mdDoc "Timeout for the lock in seconds.";
+ default = 15;
+ example = 1;
+ };
+ maxRetries = mkOption {
+ type = types.int;
+ description = lib.mdDoc "Maximum number of retries for the lock.";
+ default = 10;
+ example = 1;
+ };
+ };
+ };
+ };
+
+ storage = {
+ cdn = {
+ endpoint = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "hostname of the CDN server.";
+ example = "cdn.example.com";
+ default = null;
+ };
+ };
+
+ disk = {
+ rootPath = mkOption {
+ type = types.nullOr types.path;
+ description = lib.mdDoc "Athens disk root folder.";
+ default = "/var/lib/athens";
+ };
+ };
+
+ gcp = {
+ projectID = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "GCP project ID.";
+ example = "my-project";
+ default = null;
+ };
+ bucket = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "GCP backend storage bucket.";
+ example = "my-bucket";
+ default = null;
+ };
+ jsonKey = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Base64 encoded GCP service account key. Warning: this is stored in plain text in the config file.";
+ default = null;
+ };
+ };
+
+ minio = {
+ endpoint = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Endpoint of the minio storage backend.";
+ example = "minio.example.com:9001";
+ default = null;
+ };
+ key = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Access key id for the minio storage backend.";
+ example = "minio";
+ default = null;
+ };
+ secret = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Secret key for the minio storage backend. Warning: this is stored in plain text in the config file.";
+ example = "minio123";
+ default = null;
+ };
+ enableSSL = mkOption {
+ type = types.bool;
+ description = lib.mdDoc "Enable SSL for the minio storage backend.";
+ default = false;
+ };
+ bucket = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Bucket name for the minio storage backend.";
+ example = "gomods";
+ default = null;
+ };
+ region = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Region for the minio storage backend.";
+ example = "us-east-1";
+ default = null;
+ };
+ };
+
+ mongo = {
+ url = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "URL of the mongo database.";
+ example = "mongodb://localhost:27017";
+ default = null;
+ };
+ defaultDBName = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Name of the mongo database.";
+ example = "athens";
+ default = null;
+ };
+ certPath = mkOption {
+ type = types.nullOr types.path;
+ description = lib.mdDoc "Path to the certificate file for the mongo database.";
+ example = "/etc/ssl/mongo.pem";
+ default = null;
+ };
+ insecure = mkOption {
+ type = types.bool;
+ description = lib.mdDoc "Allow insecure connections to the mongo database.";
+ default = false;
+ };
+ };
+
+ s3 = {
+ region = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Region of the S3 storage backend.";
+ example = "eu-west-3";
+ default = null;
+ };
+ key = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Access key id for the S3 storage backend.";
+ example = "minio";
+ default = null;
+ };
+ secret = mkOption {
+ type = types.str;
+ description = lib.mdDoc "Secret key for the S3 storage backend. Warning: this is stored in plain text in the config file.";
+ default = "";
+ };
+ token = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Token for the S3 storage backend. Warning: this is stored in plain text in the config file.";
+ default = null;
+ };
+ bucket = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Bucket name for the S3 storage backend.";
+ example = "gomods";
+ default = null;
+ };
+ forcePathStyle = mkOption {
+ type = types.bool;
+ description = lib.mdDoc "Force path style for the S3 storage backend.";
+ default = false;
+ };
+ useDefaultConfiguration = mkOption {
+ type = types.bool;
+ description = lib.mdDoc "Use default configuration for the S3 storage backend.";
+ default = false;
+ };
+ credentialsEndpoint = mkOption {
+ type = types.str;
+ description = lib.mdDoc "Credentials endpoint for the S3 storage backend.";
+ default = "";
+ };
+ awsContainerCredentialsRelativeURI = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Container relative url (used by fargate).";
+ default = null;
+ };
+ endpoint = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Endpoint for the S3 storage backend.";
+ default = null;
+ };
+ };
+
+ azureblob = {
+ accountName = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Account name for the Azure Blob storage backend.";
+ default = null;
+ };
+ accountKey = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Account key for the Azure Blob storage backend. Warning: this is stored in plain text in the config file.";
+ default = null;
+ };
+ containerName = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Container name for the Azure Blob storage backend.";
+ default = null;
+ };
+ };
+
+ external = {
+ url = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "URL of the backend storage layer.";
+ example = "https://athens.example.com";
+ default = null;
+ };
+ };
+ };
+
+ index = {
+ mysql = {
+ protocol = mkOption {
+ type = types.str;
+ description = lib.mdDoc "Protocol for the MySQL database.";
+ default = "tcp";
+ };
+ host = mkOption {
+ type = types.str;
+ description = lib.mdDoc "Host for the MySQL database.";
+ default = "localhost";
+ };
+ port = mkOption {
+ type = types.int;
+ description = lib.mdDoc "Port for the MySQL database.";
+ default = 3306;
+ };
+ user = mkOption {
+ type = types.str;
+ description = lib.mdDoc "User for the MySQL database.";
+ default = "root";
+ };
+ password = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Password for the MySQL database. Warning: this is stored in plain text in the config file.";
+ default = null;
+ };
+ database = mkOption {
+ type = types.str;
+ description = lib.mdDoc "Database name for the MySQL database.";
+ default = "athens";
+ };
+ params = {
+ parseTime = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Parse time for the MySQL database.";
+ default = "true";
+ };
+ timeout = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Timeout for the MySQL database.";
+ default = "30s";
+ };
+ };
+ };
+
+ postgres = {
+ host = mkOption {
+ type = types.str;
+ description = lib.mdDoc "Host for the Postgres database.";
+ default = "localhost";
+ };
+ port = mkOption {
+ type = types.int;
+ description = lib.mdDoc "Port for the Postgres database.";
+ default = 5432;
+ };
+ user = mkOption {
+ type = types.str;
+ description = lib.mdDoc "User for the Postgres database.";
+ default = "postgres";
+ };
+ password = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Password for the Postgres database. Warning: this is stored in plain text in the config file.";
+ default = null;
+ };
+ database = mkOption {
+ type = types.str;
+ description = lib.mdDoc "Database name for the Postgres database.";
+ default = "athens";
+ };
+ params = {
+ connect_timeout = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "Connect timeout for the Postgres database.";
+ default = "30s";
+ };
+ sslmode = mkOption {
+ type = types.nullOr types.str;
+ description = lib.mdDoc "SSL mode for the Postgres database.";
+ default = "disable";
+ };
+ };
+ };
+ };
+
+ extraConfig = mkOption {
+ type = types.attrs;
+ description = lib.mdDoc ''
+ Extra configuration options for the athens config file.
+ '';
+ default = { };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ systemd.services.athens = {
+ description = "Athens Go module proxy";
+ documentation = [ "https://docs.gomods.io" ];
+
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network-online.target" ];
+ wants = [ "network-online.target" ];
+
+ serviceConfig = {
+ Restart = "on-abnormal";
+ Nice = 5;
+ ExecStart = ''${cfg.package}/bin/athens -config_file=${configFile}'';
+
+ KillMode = "mixed";
+ KillSignal = "SIGINT";
+ TimeoutStopSec = cfg.shutdownTimeout;
+
+ LimitNOFILE = 1048576;
+ LimitNPROC = 512;
+
+ DynamicUser = true;
+ PrivateTmp = true;
+ PrivateDevices = true;
+ ProtectHome = "read-only";
+ ProtectSystem = "full";
+
+ ReadWritePaths = mkIf (cfg.storage.disk.rootPath != null && (! hasPrefix "/var/lib/" cfg.storage.disk.rootPath)) [ cfg.storage.disk.rootPath ];
+ StateDirectory = mkIf (hasPrefix "/var/lib/" cfg.storage.disk.rootPath) [ (removePrefix "/var/lib/" cfg.storage.disk.rootPath) ];
+
+ CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
+ AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
+ NoNewPrivileges = true;
+ };
+ };
+
+ networking.firewall = {
+ allowedTCPPorts = optionals (cfg.unixSocket == null) [ cfg.port ]
+ ++ optionals cfg.enablePprof [ cfg.pprofPort ];
+ };
+ };
+
+}