infra/services/channel-scripts/default.nix
Raito Bezarius 84899b48ea feat(channel-scripts): support push to git and automatic cleanup of failed streaming
Now, we won't pile a bunch of failed streaming attempts and this will
automatically push to git.

Credentials are left to be done for the push to actually work.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-12-14 19:06:47 +01:00

221 lines
7.1 KiB
Nix

{ lib, config, pkgs, ... }:
# FIXME(raito): I'm really really really not happy with this design of NixOS module, clean up all of this someday.
let
inherit (lib) mkEnableOption mkOption types mkIf mapAttrsToList mkPackageOption concatStringsSep mkMerge;
cfg = config.bagel.nixpkgs.channel-scripts;
toml = pkgs.formats.toml { };
configFile = toml.generate "forkos.toml" cfg.settings;
orderLib = import ./service-order.nix { inherit lib; };
makeUpdateJob = channelName: mainJob: {
name = "update-${channelName}";
value = {
description = "Update channel ${channelName}";
path = with pkgs; [ git ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = false;
User = "channel-scripts";
DynamicUser = true;
StateDirectory = "channel-scripts";
MemoryHigh = "80%";
EnvironmentFile = [
cfg.releaseBucketCredentialsFile
];
Environment = cfg.extraEnvironment;
# TODO: we should have our own secret for this.
LoadCredential = [ "password:${config.age.secrets.alloy-push-password.path}" ];
};
unitConfig.After = [ "networking.target" ];
script =
''
# A stateful copy of nixpkgs
dir=/var/lib/channel-scripts/nixpkgs
if ! [[ -e $dir ]]; then
git clone --bare ${cfg.nixpkgsUrl} $dir
fi
GIT_DIR=$dir git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
CREDENTIAL=$(echo -en "promtail:$(cat $CREDENTIALS_DIRECTORY/password)" | base64)
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic $CREDENTIAL"
# TODO: use escapeShellArgs
exec ${cfg.package}/bin/mirror-forkos -c ${configFile} ${concatStringsSep " " cfg.extraArgs} apply ${channelName} ${mainJob}
'';
};
};
updateJobs = orderLib.mkOrderedChain (mapAttrsToList (n: { job, ... }: makeUpdateJob n job) cfg.channels);
channelOpts = { ... }: {
options = {
job = mkOption {
type = types.str;
example = "nixos/trunk-combined/tested";
};
variant = mkOption {
type = types.enum [ "primary" "small" "darwin" "aarch64" ];
example = "primary";
};
status = mkOption {
type = types.enum [ "beta" "stable" "deprecated" "unmaintained" "rolling" ];
example = "rolling";
};
};
};
in
{
options.bagel.nixpkgs.channel-scripts = {
enable = mkEnableOption ''the channel scripts.
Fast forwarding channel branches which are read-only except for this privileged bot
based on our Hydra acceptance tests.
'';
otlp.enable = mkEnableOption "the OTLP export process";
s3 = {
release = mkOption {
type = types.str;
};
channel = mkOption {
type = types.str;
};
};
package = mkPackageOption pkgs "mirror-forkos" { };
settings = mkOption {
type = types.attrsOf types.anything;
};
nixpkgsUrl = mkOption {
type = types.str;
default = "https://cl.forkos.org/nixpkgs.git";
description = "URL to the nixpkgs repository to clone and to push to";
};
binaryCacheUrl = mkOption {
type = types.str;
default = "https://cache.forkos.org";
description = "URL to the binary cache";
};
baseUriForGitRevisions = mkOption {
type = types.str;
description = "Base URI to generate link to a certain revision";
};
extraArgs = mkOption {
type = types.listOf types.str;
default = [ ];
description = "Extra arguments passed to the mirroring program";
};
releaseBucketCredentialsFile = mkOption {
type = types.path;
description = ''Path to the release bucket credentials file exporting S3-style environment variables.
For example, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` for the S3 operations to work.
'';
};
deployKeyFile = mkOption {
type = types.path;
description = ''Path to the private SSH key which is allowed to deploy things to the protected channel references on the Git repository.
'';
};
hydraUrl = mkOption {
type = types.str;
default = "https://hydra.forkos.org";
description = "URL to the Hydra instance";
};
channels = mkOption {
type = types.attrsOf (types.submodule channelOpts);
description = "List of channels to mirror";
};
extraEnvironment = mkOption {
type = types.listOf types.str;
};
};
config = mkIf cfg.enable {
bagel.nixpkgs.channel-scripts.extraEnvironment = mkMerge [
([
"RUST_LOG=info"
])
(mkIf cfg.otlp.enable [
''OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="https://tempo.forkos.org/v1/traces"''
])
];
bagel.nixpkgs.channel-scripts.settings = {
hydra_uri = cfg.hydraUrl;
binary_cache_uri = cfg.binaryCacheUrl;
base_git_uri_for_revision = cfg.baseUriForGitRevisions;
# TODO: this leaks information about where channel-scripts are hosted.
# Cleanup this later with a proper module option.
repo_dir = "/gerrit-data/channel-scripts/nixpkgs";
s3_release_bucket_name = cfg.s3.release;
s3_channel_bucket_name = cfg.s3.channel;
};
users.users.channel-scripts = {
description = "Channel scripts user";
isSystemUser = true;
group = "channel-scripts";
};
users.groups.channel-scripts = {};
systemd.services = (lib.listToAttrs updateJobs) // {
"update-all-channels" = {
description = "Start all channel updates.";
unitConfig = {
After = map
(service: "${service.name}.service")
updateJobs;
Wants = map
(service: "${service.name}.service")
updateJobs;
};
script = "true";
};
"cleanup-failed-streaming-prefixes" = {
description = "Cleanup all failed streaming prefixes on the channel bucket (channel-scripts)";
conflicts = map (service: "${service.name}.service") updateJobs;
after = [ "networking.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = false;
User = "channel-scripts";
DynamicUser = true;
StateDirectory = "channel-scripts";
EnvironmentFile = [
cfg.releaseBucketCredentialsFile
];
Environment = cfg.extraEnvironment;
LoadCredential = [ "password:${config.age.secrets.alloy-push-password.path}" ];
ExecStart = "${cfg.package}/bin/mirror-forkos -c ${configFile} ${concatStringsSep " " cfg.extraArgs} cleanup-streamed-prefixes";
};
};
};
systemd.timers."update-all-channels" = {
description = "Start all channel updates.";
wantedBy = [ "timers.target" ];
timerConfig = {
OnUnitInactiveSec = 600;
OnBootSec = 900;
AccuracySec = 300;
};
};
systemd.timers."cleanup-failed-streaming-prefixes" = {
description = "Cleanup failed streaming prefixes for channel-scripts";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "daily";
RandomizedDelaySec = "1h";
};
};
};
}