From 97b214e0eadc65ec9cc95b746df8400bb24b3066 Mon Sep 17 00:00:00 2001 From: mat ess Date: Sun, 13 Nov 2022 18:16:12 -0500 Subject: [PATCH] Split out compose.libsonnet, add to-docker-compose helper --- compose.libsonnet | 73 +++++++++++++++++++++++++++++++++++++++++++ flake.nix | 22 ++++++++++--- services.jsonnet | 79 +++++------------------------------------------ 3 files changed, 99 insertions(+), 75 deletions(-) create mode 100644 compose.libsonnet diff --git a/compose.libsonnet b/compose.libsonnet new file mode 100644 index 0000000..b63f4c3 --- /dev/null +++ b/compose.libsonnet @@ -0,0 +1,73 @@ +local optional(object, field) = std.get(object, field, {}); + +local dockerSocket = '/var/run/docker.sock:/var/run/docker.sock'; + +local mediaEnv = { + PUID: 1000, + PGID: 1000, + TZ: 'America/New_York', +}; + +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 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 mediaMounts(mounts) = { + ['/media/mat/%s' % path]: mounts[path] + for path in std.objectFields(mounts) +}; + +{ + Compose(cfg):: { + services: std.mapWithKey(mkService, cfg), + volumes: extractVolumes(cfg), + networks: { traefik: { external: true } }, + }, + + Port(port, src=port, kind='tcp'):: + local mapped = '%d:%d' % [port, src]; + '%s/%s' % [mapped, kind], + + Command:: toList('--%s=%s'), + + MediaMounts:: mediaMounts, + + 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, + }, +} diff --git a/flake.nix b/flake.nix index 5b91835..77b4a24 100644 --- a/flake.nix +++ b/flake.nix @@ -9,10 +9,24 @@ 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 ]; + perSystem = { config, self', inputs', pkgs, system, ... }: + let + to-docker-compose = pkgs.writeShellApplication { + name = "to-docker-compose"; + runtimeInputs = [ pkgs.jsonnet ]; + text = '' + jsonnet services.jsonnet \ + --tla-code secrets="{ + PLEX_CLAIM: '$PLEX_CLAIM', + ADVERTISE_IP: '$ADVERTISE_IP', + }" + ''; + }; + in + { + devShells.default = pkgs.mkShell { + buildInputs = with pkgs; [ jsonnet to-docker-compose ]; + }; }; - }; }; } diff --git a/services.jsonnet b/services.jsonnet index 3c27156..cbc45d2 100644 --- a/services.jsonnet +++ b/services.jsonnet @@ -1,78 +1,15 @@ -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, -}; +local compose = import 'compose.libsonnet'; +local Compose = compose.Compose; +local Command = compose.Command; +local Port = compose.Port; +local MediaService = compose.MediaService; +local MediaMounts = compose.MediaMounts; function(secrets={}) Compose({ traefik: { image: 'traefik:latest', - command: toCommand({ + command: Command({ 'log.level': 'ERROR', 'api.insecure': 'true', 'providers.docker': 'true', @@ -143,7 +80,7 @@ function(secrets={}) plex: { image: 'plexinc/pms-docker', environment: { - TZ: tz, + TZ: 'America/New_York', PLEX_CLAIM: std.get(secrets, 'PLEX_CLAIM'), ADVERTISE_IP: std.get(secrets, 'ADVERTISE_IP'), },