diff --git a/examples/temporal/.test.sh b/examples/temporal/.test.sh new file mode 100755 index 000000000..0cd63bfd2 --- /dev/null +++ b/examples/temporal/.test.sh @@ -0,0 +1,42 @@ +#!/bin/sh +set -x + +# Start the services in the background and store the PID +echo "Starting temporal service..." +devenv up & +DEVENV_PID=$! + +export TEMPORAL_ADDRESS=127.0.0.1:17233 + +# temporal status and store its exit status +check_temporal_status() { + echo "Waiting for service to become available..." + TEMPORAL_OUTPUT=$(temporal operator cluster health) + TEMPORAL_EXIT_STATUS=$? +} + +# Continuously check temporal status until it returns successfully (up to a maximum of 20 times) +for i in $(seq 1 20); do + check_temporal_status + if [ $TEMPORAL_EXIT_STATUS -eq 0 ]; then + echo "Service is up..." + break + else + sleep 1 + fi +done + +echo "Checking namespace..." +temporal operator namespace describe mynamespace + +# Print the captured output when temporal status succeeds +echo "Startup complete..." +temporal operator cluster system +echo "$TEMPORAL_OUTPUT" + +# Clean up by terminating all spawned processes +pkill -P $DEVENV_PID +wait $DEVENV_PID&>/dev/null + +# Exit the script +exit $TEMPORAL_EXIT_STATUS diff --git a/examples/temporal/devenv.nix b/examples/temporal/devenv.nix new file mode 100644 index 000000000..74b78569a --- /dev/null +++ b/examples/temporal/devenv.nix @@ -0,0 +1,17 @@ +{ pkgs, ... }: +{ + services.temporal = { + enable = true; + + port = 17233; + + namespaces = [ "mynamespace" ]; + + state = { + ephemeral = false; + sqlite-pragma = { + journal_mode = "wal"; + }; + }; + }; +} diff --git a/examples/temporal/devenv.yaml b/examples/temporal/devenv.yaml new file mode 100644 index 000000000..c7cb5ceda --- /dev/null +++ b/examples/temporal/devenv.yaml @@ -0,0 +1,3 @@ +inputs: + nixpkgs: + url: github:NixOS/nixpkgs/nixpkgs-unstable diff --git a/src/modules/services/temporal.nix b/src/modules/services/temporal.nix new file mode 100644 index 000000000..d73ece406 --- /dev/null +++ b/src/modules/services/temporal.nix @@ -0,0 +1,110 @@ +{ pkgs, lib, config, ... }: + +let + cfg = config.services.temporal; + types = lib.types; + + databaseFile = config.env.DEVENV_STATE + "/temporal.sqlite"; + + commandArgs = [ + "--log-format=pretty" + "--ip=${cfg.ip}" + "--port=${toString cfg.port}" + "--headless=${lib.boolToString (!cfg.ui.enable)}" + "--ui-ip=${cfg.ui.ip}" + "--ui-port=${toString cfg.ui.port}" + ] ++ + (lib.forEach cfg.namespaces (namespace: "--namespace=${namespace}")) ++ + (lib.optionals (!cfg.state.ephemeral) [ "--db-filename=${databaseFile}" ]) ++ + (lib.mapAttrsToList (name: value: "--sqlite-pragma ${name}=${value}") cfg.state.sqlite-pragma); +in +{ + options.services.temporal = { + enable = lib.mkEnableOption "Temporal process"; + + package = lib.mkOption { + type = types.package; + description = "Which package of Temporal to use."; + default = pkgs.temporal-cli; + defaultText = lib.literalExpression "pkgs.temporal-cli"; + }; + + ip = lib.mkOption { + type = types.str; + default = "127.0.0.1"; + description = "IPv4 address to bind the frontend service to."; + }; + + port = lib.mkOption { + type = types.port; + default = 7233; + description = "Port for the frontend gRPC service."; + }; + + ui = lib.mkOption { + type = types.submodule { + options = { + enable = lib.mkOption { + type = types.bool; + default = true; + description = "Enable the Web UI."; + }; + + ip = lib.mkOption { + type = types.str; + default = cfg.ip; + description = "IPv4 address to bind the Web UI to."; + }; + + port = lib.mkOption { + type = types.port; + default = cfg.port + 1000; + defaultText = lib.literalMD "[`services.temporal.port`](#servicestemporalport) + 1000"; + description = "Port for the Web UI."; + }; + }; + }; + default = { }; + description = "UI configuration."; + }; + + namespaces = lib.mkOption { + type = types.listOf types.str; + default = [ ]; + description = "Specify namespaces that should be pre-created (namespace \"default\" is always created)."; + example = [ + "my-namespace" + "my-other-namespace" + ]; + }; + + state = lib.mkOption { + type = types.submodule { + options = { + ephemeral = lib.mkOption { + type = types.bool; + default = true; + description = "When enabled, the Temporal state gets lost when the process exists."; + }; + + sqlite-pragma = lib.mkOption { + type = types.attrsOf types.str; + default = { }; + description = "Sqlite pragma statements"; + example = { + journal_mode = "wal"; + synchronous = "2"; + }; + }; + }; + }; + default = { }; + description = "State configuration."; + }; + }; + + config = lib.mkIf cfg.enable { + packages = [ cfg.package ]; + processes.temporal.exec = "${cfg.package}/bin/temporal server start-dev ${lib.concatStringsSep " " commandArgs}"; + }; +}