From 8951eb6272c1d2c5bbb321da644fd21fceb0b851 Mon Sep 17 00:00:00 2001 From: Casey Link Date: Thu, 31 Oct 2024 12:23:30 +0100 Subject: [PATCH] Add more nixos module features and improve tests --- CHANGELOG.md | 8 +- Makefile | 26 +++-- nixos-modules/datomic-console.nix | 106 ++++++++++++++++- nixos-modules/datomic-pro.nix | 141 +++++++++++++++++++---- pkgs/datomic-pro-container-image.nix | 4 + tests/container-image.nix | 128 ++++++++++++++++---- tests/fixtures/docker-compose-sqlite.yml | 60 ++++++++++ tests/fixtures/hello.clj | 47 ++++++++ tests/fixtures/testdev.properties | 10 ++ tests/fixtures/testsql.properties | 9 ++ tests/nixos-module.nix | 68 +++++++++-- 11 files changed, 537 insertions(+), 70 deletions(-) create mode 100644 tests/fixtures/docker-compose-sqlite.yml create mode 100644 tests/fixtures/hello.clj create mode 100644 tests/fixtures/testdev.properties create mode 100644 tests/fixtures/testsql.properties diff --git a/CHANGELOG.md b/CHANGELOG.md index cddbfdb..455a50a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [UNRELEASED] -## v0.2.0 (2024-10-30) - ### Breaking -- `transactor` bin renamed to `datomic-transactor` -- `console` bin renamed to `datomic-console` +- nix pkg: `transactor` bin renamed to `datomic-transactor` +- nix pkg: `console` bin renamed to `datomic-console` +- nixos module: removed the default settings that leaned towards dev/h2 storage by default ### Added @@ -22,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `unstable` container image tag that follows the `main` branch - nix pkg: Added ability to override the build and add extra native libs or java libs - nix pkg: Exposed more packages: `datomic-shell`, `datomic-run`, `datomic-repl`, `datomic-peer-server` +- nixos module: You can now configure: logging, extra classpath entries, and extra java options. ### Changed diff --git a/Makefile b/Makefile index a4a66a5..acf83f7 100644 --- a/Makefile +++ b/Makefile @@ -1,19 +1,31 @@ DOCKER ?= docker check: - nix flake check -test: check + nix --print-build-logs flake check +test/nixos: + nix --print-build-logs run '.#checks.x86_64-linux.moduleTest.driver' +test/container: + nix --print-build-logs run '.#checks.x86_64-linux.containerImageTest.driver' + +test: test/nixos test/container datomic-pro: nix build .#datomic-pro -o result --show-trace -test-pkg: datomic-pro - ./result/bin/datomic-transactor testsql.properties - #./result/bin/datomic-transactor ./result/share/datomic-pro/config/samples/dev-transactor-template.properties +test/pkg-dev: datomic-pro + mkdir -p data + ./result/bin/datomic-transactor tests/fixtures/testdev.properties + +test/pkg-sql: datomic-pro + mkdir -p data + ./result/bin/datomic-transactor tests/fixtures/testsql.properties + +test/pkg-console-dev: datomic-pro + ./result/bin/datomic-console -p 8080 app 'datomic:dev://localhost:4334/?password=datpass' -test-pkg-console: datomic-pro - ./result/bin/datomic-console -p 8080 app datomic:dev://localhost:4334/ +test/pkg-console-sql: datomic-pro + ./result/bin/datomic-console -p 8080 app 'datomic:sql://?jdbc:sqlite:data/db-sqlite.db' datomic-pro-container: nix build .#datomic-pro-container -o datomic-pro-container --show-trace diff --git a/nixos-modules/datomic-console.nix b/nixos-modules/datomic-console.nix index 3011c4a..0f7daf9 100644 --- a/nixos-modules/datomic-console.nix +++ b/nixos-modules/datomic-console.nix @@ -7,11 +7,16 @@ let cfg = config.services.datomic-console; + logbackConfigFile = pkgs.writeText "logback.xml" cfg.logbackConfig; + extraJavaOptions = + cfg.extraJavaOptions + ++ lib.optional (cfg.logbackConfig != "") "-Dlogback.configurationFile=${logbackConfigFile}"; + extraClasspath = lib.concatStringsSep ":" cfg.extraClasspathEntries; in { options = { services.datomic-console = { - enable = lib.mkEnableOption "Datomic Pro Console"; + enable = lib.mkEnableOption "Datomic Console"; package = lib.mkPackageOption pkgs "datomic-pro" { }; javaPackage = lib.mkPackageOption pkgs "jdk21_headless" { }; port = lib.mkOption { @@ -37,6 +42,96 @@ in default = "datomic-console"; description = "The name of the directory under /var/lib that will be used as the state directory for datomic."; }; + + extraJavaOptions = lib.mkOption { + description = "Extra command line options for Java."; + default = [ ]; + type = lib.types.listOf lib.types.str; + example = [ + "-Dfoo=bar" + "-Xbaz=bar" + ]; + }; + extraClasspathEntries = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = '' + Extra entries added to the Java classpath when running Datomic Console + ''; + example = [ + "/path/to/my.jer" + "/path/to/folder/of/jars/*" + ]; + }; + logbackConfig = lib.mkOption { + type = lib.types.lines; + default = '' + + + + + true + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %-10contextName %logger{36} - %msg%n + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ''; + description = '' + XML logback configuration for the datomic-pro transactor. + ''; + }; + }; }; config = lib.mkIf cfg.enable { @@ -50,13 +145,20 @@ in } ]; systemd.services.datomic-console = { - description = "Datomic Pro Console"; + description = "Datomic Console"; wantedBy = [ "multi-user.target" ]; script = '' db_uri="$(<"$CREDENTIALS_DIRECTORY/datomic-console-db-uri")" ${cfg.package}/bin/datomic-console -p ${toString cfg.port} "${cfg.alias}" "$db_uri" ''; path = [ cfg.javaPackage ]; + environment = + { + DATOMIC_JAVA_OPTS = toString extraJavaOptions; + } + // lib.optionalAttrs (cfg.extraClasspathEntries != [ ]) { + CLASSPATH = extraClasspath; + }; serviceConfig = { Type = "simple"; LoadCredential = [ "datomic-console-db-uri:${cfg.dbUriFile}" ]; diff --git a/nixos-modules/datomic-pro.nix b/nixos-modules/datomic-pro.nix index 8d0f87a..c6b5944 100644 --- a/nixos-modules/datomic-pro.nix +++ b/nixos-modules/datomic-pro.nix @@ -10,25 +10,23 @@ let settingsFormat = pkgs.formats.javaProperties { }; stateDir = "/var/lib/${cfg.stateDirectoryName}"; runtimePropertiesPath = "${stateDir}/transactor.properties"; - # default settings that will be used unless overriden by the user settingsDefault = { - host = "localhost"; - memory-index-max = "256m"; - memory-index-threshold = "32m"; - object-cache-max = "128m"; + host = "127.0.0.1"; port = 4334; - protocol = "dev"; data-dir = "${stateDir}/data"; - log-dir = "${stateDir}/log"; }; propertiesFile = settingsFormat.generate "transactor.properties" (settingsDefault // cfg.settings); + logbackConfigFile = pkgs.writeText "logback.xml" cfg.logbackConfig; + extraJavaOptions = + cfg.extraJavaOptions + ++ lib.optional (cfg.logbackConfig != "") "-Dlogback.configurationFile=${logbackConfigFile}"; + extraClasspath = lib.concatStringsSep ":" cfg.extraClasspathEntries; in { options = { services.datomic-pro = { enable = lib.mkEnableOption "Datomic Pro"; package = lib.mkPackageOption pkgs "datomic-pro" { }; - javaPackage = lib.mkPackageOption pkgs "jdk21_headless" { }; secretsFile = lib.mkOption { type = lib.types.nullOr lib.types.path; default = null; @@ -38,6 +36,94 @@ in Should be owned by root and have 0600 permissions. ''; }; + extraJavaOptions = lib.mkOption { + description = "Extra command line options for Java."; + default = [ ]; + type = lib.types.listOf lib.types.str; + example = [ + "-Dfoo=bar" + "-Xbaz=bar" + ]; + }; + extraClasspathEntries = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = '' + Extra entries added to the Java classpath when running Datomic Pro. + ''; + example = [ + "/path/to/my.jer" + "/path/to/folder/of/jars/*" + ]; + }; + logbackConfig = lib.mkOption { + type = lib.types.lines; + default = '' + + + + + true + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %-10contextName %logger{36} - %msg%n + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ''; + description = '' + XML logback configuration for the datomic-pro transactor. + ''; + }; stateDirectoryName = lib.mkOption { type = lib.types.str; @@ -47,16 +133,7 @@ in settings = lib.mkOption { type = lib.types.submodule { freeformType = settingsFormat.type; }; - default = { - host = "localhost"; - memory-index-max = "256m"; - memory-index-threshold = "32m"; - object-cache-max = "128m"; - port = 4334; - protocol = "dev"; - data-dir = "${stateDir}/data"; - log-dir = "${stateDir}/log"; - }; + default = settingsDefault; description = '' Configuration written to `transactor.properties`. @@ -70,17 +147,31 @@ in }; config = lib.mkIf cfg.enable { assertions = [ + { + assertion = lib.attrsets.hasAttr "protocol" cfg.settings; + message = '' + You must define your storage procotol with the `protocol` key in , refer to the Datomic Pro documentation. + Some possible values are `"dev"`, `"sql"`, etc. Each protocol will have additional required settings that are not validated by this NixOS module. + ''; + } { assertion = lib.strings.hasInfix "/" cfg.stateDirectoryName == false; message = '' - must be a single directory name, not a path with /. ''; } + + { + assertion = !(lib.attrsets.hasAttr "log-dir" cfg.settings); + message = '' must not contain the `log-dir` key, use instead.''; + # Ok intrepid spelunker, why can we not use log-dir? Because as of 2024-10, the log-dir is specially handled by datomic code and hardcodes a lookup for + # logback.xml in the `bin/` dir of the datomic tarball. This obviously doesn't work on NixOS. + # The solution is to NOT define log-dir, but instead just define your own logback configuration, we include a variation of the default that logs to stdout and ends up in systemd's journal. + } ]; systemd.services.datomic-pro = { description = "Datomic Pro"; wantedBy = [ "multi-user.target" ]; - path = [ cfg.javaPackage ]; preStart = '' cat ${propertiesFile} > ${runtimePropertiesPath} chmod 0600 ${runtimePropertiesPath} @@ -92,9 +183,13 @@ in script = '' ${cfg.package}/bin/datomic-transactor ${runtimePropertiesPath} ''; - environment = { - DATOMIC_JAVA_OPTS = "-Dlogback.configurationFile ${cfg.package}/share/datomic-pro/logback-sample.xml"; - }; + environment = + { + DATOMIC_JAVA_OPTS = toString extraJavaOptions; + } + // lib.optionalAttrs (cfg.extraClasspathEntries != [ ]) { + CLASSPATH = extraClasspath; + }; serviceConfig = { Type = "simple"; DynamicUser = true; diff --git a/pkgs/datomic-pro-container-image.nix b/pkgs/datomic-pro-container-image.nix index 3540062..9fd767c 100644 --- a/pkgs/datomic-pro-container-image.nix +++ b/pkgs/datomic-pro-container-image.nix @@ -66,6 +66,10 @@ let env-shim = runCommand "env-shim" { } '' mkdir -p $out/usr/bin ln -s ${coreutils}/bin/env $out/usr/bin/env + # conveniently symlink these in place so an admin can access them with podman run -it + for cmd in transactor console shell run repl; do + ln -s ${datomicBuild}/bin/datomic-$cmd $out/usr/bin/datomic-$cmd + done ''; in dockerTools.buildLayeredImage { diff --git a/tests/container-image.nix b/tests/container-image.nix index e11abb9..79c63ac 100644 --- a/tests/container-image.nix +++ b/tests/container-image.nix @@ -5,14 +5,19 @@ nixpkgs, }: -with import (nixpkgs + "/nixos/lib/testing-python.nix") { inherit system pkgs; }; -with pkgs.lib; - +let + inherit + (import (nixpkgs + "/nixos/lib/testing-python.nix") { + inherit system pkgs; + }) + makeTest + ; +in makeTest { name = "datomic-pro dev-mode container test"; nodes = { docker = - { ... }: + { pkgs, lib, ... }: { nixpkgs.overlays = [ self.overlays."${system}" ]; virtualisation = { @@ -20,31 +25,106 @@ makeTest { memorySize = 2048; docker.enable = true; }; - environment.systemPackages = with pkgs; [ jq ]; + environment.systemPackages = [ + pkgs.jq + pkgs.clojure + pkgs.datomic-pro + pkgs.bash + pkgs.vim + ]; + environment.etc."datomic-docker/logback.xml".text = '' + + + + true + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %-10contextName %logger{36} - %msg%n + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ''; + environment.etc."datomic-docker/docker-compose.yml".text = builtins.readFile ./fixtures/docker-compose-sqlite.yml; + + environment.etc."datomic-docker/deps.edn".text = '' + {:paths ["."] + :deps {com.datomic/peer {:local/root "${pkgs.datomic-pro}/share/datomic-pro/peer-${pkgs.datomic-pro.version}.jar"} + org.xerial/sqlite-jdbc {:local/root "${pkgs.sqlite-jdbc}/share/java/sqlite-jdbc-${pkgs.sqlite-jdbc.version}.jar"}} + + :aliases {:run {:jvm-opts ["-Ddatomic.uri=datomic:sql://app?jdbc:sqlite:/var/lib/datomic-docker/data/datomic-sqlite.db"] + :main-opts ["-m" "hello"]}}} + ''; + environment.etc."datomic-docker/hello.clj".text = builtins.readFile ./fixtures/hello.clj; + environment.etc."datomic-docker/.env".text = "IMAGE=ghcr.io/ramblurr/datomic-pro:${pkgs.datomic-pro.version}"; }; }; testScript = '' start_all() docker.wait_for_unit("sockets.target") - docker.succeed( - "docker load --input='${pkgs.datomic-pro-container}'" - ) - - docker.succeed("rm -rf ./data && mkdir ./data") - docker.succeed( - """ - docker run -d --name datomic -v ./data:/data -p 4335:4334 -e DATOMIC_STORAGE_ADMIN_PASSWORD=unsafe -e DATOMIC_STORAGE_DATOMIC_PASSWORD=unsafe ghcr.io/ramblurr/datomic-pro:${pkgs.datomic-pro.version} - """ - ) - docker.wait_for_open_port(4335) - def try_logs(_) -> bool: - status, _ = docker.execute("docker logs datomic | grep -q 'System started'") - return status == 0 - with docker.nested("waiting for datomic to start"): - retry(try_logs) - docker.wait_for_file("./data/db/datomic.trace.db") - docker.succeed("docker rm -f datomic") - docker.wait_for_closed_port(4335) + docker.succeed("mkdir -p /var/lib/datomic-docker/data") + docker.succeed("mkdir -p /var/lib/datomic-docker/config") + docker.succeed("docker load --input='${pkgs.datomic-pro-container}'") + + docker.succeed("cd /etc/datomic-docker && docker compose up -d") + docker.wait_for_file("/var/lib/datomic-docker/data/datomic-sqlite.db") + docker.wait_for_open_port(4334) + + docker.wait_until_succeeds("cd /etc/datomic-docker && docker compose logs datomic-transactor | grep -q 'System started'") + docker.wait_for_open_port(8081) + + # Note: running the clojure test requires internet, because maven deps will be downloaded + # unfortunately the datomic distribution does not include all deps for the peer lib. + machine.succeed("cd /etc/datomic-docker && clojure -M:run") ''; } diff --git a/tests/fixtures/docker-compose-sqlite.yml b/tests/fixtures/docker-compose-sqlite.yml new file mode 100644 index 0000000..f156adc --- /dev/null +++ b/tests/fixtures/docker-compose-sqlite.yml @@ -0,0 +1,60 @@ +--- +services: + datomic-transactor: + image: ${IMAGE} + environment: + DATOMIC_PROTOCOL: sql + DATOMIC_SQL_URL: jdbc:sqlite:/data/datomic-sqlite.db + DATOMIC_SQL_DRIVER_CLASS: org.sqlite.JDBC + DATOMIC_JAVA_OPTS: -Dlogback.configurationFile=/logback.xml + # this one is for the other containers + DATOMIC_HOST: datomic-transactor + # this one is for the host machine + DATOMIC_ALT_HOST: "127.0.0.1" + # shrink ram requirements for test environment + DATOMIC_MEMORY_INDEX_MAX: 32m + DATOMIC_OBJECT_CACHE_MAX: 32m + DATOMIC_MEMORY_INDEX_THRESHOLD: 32m + volumes: + - "/var/lib/datomic-docker/data:/data:z" + - "/var/lib/datomic-docker/config:/config:z" + - "/etc/datomic-docker/logback.xml:/logback.xml:ro" + ports: + - 127.0.0.1:4334:4334 + depends_on: + datomic-storage-migrator: + condition: service_completed_successfully + + datomic-console: + image: ${IMAGE} + command: console + environment: + # you don’t specify the db name in the uri (because console can access all dbs) + DB_URI: "datomic:sql://?jdbc:sqlite:/data/datomic-sqlite.db" + DATOMIC_JAVA_OPTS: -Dlogback.configurationFile=/logback.xml + volumes: + - "/var/lib/datomic-docker/data:/data:z" + - "/etc/datomic-docker/logback.xml:/logback.xml:ro" + ports: + - 127.0.0.1:8081:8080 + depends_on: + datomic-storage-migrator: + condition: service_completed_successfully + + datomic-storage-migrator: + image: ${IMAGE} + volumes: + - "/var/lib/datomic-docker/data:/data:z" + entrypoint: /bin/sh + command: | + -c ' + echo "Creating SQLite database and schema..." + sqlite3 /data/datomic-sqlite.db " + CREATE TABLE IF NOT EXISTS datomic_kvs ( + id TEXT NOT NULL PRIMARY KEY, + rev INTEGER, + map TEXT, + val BLOB + );" + echo "Database initialization complete." + ' diff --git a/tests/fixtures/hello.clj b/tests/fixtures/hello.clj new file mode 100644 index 0000000..ddeab2c --- /dev/null +++ b/tests/fixtures/hello.clj @@ -0,0 +1,47 @@ +(ns hello + (:require [datomic.api :as d])) + +(def db-uri (let [uri (System/getProperty "datomic.uri")] + (assert uri "datomic.uri property is nil!") + uri)) + +(def schema + [{:db/ident :hello/message + :db/valueType :db.type/string + :db/cardinality :db.cardinality/one + :db/doc "A hello world message"} + + {:db/ident :hello/timestamp + :db/valueType :db.type/instant + :db/cardinality :db.cardinality/one + :db/doc "When the message was created"}]) + +(defn create-database [ts] + (d/create-database db-uri) + (let [conn (d/connect db-uri)] + @(d/transact conn schema) + @(d/transact + conn + [{:hello/message "Hello, Datomic!" + :hello/timestamp ts}]) + conn)) + +(defn -main [] + (println "Creating database and schema...") + (try + (let [ts (java.util.Date.) + conn (create-database ts) + db (d/db conn) + results (d/q '[:find ?m ?t + :where + [?e :hello/message ?m] + [?e :hello/timestamp ?t]] + db)] + (println "Query results:") + (doseq [[message timestamp] results] + (assert (= timestamp ts)) + (println message "at" timestamp)) + (println "Database setup complete!")) + (finally + (d/shutdown true)))) + diff --git a/tests/fixtures/testdev.properties b/tests/fixtures/testdev.properties new file mode 100644 index 0000000..9dc82a2 --- /dev/null +++ b/tests/fixtures/testdev.properties @@ -0,0 +1,10 @@ +data-dir = ./data +host = 127.0.0.1 +memory-index-max = 64m +memory-index-threshold = 16m +object-cache-max = 64m +port = 4334 +protocol = dev +storage-access = remote +storage-admin-password=adminpass +storage-datomic-password=datpass diff --git a/tests/fixtures/testsql.properties b/tests/fixtures/testsql.properties new file mode 100644 index 0000000..5ce349b --- /dev/null +++ b/tests/fixtures/testsql.properties @@ -0,0 +1,9 @@ +data-dir=./data +host=0.0.0.0 +memory-index-max=256m +memory-index-threshold=32m +object-cache-max=128m +port=4334 +protocol=sql +sql-driver-class=org.sqlite.JDBC +sql-url=jdbc:sqlite:data/db-sqlite.db diff --git a/tests/nixos-module.nix b/tests/nixos-module.nix index 120384d..579bb48 100644 --- a/tests/nixos-module.nix +++ b/tests/nixos-module.nix @@ -5,14 +5,28 @@ nixpkgs, }: -with import (nixpkgs + "/nixos/lib/testing-python.nix") { inherit system pkgs; }; -with pkgs.lib; - +let + inherit + (import (nixpkgs + "/nixos/lib/testing-python.nix") { + inherit system pkgs; + }) + makeTest + ; +in makeTest { name = "datomic-pro module test"; nodes = { client = - { ... }: + { + config, + pkgs, + lib, + ... + }: + let + package = config.services.datomic-pro.package; + version = config.services.datomic-pro.package.version; + in { nixpkgs.overlays = [ self.overlays."${system}" ]; virtualisation.memorySize = 2048; @@ -23,7 +37,7 @@ makeTest { environment.etc."datomic-pro/do-not-do-this.properties" = { text = '' storage-admin-password=do-not-do-it-this-way-in-prod - storage-datomic-password=do-not-do-it-this-way-in-prod + storage-datomic-password=do-not-do-it-this-way-in-prod-peer ''; mode = "0600"; }; @@ -31,19 +45,19 @@ makeTest { enable = true; secretsFile = "/etc/datomic-pro/do-not-do-this.properties"; settings = { - enable = true; host = "localhost"; port = 4334; - memory-index-max = "256m"; - memory-index-threshold = "32m"; - object-cache-max = "128m"; protocol = "dev"; storage-access = "remote"; + # the follow memory tweaks are only for the test env + memory-index-max = "64m"; + memory-index-threshold = "16m"; + object-cache-max = "64m"; }; }; environment.etc."datomic-console/do-not-do-this" = { - text = "datomic:dev://localhost:4334/?password=do-not-do-it-this-way-in-prod"; + text = "datomic:dev://localhost:4334/?password=do-not-do-it-this-way-in-prod-peer"; mode = "0600"; }; services.datomic-console = { @@ -52,6 +66,31 @@ makeTest { port = 8080; dbUriFile = "/etc/datomic-console/do-not-do-this"; }; + environment.systemPackages = [ + pkgs.clojure + pkgs.vim + pkgs.bash + package + ]; + users.users.root = { + hashedPassword = lib.mkForce null; + hashedPasswordFile = lib.mkForce null; + initialPassword = lib.mkForce null; + password = lib.mkForce "root"; + }; + environment.etc."datomic-test/deps.edn" = { + mode = "0600"; + text = '' + {:paths ["."] + :deps {com.datomic/peer {:local/root "${package}/share/datomic-pro/peer-${version}.jar"}} + :aliases {:run {:jvm-opts ["-Ddatomic.uri=datomic:dev://localhost:4334/test-db?password=do-not-do-it-this-way-in-prod-peer"] + :main-opts ["-m" "hello"]}}} + ''; + }; + environment.etc."datomic-test/hello.clj" = { + mode = "0600"; + text = builtins.readFile ./fixtures/hello.clj; + }; }; }; @@ -59,9 +98,18 @@ makeTest { start_all() machine.wait_for_unit("datomic-pro.service") machine.wait_for_open_port(4334) + machine.wait_until_succeeds("journalctl -u datomic-pro -o cat | grep -q 'System started'") + + # Note: running the clojure test requires internet, because maven deps will be downloaded + # unfortunately the datomic distribution does not include all deps for the peer lib. + machine.succeed("cd /etc/datomic-test && clojure -M:run") machine.wait_for_unit("datomic-console.service") machine.wait_for_open_port(8080) + machine.succeed("curl --fail http://localhost:8080/browse") + + print(machine.succeed("datomic-run -m datomic.integrity 'datomic:dev://localhost:4334/test-db?password=do-not-do-it-this-way-in-prod-peer'")) + ''; }