From 3c9227080eb22f119a8c848aa14422f6d02f763a Mon Sep 17 00:00:00 2001
From: Yaroslav Dynnikov <yaroslav.dynnikov@gmail.com>
Date: Mon, 17 Oct 2022 11:10:35 +0300
Subject: [PATCH] feature: new lua api: whoami, peer_info

Example:

```console
tarantool> picolib.whoami()
---
- raft_id: 1
  cluster_id: demo
  instance_id: i1
...

tarantool> picolib.peer_info("i1")
---
- raft_id: 1
  instance_id: i1
  instance_uuid: 68d4a766-4144-3248-aeb4-e212356716e4
  replicaset_uuid: e0df68c5-e7f9-395f-86b3-30ad9e1b7b07
  replicaset_id: r1
  advertise_address: localhost:3301
  target_grade: Online
  current_grade: Online
...
```
---
 src/main.rs                    | 33 +++++++++++++++++++++++++
 test/int/test_basics.py        | 23 ++++++++++++++++++
 test/int/test_uninitialized.py | 44 ++++++++++++++++++++++++++++++++++
 3 files changed, 100 insertions(+)
 create mode 100644 test/int/test_uninitialized.py

diff --git a/src/main.rs b/src/main.rs
index 969ed8ce29..b6b7bfcd58 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -56,6 +56,39 @@ fn picolib_setup(args: &args::Run) {
     luamod.set("VERSION", env!("CARGO_PKG_VERSION"));
     luamod.set("args", args);
 
+    luamod.set(
+        "whoami",
+        tlua::function0(|| -> Result<_, Error> {
+            let node = traft::node::global()?;
+            let raft_storage = &node.storage.raft;
+
+            Ok(tlua::AsTable((
+                ("raft_id", raft_storage.raft_id()?),
+                ("cluster_id", raft_storage.cluster_id()?),
+                ("instance_id", raft_storage.instance_id()?),
+            )))
+        }),
+    );
+
+    luamod.set(
+        "peer_info",
+        tlua::function1(|iid: String| -> Result<_, Error> {
+            let node = traft::node::global()?;
+            let peer = node.storage.peers.get(&InstanceId::from(iid))?;
+
+            Ok(tlua::AsTable((
+                ("raft_id", peer.raft_id),
+                ("advertise_address", peer.peer_address),
+                ("instance_id", peer.instance_id.0),
+                ("instance_uuid", peer.instance_uuid),
+                ("replicaset_id", peer.replicaset_id),
+                ("replicaset_uuid", peer.replicaset_uuid),
+                ("current_grade", peer.current_grade),
+                ("target_grade", peer.target_grade),
+            )))
+        }),
+    );
+
     luamod.set(
         "raft_status",
         tlua::function0(|| traft::node::global().map(|n| n.status())),
diff --git a/test/int/test_basics.py b/test/int/test_basics.py
index 57c8f48fb3..0e199994e6 100644
--- a/test/int/test_basics.py
+++ b/test/int/test_basics.py
@@ -173,3 +173,26 @@ def test_graceful_stop(instance: Instance):
     )
     with open(os.path.join(instance.data_dir, last_xlog), "rb") as f:
         assert f.read()[-4:] == b"\xd5\x10\xad\xed"
+
+
+def test_whoami(instance: Instance):
+    assert instance.call("picolib.whoami") == {
+        "raft_id": 1,
+        "instance_id": "i1",
+        "cluster_id": instance.cluster_id,
+    }
+
+
+def test_peer_info(instance: Instance):
+    def peer_info(iid: str):
+        return instance.call("picolib.peer_info", iid)
+
+    # Don't compare entire structure, a couple of fields is enough
+    myself = peer_info("i1")
+    assert myself["raft_id"] == 1
+    assert myself["instance_id"] == "i1"
+    assert myself["replicaset_id"] == "r1"
+
+    with pytest.raises(ReturnError) as e:
+        peer_info("i2")
+    assert e.value.args == ('peer with id "i2" not found',)
diff --git a/test/int/test_uninitialized.py b/test/int/test_uninitialized.py
new file mode 100644
index 0000000000..9e16a994aa
--- /dev/null
+++ b/test/int/test_uninitialized.py
@@ -0,0 +1,44 @@
+import funcy  # type: ignore
+import pytest
+
+from typing import Any, Callable, Generator
+
+from conftest import (
+    eprint,
+    Cluster,
+    Instance,
+    ReturnError,
+)
+
+
+@pytest.fixture
+def uninitialized_instance(cluster: Cluster) -> Generator[Instance, None, None]:
+    """Returns a running instance that is stuck in discovery phase."""
+
+    # Connecting TCP/0 always results in "Connection refused"
+    instance = cluster.add_instance(peers=[":0"], wait_online=False)
+    instance.start()
+
+    @funcy.retry(tries=30, timeout=0.2)
+    def wait_running():
+        assert instance.eval("return box.info.status") == "running"
+        eprint(f"{instance} is running (but stuck in discovery phase)")
+
+    wait_running()
+    yield instance
+
+
+def test_raft_api(uninitialized_instance: Instance):
+    functions: list[Callable[[Instance], Any]] = [
+        lambda i: i._raft_status(),
+        lambda i: i.call("picolib.raft_propose_nop"),
+        lambda i: i.call("picolib.raft_propose_info", "who cares"),
+        lambda i: i.call("picolib.whoami"),
+        lambda i: i.call("picolib.peer_info", "i1"),
+        lambda i: i.call("picolib.peer_info", "i2"),
+    ]
+
+    for f in functions:
+        with pytest.raises(ReturnError) as e:
+            f(uninitialized_instance)
+        assert e.value.args == ("uninitialized yet",)
-- 
GitLab