diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index f336dc8a..a69049a6 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -159,22 +159,17 @@ jobs: pkill --echo hc && pkill --echo holochain && pkill --echo lair-keystore - - name: Smoke test - remote_call + - name: Smoke test - remote_call_rate run: | - # Start a sandbox conductor and run it in the background - nix develop -c bash -c "hc s clean && echo "1234" | hc s --piped create && echo "1234" | hc s --piped -f 8888 run &" - # Start a TryCP instance nix develop -c bash -c "source ./scripts/trycp.sh && start_trycp" - # Run the scenario for 5 seconds - RUST_LOG=info nix run .#remote_call -- --targets targets-ci.yaml --duration 5 --no-progress + # Run the scenario for 30 seconds + RUST_LOG=info nix run .#remote_call_rate -- --targets targets-ci.yaml --instances-per-target 2 --duration 30 --no-progress # Stop the TryCP instance nix develop -c bash -c "source ./scripts/trycp.sh && stop_trycp" - pkill --echo hc && pkill --echo holochain && pkill --echo lair-keystore - - name: Build scenario bundles run: | set -euxo pipefail diff --git a/Cargo.lock b/Cargo.lock index 406cd82b..4f2d8667 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -240,9 +240,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.72" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -447,9 +447,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" [[package]] name = "cfg-if" @@ -489,9 +489,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", "clap_derive", @@ -499,9 +499,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", @@ -511,9 +511,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -523,9 +523,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "colorchoice" @@ -1222,9 +1222,9 @@ dependencies = [ [[package]] name = "fixt" -version = "0.3.1-rc.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58235813ae9d8d0af3681720f10af44790739ba9913f21976a0f0987396e6efc" +checksum = "977cd7a96311c3a16ad8aa924628d55383effba61508c732c9dc43a6d449d877" dependencies = [ "holochain_serialized_bytes", "lazy_static", @@ -1561,9 +1561,9 @@ dependencies = [ [[package]] name = "hdi" -version = "0.4.1-rc.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537e36166cbb67222fd6d843dc188f6fd8df8841687479d59d3bf154c52f5343" +checksum = "03aed8ad8b59c0fffd1eaf3a064c28d6dcbb8a6e386c154ca2f5a3f52d00e3a0" dependencies = [ "getrandom 0.2.15", "hdk_derive", @@ -1579,9 +1579,9 @@ dependencies = [ [[package]] name = "hdk" -version = "0.3.1-rc.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd19f000b1fc3a010fecd61b8d9361074c0547c57ca68c891e48dc60beb2d17" +checksum = "9319cd1bf3c04663cf76f7466a5da86dcc20641fbdb9faea623056ff1bb5237a" dependencies = [ "getrandom 0.2.15", "hdi", @@ -1599,9 +1599,9 @@ dependencies = [ [[package]] name = "hdk_derive" -version = "0.3.1-rc.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46989b5ccdeaaca2a659aa81e2d4b131f139beb40de4da81a09ee4050b952b9e" +checksum = "49d060f62bb0bf59a833a6f8741bf780b499c2f87b5c9d9d861656f6f16bd02a" dependencies = [ "darling 0.14.4", "heck 0.5.0", @@ -1657,9 +1657,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "holo_hash" -version = "0.3.1-rc.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac85fb2c1a69522c3381ad53ba8d2c001a91232a63f91e0f200186bf97c3f874" +checksum = "9404dd8b3b47ef84ad39cd4cd679d6bf8f98c879fa33a966357246d3988616cd" dependencies = [ "base64 0.22.1", "blake2b_simd", @@ -1680,9 +1680,9 @@ dependencies = [ [[package]] name = "holochain_client" -version = "0.5.0-rc.4" +version = "0.5.0-rc.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edd7fb6a7835e0a56c588b9b0e9ff5073a2eeff7d21a48e8efe19b25806ecad" +checksum = "749b6cdb1a99d0054b59f88907abb4ca8c2e1b57496e324a368b62179785bb6f" dependencies = [ "again", "anyhow", @@ -1721,9 +1721,9 @@ dependencies = [ [[package]] name = "holochain_conductor_api" -version = "0.3.1-rc.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14875094601b112014c6d9a907a5f2542ec91ff50f20c8e53f6fc8caac7a7d84" +checksum = "7749c521f500aaf7fb1b34a8385d56707ce8076e319580dc74d75e5a02b4671d" dependencies = [ "derive_more", "holo_hash", @@ -1744,9 +1744,9 @@ dependencies = [ [[package]] name = "holochain_integrity_types" -version = "0.3.1-rc.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "724b18e6c062a0e387333ee2d75206358f6f2ebd674533c2ae93e37a0871a05c" +checksum = "fe62dd90d6ddf5e02284a651713ab63112ce141002e4ee78e4e130ec90795eec" dependencies = [ "derive_builder", "holo_hash", @@ -1763,9 +1763,9 @@ dependencies = [ [[package]] name = "holochain_keystore" -version = "0.3.1-rc.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8babd570ea65a557bd0dfee5eb8e15747c48665075defa1d9867ed2dfd9ab1ed" +checksum = "97678069682249c5fa100d6de032868d012ae4e416f87ba12b43e7f3074bea77" dependencies = [ "base64 0.22.1", "derive_more", @@ -1792,9 +1792,9 @@ dependencies = [ [[package]] name = "holochain_nonce" -version = "0.3.1-rc.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "981bf3c6b4bceace67e9e1598e7a761b0116c1761e94ffc4164cd9b3a21b8b2c" +checksum = "f206e624cefcc714e156e2459727d6dfef4c587d3cda69cf9d88c9b2b1418259" dependencies = [ "getrandom 0.2.15", "holochain_secure_primitive", @@ -1803,9 +1803,9 @@ dependencies = [ [[package]] name = "holochain_secure_primitive" -version = "0.3.1-rc.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7877034592d7f0c4ce9e52ad34e7289a1b9cf93d5292462b988fba2269406066" +checksum = "edf2267568b756d9772a6ec62cf83b0519866d0e6f33c74ef8c77bfff0432d48" dependencies = [ "paste", "serde", @@ -1839,9 +1839,9 @@ dependencies = [ [[package]] name = "holochain_sqlite" -version = "0.3.1-rc.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda59967363d8cf15514b3e9d9704be6117e4f150153acb13196f4aad6ee291d" +checksum = "d8d7793ad7c8f0c56bda030ba16ce2b2ebda5b212256d0269bce0bf005dac81a" dependencies = [ "anyhow", "async-trait", @@ -1882,9 +1882,9 @@ dependencies = [ [[package]] name = "holochain_state_types" -version = "0.3.1-rc.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac8fb1f60322be86c78d92a28be1820e4b2fb71b61cadf02f7317dd8fea86d8" +checksum = "62c5b35375b270a899d3f731d504431becfd41d706c2d35c0b0ac0cb0ea0bac5" dependencies = [ "holo_hash", "holochain_integrity_types", @@ -1893,9 +1893,9 @@ dependencies = [ [[package]] name = "holochain_trace" -version = "0.3.1-rc.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78532089679f1d3d8bb88cd954f90b04b257ba5231c980633e88df5b60ae95cf" +checksum = "8b35d4236d4c1fb42273f8d5ccd3721f20601a4726f2f29df6e8c90938285f01" dependencies = [ "chrono", "derive_more", @@ -1911,9 +1911,9 @@ dependencies = [ [[package]] name = "holochain_types" -version = "0.3.1-rc.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9c78f6290d8ef36581e39c69b335d21d41afe7df9cc4f3757268be08257283" +checksum = "2b4358d68ae3aef3bcd990c046ce6cce7fbe0d924251b5aad81db1548e7abc03" dependencies = [ "anyhow", "async-trait", @@ -1960,9 +1960,9 @@ dependencies = [ [[package]] name = "holochain_util" -version = "0.3.1-rc.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6461b88941572dc5c9482874fea97ed7b5cd864bd623d096fa0a940ada98ac88" +checksum = "273aa62ff9274a22ce2f8830578edc177b77249fca5ff06bb486b4fc1d928ce4" dependencies = [ "backtrace", "cfg-if", @@ -2006,9 +2006,9 @@ dependencies = [ [[package]] name = "holochain_websocket" -version = "0.3.1-rc.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbdc4b1f9108b8aceab889201becc8f00b20163158f1aded65e52b058876a4a0" +checksum = "c86f7b4866e33556abbcaff03967ba52df1df2daa12555dcb8d620218f92fb2f" dependencies = [ "async-trait", "futures", @@ -2037,9 +2037,9 @@ dependencies = [ [[package]] name = "holochain_zome_types" -version = "0.3.1-rc.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa73998b5509115d9b07940167f96aea2ad724f587603bfa5198f2f4fbe987a4" +checksum = "bd576c22f2752cf05d018a94797fe4ac4ccbf66d8ba448a1d267bb9657ad3ea5" dependencies = [ "derive_builder", "derive_more", @@ -2118,12 +2118,12 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", - "futures-core", + "futures-util", "http 1.1.0", "http-body 1.0.0", "pin-project-lite", @@ -2131,9 +2131,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "d0e7a4dd27b9476dc40cb050d3632d3bba3a70ddbff012285f7f8559a1e7e545" [[package]] name = "httpdate" @@ -2277,6 +2277,124 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -2285,12 +2403,14 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", + "smallvec", + "utf8_iter", ] [[package]] @@ -2341,7 +2461,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "321f0f839cd44a4686e9504b0a62b4d69a50b62072144c71c68f5873c167b8d9" dependencies = [ "ahash 0.8.11", - "clap 4.5.4", + "clap 4.5.7", "crossbeam-channel", "crossbeam-utils", "dashmap", @@ -2443,9 +2563,9 @@ dependencies = [ [[package]] name = "kitsune_p2p_bin_data" -version = "0.3.1-rc.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0afa937935e8d3eae00cc3b324988ced94d8efa8039550eeb3f0a89e3e181aee" +checksum = "820631479f0e3e6a843a34d8fbe96c11c0cbedfaaeb290a559f8c7cfb09384df" dependencies = [ "base64 0.22.1", "derive_more", @@ -2458,9 +2578,9 @@ dependencies = [ [[package]] name = "kitsune_p2p_block" -version = "0.3.1-rc.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca6d86228768cda5b6fc1646294fba6076a193017e813564fa3d0a4bc8691d" +checksum = "eb67ae6c87581d2f6906bddb651f16f67caf1f3143e2344d3c9d32233afc3357" dependencies = [ "kitsune_p2p_bin_data", "kitsune_p2p_timestamp", @@ -2469,9 +2589,9 @@ dependencies = [ [[package]] name = "kitsune_p2p_dht" -version = "0.3.1-rc.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ff93b9d045ebd77781d8b61835f55112b898dc36c61d72557f58792489bee8" +checksum = "61621c362da7b74e19b89bc097441743a7eaae0a167a2c33ef501419e0d277c1" dependencies = [ "derivative", "derive_more", @@ -2489,9 +2609,9 @@ dependencies = [ [[package]] name = "kitsune_p2p_dht_arc" -version = "0.3.1-rc.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e06db37fa121dfa22ebde3271db5d385cd496ed03d915ad37104cceb87a22ca" +checksum = "cc4e3eea7e151b8be2f6e7af6b625ae864c1709a654b8396bd6af72f5e70e470" dependencies = [ "derive_more", "gcollections", @@ -2504,9 +2624,9 @@ dependencies = [ [[package]] name = "kitsune_p2p_timestamp" -version = "0.3.1-rc.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1dcdf3cae376a56ab109908d72b9b678e95920c087e7b255c505c84cdb9787" +checksum = "784e4eedd4a5ec72fbf433fb4957c93449fd3e47636874dbc8e119a5d9031845" dependencies = [ "chrono", "rusqlite", @@ -2515,9 +2635,9 @@ dependencies = [ [[package]] name = "kitsune_p2p_types" -version = "0.3.1-rc.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14901c550c67abdb22fc271730bc3b026bbaa6fd089fee89302e7101a7cfce37" +checksum = "76acb97b596371959305c0f327316c7d58f267132a202c10959786c8aabe496a" dependencies = [ "base64 0.22.1", "derive_more", @@ -2677,6 +2797,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "local_signals" version = "0.1.0" @@ -2758,9 +2884,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -2835,9 +2961,9 @@ checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" [[package]] name = "mr_bundle" -version = "0.3.1-rc.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aadd40a6201bc886d4acf554c5e62c42bb8e05d021adee765569dea5268f419" +checksum = "b0d57525022d61a753a0ce52bbee5aeeb60b42f715918e1af81def36565fe49f" dependencies = [ "derive_more", "flate2", @@ -3032,9 +3158,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" dependencies = [ "memchr", ] @@ -3568,14 +3694,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -3589,13 +3715,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", ] [[package]] @@ -3606,9 +3732,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "region" @@ -3625,10 +3751,29 @@ dependencies = [ [[package]] name = "remote_call" version = "0.1.0" +dependencies = [ + "hdk", + "remote_call_integrity", +] + +[[package]] +name = "remote_call_integrity" +version = "0.1.0" +dependencies = [ + "hdi", + "serde", +] + +[[package]] +name = "remote_call_rate" +version = "0.1.0" dependencies = [ "anyhow", "happ_builder", "holochain_types", + "rand 0.8.5", + "remote_call_integrity", + "tokio", "trycp_wind_tunnel_runner", ] @@ -4422,11 +4567,10 @@ dependencies = [ [[package]] name = "sqlformat" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c" +checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f" dependencies = [ - "itertools 0.12.1", "nom", "unicode_categories", ] @@ -4582,6 +4726,17 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "sysinfo" version = "0.28.4" @@ -4832,6 +4987,16 @@ dependencies = [ "serde", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -4989,7 +5154,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.11", + "winnow 0.6.13", ] [[package]] @@ -5131,9 +5296,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trycp_api" -version = "0.16.0-dev.7" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2fa96e0ffb9db0b166eb802ef4cc2e790d8d890fbdf316ee829a09148723ab4" +checksum = "d0c4959cf96dd36f063336eb415b41c765f111bca3225ad0a33002b4eff8ca46" dependencies = [ "serde", "serde_bytes", @@ -5142,12 +5307,12 @@ dependencies = [ [[package]] name = "trycp_client" -version = "0.16.0-dev.7" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7e8087b9aa5f8d1f53538c8e2ce2b2a28fb652e0deed3a7d5a85bfdf9ab19e" +checksum = "d718e080cf594c08826f0058c7bebbdca0352b2d044d1654d3e6a0e39dcf3de1" dependencies = [ "futures", - "rmp-serde 0.15.5", + "rmp-serde 1.1.2", "serde_json", "tokio", "tokio-tungstenite", @@ -5163,8 +5328,10 @@ dependencies = [ "holochain_client", "holochain_conductor_api", "holochain_nonce", + "holochain_serialized_bytes", "holochain_types", "holochain_zome_types", + "kitsune_p2p_types", "rand 0.8.5", "rmp-serde 1.1.2", "serde", @@ -5182,14 +5349,17 @@ name = "trycp_wind_tunnel_runner" version = "0.2.0-alpha.2" dependencies = [ "anyhow", - "clap 4.5.4", + "clap 4.5.7", "derive_more", "env_logger 0.11.3", "holochain_client", + "holochain_conductor_api", "holochain_types", + "holochain_wind_tunnel_runner", "log", "serde", "serde_yaml", + "tokio", "trycp_client_instrumented", "wind_tunnel_core", "wind_tunnel_runner", @@ -5244,27 +5414,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-normalization" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-segmentation" version = "1.11.0" @@ -5326,9 +5481,9 @@ dependencies = [ [[package]] name = "url" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" dependencies = [ "form_urlencoded", "idna", @@ -5358,11 +5513,23 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" @@ -5727,9 +5894,9 @@ dependencies = [ [[package]] name = "wide" -version = "0.7.22" +version = "0.7.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c5cb32c74fe55350a3272ba792f050613e692253ae0d89ad5d83eb0dcea15e1" +checksum = "8a040b111774ab63a19ef46bbc149398ab372b4ccdcfd719e9814dbd7dfd76c8" dependencies = [ "bytemuck", "safe_arch", @@ -5803,7 +5970,7 @@ name = "wind_tunnel_runner" version = "0.2.0-alpha.2" dependencies = [ "anyhow", - "clap 4.5.4", + "clap 4.5.7", "derive_more", "env_logger 0.11.3", "indicatif", @@ -6028,9 +6195,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.11" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c52728401e1dc672a56e81e593e912aa54c78f40246869f78359a2bf24d29d" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] @@ -6061,6 +6228,12 @@ version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + [[package]] name = "write_query" version = "0.1.0" @@ -6081,6 +6254,12 @@ dependencies = [ "holochain_wind_tunnel_runner", ] +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "wyz" version = "0.5.1" @@ -6116,6 +6295,30 @@ dependencies = [ "time", ] +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.34" @@ -6136,12 +6339,55 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +[[package]] +name = "zerovec" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "zip" version = "2.1.3" diff --git a/Cargo.toml b/Cargo.toml index 176c2687..874b898e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ members = [ "scenarios/write_read", "scenarios/write_query", "scenarios/local_signals", - "scenarios/remote_call", + "scenarios/remote_call_rate", "zomes/return_single_value/coordinator", "zomes/crud/coordinator", @@ -32,6 +32,8 @@ members = [ "zomes/callback/integrity", "zomes/large/coordinator", "zomes/signal/coordinator", + "zomes/remote_call/coordinator", + "zomes/remote_call/integrity", ] # By default, don't build the scenarios or zomes. @@ -79,17 +81,19 @@ rand = "0.8" ed25519-dalek = "2.1" # Deps for Holochain -holochain_client = { version = "=0.5.0-rc.4" } -trycp_client = { version = "0.16.0-dev.7" } -trycp_api = { version = "0.16.0-dev.7" } -holochain_zome_types = { version = "0.3.1-rc.1" } -holo_hash = { version = "0.3.1-rc.1" } -holochain_types = { version = "0.3.1-rc.1" } -holochain_conductor_api = { version = "0.3.1-rc.1" } -holochain_nonce = { version = "0.3.1-rc.0" } -hdk = { version = "0.3.1-rc.1" } -hdi = "0.4.1-rc.1" -mr_bundle = "0.3.1-rc.0" +holochain_client = { version = "=0.5.0-rc.5" } +trycp_client = { version = "0.16.0" } +trycp_api = { version = "0.16.0" } +holochain_zome_types = { version = "0.3.1" } +holo_hash = { version = "0.3.1" } +holochain_types = { version = "0.3.1" } +holochain_conductor_api = { version = "0.3.1" } +holochain_nonce = { version = "0.3.1" } +kitsune_p2p_types = { version = "0.3.1" } +hdk = { version = "0.3.1" } +hdi = "0.4.1" +mr_bundle = "0.3.1" +holochain_serialized_bytes = "0.0.54" # Framework wind_tunnel_core = { path = "./framework/core", version = "0.2.0-alpha.2" } @@ -110,6 +114,7 @@ happ_builder = { path = "./happ_builder", version = "0.1.0" } crud_integrity = { path = "./zomes/crud/integrity" } timed_integrity = { path = "./zomes/timed/integrity" } callback_integrity = { path = "./zomes/callback/integrity" } +remote_call_integrity = { path = "./zomes/remote_call/integrity" } [workspace.lints.rust] unsafe_code = "forbid" diff --git a/README.md b/README.md index 33926300..5155a042 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,7 @@ Before you can run this, you'll need to provide the agent behaviour hook. Add th fn agent_behaviour( ctx: &mut AgentContext, ) -> HookResult { - println!("Hello from, {}", ctx.agent_id()); + println!("Hello from, {}", ctx.agent_name()); std::thread::sleep(std::time::Duration::from_secs(1)); Ok(()) } diff --git a/bindings/runner/src/common.rs b/bindings/runner/src/common.rs index 28d64d17..95f60c79 100644 --- a/bindings/runner/src/common.rs +++ b/bindings/runner/src/common.rs @@ -93,7 +93,8 @@ pub fn configure_app_ws_url( /// Opinionated app installation which will give you what you need in most cases. /// -/// The [RoleName] you provide is used to find the cell id of the installed app. +/// The [RoleName] you provide is used to find the cell id within the installed app that you want +/// to call during your scenario. /// /// Requires: /// - The [HolochainRunnerContext] to have a valid `app_ws_url`. Consider calling [configure_app_ws_url] in your setup before using this function. @@ -140,7 +141,7 @@ where { let admin_ws_url = ctx.runner_context().get_connection_string().to_string(); let app_ws_url = ctx.runner_context().get().app_ws_url(); - let agent_id = ctx.agent_id().to_string(); + let agent_name = ctx.agent_name().to_string(); let reporter = ctx.runner_context().reporter(); let (installed_app_id, cell_id, app_client) = ctx @@ -156,7 +157,7 @@ where .map_err(handle_api_err)?; log::debug!("Generated agent pub key: {:}", key); - let installed_app_id = format!("{}-app", agent_id).to_string(); + let installed_app_id = format!("{}-app", agent_name).to_string(); let app_info = client .install_app(InstallAppPayload { source: AppBundleSource::Path(app_path), @@ -195,7 +196,7 @@ where .await?; log::debug!("Authorized signing credentials"); - let mut signer = ClientAgentSigner::default(); + let signer = ClientAgentSigner::default(); signer.add_credentials(cell_id.clone(), credentials); let issued = client diff --git a/bindings/runner/src/macros.rs b/bindings/runner/src/macros.rs index c6a226ef..45cc4cb4 100644 --- a/bindings/runner/src/macros.rs +++ b/bindings/runner/src/macros.rs @@ -1,7 +1,7 @@ #[macro_export] macro_rules! scenario_happ_path { ($name:literal) => {{ - let local_path = Path::new(env!("CARGO_MANIFEST_DIR")) + let local_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .join("../../happs") .join(env!("CARGO_PKG_NAME")) .join(format!("{}.happ", $name)); diff --git a/bindings/trycp_client/Cargo.toml b/bindings/trycp_client/Cargo.toml index 5abc1add..997e7b81 100644 --- a/bindings/trycp_client/Cargo.toml +++ b/bindings/trycp_client/Cargo.toml @@ -29,6 +29,8 @@ holo_hash = { workspace = true } holochain_types = { workspace = true } holochain_conductor_api = { workspace = true } holochain_nonce = { workspace = true } +holochain_serialized_bytes = { workspace = true } +kitsune_p2p_types = { workspace = true } [lints] workspace = true diff --git a/bindings/trycp_client/src/lib.rs b/bindings/trycp_client/src/lib.rs index 0ca74ab5..c1ecc793 100644 --- a/bindings/trycp_client/src/lib.rs +++ b/bindings/trycp_client/src/lib.rs @@ -15,6 +15,8 @@ use wind_tunnel_instruments_derive::wind_tunnel_instrument; pub mod prelude { pub use super::TryCPClientInstrumented as TryCPClient; + + pub use holochain_client::AuthorizeSigningCredentialsPayload; } #[derive(Clone)] @@ -225,19 +227,95 @@ mod admin_impl { AuthorizeSigningCredentialsPayload, EnableAppResponse, SigningCredentials, }; use holochain_conductor_api::{ - AdminRequest, AdminResponse, AppAuthenticationTokenIssued, AppInfo, AppInterfaceInfo, - AppStatusFilter, IssueAppAuthenticationTokenPayload, StorageInfo, + AdminRequest, AdminResponse, AppAuthenticationToken, AppAuthenticationTokenIssued, AppInfo, + AppInterfaceInfo, AppStatusFilter, FullStateDump, IssueAppAuthenticationTokenPayload, + StorageInfo, }; + use holochain_serialized_bytes::encode; use holochain_types::app::{DeleteCloneCellPayload, InstallAppPayload, InstalledAppId}; use holochain_types::prelude::{GrantedFunctions, Record}; use holochain_types::websocket::AllowedOrigins; use holochain_zome_types::capability::GrantZomeCallCapabilityPayload; use holochain_zome_types::cell::CellId; use holochain_zome_types::dna_def::DnaDef; + use kitsune_p2p_types::agent_info::AgentInfoSigned; use super::*; impl TryCPClientInstrumented { + #[wind_tunnel_instrument(prefix = "trycp_admin_")] + pub async fn get_dna_definition( + &self, + id: String, + dna_hash: DnaHash, + timeout: Option, + ) -> io::Result { + let response = self + .call_admin( + id, + AdminRequest::GetDnaDefinition(Box::new(dna_hash)), + timeout, + ) + .await?; + + match response { + AdminResponse::DnaDefinitionReturned(dna_def) => Ok(dna_def), + _ => Err(io::Error::other("Unexpected admin response")), + } + } + + #[wind_tunnel_instrument(prefix = "trycp_admin_")] + pub async fn install_app( + &self, + id: String, + payload: InstallAppPayload, + timeout: Option, + ) -> io::Result { + let response = self + .call_admin( + id.clone(), + AdminRequest::InstallApp(Box::new(payload)), + timeout, + ) + .await?; + + match response { + AdminResponse::AppInstalled(app) => Ok(app), + _ => Err(io::Error::other("Unexpected admin response")), + } + } + + #[wind_tunnel_instrument(prefix = "trycp_admin_")] + pub async fn uninstall_app( + &self, + id: String, + installed_app_id: String, + timeout: Option, + ) -> io::Result<()> { + let response = self + .call_admin(id, AdminRequest::UninstallApp { installed_app_id }, timeout) + .await?; + + match response { + AdminResponse::AppUninstalled => Ok(()), + _ => Err(io::Error::other("Unexpected admin response")), + } + } + + #[wind_tunnel_instrument(prefix = "trycp_admin_")] + pub async fn list_dnas( + &self, + id: String, + timeout: Option, + ) -> io::Result> { + let response = self.call_admin(id, AdminRequest::ListDnas, timeout).await?; + + match response { + AdminResponse::DnasListed(dnas) => Ok(dnas), + _ => Err(io::Error::other("Unexpected admin response")), + } + } + #[wind_tunnel_instrument(prefix = "trycp_admin_")] pub async fn generate_agent_pub_key( &self, @@ -255,17 +333,68 @@ mod admin_impl { } #[wind_tunnel_instrument(prefix = "trycp_admin_")] - pub async fn list_app_interfaces( + pub async fn list_cell_ids( &self, id: String, timeout: Option, - ) -> io::Result> { + ) -> io::Result> { let response = self - .call_admin(id, AdminRequest::ListAppInterfaces, timeout) + .call_admin(id, AdminRequest::ListCellIds, timeout) .await?; match response { - AdminResponse::AppInterfacesListed(info) => Ok(info), + AdminResponse::CellIdsListed(ids) => Ok(ids), + _ => Err(io::Error::other("Unexpected admin response")), + } + } + + #[wind_tunnel_instrument(prefix = "trycp_admin_")] + pub async fn list_apps( + &self, + id: String, + status_filter: Option, + timeout: Option, + ) -> io::Result> { + let response = self + .call_admin(id, AdminRequest::ListApps { status_filter }, timeout) + .await?; + + match response { + AdminResponse::AppsListed(apps) => Ok(apps), + _ => Err(io::Error::other("Unexpected admin response")), + } + } + + #[wind_tunnel_instrument(prefix = "trycp_admin_")] + pub async fn enable_app( + &self, + id: String, + installed_app_id: String, + timeout: Option, + ) -> io::Result { + let response = self + .call_admin(id, AdminRequest::EnableApp { installed_app_id }, timeout) + .await?; + + match response { + AdminResponse::AppEnabled { app, errors } => Ok(EnableAppResponse { app, errors }), + _ => Err(io::Error::other("Unexpected admin response")), + } + } + + #[wind_tunnel_instrument(prefix = "trycp_admin_")] + pub async fn disable_app( + &self, + id: String, + installed_app_id: String, + timeout: Option, + ) -> io::Result<()> { + let response = self + .call_admin(id, AdminRequest::DisableApp { installed_app_id }, timeout) + .await?; + + match response { + AdminResponse::AppDisabled => Ok(()), _ => Err(io::Error::other("Unexpected admin response")), } } @@ -298,111 +427,175 @@ mod admin_impl { } #[wind_tunnel_instrument(prefix = "trycp_admin_")] - pub async fn list_apps( + pub async fn list_app_interfaces( &self, id: String, - status_filter: Option, timeout: Option, - ) -> io::Result> { + ) -> io::Result> { let response = self - .call_admin(id, AdminRequest::ListApps { status_filter }, timeout) + .call_admin(id, AdminRequest::ListAppInterfaces, timeout) .await?; match response { - AdminResponse::AppsListed(apps) => Ok(apps), + AdminResponse::AppInterfacesListed(info) => Ok(info), _ => Err(io::Error::other("Unexpected admin response")), } } #[wind_tunnel_instrument(prefix = "trycp_admin_")] - pub async fn install_app( + pub async fn dump_state( &self, id: String, - payload: InstallAppPayload, + cell_id: CellId, timeout: Option, - ) -> io::Result { + ) -> io::Result { let response = self .call_admin( - id.clone(), - AdminRequest::InstallApp(Box::new(payload)), + id, + AdminRequest::DumpState { + cell_id: Box::new(cell_id), + }, timeout, ) .await?; match response { - AdminResponse::AppInstalled(app) => Ok(app), + AdminResponse::StateDumped(s) => Ok(s), _ => Err(io::Error::other("Unexpected admin response")), } } #[wind_tunnel_instrument(prefix = "trycp_admin_")] - pub async fn uninstall_app( + pub async fn dump_conductor_state( &self, id: String, - installed_app_id: String, timeout: Option, - ) -> io::Result<()> { + ) -> io::Result { let response = self - .call_admin(id, AdminRequest::UninstallApp { installed_app_id }, timeout) + .call_admin(id, AdminRequest::DumpConductorState, timeout) .await?; match response { - AdminResponse::AppUninstalled => Ok(()), + AdminResponse::ConductorStateDumped(s) => Ok(s), _ => Err(io::Error::other("Unexpected admin response")), } } #[wind_tunnel_instrument(prefix = "trycp_admin_")] - pub async fn enable_app( + pub async fn dump_full_state( &self, id: String, - installed_app_id: String, + cell_id: CellId, + dht_ops_cursor: Option, timeout: Option, - ) -> io::Result { + ) -> io::Result { let response = self - .call_admin(id, AdminRequest::EnableApp { installed_app_id }, timeout) + .call_admin( + id, + AdminRequest::DumpFullState { + cell_id: Box::new(cell_id), + dht_ops_cursor, + }, + timeout, + ) .await?; match response { - AdminResponse::AppEnabled { app, errors } => Ok(EnableAppResponse { app, errors }), + AdminResponse::FullStateDumped(fds) => Ok(fds), _ => Err(io::Error::other("Unexpected admin response")), } } #[wind_tunnel_instrument(prefix = "trycp_admin_")] - pub async fn disable_app( + pub async fn dump_network_metrics( &self, id: String, - installed_app_id: String, + dna_hash: Option, + timeout: Option, + ) -> io::Result { + let response = self + .call_admin(id, AdminRequest::DumpNetworkMetrics { dna_hash }, timeout) + .await?; + + match response { + AdminResponse::NetworkMetricsDumped(metrics) => Ok(metrics), + _ => Err(io::Error::other("Unexpected admin response")), + } + } + + #[wind_tunnel_instrument(prefix = "trycp_admin_")] + pub async fn dump_network_stats( + &self, + id: String, + timeout: Option, + ) -> io::Result { + let response = self + .call_admin(id, AdminRequest::DumpNetworkStats, timeout) + .await?; + + match response { + AdminResponse::NetworkStatsDumped(stats) => Ok(stats), + _ => Err(io::Error::other("Unexpected admin response")), + } + } + + #[wind_tunnel_instrument(prefix = "trycp_admin_")] + pub async fn add_agent_info( + &self, + id: String, + agent_infos: Vec, timeout: Option, ) -> io::Result<()> { let response = self - .call_admin(id, AdminRequest::DisableApp { installed_app_id }, timeout) + .call_admin(id, AdminRequest::AddAgentInfo { agent_infos }, timeout) .await?; match response { - AdminResponse::AppDisabled => Ok(()), + AdminResponse::AgentInfoAdded => Ok(()), _ => Err(io::Error::other("Unexpected admin response")), } } #[wind_tunnel_instrument(prefix = "trycp_admin_")] - pub async fn get_dna_definition( + pub async fn agent_info( &self, id: String, - dna_hash: DnaHash, + cell_id: Option, timeout: Option, - ) -> io::Result { + ) -> io::Result> { + let response = self + .call_admin(id, AdminRequest::AgentInfo { cell_id }, timeout) + .await?; + + match response { + AdminResponse::AgentInfo(agents) => Ok(agents), + _ => Err(io::Error::other("Unexpected admin response")), + } + } + + #[wind_tunnel_instrument(prefix = "trycp_admin_")] + pub async fn graft_records( + &self, + id: String, + cell_id: CellId, + validate: bool, + records: Vec, + timeout: Option, + ) -> io::Result<()> { let response = self .call_admin( id, - AdminRequest::GetDnaDefinition(Box::new(dna_hash)), + AdminRequest::GraftRecords { + cell_id, + validate, + records, + }, timeout, ) .await?; match response { - AdminResponse::DnaDefinitionReturned(dna_def) => Ok(dna_def), + AdminResponse::RecordsGrafted => Ok(()), _ => Err(io::Error::other("Unexpected admin response")), } } @@ -466,44 +659,43 @@ mod admin_impl { } #[wind_tunnel_instrument(prefix = "trycp_admin_")] - pub async fn dump_network_stats( + pub async fn issue_app_auth_token( &self, id: String, + payload: IssueAppAuthenticationTokenPayload, timeout: Option, - ) -> io::Result { + ) -> io::Result { let response = self - .call_admin(id, AdminRequest::DumpNetworkStats, timeout) + .call_admin( + id, + AdminRequest::IssueAppAuthenticationToken(payload), + timeout, + ) .await?; match response { - AdminResponse::NetworkStatsDumped(stats) => Ok(stats), + AdminResponse::AppAuthenticationTokenIssued(token) => Ok(token), _ => Err(io::Error::other("Unexpected admin response")), } } #[wind_tunnel_instrument(prefix = "trycp_admin_")] - pub async fn graft_records( + pub async fn revoke_app_auth_token( &self, id: String, - cell_id: CellId, - validate: bool, - records: Vec, + token: AppAuthenticationToken, timeout: Option, ) -> io::Result<()> { let response = self .call_admin( id, - AdminRequest::GraftRecords { - cell_id, - validate, - records, - }, + AdminRequest::RevokeAppAuthenticationToken(token), timeout, ) .await?; match response { - AdminResponse::RecordsGrafted => Ok(()), + AdminResponse::AppAuthenticationTokenRevoked => Ok(()), _ => Err(io::Error::other("Unexpected admin response")), } } @@ -551,45 +743,30 @@ mod admin_impl { }) } - #[wind_tunnel_instrument(prefix = "trycp_admin_")] - pub async fn issue_app_auth_token( - &self, - id: String, - payload: IssueAppAuthenticationTokenPayload, - timeout: Option, - ) -> io::Result { - let response = self - .call_admin( - id, - AdminRequest::IssueAppAuthenticationToken(payload), - timeout, - ) - .await?; - - match response { - AdminResponse::AppAuthenticationTokenIssued(token) => Ok(token), - _ => Err(io::Error::other("Unexpected admin response")), - } - } - async fn call_admin( &self, id: String, request: AdminRequest, timeout: Option, ) -> io::Result { + let message = encode(&request).map_err(io::Error::other)?; + let response = self .trycp_client .request( - Request::CallAdminInterface { - id, - message: rmp_serde::to_vec(&request).map_err(io::Error::other)?, - }, + Request::CallAdminInterface { id, message }, timeout.unwrap_or(self.timeout), ) .await?; - read_response(response) + match read_response(response) { + Ok(AdminResponse::Error(e)) => { + // Explicitly map the error response to an error to prevent crashes when + // checking the expected response types. + Err(io::Error::other(format!("{e:?}"))) + } + other => other, + } } } } @@ -598,6 +775,7 @@ mod app_impl { use super::*; use holochain_conductor_api::{AppInfo, AppRequest, AppResponse, NetworkInfo}; use holochain_nonce::fresh_nonce; + use holochain_serialized_bytes::encode; use holochain_types::app::{ CreateCloneCellPayload, DisableCloneCellPayload, EnableCloneCellPayload, NetworkInfoRequestPayload, @@ -744,18 +922,24 @@ mod app_impl { request: AppRequest, timeout: Option, ) -> io::Result { + let message = encode(&request).map_err(io::Error::other)?; + let response = self .trycp_client .request( - Request::CallAppInterface { - port, - message: rmp_serde::to_vec(&request).map_err(io::Error::other)?, - }, + Request::CallAppInterface { port, message }, timeout.unwrap_or(self.timeout), ) .await?; - read_response(response) + match read_response(response) { + Ok(AppResponse::Error(r)) => { + // Explicitly map the error response to an error to prevent crashes when + // checking the expected response types. + Err(io::Error::other(format!("{r:?}"))) + } + other => other, + } } } } diff --git a/bindings/trycp_runner/Cargo.toml b/bindings/trycp_runner/Cargo.toml index 0e6dbe37..9a906e19 100644 --- a/bindings/trycp_runner/Cargo.toml +++ b/bindings/trycp_runner/Cargo.toml @@ -17,12 +17,15 @@ serde = { workspace = true } serde_yaml = { workspace = true } env_logger = { workspace = true } log = { workspace = true } +tokio = { workspace = true } wind_tunnel_runner = { workspace = true } +holochain_wind_tunnel_runner = { workspace = true } trycp_client_instrumented = { workspace = true } holochain_client = { workspace = true } holochain_types = { workspace = true } wind_tunnel_core = { workspace = true } +holochain_conductor_api = { workspace = true } [lints] workspace = true diff --git a/bindings/trycp_runner/src/cli.rs b/bindings/trycp_runner/src/cli.rs index 9ddfa1c2..b2de74b7 100644 --- a/bindings/trycp_runner/src/cli.rs +++ b/bindings/trycp_runner/src/cli.rs @@ -18,6 +18,12 @@ pub struct WindTunnelTryCPScenarioCli { #[clap(long)] pub targets: PathBuf, + /// The number of Holochain conductor instances to run on each target node. + /// + /// Max value: 255 + #[clap(long, default_value = "1")] + pub instances_per_target: u8, + /// Assign a behaviour to a number of agents. Specify the behaviour and number of agents to assign /// it to in the format `behaviour:count`. For example `--behaviour=login:5`. /// @@ -63,7 +69,7 @@ impl TryInto for WindTunnelTryCPScenarioCli { // Connection string is already forwarded but is supposed to be a single value. // Pack values together and extract by agent id in helpers. connection_string: targets.nodes.join(","), - agents: Some(targets.nodes.len()), + agents: Some(targets.nodes.len() * self.instances_per_target as usize), behaviour: self.behaviour, duration: self.duration, soak: self.soak, diff --git a/bindings/trycp_runner/src/common.rs b/bindings/trycp_runner/src/common.rs index 1edc8e17..9c958e9d 100644 --- a/bindings/trycp_runner/src/common.rs +++ b/bindings/trycp_runner/src/common.rs @@ -1,8 +1,18 @@ use crate::context::TryCPAgentContext; use crate::runner_context::TryCPRunnerContext; -use std::sync::Arc; +use anyhow::Context; +use holochain_client::AuthorizeSigningCredentialsPayload; +use holochain_conductor_api::{CellInfo, IssueAppAuthenticationTokenPayload}; +use holochain_types::app::{AppBundle, AppBundleSource, InstallAppPayload}; +use holochain_types::prelude::RoleName; +use holochain_types::websocket::AllowedOrigins; +use std::path::PathBuf; +use std::sync::{Arc, OnceLock}; +use std::time::{Duration, Instant}; use trycp_client_instrumented::prelude::TryCPClient; -use wind_tunnel_runner::prelude::{AgentContext, HookResult, UserValuesConstraint}; +use wind_tunnel_runner::prelude::{ + AgentContext, HookResult, UserValuesConstraint, WindTunnelResult, +}; /// Connects to a TryCP server using the current agent index and the list of targets. /// @@ -21,11 +31,12 @@ pub fn connect_trycp_client( ) -> HookResult { let agent_index = ctx.agent_index(); + let nodes = ctx.runner_context().get_connection_string().split(','); let target = ctx .runner_context() .get_connection_string() .split(',') - .nth(agent_index); + .nth(agent_index % nodes.count()); // This should never happen because the behaviour assignment should have checked that there were enough agents. let target = target @@ -45,6 +56,253 @@ pub fn connect_trycp_client( Ok(()) } +/// Opinionated app installation which will give you what you need in most cases. +/// +/// The [RoleName] you provide is used to find the cell id within the installed app that you want +/// to call during your scenario. +/// +/// Requires: +/// - The [TryCPAgentContext] must already have a connected TryCP client. You can use +/// `connect_trycp_client` to do this. +/// +/// Call this function as follows: +/// ```rust +/// use std::path::Path; +/// use trycp_wind_tunnel_runner::prelude::{TryCPAgentContext, TryCPRunnerContext, AgentContext, HookResult, install_app}; +/// +/// fn agent_setup(ctx: &mut AgentContext) -> HookResult { +/// install_app(ctx, Path::new("path/to/your/happ").to_path_buf(), &"your_role_name".to_string())?; +/// Ok(()) +/// } +/// ``` +/// +/// After calling this function you will be able to use the `app_port` and `cell_id` in your agent hooks: +/// ```rust +/// +/// use trycp_wind_tunnel_runner::prelude::{TryCPAgentContext, TryCPRunnerContext, AgentContext, HookResult}; +/// +/// fn agent_behaviour(ctx: &mut AgentContext) -> HookResult { +/// let app_agent_client = ctx.get().app_port(); +/// let cell_id = ctx.get().cell_id(); +/// +/// Ok(()) +/// } +/// ``` +/// +/// Method: +/// - Uses the existing connection to a TryCP server. +/// - Generates an agent public key. +/// - Installs the app using the provided `app_path` and the agent public key. +/// - Enables the app. +/// - Attaches an app interface. +/// - Authorizes signing credentials. +/// - Registers the signing credentials so that they will be available for zome calls. +/// - Sets the `app_port` and `cell_id` values in [TryCPAgentContext]. +pub fn install_app( + ctx: &mut AgentContext>, + app_path: PathBuf, + role_name: &RoleName, +) -> WindTunnelResult<()> +where + SV: UserValuesConstraint, +{ + let run_id = ctx.runner_context().get_run_id().to_string(); + let client = ctx.get().trycp_client(); + let agent_name = ctx.agent_name().to_string(); + + let (app_port, cell_id, credentials) = ctx + .runner_context() + .executor() + .execute_in_place(async move { + let agent_key = client + .generate_agent_pub_key(agent_name.clone(), None) + .await?; + + let content = std::fs::read(app_path)?; + + let installed_app_id = format!("{}-app", agent_name).to_string(); + let app_info = client + .install_app( + agent_name.clone(), + InstallAppPayload { + source: AppBundleSource::Bundle(AppBundle::decode(&content)?), + agent_key, + installed_app_id: Some(installed_app_id.clone()), + membrane_proofs: Default::default(), + network_seed: Some(run_id), + }, + None, + ) + .await?; + + let enable_result = client + .enable_app(agent_name.clone(), app_info.installed_app_id.clone(), None) + .await?; + if !enable_result.errors.is_empty() { + return Err(anyhow::anyhow!( + "Failed to enable app: {:?}", + enable_result.errors + )); + } + + let app_port = client + .attach_app_interface(agent_name.clone(), None, AllowedOrigins::Any, None, None) + .await?; + + let issued = client + .issue_app_auth_token( + agent_name.clone(), + IssueAppAuthenticationTokenPayload { + installed_app_id, + expiry_seconds: 30, + single_use: true, + }, + None, + ) + .await?; + + client + .connect_app_interface(issued.token, app_port, None) + .await?; + + let cell_id = match app_info + .cell_info + .get(role_name) + .ok_or(anyhow::anyhow!("Role not found"))? + .first() + .ok_or(anyhow::anyhow!("Cell not found"))? + { + CellInfo::Provisioned(pc) => pc.cell_id.clone(), + _ => anyhow::bail!("Cell not provisioned"), + }; + log::debug!("Got cell id: {:}", cell_id); + + let credentials = client + .authorize_signing_credentials( + agent_name.clone(), + AuthorizeSigningCredentialsPayload { + cell_id: cell_id.clone(), + functions: None, // Equivalent to all functions + }, + None, + ) + .await?; + + Ok((app_port, cell_id, credentials)) + }) + .context("Failed to install app")?; + + ctx.get_mut().app_port = Some(app_port); + ctx.get_mut().cell_id = Some(cell_id.clone()); + ctx.get_mut().signer().add_credentials(cell_id, credentials); + + Ok(()) +} + +/// Tries to wait for a minimum number of peers to be discovered. +/// +/// If you call this function in you agent setup then the scenario will become configurable using +/// the `MIN_PEERS` environment variable. The default value is 2. +/// +/// Note that the number of peers seen by each node includes itself. So having two nodes means that +/// each node will immediately see one peer after app installation. +/// +/// Example: +/// ```rust +/// use std::path::Path; +/// use std::time::Duration; +/// use trycp_wind_tunnel_runner::prelude::{TryCPAgentContext, TryCPRunnerContext, AgentContext, HookResult, install_app, try_wait_for_min_peers}; +/// +/// fn agent_setup(ctx: &mut AgentContext) -> HookResult { +/// install_app(ctx, Path::new("path/to/your/happ").to_path_buf(), &"your_role_name".to_string())?; +/// try_wait_for_min_peers(ctx, Duration::from_secs(60))?; +/// Ok(()) +/// } +/// ``` +/// +/// Note that if no apps have been installed, you are waiting for too many peers, or anything else +/// prevents enough peers being discovered then the function will wait up to the `wait_for` duration +/// before continuing. It will not fail if too few peers were discovered. +/// +/// Note that the smallest resolution is 1s. This is because the function will sleep between +/// querying peers from the conductor. You could probably not use this function for performance +/// testing peer discovery! +pub fn try_wait_for_min_peers( + ctx: &mut AgentContext>, + wait_for: Duration, +) -> HookResult +where + SV: UserValuesConstraint, +{ + static MIN_PEERS: OnceLock = OnceLock::new(); + + let client = ctx.get().trycp_client(); + let agent_name = ctx.agent_name().to_string(); + + let min_peers = *MIN_PEERS.get_or_init(|| { + std::env::var("MIN_PEERS") + .ok() + .map(|s| s.parse().expect("MIN_PEERS must be a number")) + .unwrap_or(2) + }); + ctx.runner_context() + .executor() + .execute_in_place(async move { + let start_discovery = Instant::now(); + for _ in 0..wait_for.as_secs() { + let agent_list = client.agent_info(agent_name.clone(), None, None).await?; + + if agent_list.len() >= min_peers { + break; + } + + tokio::time::sleep(Duration::from_secs(1)).await; + } + + println!( + "Discovery for agent {} took: {}s", + agent_name, + start_discovery.elapsed().as_secs() + ); + + Ok(()) + })?; + + Ok(()) +} + +/// Shuts down the Holochain conductor managed by the TryCP server for the current agent. +/// +/// You *MUST* call this function in your agent teardown. Otherwise, dropping the agent context will +/// panic when the TryCP client is dropped. +/// +/// ```rust +/// use trycp_wind_tunnel_runner::prelude::{TryCPRunnerContext, AgentContext, TryCPAgentContext, HookResult, shutdown_remote}; +/// +/// fn agent_teardown(ctx: &mut AgentContext) -> HookResult { +/// shutdown_remote(ctx)?; +/// Ok(()) +/// } +/// ``` +pub fn shutdown_remote( + ctx: &mut AgentContext>, +) -> HookResult +where + SV: UserValuesConstraint, +{ + let client = ctx.get().trycp_client(); + let agent_name = ctx.agent_name().to_string(); + + ctx.runner_context() + .executor() + .execute_in_place(async move { + client.shutdown(agent_name.clone(), None, None).await?; + Ok(()) + })?; + + Ok(()) +} + /// Asks the TryCP server to reset all state for managed Holochain instances. /// /// You must call `connect_trycp_client` before calling this function. Or otherwise ensure that the @@ -61,8 +319,8 @@ pub fn connect_trycp_client( /// Ok(()) /// } /// ``` -pub fn reset_trycp_remote( - ctx: &mut AgentContext, +pub fn reset_trycp_remote( + ctx: &mut AgentContext>, ) -> HookResult { let client = ctx.get().trycp_client(); diff --git a/bindings/trycp_runner/src/context.rs b/bindings/trycp_runner/src/context.rs index cd728683..222aef2a 100644 --- a/bindings/trycp_runner/src/context.rs +++ b/bindings/trycp_runner/src/context.rs @@ -1,4 +1,5 @@ use holochain_client::ClientAgentSigner; +use holochain_types::prelude::CellId; use std::collections::HashMap; use std::fmt::Debug; use std::sync::Arc; @@ -18,6 +19,8 @@ impl UserValuesConstraint for DefaultScenarioValues {} pub struct TryCPAgentContext { pub(crate) signer: Option>, pub(crate) trycp_client: Option, + pub(crate) app_port: Option, + pub(crate) cell_id: Option, pub scenario_values: T, } @@ -38,6 +41,16 @@ impl TryCPAgentContext { ) } + /// Get the app port that was configured during app installation. + pub fn app_port(&self) -> u16 { + self.app_port.expect("app_port is not set") + } + + /// Get the cell id that was configured during app installation. + pub fn cell_id(&self) -> CellId { + self.cell_id.clone().expect("cell_id is not set") + } + /// Close the TryCP client by dropping it. /// /// Calling [TryCPAgentContext::trycp_client] after this function, or this function again after, will panic. diff --git a/bindings/trycp_runner/src/lib.rs b/bindings/trycp_runner/src/lib.rs index 55718975..3531ffd5 100644 --- a/bindings/trycp_runner/src/lib.rs +++ b/bindings/trycp_runner/src/lib.rs @@ -6,13 +6,28 @@ mod runner_context; pub mod prelude { pub use crate::cli::WindTunnelTryCPScenarioCli; - pub use crate::common::{connect_trycp_client, disconnect_trycp_client, reset_trycp_remote}; + pub use crate::common::{ + connect_trycp_client, disconnect_trycp_client, install_app, reset_trycp_remote, + shutdown_remote, try_wait_for_min_peers, + }; pub use crate::context::{DefaultScenarioValues, TryCPAgentContext}; pub use crate::definition::TryCPScenarioDefinitionBuilder; pub use crate::runner_context::TryCPRunnerContext; + pub use trycp_client_instrumented::prelude::*; + /// Re-export of the `wind_tunnel_runner` prelude. /// /// This is for convenience so that you can depend on a single crate for the runner in your scenarios. pub use wind_tunnel_runner::prelude::*; + + /// Re-export some of the `holochain_wind_tunnel_runner`. + /// + /// This is really a runner for a separate purpose but some of its functionality is useful for + /// the TryCP runner. It doesn't make sense to include both in scenarios, so this is a way to + /// make functionality available without coping it. + pub use holochain_wind_tunnel_runner::scenario_happ_path; + + /// Re-export types from the Holochain crates that shouldn't need to be imported into every scenario + pub use holochain_conductor_api::{CellInfo, IssueAppAuthenticationTokenPayload}; } diff --git a/conductor-config.yaml b/conductor-config.yaml new file mode 100644 index 00000000..82e233a5 --- /dev/null +++ b/conductor-config.yaml @@ -0,0 +1,6 @@ +network: + network_type: quic_bootstrap + transport_pool: + - type: webrtc + signal_url: "wss://signal.holo.host" + bootstrap_service: "https://bootstrap.holo.host" diff --git a/flake.lock b/flake.lock index 1274a9eb..aa2aaaa9 100644 --- a/flake.lock +++ b/flake.lock @@ -76,11 +76,11 @@ ] }, "locked": { - "lastModified": 1717386774, - "narHash": "sha256-YYMGHDo4f+tu7k2q6hWNiI5C8gAN5eksb3g3EbKFf4k=", + "lastModified": 1718078026, + "narHash": "sha256-LbQabH6h86ZzTvDnaZHmMwedRZNB2jYtUQzmoqWQoJ8=", "owner": "ipetkov", "repo": "crane", - "rev": "ad21f86e47a2751faa99aecd0d494be70411d5e9", + "rev": "a3f0c63eed74a516298932b9b1627dd80b9c3892", "type": "github" }, "original": { @@ -118,11 +118,11 @@ ] }, "locked": { - "lastModified": 1717025063, - "narHash": "sha256-dIubLa56W9sNNz0e8jGxrX3CAkPXsq7snuFA/Ie6dn8=", + "lastModified": 1718078026, + "narHash": "sha256-LbQabH6h86ZzTvDnaZHmMwedRZNB2jYtUQzmoqWQoJ8=", "owner": "ipetkov", "repo": "crane", - "rev": "480dff0be03dac0e51a8dfc26e882b0d123a450e", + "rev": "a3f0c63eed74a516298932b9b1627dd80b9c3892", "type": "github" }, "original": { @@ -390,11 +390,11 @@ ] }, "locked": { - "lastModified": 1717439305, - "narHash": "sha256-Y/PmDKlzno6AnE/whmV5oRgZgYmBEkr4y83GYnZwahQ=", + "lastModified": 1718285062, + "narHash": "sha256-FVXY8scfrt2QQ1v3iqAK8GTaMmSOClNFlI0/T+qZeTU=", "owner": "holochain", "repo": "holochain", - "rev": "4cf086772054b6f63bb4adf2d22671137506e012", + "rev": "4be2cfa4aa5d92dcea35f76aff344af5127e07fb", "type": "github" }, "original": { @@ -406,16 +406,16 @@ "holochain_2": { "flake": false, "locked": { - "lastModified": 1715896367, - "narHash": "sha256-jRxb8HsXUV7Fba3hMCL+TycbvF7v+JT53eIryZPu878=", + "lastModified": 1718142789, + "narHash": "sha256-Lam1hWLqi+zv0umdTIIHK9YKHVWQrI/Z4AySo97xK9E=", "owner": "holochain", "repo": "holochain", - "rev": "9f293072c41bd053e9cd88f746a456ee1dee1990", + "rev": "582f05b66b690448b1574d1aa6004114ff98187f", "type": "github" }, "original": { "owner": "holochain", - "ref": "holochain-0.3.1-rc.0", + "ref": "holochain-0.3.1", "repo": "holochain", "type": "github" } @@ -423,16 +423,16 @@ "holochain_3": { "flake": false, "locked": { - "lastModified": 1717415946, - "narHash": "sha256-Fjvmz9JKwwKzluJqax8dk3Y8gjnKW2ec2+rBwujK+nA=", + "lastModified": 1718142789, + "narHash": "sha256-Lam1hWLqi+zv0umdTIIHK9YKHVWQrI/Z4AySo97xK9E=", "owner": "holochain", "repo": "holochain", - "rev": "dc34e4e851f1a8e4bfa3a36ff1adf6c8a2c64e47", + "rev": "582f05b66b690448b1574d1aa6004114ff98187f", "type": "github" }, "original": { "owner": "holochain", - "ref": "holochain-0.3.1-rc.1", + "ref": "holochain-0.3.1", "repo": "holochain", "type": "github" } @@ -477,11 +477,11 @@ ] }, "locked": { - "lastModified": 1717008431, - "narHash": "sha256-cRPG2hDyFlxsT2bGlINJOjhZ4VXPBZ3lryXXXgKZ+rQ=", + "lastModified": 1718285062, + "narHash": "sha256-FVXY8scfrt2QQ1v3iqAK8GTaMmSOClNFlI0/T+qZeTU=", "owner": "holochain", "repo": "holochain", - "rev": "54e08e370c674c87d2dcac3146cd0aff76df93aa", + "rev": "4be2cfa4aa5d92dcea35f76aff344af5127e07fb", "type": "github" }, "original": { @@ -527,11 +527,11 @@ "launcher": { "flake": false, "locked": { - "lastModified": 1716406557, - "narHash": "sha256-6eWndYspznXKYIJcuPCWFZ3lxdh7TrlEUGP1zaE5Mgs=", + "lastModified": 1717431387, + "narHash": "sha256-+VvWwBmxcgePV1L6kU2mSkg3emMiMgpdQnCqvQJkRPk=", "owner": "holochain", "repo": "launcher", - "rev": "f20844aa5041432547c55176d71e67b32e302f77", + "rev": "9d9cab5e6b57e1c278113921ff203e515c8bbd2e", "type": "github" }, "original": { @@ -733,11 +733,11 @@ ] }, "locked": { - "lastModified": 1717406456, - "narHash": "sha256-voWbxOZu1DM6hLOlOkLqSrZ8BsOY6F9h/tYB9C0jOXs=", + "lastModified": 1717899611, + "narHash": "sha256-9Z95F8lnY/5sOf7Z4IdABKz1ulB0ueNrZU864rQj280=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "711ff40be18b09bf096930f52e1fb2fcbee9adf0", + "rev": "1f536afad5c18ea4ae6bb592c3fef038e1e33568", "type": "github" }, "original": { @@ -757,11 +757,11 @@ ] }, "locked": { - "lastModified": 1717406456, - "narHash": "sha256-voWbxOZu1DM6hLOlOkLqSrZ8BsOY6F9h/tYB9C0jOXs=", + "lastModified": 1718331519, + "narHash": "sha256-6Ru37wS8uec626nHVIh6hSpCYB7eNc3RPFa2U//bhw4=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "711ff40be18b09bf096930f52e1fb2fcbee9adf0", + "rev": "419e7fae2731f41dd9b3e34dfe8802be68558b92", "type": "github" }, "original": { @@ -780,11 +780,11 @@ ] }, "locked": { - "lastModified": 1716949111, - "narHash": "sha256-ms3aD3Z2jKd1dk8qd0D/N7C8vFxn6z6LQ1G7cvNTVJ8=", + "lastModified": 1717899611, + "narHash": "sha256-9Z95F8lnY/5sOf7Z4IdABKz1ulB0ueNrZU864rQj280=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "2e7ccf572ce0f0547d4cf4426de4482936882d0e", + "rev": "1f536afad5c18ea4ae6bb592c3fef038e1e33568", "type": "github" }, "original": { @@ -802,11 +802,11 @@ ] }, "locked": { - "lastModified": 1717035469, - "narHash": "sha256-MzH+yjKULH3HCRj9QCTwBvqq4LZkR0ZqRE/QfGOGC2E=", + "lastModified": 1718331519, + "narHash": "sha256-6Ru37wS8uec626nHVIh6hSpCYB7eNc3RPFa2U//bhw4=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "095702e63a40e86f339d11864da9dc965b70a01e", + "rev": "419e7fae2731f41dd9b3e34dfe8802be68558b92", "type": "github" }, "original": { @@ -818,11 +818,11 @@ "scaffolding": { "flake": false, "locked": { - "lastModified": 1716370784, - "narHash": "sha256-iCsenLqNBpKDfGGZuXbxH0BYjgrG5Me8dvzan7VIQ5Q=", + "lastModified": 1717661456, + "narHash": "sha256-e+9YRRFJg89rfHDWtumEa33rpa2vmij/zw7Uwl6BP/g=", "owner": "holochain", "repo": "scaffolding", - "rev": "b4b1e0f8298551c44e99d3897eedd995b4b54314", + "rev": "1ffc9eb350b82784a8fb609073f1c7eccf2e0fc0", "type": "github" }, "original": { @@ -835,11 +835,11 @@ "scaffolding_2": { "flake": false, "locked": { - "lastModified": 1717435938, - "narHash": "sha256-cF4TNX5gz8AvBsdbWanwMaLV4gXuobfpUTN5f9PIFNY=", + "lastModified": 1717661456, + "narHash": "sha256-e+9YRRFJg89rfHDWtumEa33rpa2vmij/zw7Uwl6BP/g=", "owner": "holochain", "repo": "scaffolding", - "rev": "341d6a43fc357398a7361e58ad33df91f48177e6", + "rev": "1ffc9eb350b82784a8fb609073f1c7eccf2e0fc0", "type": "github" }, "original": { @@ -922,16 +922,16 @@ "versions": "versions" }, "locked": { - "lastModified": 1717441912, - "narHash": "sha256-InCQUqh3vS/qzxXfMnBc+hRGVScu3Ab7vCSJ3kCDDyU=", + "lastModified": 1718362187, + "narHash": "sha256-bWr+Mc59vFZ00Y3p0FjvbpZEaZFmt9UUw04yt9xmAkk=", "owner": "holochain", "repo": "tryorama", - "rev": "9457e503e4c87310a1315abc831ce2e74efdc7e3", + "rev": "0c983d068f8b1423c0e39b927bce63360b058826", "type": "github" }, "original": { "owner": "holochain", - "ref": "main", + "ref": "v0.16.0", "repo": "tryorama", "type": "github" } @@ -944,16 +944,16 @@ "scaffolding": "scaffolding" }, "locked": { - "dir": "versions/0_3_rc", - "lastModified": 1717008431, - "narHash": "sha256-cRPG2hDyFlxsT2bGlINJOjhZ4VXPBZ3lryXXXgKZ+rQ=", + "dir": "versions/0_3", + "lastModified": 1718285062, + "narHash": "sha256-FVXY8scfrt2QQ1v3iqAK8GTaMmSOClNFlI0/T+qZeTU=", "owner": "holochain", "repo": "holochain", - "rev": "54e08e370c674c87d2dcac3146cd0aff76df93aa", + "rev": "4be2cfa4aa5d92dcea35f76aff344af5127e07fb", "type": "github" }, "original": { - "dir": "versions/0_3_rc", + "dir": "versions/0_3", "owner": "holochain", "repo": "holochain", "type": "github" @@ -967,16 +967,16 @@ "scaffolding": "scaffolding_2" }, "locked": { - "dir": "versions/0_3_rc", - "lastModified": 1717439305, - "narHash": "sha256-Y/PmDKlzno6AnE/whmV5oRgZgYmBEkr4y83GYnZwahQ=", + "dir": "versions/0_3", + "lastModified": 1718285062, + "narHash": "sha256-FVXY8scfrt2QQ1v3iqAK8GTaMmSOClNFlI0/T+qZeTU=", "owner": "holochain", "repo": "holochain", - "rev": "4cf086772054b6f63bb4adf2d22671137506e012", + "rev": "4be2cfa4aa5d92dcea35f76aff344af5127e07fb", "type": "github" }, "original": { - "dir": "versions/0_3_rc", + "dir": "versions/0_3", "owner": "holochain", "repo": "holochain", "type": "github" diff --git a/flake.nix b/flake.nix index 171a2a9b..87b69e80 100644 --- a/flake.nix +++ b/flake.nix @@ -2,14 +2,14 @@ description = "Flake for Holochain testing"; inputs = { - versions.url = "github:holochain/holochain?dir=versions/0_3_rc"; + versions.url = "github:holochain/holochain?dir=versions/0_3"; holochain = { url = "github:holochain/holochain"; inputs.versions.follows = "versions"; }; - tryorama.url = "github:holochain/tryorama/main"; + tryorama.url = "github:holochain/tryorama/v0.16.0"; crane = { url = "github:ipetkov/crane"; diff --git a/framework/runner/src/context.rs b/framework/runner/src/context.rs index d65083d2..baa4952f 100644 --- a/framework/runner/src/context.rs +++ b/framework/runner/src/context.rs @@ -15,6 +15,7 @@ pub struct RunnerContext { executor: Arc, reporter: Arc, shutdown_handle: ShutdownHandle, + run_id: String, connection_string: String, value: RV, } @@ -24,12 +25,14 @@ impl RunnerContext { executor: Arc, reporter: Arc, shutdown_handle: ShutdownHandle, + run_id: String, connection_string: String, ) -> Self { Self { executor, reporter, shutdown_handle, + run_id, connection_string, value: Default::default(), } @@ -58,6 +61,11 @@ impl RunnerContext { self.shutdown_handle.new_listener() } + /// The unique identifier for the scenario run. + pub fn get_run_id(&self) -> &str { + &self.run_id + } + /// Connection string for the target service of the scenario, supplied by the user via the CLI. pub fn get_connection_string(&self) -> &str { &self.connection_string @@ -94,7 +102,7 @@ impl RunnerContext { #[derive(Debug)] pub struct AgentContext { agent_index: usize, - agent_id: String, + agent_name: String, runner_context: Arc>, shutdown_listener: DelegatedShutdownListener, value: V, @@ -103,13 +111,13 @@ pub struct AgentContext { impl AgentContext { pub(crate) fn new( agent_index: usize, - agent_id: String, + agent_name: String, runner_context: Arc>, shutdown_listener: DelegatedShutdownListener, ) -> Self { Self { agent_index, - agent_id, + agent_name, runner_context, shutdown_listener, value: Default::default(), @@ -121,11 +129,11 @@ impl AgentContext { self.agent_index } - /// A value generated by the runner that you can use to identify yourself making requests. + /// A value generated by the runner that you can use to identify yourself when making requests. /// - /// This value is unique within the runner but it is *not* unique across multiple runners. - pub fn agent_id(&self) -> &str { - &self.agent_id + /// This value is unique within the runner but *not* unique across multiple runners. + pub fn agent_name(&self) -> &str { + &self.agent_name } /// A handle to the runner context for the scenario. diff --git a/framework/runner/src/run.rs b/framework/runner/src/run.rs index c1dbaaeb..93de3b9c 100644 --- a/framework/runner/src/run.rs +++ b/framework/runner/src/run.rs @@ -34,7 +34,7 @@ pub fn run( let reporter = { let _h = runtime.handle().enter(); - let mut report_config = ReportConfig::new(run_id, definition.name.clone()); + let mut report_config = ReportConfig::new(run_id.clone(), definition.name.clone()); match definition.reporter { ReporterOpt::InMemory => { @@ -62,6 +62,7 @@ pub fn run( executor, reporter, shutdown_handle.clone(), + run_id, definition.connection_string.clone(), ); @@ -114,22 +115,37 @@ pub fn run( // For the behaviour implementation to listen for shutdown and respond appropriately let delegated_shutdown_listener = shutdown_handle.new_listener(); - let agent_id = format!("agent-{}", agent_index); + let agent_name = format!("agent-{}", agent_index); handles.push( std::thread::Builder::new() - .name(agent_id.clone()) + .name(agent_name.clone()) .spawn(move || { // TODO synchronize these setups so that the scenario waits for all of them to complete before proceeding. let mut context = AgentContext::new( agent_index, - agent_id.clone(), + agent_name.clone(), runner_context, delegated_shutdown_listener, ); if let Some(setup_agent_fn) = setup_agent_fn { if let Err(e) = setup_agent_fn(&mut context) { - log::error!("Agent setup failed for agent {}: {:?}", agent_id, e); + log::error!("Agent setup failed for agent {}: {:?}", agent_name, e); + + // Attempt to run the shutdown hook if the agent setup was cancelled. + if e.is::() { + log::info!("Agent setup was cancelled, running teardown."); + if let Some(teardown_agent_fn) = teardown_agent_fn { + if let Err(e) = teardown_agent_fn(&mut context) { + log::error!( + "Agent teardown failed for agent {}: {:?}", + agent_name, + e + ); + } + } + } + return; } } @@ -139,7 +155,7 @@ pub fn run( if let Some(behaviour) = agent_behaviour_fn { loop { if cycle_shutdown_receiver.should_shutdown() { - log::debug!("Stopping agent {}", agent_id); + log::debug!("Stopping agent {}", agent_name); break; } @@ -152,7 +168,7 @@ pub fn run( Err(e) if e.is::() => { // A single agent has failed, we don't want to stop the whole // scenario so warn and exit the loop. - log::warn!("Agent {} bailed: {:?}", agent_id, e); + log::warn!("Agent {} bailed: {:?}", agent_name, e); behaviour_ran_to_complete = false; break; } @@ -165,7 +181,7 @@ pub fn run( if let Some(teardown_agent_fn) = teardown_agent_fn { if let Err(e) = teardown_agent_fn(&mut context) { - log::error!("Agent teardown failed for agent {}: {:?}", agent_id, e); + log::error!("Agent teardown failed for agent {}: {:?}", agent_name, e); } } diff --git a/influx/templates/dashboards/remote_call.json b/influx/templates/dashboards/remote_call.json new file mode 100644 index 00000000..0ad72605 --- /dev/null +++ b/influx/templates/dashboards/remote_call.json @@ -0,0 +1 @@ +[{"apiVersion":"influxdata.com/v2alpha1","kind":"Dashboard","metadata":{"name":"pedantic-euler-4a2001"},"spec":{"charts":[{"axes":[{"base":"10","name":"x","scale":"linear"},{"base":"10","label":"Dispatch time (seconds)","name":"y","scale":"linear","suffix":"s"}],"colorizeRows":true,"colors":[{"id":"92M25TXU9t6-UDIvyh22d","name":"Color Blind Friendly - Light","type":"scale","hex":"#FFFFFF"},{"id":"D50f-D1Ka9bFFOZaHbKN_","name":"Color Blind Friendly - Light","type":"scale","hex":"#E69F00"},{"id":"AHCQfpXxS9po30FrR59tH","name":"Color Blind Friendly - Light","type":"scale","hex":"#56B4E9"},{"id":"cDRzxXim0OHqnHKdPwQeS","name":"Color Blind Friendly - Light","type":"scale","hex":"#009E73"},{"id":"7Sk0A9sYYYkkwqPnxnxKh","name":"Color Blind Friendly - Light","type":"scale","hex":"#F0E442"},{"id":"AYKVmPRrteI-66haJvb89","name":"Color Blind Friendly - Light","type":"scale","hex":"#0072B2"},{"id":"ZV6L-qVPZkUP2jiEvTofA","name":"Color Blind Friendly - Light","type":"scale","hex":"#D55E00"},{"id":"deHbQp4dKZOsw_auUQkNQ","name":"Color Blind Friendly - Light","type":"scale","hex":"#CC79A7"}],"geom":"line","height":4,"hoverDimension":"auto","kind":"Xy","legendColorizeRows":true,"legendOpacity":1,"legendOrientationThreshold":100000000,"name":"Remote call dispatch","opacity":1,"orientationThreshold":100000000,"position":"overlaid","queries":[{"query":"from(bucket: \"windtunnel\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"wt.custom.remote_call_dispatch\")\n |> filter(fn: (r) => r[\"scenario_name\"] == \"remote_call_rate\")\n |> filter(fn: (r) => r[\"run_id\"] == v.RunId)\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")"}],"staticLegend":{"colorizeRows":true,"opacity":1,"orientationThreshold":100000000,"widthRatio":1},"width":12,"widthRatio":1,"xCol":"_time","yCol":"_value"},{"axes":[{"base":"10","name":"x","scale":"linear"},{"base":"10","label":"count","name":"y","scale":"linear"}],"colorizeRows":true,"colors":[{"id":"92M25TXU9t6-UDIvyh22d","name":"Color Blind Friendly - Light","type":"scale","hex":"#FFFFFF"},{"id":"D50f-D1Ka9bFFOZaHbKN_","name":"Color Blind Friendly - Light","type":"scale","hex":"#E69F00"},{"id":"AHCQfpXxS9po30FrR59tH","name":"Color Blind Friendly - Light","type":"scale","hex":"#56B4E9"},{"id":"cDRzxXim0OHqnHKdPwQeS","name":"Color Blind Friendly - Light","type":"scale","hex":"#009E73"},{"id":"7Sk0A9sYYYkkwqPnxnxKh","name":"Color Blind Friendly - Light","type":"scale","hex":"#F0E442"},{"id":"AYKVmPRrteI-66haJvb89","name":"Color Blind Friendly - Light","type":"scale","hex":"#0072B2"},{"id":"ZV6L-qVPZkUP2jiEvTofA","name":"Color Blind Friendly - Light","type":"scale","hex":"#D55E00"},{"id":"deHbQp4dKZOsw_auUQkNQ","name":"Color Blind Friendly - Light","type":"scale","hex":"#CC79A7"}],"geom":"line","height":4,"hoverDimension":"auto","kind":"Xy","legendColorizeRows":true,"legendOpacity":1,"legendOrientationThreshold":100000000,"name":"Remote call throughput","opacity":1,"orientationThreshold":100000000,"position":"overlaid","queries":[{"query":"from(bucket: \"windtunnel\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"scenario_name\"] == \"remote_call_rate\")\n |> filter(fn: (r) => r[\"run_id\"] == v.RunId)\n |> filter(fn: (r) => r[\"_measurement\"] == \"wt.custom.remote_call_dispatch\")\n |> filter(fn: (r) => r[\"_field\"] == \"value\")\n |> aggregateWindow(every: v.windowPeriod, fn: count, createEmpty: false)\n |> yield(name: \"count\")"}],"staticLegend":{"colorizeRows":true,"opacity":1,"orientationThreshold":100000000,"widthRatio":1},"width":12,"widthRatio":1,"xCol":"_time","yCol":"_value","yPos":4},{"axes":[{"base":"10","name":"x","scale":"linear"},{"base":"10","label":"Round trip time (seconds)","name":"y","scale":"linear","suffix":"s"}],"colorizeRows":true,"colors":[{"id":"92M25TXU9t6-UDIvyh22d","name":"Color Blind Friendly - Light","type":"scale","hex":"#FFFFFF"},{"id":"D50f-D1Ka9bFFOZaHbKN_","name":"Color Blind Friendly - Light","type":"scale","hex":"#E69F00"},{"id":"AHCQfpXxS9po30FrR59tH","name":"Color Blind Friendly - Light","type":"scale","hex":"#56B4E9"},{"id":"cDRzxXim0OHqnHKdPwQeS","name":"Color Blind Friendly - Light","type":"scale","hex":"#009E73"},{"id":"7Sk0A9sYYYkkwqPnxnxKh","name":"Color Blind Friendly - Light","type":"scale","hex":"#F0E442"},{"id":"AYKVmPRrteI-66haJvb89","name":"Color Blind Friendly - Light","type":"scale","hex":"#0072B2"},{"id":"ZV6L-qVPZkUP2jiEvTofA","name":"Color Blind Friendly - Light","type":"scale","hex":"#D55E00"},{"id":"deHbQp4dKZOsw_auUQkNQ","name":"Color Blind Friendly - Light","type":"scale","hex":"#CC79A7"}],"geom":"line","height":4,"hoverDimension":"auto","kind":"Xy","legendColorizeRows":true,"legendOpacity":1,"legendOrientationThreshold":100000000,"name":"Remote call round trip (zome call)","opacity":1,"orientationThreshold":100000000,"position":"overlaid","queries":[{"query":"from(bucket: \"windtunnel\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"wt.instruments.operation_duration\")\n |> filter(fn: (r) => r[\"scenario_name\"] == \"remote_call_rate\")\n |> filter(fn: (r) => r[\"run_id\"] == v.RunId)\n |> filter(fn: (r) => r[\"operation_id\"] == \"trycp_app_call_zome\")\n |> filter(fn: (r) => r[\"zome_name\"] == \"remote_call\")\n |> filter(fn: (r) => r[\"fn_name\"] == \"call_echo_timestamp\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")"}],"staticLegend":{"colorizeRows":true,"opacity":1,"orientationThreshold":100000000,"widthRatio":1},"width":12,"widthRatio":1,"xCol":"_time","yCol":"_value","yPos":8},{"axes":[{"base":"10","name":"x","scale":"linear"},{"base":"10","label":"Dispatch time (seconds)","name":"y","scale":"linear","suffix":"s"}],"colorizeRows":true,"colors":[{"id":"92M25TXU9t6-UDIvyh22d","name":"Color Blind Friendly - Light","type":"scale","hex":"#FFFFFF"},{"id":"D50f-D1Ka9bFFOZaHbKN_","name":"Color Blind Friendly - Light","type":"scale","hex":"#E69F00"},{"id":"AHCQfpXxS9po30FrR59tH","name":"Color Blind Friendly - Light","type":"scale","hex":"#56B4E9"},{"id":"cDRzxXim0OHqnHKdPwQeS","name":"Color Blind Friendly - Light","type":"scale","hex":"#009E73"},{"id":"7Sk0A9sYYYkkwqPnxnxKh","name":"Color Blind Friendly - Light","type":"scale","hex":"#F0E442"},{"id":"AYKVmPRrteI-66haJvb89","name":"Color Blind Friendly - Light","type":"scale","hex":"#0072B2"},{"id":"ZV6L-qVPZkUP2jiEvTofA","name":"Color Blind Friendly - Light","type":"scale","hex":"#D55E00"},{"id":"deHbQp4dKZOsw_auUQkNQ","name":"Color Blind Friendly - Light","type":"scale","hex":"#CC79A7"}],"geom":"line","height":4,"hoverDimension":"auto","kind":"Xy","legendColorizeRows":true,"legendOpacity":1,"legendOrientationThreshold":100000000,"name":"Remote call round trip (real)","opacity":1,"orientationThreshold":100000000,"position":"overlaid","queries":[{"query":"from(bucket: \"windtunnel\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"wt.custom.remote_call_round_trip\")\n |> filter(fn: (r) => r[\"scenario_name\"] == \"remote_call_rate\")\n |> filter(fn: (r) => r[\"run_id\"] == v.RunId)\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")"}],"staticLegend":{"colorizeRows":true,"opacity":1,"orientationThreshold":100000000,"widthRatio":1},"width":12,"widthRatio":1,"xCol":"_time","yCol":"_value","yPos":12}],"description":"Custom charts for the remote_call_rate scenario","name":"Remote call"}}] \ No newline at end of file diff --git a/nix/modules/workspace.nix b/nix/modules/workspace.nix index 82d4079c..1567cb74 100644 --- a/nix/modules/workspace.nix +++ b/nix/modules/workspace.nix @@ -8,11 +8,18 @@ then pkgs.openssl # pkgsStatic is considered a cross build and this is not yet supported else pkgs.pkgsStatic.openssl; + nonCargoBuildFiles = path: _type: builtins.match ".*conductor-config.yaml$" path != null; + includeFilesFilter = path: type: + (craneLib.filterCargoSources path type) || (nonCargoBuildFiles path type); + commonArgs = { pname = "workspace"; version = "0.1.0"; - src = craneLib.cleanCargoSource (craneLib.path ./../..); + src = pkgs.lib.cleanSourceWith { + src = ./../..; + filter = includeFilesFilter; + }; strictDeps = true; cargoExtraArgs = "--locked --workspace"; diff --git a/scenarios/app_install/src/main.rs b/scenarios/app_install/src/main.rs index c3b19c01..74a94b60 100644 --- a/scenarios/app_install/src/main.rs +++ b/scenarios/app_install/src/main.rs @@ -1,6 +1,6 @@ use holochain_wind_tunnel_runner::prelude::*; use holochain_wind_tunnel_runner::scenario_happ_path; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::time::Instant; #[derive(Debug, Default)] diff --git a/scenarios/dht_sync_lag/src/main.rs b/scenarios/dht_sync_lag/src/main.rs index e99790a9..deda4c4d 100644 --- a/scenarios/dht_sync_lag/src/main.rs +++ b/scenarios/dht_sync_lag/src/main.rs @@ -3,7 +3,6 @@ use holochain_types::prelude::{ActionHash, Record, Timestamp}; use holochain_wind_tunnel_runner::prelude::*; use holochain_wind_tunnel_runner::scenario_happ_path; use std::collections::HashSet; -use std::path::Path; use std::time::SystemTime; use timed_integrity::TimedEntry; diff --git a/scenarios/first_call/src/main.rs b/scenarios/first_call/src/main.rs index b070b62a..f293e238 100644 --- a/scenarios/first_call/src/main.rs +++ b/scenarios/first_call/src/main.rs @@ -1,7 +1,6 @@ use holochain_types::prelude::ActionHash; use holochain_wind_tunnel_runner::prelude::*; use holochain_wind_tunnel_runner::scenario_happ_path; -use std::path::Path; #[derive(Debug, Default)] struct ScenarioValues { diff --git a/scenarios/local_signals/src/main.rs b/scenarios/local_signals/src/main.rs index b795efbf..ca485df2 100644 --- a/scenarios/local_signals/src/main.rs +++ b/scenarios/local_signals/src/main.rs @@ -1,6 +1,5 @@ use holochain_wind_tunnel_runner::prelude::*; use holochain_wind_tunnel_runner::scenario_happ_path; -use std::path::Path; use std::sync::atomic::AtomicU32; use std::sync::Arc; use std::time::Instant; diff --git a/scenarios/remote_call/README.md b/scenarios/remote_call/README.md deleted file mode 100644 index ead6e871..00000000 --- a/scenarios/remote_call/README.md +++ /dev/null @@ -1,15 +0,0 @@ -## remote_call - -### Description - -This scenario tests the throughput of `remote_call` operations. - -**warning** This is a TryCP-based scenario and needs to be run differently to other scenarios. - -### Suggested command - -You can run the scenario locally with the following command: - -```bash -RUST_LOG=info cargo run --package remote_call -- --targets targets.yaml --duration 300 -``` diff --git a/scenarios/remote_call/src/main.rs b/scenarios/remote_call/src/main.rs deleted file mode 100644 index 9ee3b9d7..00000000 --- a/scenarios/remote_call/src/main.rs +++ /dev/null @@ -1,47 +0,0 @@ -use trycp_wind_tunnel_runner::prelude::*; - -fn agent_setup(ctx: &mut AgentContext) -> HookResult { - connect_trycp_client(ctx)?; - - reset_trycp_remote(ctx)?; - - Ok(()) -} - -fn agent_behaviour(ctx: &mut AgentContext) -> HookResult { - let client = ctx.get().trycp_client(); - - ctx.runner_context() - .executor() - .execute_in_place(async move { - client - .configure_player("test".to_string(), "".to_string(), None) - .await?; - - client.reset(None).await?; - - Ok(()) - })?; - - Ok(()) -} - -fn agent_teardown(ctx: &mut AgentContext) -> HookResult { - disconnect_trycp_client(ctx)?; - Ok(()) -} - -fn main() -> WindTunnelResult<()> { - let builder = - TryCPScenarioDefinitionBuilder::::new_with_init( - env!("CARGO_PKG_NAME"), - )? - .into_std() - .use_agent_setup(agent_setup) - .use_agent_behaviour(agent_behaviour) - .use_agent_teardown(agent_teardown); - - run(builder)?; - - Ok(()) -} diff --git a/scenarios/remote_call/Cargo.toml b/scenarios/remote_call_rate/Cargo.toml similarity index 54% rename from scenarios/remote_call/Cargo.toml rename to scenarios/remote_call_rate/Cargo.toml index 68fb61c0..31bf1252 100644 --- a/scenarios/remote_call/Cargo.toml +++ b/scenarios/remote_call_rate/Cargo.toml @@ -1,13 +1,17 @@ [package] -name = "remote_call" +name = "remote_call_rate" version = "0.1.0" edition = "2021" build = "../scenario_build.rs" [dependencies] anyhow = { workspace = true } +tokio = { workspace = true } +rand = { workspace = true } + holochain_types = { workspace = true } trycp_wind_tunnel_runner = { workspace = true } +remote_call_integrity = { workspace = true} [build-dependencies] happ_builder = { workspace = true } @@ -15,4 +19,10 @@ happ_builder = { workspace = true } [lints] workspace = true -[package.metadata] +[package.metadata.required-dna] +name = "remote_call" +zomes = ["remote_call"] + +[package.metadata.required-happ] +name = "remote_call" +dnas = ["remote_call"] diff --git a/scenarios/remote_call_rate/README.md b/scenarios/remote_call_rate/README.md new file mode 100644 index 00000000..e653bda8 --- /dev/null +++ b/scenarios/remote_call_rate/README.md @@ -0,0 +1,30 @@ +## remote_call + +### Description + +This scenario tests the throughput of `remote_call` operations. + +**warning** This is a TryCP-based scenario and needs to be run differently to other scenarios. + +### Waiting for peer discovery + +This scenario reads the environment variable `MIN_PEERS` and waits for at least that many peers to be available before +starting the agent behaviour. It will wait up to two minutes then proceed regardless. + +The scenario is not able to check that you have configured more peers than the minimum you have set, so you should +ensure that you have configured enough peers to meet the minimum. + +Note that the number of peers seen by each node includes itself. So having two nodes means that each node will +immediately see one peer after app installation. + +This configuration defaults to 2 peers. + +### Suggested command + +You can run the scenario locally with the following command: + +```bash +RUST_LOG=info MIN_PEERS=2 cargo run --package remote_call_rate -- --targets targets.yaml --instances-per-target 2 --duration 300 +``` + +This assumes that `trycp_server` is running. See the script `scripts/trycp.sh` and run with `start_trycp`. diff --git a/scenarios/remote_call_rate/src/main.rs b/scenarios/remote_call_rate/src/main.rs new file mode 100644 index 00000000..8a697b68 --- /dev/null +++ b/scenarios/remote_call_rate/src/main.rs @@ -0,0 +1,153 @@ +use anyhow::Context; +use holochain_types::prelude::{AgentPubKey, ExternIO}; +use rand::seq::SliceRandom; +use rand::thread_rng; +use remote_call_integrity::TimedResponse; +use std::time::{Duration, Instant}; +use trycp_wind_tunnel_runner::prelude::*; + +const CONDUCTOR_CONFIG: &str = include_str!("../../../conductor-config.yaml"); + +#[derive(Debug, Default)] +pub struct ScenarioValues { + remote_call_peers: Vec, +} + +impl UserValuesConstraint for ScenarioValues {} + +fn agent_setup( + ctx: &mut AgentContext>, +) -> HookResult { + connect_trycp_client(ctx)?; + reset_trycp_remote(ctx)?; + + let client = ctx.get().trycp_client(); + let agent_name = ctx.agent_name().to_string(); + + ctx.runner_context() + .executor() + .execute_in_place(async move { + client + .configure_player(agent_name.clone(), CONDUCTOR_CONFIG.to_string(), None) + .await?; + + client + .startup(agent_name.clone(), Some("warn".to_string()), None) + .await?; + + Ok(()) + })?; + + install_app( + ctx, + scenario_happ_path!("remote_call"), + &"remote_call".to_string(), + )?; + try_wait_for_min_peers(ctx, Duration::from_secs(120))?; + + Ok(()) +} + +fn agent_behaviour( + ctx: &mut AgentContext>, +) -> HookResult { + let client = ctx.get().trycp_client(); + + let agent_name = ctx.agent_name().to_string(); + let app_port = ctx.get().app_port(); + let cell_id = ctx.get().cell_id(); + let next_remote_call_peer = ctx.get_mut().scenario_values.remote_call_peers.pop(); + let reporter = ctx.runner_context().reporter(); + + let new_peers = ctx + .runner_context() + .executor() + .execute_in_place(async move { + match next_remote_call_peer { + None => { + // No more agents available to call, get a new list. + // This is also the initial condition. + let mut new_peer_list = client + .agent_info(agent_name, None, None) + .await? + .into_iter() + .map(|info| AgentPubKey::from_raw_36(info.agent.0.clone())) + .filter(|k| k != cell_id.agent_pubkey()) // Don't call ourselves! + .collect::>(); + new_peer_list.shuffle(&mut thread_rng()); + Ok(new_peer_list) + } + Some(agent_pub_key) => { + // Send a remote call to this agent + let start = Instant::now(); + let response = client + .call_zome( + app_port, + cell_id, + "remote_call", + "call_echo_timestamp", + ExternIO::encode(agent_pub_key).context("Encoding failure")?, + None, + ) + .await?; + let round_trip_time_s = start.elapsed(); + + let response: TimedResponse = response + .decode() + .map_err(|e| anyhow::anyhow!("Decoding failure: {:?}", e))?; + + let dispatch_time_s = response.request_value.as_micros() as f64 / 1_000_000.0; + let receive_time_s = response.value.as_micros() as f64 / 1_000_000.0; + + reporter.add_custom( + ReportMetric::new("remote_call_dispatch") + .with_field("value", receive_time_s - dispatch_time_s), + ); + reporter.add_custom( + ReportMetric::new("remote_call_round_trip") + .with_field("value", round_trip_time_s.as_secs_f64()), + ); + + // Add no new agents, that should only happen when we exhaust the list. + Ok(Vec::with_capacity(0)) + } + } + })?; + + ctx.get_mut() + .scenario_values + .remote_call_peers + .extend(new_peers); + + Ok(()) +} + +fn agent_teardown( + ctx: &mut AgentContext>, +) -> HookResult { + // Best effort to remove data and cleanup. + // You should comment out this line if you want to examine the result of the scenario run! + let _ = reset_trycp_remote(ctx); + + // Alternatively, you can just shut down the remote conductor instead of shutting it down and removing data. + // shutdown_remote(ctx)?; + + disconnect_trycp_client(ctx)?; + + Ok(()) +} + +fn main() -> WindTunnelResult<()> { + let builder = TryCPScenarioDefinitionBuilder::< + TryCPRunnerContext, + TryCPAgentContext, + >::new_with_init(env!("CARGO_PKG_NAME"))? + .into_std() + .use_agent_setup(agent_setup) + .use_agent_behaviour(agent_behaviour) + .use_agent_teardown(agent_teardown); + + run(builder)?; + + Ok(()) +} diff --git a/scenarios/single_write_many_read/src/main.rs b/scenarios/single_write_many_read/src/main.rs index b4641ce8..51d0a246 100644 --- a/scenarios/single_write_many_read/src/main.rs +++ b/scenarios/single_write_many_read/src/main.rs @@ -1,7 +1,6 @@ use holochain_types::prelude::{ActionHash, Record}; use holochain_wind_tunnel_runner::prelude::*; use holochain_wind_tunnel_runner::scenario_happ_path; -use std::path::Path; #[derive(Debug, Default)] struct ScenarioValues { diff --git a/scenarios/write_query/src/main.rs b/scenarios/write_query/src/main.rs index d67c95d4..d785f18f 100644 --- a/scenarios/write_query/src/main.rs +++ b/scenarios/write_query/src/main.rs @@ -1,7 +1,6 @@ use holochain_types::prelude::ActionHash; use holochain_wind_tunnel_runner::prelude::*; use holochain_wind_tunnel_runner::scenario_happ_path; -use std::path::Path; #[derive(Debug, Default)] struct ScenarioValues { diff --git a/scenarios/write_read/src/main.rs b/scenarios/write_read/src/main.rs index b1a2558c..7d39fb86 100644 --- a/scenarios/write_read/src/main.rs +++ b/scenarios/write_read/src/main.rs @@ -1,7 +1,6 @@ use holochain_types::prelude::{ActionHash, Record}; use holochain_wind_tunnel_runner::prelude::*; use holochain_wind_tunnel_runner::scenario_happ_path; -use std::path::Path; fn setup(ctx: &mut RunnerContext) -> HookResult { configure_app_ws_url(ctx)?; diff --git a/scenarios/zome_call_single_value/src/main.rs b/scenarios/zome_call_single_value/src/main.rs index b62d379a..ec99ac14 100644 --- a/scenarios/zome_call_single_value/src/main.rs +++ b/scenarios/zome_call_single_value/src/main.rs @@ -1,6 +1,5 @@ use holochain_wind_tunnel_runner::prelude::*; use holochain_wind_tunnel_runner::scenario_happ_path; -use std::path::Path; fn setup(ctx: &mut RunnerContext) -> HookResult { configure_app_ws_url(ctx)?; diff --git a/targets.yaml b/targets.yaml index d20dbf59..d7b394f7 100644 --- a/targets.yaml +++ b/targets.yaml @@ -1,4 +1,3 @@ description: Target nodes for tests to connect to, each one must be running TryCP server nodes: - ws://localhost:9000 - - ws://192.168.1.80:9000 diff --git a/zomes/remote_call/coordinator/Cargo.toml b/zomes/remote_call/coordinator/Cargo.toml new file mode 100644 index 00000000..9a84812f --- /dev/null +++ b/zomes/remote_call/coordinator/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "remote_call" +version = "0.1.0" +edition = "2021" +description = "A Holochain coordinator zome for remote calls" + +[lib] +name = "remote_call_coordinator" +path = "src/lib.rs" +crate-type = ["cdylib", "rlib"] + +[dependencies] +hdk = { workspace = true } +remote_call_integrity = { workspace = true } + +[lints] +workspace = true diff --git a/zomes/remote_call/coordinator/src/lib.rs b/zomes/remote_call/coordinator/src/lib.rs new file mode 100644 index 00000000..2b7a10c9 --- /dev/null +++ b/zomes/remote_call/coordinator/src/lib.rs @@ -0,0 +1,40 @@ +use hdk::prelude::*; +use remote_call_integrity::*; + +#[hdk_extern] +fn init() -> ExternResult { + create_cap_grant(CapGrantEntry { + tag: "unrestricted-access".into(), + access: CapAccess::Unrestricted, + functions: GrantedFunctions::Listed(BTreeSet::from([( + zome_info()?.name, + "echo_timestamp".into(), + )])), + })?; + + Ok(InitCallbackResult::Pass) +} + +#[hdk_extern] +fn call_echo_timestamp(to: AgentPubKey) -> ExternResult { + let response = call_remote( + to, + zome_info()?.name, + "echo_timestamp".into(), + None, + TimedRequest { value: sys_time()? }, + )?; + + match response { + ZomeCallResponse::Ok(extern_io) => Ok(extern_io.decode().map_err(|e| wasm_error!(e))?), + e => Err(wasm_error!(WasmErrorInner::Guest(format!("{:?}", e)))), + } +} + +#[hdk_extern] +fn echo_timestamp(request: TimedRequest) -> ExternResult { + Ok(TimedResponse { + request_value: request.value, + value: sys_time()?, + }) +} diff --git a/zomes/remote_call/integrity/Cargo.toml b/zomes/remote_call/integrity/Cargo.toml new file mode 100644 index 00000000..8b97fb01 --- /dev/null +++ b/zomes/remote_call/integrity/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "remote_call_integrity" +version = "0.1.0" +edition = "2021" +description = "A Holochain integrity zome for remote calls" + +[lib] +name = "remote_call_integrity" +path = "src/lib.rs" +crate-type = ["cdylib", "rlib"] + +[dependencies] +hdi = { workspace = true } +serde = { workspace = true } + +[lints] +workspace = true diff --git a/zomes/remote_call/integrity/src/lib.rs b/zomes/remote_call/integrity/src/lib.rs new file mode 100644 index 00000000..9dfb83cf --- /dev/null +++ b/zomes/remote_call/integrity/src/lib.rs @@ -0,0 +1,12 @@ +use hdi::prelude::*; + +#[derive(Serialize, Deserialize, SerializedBytes, Debug)] +pub struct TimedRequest { + pub value: Timestamp, +} + +#[derive(Serialize, Deserialize, SerializedBytes, Debug)] +pub struct TimedResponse { + pub request_value: Timestamp, + pub value: Timestamp, +}