commit 09f4f0c8a882c57d9d03b79b3da1da405958136b Author: Magic_RB Date: Mon Jan 11 18:35:35 2021 +0100 Initial Commit diff --git a/README.org b/README.org new file mode 100644 index 0000000..dabbbdf --- /dev/null +++ b/README.org @@ -0,0 +1,21 @@ +* Red Alder systems + +** Overview + + This repository contains Docker images built from [[https://github.com/NixOS/nixpkgs][Nixpkgs]] and custom Nix packages created by Red Alder. The Docker + images can be accessed with =systems.dockerImages.= and the packages are accessible in a format similar to + Flakes. Meaning =systems.flakes.=, contains the outputs of a Flake which contains the package and a + NixOS module if applicable, for example =systems.flakes.klippy.defaultPackage."x86_64-linux"= and + =systems.flakes.moonraker.nixosModules=. Even though the Nix packages are almost Flakes, this repository is + completely pure. + +** Current Contents +*** Docker Images + - [[./docker/csi-driver-nfs][csi-driver-nfs]] + - [[./docker/klippy-moonraker][klippy-moonraker]] + - [[./docker/postgresql][postgresql]] + +*** Nix Packages + - [[./nix-packages/klippy][klippy]] + - [[./nix-packages/mainsail][mainsail]] + - [[./nix-packages/moonraker][moonraker]] diff --git a/docker/csi-driver-nfs/default.nix b/docker/csi-driver-nfs/default.nix new file mode 100644 index 0000000..144a631 --- /dev/null +++ b/docker/csi-driver-nfs/default.nix @@ -0,0 +1,25 @@ +{ pkgs, nixpkgs, ... }@inputs: +let + ldflags = "-s -w -extldflags '-static'"; + csi-driver-nfs = pkgs.buildGoModule { + name = "csi-driver-nfs"; + version = "2.0"; + + vendorSha256 = null; + + doCheck = false; + + src = inputs.csi-driver-nfs; + }; + init = pkgs.writeShellScriptBin "init" '' + ${csi-driver-nfs}/bin/nfsplugin $@ + ''; +in +pkgs.dockerTools.buildLayeredImage { + name = "csi-driver-nfs"; + tag = "latest"; + + config = { + Entrypoint = [ "${init}/bin/init" ]; + }; +} diff --git a/docker/klippy-moonraker/default.nix b/docker/klippy-moonraker/default.nix new file mode 100644 index 0000000..666c74a --- /dev/null +++ b/docker/klippy-moonraker/default.nix @@ -0,0 +1,103 @@ +{ pkgs, nixpkgs, ... }: +let + init = let + klippy_input_tty_default = "/tmp/printer"; + klippy_api_server_default = "/tmp/klippy_uds"; + moonraker_logfile_default = "/moonraker.log"; + in pkgs.writeShellScriptBin "init" '' + #!${pkgs.bash} + ## + ## KLIPPY_INPUT_TTY ? ${klippy_input_tty_default} - input tty path for Klippy + ## KLIPPY_API_SERVER ? ${klippy_api_server_default} - api server path for Klippy + ## [ KLIPPY_VERBOSE ] - enable verbosity for Klippy + ## [ KLIPPY_DICTIONARY ] - specifies dictionary path for Klippy + ## [ KLIPPY_LOGFILE ] - enable logging and specify log file path for Klippy + ## KLIPPY_CONFIG - specifies config file path for Klippy + ## + ## MOONRAKER_CONFIG - specifies config file path for Moonraker + ## MOONRAKER_LOGFILE ? ${moonraker_logfile_default} - specifies log file path for Moonraker + ## + ### Recommended volumes (many directories which exist in normal Docker containers, do not exist in this one) + ## + ## Note: paths are in /root, due to moonraker refusing to use paths that are not in $HOME or /etc/moonraker + ## - /root/virtual_sdcard - used to store gcode files for printing + ## - /root/config/klippy - place your config file for klippy here + ## - /root/config/moonraker + + if [[ ! -z "$KLIPPY_INPUT_TTY" ]] ; then + _klippy_input_tty="$KLIPPY_INPUT_TTY" + else + _klippy_input_tty="${klippy_input_tty_default}" + fi + + if [[ ! -z "$KLIPPY_API_SERVER" ]] ; then + _klippy_api_server="$KLIPPY_API_SERVER" + else + _klippy_api_server="${klippy_api_server_default}" + fi + + _klippy_verbose="$KLIPPY_VERBOSE" + _klippy_dictionary="$KLIPPY_DICTIONARY" + _klippy_logfile="$KLIPPY_LOGFILE" + + if [[ ! -z "$KLIPPY_CONFIG" ]] ; then + if [[ -f "$MOONRAKER_CONFIG" ]] ; then + _klippy_config="$KLIPPY_CONFIG" + else + echo "File \"$KLIPPY_CONFIG\" specified in \`KLIPYY_CONFIG\` does not exist!" ; exit 1 + fi + else + echo 'You must specify the `KLIPPY_CONFIG` parameter!' ; exit 1 + fi + + + if [[ ! -z "$MOONRAKER_CONFIG" ]] ; then + if [[ -f "$MOONRAKER_CONFIG" ]] ; then + _moonraker_config="$MOONRAKER_CONFIG" + else + echo "File \"$MOONRAKER_CONFIG\" specified in \`MOONRAKER_CONFIG\' does not exist!" ; exit 1 + fi + else + echo 'You must specify the `MOONRAKER_CONFIG` parameter!' ; exit 1 + fi + + if [[ ! -z "$MOONRAKER_LOGFILE" ]] ; then + _moonraker_logfile="$MOONRAKER_LOGFILE" + else + _moonraker_logfile="${moonraker_logfile_default}"; + fi + + ${pkgs.busybox}/bin/mkdir -p \ + /usr/bin \ + /tmp \ + /root/{virtual_sdcard,config/{klippy,moonraker}} + + ${pkgs.busybox}/bin/ln -s ${pkgs.busybox}/bin/env /usr/bin/env + + ${pkgs.klippy}/bin/klippy \ + --input-tty "$_klippy_input_tty" \ + --api-server "$_klippy_api_server" \ + $([[ ! -z "$_klippy_logfile" ]] && echo --logfile "$_klippy_logfile" || echo "") \ + $([[ ! -z "$_klippy_verbose" ]] && echo -v || echo "") \ + $([[ ! -z "$_klippy_dictionary" ]] && echo --dictionary "$_klippy_dictionary" || echo"") \ + "$_klippy_config" & + klippy_pid=$! + + ${pkgs.moonraker}/bin/moonraker \ + --configfile "$_moonraker_config" \ + --logfile "$_moonraker_logfile" & + moonraker_pid=$! + + wait "$moonraker_pid" + wait "$klippy_pid" + ''; +in +pkgs.dockerTools.buildLayeredImage { + name = "klippy-moonraker"; + tag = "latest"; + + config = { + Cmd = [ "${init}/bin/init" ]; + }; +} + diff --git a/docker/postgresql/default.nix b/docker/postgresql/default.nix new file mode 100644 index 0000000..446a33f --- /dev/null +++ b/docker/postgresql/default.nix @@ -0,0 +1,214 @@ +{ pkgs, nixpkgs, ... }: +let + init = let + defaults = { + user = "postgres"; + db = "postgres"; + host_auth_method = "scram-sha-256"; + data = "/pgdata"; + waldir = "/waldir"; + }; + uid = builtins.toString 5000; + gid = builtins.toString 5000; + postgres = pkgs.postgresql_12; + in pkgs.writeShellScriptBin "init" '' + if [[ $(${pkgs.busybox}/bin/id -u) = 0 ]] ; then +${pkgs.busybox}/bin/cat << EOF +### PostgreSQL Nix Image MANUAL +## +## POSTGRES_USER ? ${defaults.user} - default user +## POSTGRES_PASSWORD - password for POSTGRES_USER +## POSTGRES_DB ? ${defaults.db} - default database +## [ NO_CREATE_DB ] - disables the creation of a default user + db # Currently UNUSED !! +## POSTGRES_INITDB_ARGS ? "" - passed to \`initdb\` +## [ POSTGRES_INITDB_WALDIR ] ? ${defaults.waldir} - write-ahead log directory +## POSTGRES_HOST_AUTH_METHOD ? ${defaults.host_auth_method} - password authentication method +## [ PG_HBA ] - overrides the creation of pg_hba.conf +## PGDATA ? ${defaults.data} - PostgreSQL data folder + +### Recommended volumes (many directories which exist in normal Docker containers, do not exist in this one) +## +## - ${defaults.data} - PostgreSQL data folder +## - ${defaults.waldir} - PostgreSQL waldir + +EOF + + export _postgres_user="''${POSTGRES_USER:-${defaults.user}}" + + if [[ ! -z "$POSTGRES_PASSWORD" ]] ; then + export _postgres_password="$POSTGRES_PASSWORD" + else + echo 'You must specify the `POSTGRES_PASSWORD` parameter!' ; exit 1 + fi + + export _postgres_db="''${POSTGRES_DB:-${defaults.db}}" + export _no_create_db="''${NO_CREATE_DB:-}" + export _postgres_initdb_args="''${POSTGRES_INITDB_ARGS:-}" + + if [[ ! -z "''${POSTGRES_INITDB_WALDIR+x}" ]] ; then + export _postgres_initdb_waldir="''${POSTGRES_INITDB_WALDIR:-${defaults.waldir}}" + else + export _postgres_initdb_waldir="" + fi + + export _postgres_host_auth_method="''${POSTGRES_HOST_AUTH_METHOD:-${defaults.host_auth_method}}" + if [[ ! -z "''${PG_HBA+x}" ]] ; then + if [[ ! -z "$PG_HBA" ]] ; then + if [[ -f "$PG_HBA" ]] ; then + export _pg_hba="$PG_HBA" + else + echo "FILE \"$PG_HBA\" specified in \`PG_HBA'\ does not exist!" ; exit 1 + fi + else + echo 'Parameter `PG_HBA` is set, but empty!' ; exit 1 + fi + else + _ph_hba="" + fi + + export _pgdata="''${PGDATA:-${defaults.data}}" + +${pkgs.busybox}/bin/cat << EOF +## Starting with options: +## POSTGRES_USER = "$_postgres_user" +## POSTGRES_PASSWORD = +## POSTGRES_DB = "$_postgres_db" +## NO_CREATE_DB = "$([[ ! -z "$_no_create_db" ]] && echo true || echo false)" +## POSTGRES_INITDB_ARGS = "$_postgres_initdb_args" +## POSTGRES_INITDB_WALDIR = "$([[ ! -z "$_postgres_initdb_waldir" ]] && echo $_postgres_initdb_waldir || echo null)" +## POSTGRES_HOST_AUTH_METHOD = "$_postgres_host_auth_method" +## PG_HBA = "$([[ ! -z "$_pg_hba" ]] && echo $_ph_hba || echo null)" +## PGDATA="$_pgdata" +EOF + + echo "postgres:x:${uid}:${gid}:PostgreSQL:$_pgdata:${pkgs.bash}/bin/bash" > /etc/passwd + echo "postgres:x:${gid}:" > /etc/group + + if [[ ! -d "$_pgdata" ]] ; then + echo "Creating \`PGDATA\` and chown-ing to \"${uid}:${gid}\"" + ${pkgs.busybox}/bin/mkdir -p "$_pgdata" + ${pkgs.busybox}/bin/chown postgres:postgres "$_pgdata" + else + _pgdata_uid=$(${pkgs.busybox}/bin/stat -c "%U" "$_pgdata") + _pgdata_gid=$(${pkgs.busybox}/bin/stat -c "%G" "$_pgdata") + + if [[ $_pgdata_uid != ${uid} ]] || [[ $_pgdata_gid != ${gid} ]] ; then + echo "\`PGDATA\` already exists but has incorrect owner! \ + ''${_pgdata_uid}:''${_pgdata_gid}" ; echo 1 + fi + fi + + if [[ -s "$_pgdata/PG_VERSION" ]] ; then + _database_exists="true" + else + _database_exists="" + fi + + if [[ ! -z "$_postgres_initdb_waldir" ]] ; then + if [[ ! -d "$_postgres_initdb_waldir" ]] ; then + echo "Creating \`POSTGRES_INITDB_WALDIR\` and chown-ing to \"${uid}:${gid}\"" + ${pkgs.busybox}/bin/mkdir -p "$_postgres_initdb_waldir" + ${pkgs.busybox}/bin/chown postgres:postgres "$_postgres_initdb_waldir" + else + _waldir_uid=$(${pkgs.busybox}/bin/stat -c "%U" "$_postgres_initdb_waldir") + _waldir_gid=$(${pkgs.busybox}/bin/stat -c "%G" "$_postgres_initdb_waldir") + + if [[ $_waldir_uid != ${uid} ]] || [[ $_waldir_gid != ${gid} ]] ; then + echo "\`POSTGRES_INITDB_WALDIR\` already exists but has incorrect owner! \ + ''${_waldir_uid}:''${_waldir_gid}" ; echo 1 + fi + fi + fi + + ${pkgs.busybox}/bin/mkdir -p /tmp /bin /run/postgresql + ${pkgs.busybox}/bin/chown postgres:postgres /tmp /run/postgresql + ${pkgs.busybox}/bin/ln -s ${pkgs.busybox}/bin/sh /bin/sh + + exec ${pkgs.busybox}/bin/su -p postgres -c "$0" + else + ## Next 4 functions are "borrowed" straight from: + ## https://github.com/docker-library/postgres/blob/master/12/docker-entrypoint.sh + ## Thanks! + # start socket-only postgresql server for setting up or running scripts + # all arguments will be passed along as arguments to `postgres` (via pg_ctl) + temp_server_start() { + set -- "$@" -c listen_addresses="" -p "5432" + + PGUSER="$_postgres_user" \ + ${postgres}/bin/pg_ctl -D "$_pgdata" \ + -o "$(printf '%q ' "$@")" \ + -w start + } + + # stop postgresql server after done setting up user and running scripts + temp_server_stop() { + PGUSER="$_postgres_user" \ + ${postgres}/bin/pg_ctl -D "$_pgdata" -m fast -w stop + } + + # Execute sql script, passed via stdin (or -f flag of pqsl) + # usage: docker_process_sql [psql-cli-args] + # ie: docker_process_sql --dbname=mydb <<<'INSERT ...' + # ie: docker_process_sql -f my-file.sql + # ie: docker_process_sql > "$_pgdata/pg_hba.conf" + else + cp "$_pg_hba" "$_pgdata/pg_hba.conf" + fi + + ${postgres}/bin/postgres -D "$_pgdata" + fi + ''; +in +pkgs.dockerTools.buildLayeredImage { + name = "postgresql"; + tag = "latest"; + + config = { + Cmd = [ "${init}/bin/init" ]; + }; +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..84510a9 --- /dev/null +++ b/flake.lock @@ -0,0 +1,77 @@ +{ + "nodes": { + "csi-driver-nfs": { + "flake": false, + "locked": { + "lastModified": 1610374369, + "narHash": "sha256-+CoyhWufws6zEtFU2uJA4NamSCUGVmoMOA91kormXCo=", + "owner": "MagicRB", + "repo": "csi-driver-nfs", + "rev": "560a4ebc1587dd4d447b8be2dee36db4d67fabbc", + "type": "github" + }, + "original": { + "owner": "MagicRB", + "repo": "csi-driver-nfs", + "type": "github" + } + }, + "klippy": { + "flake": false, + "locked": { + "lastModified": 1609168797, + "narHash": "sha256-1ojKClZTqa0amwUWz1z9GFuEaXtuw7xu0JEgF2N1UQ8=", + "owner": "KevinOConnor", + "repo": "klipper", + "rev": "e68cf08d15a985ecce7497b58408ee233dd54eb9", + "type": "github" + }, + "original": { + "owner": "KevinOConnor", + "repo": "klipper", + "rev": "e68cf08d15a985ecce7497b58408ee233dd54eb9", + "type": "github" + } + }, + "moonraker": { + "flake": false, + "locked": { + "lastModified": 1609164221, + "narHash": "sha256-+k4bFTGWvJ+un8R9dRhjtDt4dVvHfu2/gSp5iEuoX/w=", + "owner": "MagicRB", + "repo": "moonraker", + "rev": "e63f104695278231c3961b12bc5f5e13f5eeea62", + "type": "github" + }, + "original": { + "owner": "MagicRB", + "ref": "master", + "repo": "moonraker", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1609187327, + "narHash": "sha256-6lCwWpGpZxbSYclryv4ixsxxbISe7HuLyyQnGWCC8ZQ=", + "path": "/nix/store/dcmj4p477fgzxz33rxss9wnmslnsahgx-source", + "rev": "e065200fc90175a8f6e50e76ef10a48786126e1c", + "type": "path" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "csi-driver-nfs": "csi-driver-nfs", + "klippy": "klippy", + "moonraker": "moonraker", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..6d50be0 --- /dev/null +++ b/flake.nix @@ -0,0 +1,44 @@ +{ + inputs = { + nixpkgs.url = "nixpkgs"; + + csi-driver-nfs = { + flake = false; + url = "github:MagicRB/csi-driver-nfs"; + }; + + klippy = { + url = "github:KevinOConnor/klipper?rev=e68cf08d15a985ecce7497b58408ee233dd54eb9"; + flake = false; + }; + + moonraker = { + url = "github:MagicRB/moonraker?ref=master"; + flake = false; + }; + }; + + outputs = { self, nixpkgs, ... }@inputs: + let + lib = + let + system = "x86_64-linux"; + pkgs = import nixpkgs { inherit system; }; + in + import ./lib.nix { inherit system nixpkgs pkgs inputs; }; + flakes = lib.flakes ./nix-packages [ + "klippy" + "mainsail" + "moonraker" + ]; + dockerImages = lib.dockerImages ./docker [ + "klippy-moonraker" + "postgresql" + "csi-driver-nfs" + ]; + pkgs = lib.pkgsWithFlakes flakes; + moduleArg = ({ inherit pkgs; } // inputs); + in { + inherit flakes dockerImages; + }; +} diff --git a/lib.nix b/lib.nix new file mode 100644 index 0000000..e3f1c7a --- /dev/null +++ b/lib.nix @@ -0,0 +1,22 @@ +{ nixpkgs, pkgs, system, inputs }: +with pkgs.lib; { + flakes = path: modules: genAttrs modules (module: + let + self = (import (path + "/${module}/flake.nix")).outputs (inputs // { inherit self; }); + in + self + ); + dockerImages = path: modules: genAttrs modules (module: + import (path + "/${module}") ({ inherit pkgs; } // inputs) + ); + pkgsWithFlakes = flakes: import nixpkgs + { + inherit system; + overlays = builtins.concatLists (forEach flakes (flake: + if builtins.hasAttr "overlay" flake then + [ flake.overlay ] + else + [] + )); + }; +} diff --git a/nix-packages/klippy/flake.lock b/nix-packages/klippy/flake.lock new file mode 100644 index 0000000..a435175 --- /dev/null +++ b/nix-packages/klippy/flake.lock @@ -0,0 +1,42 @@ +{ + "nodes": { + "klippy": { + "flake": false, + "locked": { + "lastModified": 1609168797, + "narHash": "sha256-1ojKClZTqa0amwUWz1z9GFuEaXtuw7xu0JEgF2N1UQ8=", + "owner": "KevinOConnor", + "repo": "klipper", + "rev": "e68cf08d15a985ecce7497b58408ee233dd54eb9", + "type": "github" + }, + "original": { + "owner": "KevinOConnor", + "repo": "klipper", + "rev": "e68cf08d15a985ecce7497b58408ee233dd54eb9", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1609867866, + "narHash": "sha256-ITciATW51g/VqL4CTCh74Cu/wqIztkoJNZJxqj2vEMs=", + "path": "/nix/store/gz0m2hylws210m0nr3ih8b8pvnikwi6f-source", + "rev": "4a75ca4a4e7d14e7b0b0230b3ea57b5bd7c16218", + "type": "path" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "klippy": "klippy", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/nix-packages/klippy/flake.nix b/nix-packages/klippy/flake.nix new file mode 100644 index 0000000..63489b6 --- /dev/null +++ b/nix-packages/klippy/flake.nix @@ -0,0 +1,119 @@ +{ + inputs = { + nixpkgs.url = "nixpkgs"; + + klippy = { + url = "github:KevinOConnor/klipper?rev=e68cf08d15a985ecce7497b58408ee233dd54eb9"; + flake = false; + }; + }; + + outputs = { self, nixpkgs, ... }@inputs: + let + supportedSystems = [ "x86_64-linux" "i686-linux" "aarch64-linux" ]; + forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f system); + in + { + overlay = final: prev: + with final; { + klippy = let + pythonPackages = python-packages: with python-packages; [ + greenlet + cffi + pyserial + jinja2 + ]; + pythonWithPackages = (python2.withPackages pythonPackages); + in stdenv.mkDerivation + { + name = "klippy"; + src = inputs.klippy; + buildInputs = with pkgs; [ makeWrapper ]; + + dontBuild = true; + installPhase = '' + mkdir -p $out/share/klippy + cp -r klippy/* $out/share/klippy + + mkdir -p $out/bin + chmod +x $out/share/klippy/klippy.py + makeWrapper \ + $out/share/klippy/klippy.py \ + $out/bin/klippy \ + --prefix PATH : ${lib.makeBinPath (with pkgs; [ + pythonWithPackages + ])} + + ${pythonWithPackages}/bin/python $out/share/klippy/chelper/__init__.py + ''; + }; + + }; + + defaultPackage = forAllSystems (system: (import nixpkgs { + inherit system; + overlays = [ self.overlay ]; + }).klippy); + + nixosModules.klippy = { pkgs, config, ... }: with nixpkgs.lib; + let + cfg = config.services.klippy; + in { + options.services.klippy = import ./options.nix { inherit pkgs config; }; + + config = + { + nixpkgs.overlays = [ self.overlay ]; + + users.users."${cfg.user}" = { + uid = cfg.uid; + description = "klippy user"; + group = "${cfg.group}"; + extraGroups = cfg.extraGroups; + }; + + users.groups."${cfg.group}" = { + gid = cfg.gid; + }; + + systemd.services = mapAttrs' + (name: value: + let + configFile = pkgs.writeText "printer.cfg" '' + ${builtins.readFile value.config.file} + + ${foldl (str: acc: acc + str) "" (map (x: "[include " + x + "]\n") value.config.extraImports)} + + ${if value.config.virtualSd != "" then "[virtual_sdcard]\npath: " + value.config.virtualSd else ""} + ''; + in + (nameValuePair + ("klippy-" + name) + { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + description = "Starts klipper-" + name; + serviceConfig = { + Type = "simple"; + User = cfg.user; + Group = cfg.group; + RemainAfterExit = "yes"; + ExecStart = '' + ${value.package}/bin/klippy \ + --input-tty ${value.inputTty} \ + ${if value.apiServer != "" then "--api-server " + value.apiServer else ""} \ + ${if value.logFile != "" then "--logfile " + value.logFile else ""} \ + ${if value.verbose then "-v" else ""} \ + ${if value.dictionary != "" then "--dictionary " + value.dictionary else ""} \ + ${configFile} + ''; + ExecStartPost = "${pkgs.bash}/bin/bash -c '(sleep 5 ; [[ -e \"${value.apiServer}\" ]] && chmod 664 \"${value.apiServer}\" ) & disown'"; + Restart = "always"; + RestartSec = 10; + }; + })) + cfg.instances; + }; + }; + }; +} diff --git a/nix-packages/klippy/options.nix b/nix-packages/klippy/options.nix new file mode 100644 index 0000000..2cb7df6 --- /dev/null +++ b/nix-packages/klippy/options.nix @@ -0,0 +1,134 @@ +{ pkgs, ... }: with pkgs.lib; + { + instances = mkOption { + description = "Klippy instances"; + default = {}; + example = { + package = pkgs.klippy; + inputTty = "/tmp/printer"; + logFile = "/var/log/klippy.log"; + }; + type = types.attrsOf (types.submodule { + options = { + package = mkOption { + default = pkgs.klippy; + type = types.package; + description = '' + klippy package + ''; + }; + + inputTty = mkOption { + default = "/tmp/printer"; + type = types.str; + description = '' + inputs tty name + ''; + }; + + apiServer = mkOption { + default = "/tmp/klippy_uds"; + type = types.str; + description = '' + api server unix domain socket filename + ''; + }; + + logFile = mkOption { + default = ""; + type = types.str; + description = '' + write log to file instead of stderr + ''; + }; + + verbose = mkOption { + default = false; + type = types.bool; + description = '' + enable debug messages + ''; + }; + + dictionary = mkOption { + default = ""; + type = types.str; + description = '' + file to read for mcu protocol dictionary + ''; + }; + + config = mkOption { + default = {}; + type = types.submodule { + options = { + file = mkOption { + default = ""; + type = types.path; + description = '' + klipper config file + ''; + }; + + extraImports = mkOption { + default = []; + type = types.listOf types.path; + description = '' + extra imports added at the end of the config + ''; + }; + + virtualSd = mkOption { + default = ""; + type = types.path; + description = '' + virtual sd folder + ''; + }; + }; + }; + }; + }; + }); + }; + + user = mkOption { + default = "klippy"; + type = types.str; + description = '' + the user for klippy + ''; + }; + + uid = mkOption { + default = 5687; + type = types.int; + description = '' + the uid for the user for klippy + ''; + }; + + group = mkOption { + default = "klippy"; + type = types.str; + description = '' + the group for klippy + ''; + }; + + gid = mkOption { + default = 5687; + type = types.int; + description = '' + the gid for the group for klippy + ''; + }; + + extraGroups = mkOption { + default = [ "dialout" ]; + type = types.listOf types.str; + description = '' + extra groups for klippy + ''; + }; + } diff --git a/nix-packages/mainsail/flake.lock b/nix-packages/mainsail/flake.lock new file mode 100644 index 0000000..d7b4112 --- /dev/null +++ b/nix-packages/mainsail/flake.lock @@ -0,0 +1,24 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1609867866, + "narHash": "sha256-ITciATW51g/VqL4CTCh74Cu/wqIztkoJNZJxqj2vEMs=", + "path": "/nix/store/gz0m2hylws210m0nr3ih8b8pvnikwi6f-source", + "rev": "4a75ca4a4e7d14e7b0b0230b3ea57b5bd7c16218", + "type": "path" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/nix-packages/mainsail/flake.nix b/nix-packages/mainsail/flake.nix new file mode 100644 index 0000000..3c28dff --- /dev/null +++ b/nix-packages/mainsail/flake.nix @@ -0,0 +1,127 @@ +{ + inputs = { + nixpkgs.url = "nixpkgs"; + }; + + outputs = { self, nixpkgs, ... }@inputs: + let + supportedSystems = [ "x86_64-linux" "i686-linux" "aarch64-linux" ]; + forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f system); + in { + nixosModules.mainsail = { pkgs, config, ... }: with nixpkgs.lib; + let + cfg = config.services.mainsail; + in { + options.services.mainsail = { + enable = mkOption { + description = "Enable Mainsail NOTE: enable nginx too"; + default = false; + type = types.bool; + }; + + apiServer = mkOption { + description = "IP and port of the moonraker apiserver"; + default = "localhost:7125"; + type = types.str; + }; + + printerCam = mkOption { + description = "IP and port of the webcam server"; + default = ""; + type = types.str; + }; + + virtualHost = mkOption { + description = "The virtual host for mainsail in nginx"; + default = "mainsail.localhost"; + type = types.str; + }; + }; + + config = mkIf cfg.enable { + services.nginx = { + enable = mkForce true; + + recommendedGzipSettings = true; + recommendedOptimisation = true; + + clientMaxBodySize = "200M"; + + # commonHttpConfig = '' + # map $http_upgrade $connection_upgrade { + # default upgrade; + # \'\' close; + # } + # ''; + + virtualHosts."${cfg.virtualHost}" = { + root = pkgs.fetchzip { + url = "https://github.com/meteyou/mainsail/releases/download/v0.4.0/mainsail.zip"; + sha256 = "PBu3ktVCchi+E54cuKzsRhZXWkWG/NvOf+IZNSNN8fw="; + stripRoot = false; + }; + + locations = let + apiServer = cfg.apiServer; + printerCam = cfg.printerCam; + extraConfig = '' + # proxy_set_header Host $host; + # proxy_set_header X-Real-IP $remote_addr; + # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # proxy_set_header X-Scheme $scheme; + ''; + in { + "/" = { + extraConfig = '' + try_files $uri $uri/ /index.html; + ''; + }; + + "/printer" = { + proxyPass = "http://${apiServer}/printer"; + inherit extraConfig; + }; + + "/api" = { + proxyPass = "http://${apiServer}/api"; + inherit extraConfig; + }; + + "/access" = { + proxyPass = "http://${apiServer}/access"; + inherit extraConfig; + }; + + "/websocket" = { + proxyPass = "http://${apiServer}/websocket"; + extraConfig = '' + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_read_timeout 86400; + ''; + }; + + "/machine" = { + proxyPass = "http://${apiServer}/machine"; + inherit extraConfig; + }; + + "/server" = { + proxyPass = "http://${apiServer}/server"; + inherit extraConfig; + }; + + "/webcam/" = mkIf (printerCam != "") { + proxyPass = "http://${printerCam}/"; + }; + }; + }; + }; + }; + }; + }; +} diff --git a/nix-packages/moonraker/#flake.lock# b/nix-packages/moonraker/#flake.lock# new file mode 100644 index 0000000..d84148c --- /dev/null +++ b/nix-packages/moonraker/#flake.lock# @@ -0,0 +1,31 @@ +r + "nodes": { + "moonraker": { + "flake": false, + "locked": { + "lastModified": 1609164221, + "narHash": "sha256-+k4bFTGWvJ+un8R9dRhjtDt4dVvHfu2/gSp5iEuoX/w=", + "owner": "MagicRB", + "repo": "moonraker", + "rev": "e63f104695278231c3961b12bc5f5e13f5eeea62", + "type": "github" + }, + "original": { + "owner": "MagicRB", + "ref": "master", + "repo": "moonraker", + "type": "github" + } + }, + "root": { + "inputs": { + "moonraker": "moonraker", + "nixpkgs": [ + "nixpkgs" + ] + } + } + }, + "root": "root", + "version": 7 +} diff --git a/nix-packages/moonraker/config.nix b/nix-packages/moonraker/config.nix new file mode 100644 index 0000000..62be589 --- /dev/null +++ b/nix-packages/moonraker/config.nix @@ -0,0 +1,18 @@ +value: lib: with lib; +'' + [server] + host: ${value.config.server.host} + port: ${builtins.toString value.config.server.port} + klippy_uds_address: ${value.config.server.klippyUdsAddress} + max_upload_size: ${builtins.toString value.config.server.maxUploadSize} + enable_debug_logging: ${if value.config.server.enableDebugLogging then "True" else "False"} + ${if value.config.server.configPath != "" then "config_path: " + value.config.authorization.configPath else ""} + + [authorization] + enabled: ${if value.config.authorization.enabled then "True" else "False"} + ${if value.config.authorization.apiKeyFile != "" then "api_key_file: " + value.config.authorization.apiKeyFile else ""} + trusted_clients: + ${foldl (str: acc: acc + str) "" (map (x: " " + x + "\n") value.config.authorization.trustedClients)} + cors_domains: + ${foldl (str: acc: acc + str) "" (map (x: " " + x + "\n") value.config.authorization.corsDomains)} +'' diff --git a/nix-packages/moonraker/flake.lock b/nix-packages/moonraker/flake.lock new file mode 100644 index 0000000..38181ba --- /dev/null +++ b/nix-packages/moonraker/flake.lock @@ -0,0 +1,42 @@ +{ + "nodes": { + "moonraker": { + "flake": false, + "locked": { + "lastModified": 1609164221, + "narHash": "sha256-+k4bFTGWvJ+un8R9dRhjtDt4dVvHfu2/gSp5iEuoX/w=", + "owner": "MagicRB", + "repo": "moonraker", + "rev": "e63f104695278231c3961b12bc5f5e13f5eeea62", + "type": "github" + }, + "original": { + "owner": "MagicRB", + "ref": "master", + "repo": "moonraker", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1609867866, + "narHash": "sha256-ITciATW51g/VqL4CTCh74Cu/wqIztkoJNZJxqj2vEMs=", + "path": "/nix/store/gz0m2hylws210m0nr3ih8b8pvnikwi6f-source", + "rev": "4a75ca4a4e7d14e7b0b0230b3ea57b5bd7c16218", + "type": "path" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "moonraker": "moonraker", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/nix-packages/moonraker/flake.nix b/nix-packages/moonraker/flake.nix new file mode 100644 index 0000000..a0ae0d6 --- /dev/null +++ b/nix-packages/moonraker/flake.nix @@ -0,0 +1,106 @@ +{ + inputs = { + nixpkgs.url = "nixpkgs"; + + moonraker = { + url = "github:MagicRB/moonraker?ref=master"; + flake = false; + }; + }; + + outputs = { self, nixpkgs, ... }@inputs: + let + supportedSystems = [ "x86_64-linux" "i686-linux" "aarch64-linux" ]; + forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f system); + in + { + overlay = final: prev: + with final; { + moonraker = let + pythonPackages = python-packages: with python-packages; [ + tornado + pyserial + pillow + ]; + pythonWithPackages = (python3.withPackages pythonPackages); + in stdenv.mkDerivation + { + name = "moonraker"; + src = inputs.moonraker; + buildInputs = with pkgs; [ makeWrapper ]; + + dontBuild = true; + installPhase = '' + mkdir -p $out/share/moonraker + cp -r * $out/share/moonraker + + mkdir -p out/bin + chmod +x $out/share/moonraker/moonraker/moonraker.py + + makeWrapper \ + $out/share/moonraker/moonraker/moonraker.py \ + $out/bin/moonraker \ + --prefix PATH : ${lib.makeBinPath (with pkgs; [ + pythonWithPackages + ])} + ''; + }; + }; + + nixosModules.moonraker = { pkgs, config, ... }: with nixpkgs.lib; + let + cfg = config.services.moonraker; + in { + options.services.moonraker = import ./options.nix { inherit pkgs config; }; + + config = + { + nixpkgs.overlays = [ self.overlay ]; + + users.users."${cfg.user}" = mkIf cfg.createUser { + uid = cfg.uid; + description = "klippy user"; + group = "${cfg.group}"; + extraGroups = cfg.extraGroups; + }; + + users.groups."${cfg.group}" = mkIf cfg.createGroup { + gid = cfg.gid; + }; + + systemd.services = mapAttrs' + (name: value: + let + configFile = pkgs.writeText "moonraker.conf" (import ./config.nix value pkgs.lib); + in (nameValuePair + ("moonraker-" + name) + { + wantedBy = [ "muti-user.target" ]; + after = [ "network.target" ]; + description = "Starts moonraker-" + name; + serviceConfig = { + Type = "simple"; + User = cfg.user; + Group = cfg.group; + RemainAfterExit = "yes"; + ExecStart = '' + ${value.package}/bin/moonraker \ + --configfile ${configFile} \ + --logfile ${value.logFile} + ''; + Restart = "always"; + RestartSec = 10; + }; + })) + cfg.instances; + }; + }; + + defaultPackage = forAllSystems (system: (import nixpkgs { + inherit system; + overlays = [ self.overlay ]; + }).moonraker); + }; +} + + diff --git a/nix-packages/moonraker/options.nix b/nix-packages/moonraker/options.nix new file mode 100644 index 0000000..b1bd58f --- /dev/null +++ b/nix-packages/moonraker/options.nix @@ -0,0 +1,183 @@ +{ pkgs, ... }: with pkgs.lib; { + instances = mkOption { + default = {}; + description = "Moonraker instances"; + type = types.attrsOf (types.submodule { + options = { + package = mkOption { + default = pkgs.moonraker; + type = types.package; + description = '' + moonraker package + ''; + }; + + config = mkOption { + default = {}; + type = types.submodule { + options = { + server = mkOption { + default = {}; + type = types.submodule { + options = { + host = mkOption { + default = "0.0.0.0"; + type = types.str; + description = '' + The host address in which to bind the HTTP server. + ''; + }; + + port = mkOption { + default = 7125; + type = types.int; + description = '' + The port the HTTP server will listen on. + ''; + }; + + klippyUdsAddress = mkOption { + default = "/tmp/klippy_uds"; + type = types.str; + description = '' + The address of Unix Domain Socket used to communicate with Klippy. + ''; + }; + + maxUploadSize = mkOption { + default = 200; + type = types.int; + description = '' + The maximum size allowed for a file upload. Default is 200 MiB. + ''; + }; + + enableDebugLogging = mkOption { + default = true; + type = types.bool; + description = '' + When set to True Moonraker will log in verbose mode. + ''; + }; + + configPath = mkOption { + default = ""; + type = types.str; + description = '' + The path to a directory where configuration files are located. + ''; + }; + }; + }; + }; + + authorization = mkOption { + default = {}; + type = types.submodule { + options = { + enabled = mkOption { + default = true; + type = types.bool; + description = '' + Enables authorization. + ''; + }; + + apiKeyFile = mkOption { + default = "/tmp/.moonraker_api_key"; + type = types.str; + description = '' + Path of the file that stores Moonraker's API key. + ''; + }; + + trustedClients = mkOption { + default = []; + type = types.listOf types.str; + description = '' + A list of newline separated ip addresses and/or ip ranges that are trusted. + ''; + }; + + corsDomains = mkOption { + default = []; + type = types.listOf types.str; + description = '' + Enables CORS for the specified domains. + ''; + }; + }; + }; + }; + }; + }; + }; + + logFile = mkOption { + default = "/tmp/moonraker.log"; + type = types.str; + description = '' + moonraker log file + ''; + }; + }; + }); + }; + + + user = mkOption { + default = "moonraker"; + type = types.str; + description = '' + moonraker user + ''; + }; + + uid = mkOption { + default = 5688; + type = types.int; + description = '' + moonraker user id + ''; + }; + + group = mkOption { + default = "moonraker"; + type = types.str; + description = '' + moonraker group + ''; + }; + + gid = mkOption { + default = 5688; + type = types.int; + description = '' + moonraker group id + ''; + }; + + extraGroups = mkOption { + default = [ "klippy" ]; + type = types.listOf types.str; + description = '' + extra groups for moonraker user + ''; + }; + + createUser = mkOption { + default = true; + type = types.bool; + description = '' + create user + ''; + }; + + createGroup = mkOption { + default = true; + type = types.bool; + description = '' + create group + ''; + }; +}