From 0c7c027fba5fbca827f0edf7106bf4eee854844d Mon Sep 17 00:00:00 2001 From: mat ess Date: Sat, 12 Nov 2022 20:06:12 -0500 Subject: [PATCH] Add flake + jsonnet --- .envrc | 1 + flake.lock | 64 +++++++++++++++++ flake.nix | 18 +++++ services.jsonnet | 182 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 265 insertions(+) create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 services.jsonnet diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..23f076f --- /dev/null +++ b/flake.lock @@ -0,0 +1,64 @@ +{ + "nodes": { + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1666885127, + "narHash": "sha256-uXA/3lhLhwOTBMn9a5zJODKqaRT+SuL5cpEmOz2ULoo=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "0e101dbae756d35a376a5e1faea532608e4a4b9a", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1667629849, + "narHash": "sha256-P+v+nDOFWicM4wziFK9S/ajF2lc0N2Rg9p6Y35uMoZI=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3bacde6273b09a21a8ccfba15586fb165078fb62", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "dir": "lib", + "lastModified": 1665349835, + "narHash": "sha256-UK4urM3iN80UXQ7EaOappDzcisYIuEURFRoGQ/yPkug=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "34c5293a71ffdb2fe054eb5288adc1882c1eb0b1", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..5b91835 --- /dev/null +++ b/flake.nix @@ -0,0 +1,18 @@ +{ + description = "Homelab configuration tools"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + }; + + outputs = { self, flake-parts, ... }: + flake-parts.lib.mkFlake { inherit self; } { + systems = [ "x86_64-linux" "aarch64-darwin" ]; + perSystem = { config, self', inputs', pkgs, system, ... }: { + devShells.default = pkgs.mkShell { + buildInputs = with pkgs; [ jsonnet ]; + }; + }; + }; +} diff --git a/services.jsonnet b/services.jsonnet new file mode 100644 index 0000000..3c27156 --- /dev/null +++ b/services.jsonnet @@ -0,0 +1,182 @@ +local optional(object, field) = std.get(object, field, {}); + +local dockerSocket = '/var/run/docker.sock:/var/run/docker.sock'; + +local tz = 'America/New_York'; +local mediaEnv = { + PUID: 1000, + PGID: 1000, + TZ: tz, +}; + +local formatHelper(fmt) = function(name, option) fmt % [name, option]; +local toList(fmt) = function(object) std.objectValues(std.mapWithKey(formatHelper(fmt), object)); +local toLabels = toList('%s=%s'); +local toVolumes = toList('%s:%s'); +local toCommand = toList('--%s=%s'); + +local traefikLabels(name, host, port, extras) = toLabels({ + 'traefik.enable': 'true', + ['traefik.http.routers.%s.rule' % name]: 'Host(`%s.mat`)' % host, + ['traefik.http.routers.%s.entrypoints' % name]: 'web', + ['traefik.http.services.%s.loadbalancer.server.port' % name]: port, + ['traefik.http.routers.%s.service' % name]: '%s' % name, + 'traefik.docker.network': 'traefik', +} + extras); + +local mkService(name, svc) = svc { + container_name: name, + networks: ['traefik'], + volumes: toVolumes(optional(svc, 'volumes')) + + toVolumes(optional(svc, 'mounts')) + + if std.get(svc, 'docker', false) + then [dockerSocket] + else [], + labels: traefikLabels(name, std.get(svc, 'host', name), svc.webPort, optional(svc, 'traefik')), + restart: 'always', +}; + +local extractVolumes(cfg) = { + [name]: { external: true } + for name in std.flattenArrays([ + std.objectFields(optional(svc, 'volumes')) + for svc in std.objectValues(cfg) + ]) +}; + +local Compose(cfg) = { + services: std.mapWithKey(mkService, cfg), + volumes: extractVolumes(cfg), + networks: { traefik: { external: true } }, +}; + +local Port(port, src=port, kind='tcp') = + local mapped = '%d:%d' % [port, src]; + '%s/%s' % [mapped, kind]; + +local MediaMounts(mounts) = { + ['/media/mat/%s' % path]: mounts[path] + for path in std.objectFields(mounts) +}; + +local MediaService(name, tag='latest', env={}, mounts={}, webPort, ports=[]) = { + image: 'lscr.io/linuxserver/%s:%s' % [name, tag], + environment: mediaEnv + env, + volumes: { ['%s_config' % name]: '/config' }, + mounts:: MediaMounts(mounts), + webPort:: webPort, + ports: ports, +}; + +function(secrets={}) + Compose({ + traefik: { + image: 'traefik:latest', + command: toCommand({ + 'log.level': 'ERROR', + 'api.insecure': 'true', + 'providers.docker': 'true', + 'providers.docker.exposedbydefault': 'false', + 'entrypoints.web.address': ':80', + // 'entrypoints.websecure.address': ':443', + }), + docker:: true, + webPort:: 80, + ports: [Port(80), /* Port(443), */ Port(8080)], + traefik:: { + // 'traefik.http.routers.http-catchall.rule': 'hostregexp(`{host:.+}`)' + // 'traefik.http.routers.http-catchall.entrypoints': 'web' + // 'traefik.http.routers.http-catchall.middlewares': 'redirect-to-https' + // 'traefik.http.middlewares.redirect-to-https.redirectscheme.scheme': 'https' + }, + }, + portainer: { + image: 'portainer/portainer-ce:latest', + docker:: true, + volumes: { portainer_data: '/data' }, + webPort:: 9000, + ports: [Port(9443)], + }, + deluge: MediaService( + name='deluge', + env={ DELUGE_LOGLEVEL: 'error' }, + mounts={ torrents: '/downloads' }, + webPort=8112, + ports=[Port(54979), Port(54979, kind='udp')], + ), + prowlarr: MediaService( + name='prowlarr', + tag='develop', + webPort=9696, + mounts={ + torrents: '/downloads', + 'passport-5tb': '/passport-5tb', + 'passport-1tb': '/passport-1tb', + }, + ), + bazarr: MediaService( + name='bazarr', + webPort=6767, + mounts={ + 'passport-5tb': '/passport-5tb', + 'passport-1tb': '/passport-1tb', + }, + ), + radarr: MediaService( + name='radarr', + webPort=7878, + mounts={ + 'passport-5tb/movies': '/passport-5tb', + 'passport-1tb/movies': '/passport-1tb', + torrents: '/downloads', + }, + ), + sonarr: MediaService( + name='sonarr', + webPort=8989, + mounts={ + 'passport-5tb/tv': '/passport-5tb', + 'passport-1tb/tv': '/passport-1tb', + torrents: '/downloads', + } + ), + plex: { + image: 'plexinc/pms-docker', + environment: { + TZ: tz, + PLEX_CLAIM: std.get(secrets, 'PLEX_CLAIM'), + ADVERTISE_IP: std.get(secrets, 'ADVERTISE_IP'), + }, + volumes: { plex_config: '/config' }, + mounts:: MediaMounts({ + 'torrents/plex-transcode': '/transcode', + 'passport-5tb': '/passport-5tb', + 'passport-1tb': '/passport-1tb', + }), + devices: ['/dev/dri:/dev/dri'], + webPort:: 32400, + ports: [ + Port(56463, src=32400), + Port(3005), + Port(8324), + Port(32469), + Port(1900, kind='udp'), + Port(32410, kind='udp'), + Port(32412, kind='udp'), + Port(32413, kind='udp'), + Port(32414, kind='udp'), + ], + }, + archivebox: { + image: 'archivebox/archivebox:dev', + command: 'server --quick-init 0.0.0.0:8000', + environment: { + ALLOWED_HOSTS: '*', + MEDIA_MAX_SIZE: '750m', + RESOLUTION: '1024,768', + }, + mounts:: MediaMounts({ 'passport-5tb/archivebox': '/data' }), + webPort:: 8000, + host:: 'archive', + }, + })