Skip to content
This repository has been archived by the owner on Sep 26, 2022. It is now read-only.

Commit

Permalink
plugin - Adds ping command to plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
sr-gi committed May 19, 2021
1 parent ada366d commit 78e5eac
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 2 deletions.
1 change: 1 addition & 0 deletions watchtower-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ commitment transaction is generated. It also keeps a summary of the messages sen

The plugin has the following methods:

- `pingtower ip[:port]`: pings a tower to check if it's online.
- `registertower tower_id` : registers the user id (compressed public key) with a given tower.
- `list_towers`: lists all registered towers.
- `gettowerinfo tower_id`: gets all the locally stored data about a given tower.
Expand Down
36 changes: 35 additions & 1 deletion watchtower-plugin/arg_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,48 @@
from common.exceptions import InvalidParameter


def parse_ping_arguments(host, port, config):
"""
Parses the arguments of the ping command and checks that they are correct.
Args:
host (:obj:`str`): the ip or hostname to connect to.
port (:obj:`int`): the port to connect to, optional.
config: (:obj:`dict`): the configuration dictionary.
Returns:
:obj:`tuple`: the network address.
Raises:
:obj:`common.exceptions.InvalidParameter`: if any of the parameters is wrong or missing.
"""

if not isinstance(host, str):
raise InvalidParameter(f"host must be string not {str(host)}")

# host and port specified
if host and port:
tower_netaddr = f"{host}:{port}"
else:
# host was specified, but no port, defaulting
if ":" not in host:
tower_netaddr = f"{host}:{config.get('DEFAULT_PORT')}"
elif host.endswith(":"):
tower_netaddr = f"{host}{config.get('DEFAULT_PORT')}"
else:
tower_netaddr = host

return tower_netaddr


def parse_register_arguments(tower_id, host, port, config):
"""
Parses the arguments of the register command and checks that they are correct.
Args:
tower_id (:obj:`str`): the identifier of the tower to connect to (a compressed public key).
host (:obj:`str`): the ip or hostname to connect to, optional.
host (:obj:`int`): the port to connect to, optional.
port (:obj:`int`): the port to connect to, optional.
config: (:obj:`dict`): the configuration dictionary.
Returns:
Expand Down
32 changes: 32 additions & 0 deletions watchtower-plugin/net/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,38 @@ def send_appointment(tower_id, tower, appointment_dict, signature):
return response


def get_request(endpoint):
"""
Sends a get request to the tower.
Args:
endpoint (:obj:`str`): the endpoint to send the post request.
Returns:
:obj:`Response`: a Response object with the obtained data.
Raises:
:obj:`ConnectionError`: if the client cannot connect to the tower.
:obj:`TowerResponseError`: if the returned status code is not a 200-family code.
"""

try:
response = requests.get(url=endpoint)

if response.ok:
return response
else:
raise TowerResponseError(
"The server returned an error", status_code=response.status_code, reason=response.reason, data=response,
)

except ConnectionError:
raise TowerConnectionError(f"Cannot connect to {endpoint}. Tower cannot be reached")

except (InvalidSchema, MissingSchema, InvalidURL):
raise TowerConnectionError(f"Invalid URL. No schema, or invalid schema, found (url={endpoint})")


def post_request(data, endpoint, tower_id):
"""
Sends a post request to the tower.
Expand Down
51 changes: 51 additions & 0 deletions watchtower-plugin/test_watchtower.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def __init__(self, tower_sk):

# Adds all the routes to the functions listed above.
routes = {
"/ping": (self.ping, ["GET"]),
"/register": (self.register, ["POST"]),
"/add_appointment": (self.add_appointment, ["POST"]),
"/get_appointment": (self.get_appointment, ["POST"]),
Expand All @@ -49,6 +50,14 @@ def __init__(self, tower_sk):
logging.getLogger("werkzeug").setLevel(logging.ERROR)
os.environ["WERKZEUG_RUN_MAIN"] = "true"

def ping(self):
if mocked_return == "online":
return ping_online()
elif mocked_return == "offline":
return ping_offline()
elif mocked_return == "error":
return ping_error()

def register(self):
user_id = request.get_json().get("public_key")

Expand Down Expand Up @@ -127,6 +136,18 @@ def add_appointment_success(appointment, signature, user, tower_sk):
return response, rcode


def ping_online():
return "", constants.HTTP_EMPTY


def ping_offline():
raise ConnectionError()


def ping_error():
return "", constants.HTTP_NOT_FOUND


def add_appointment_reject_no_slots():
# This covers non-registered users and users with no available slots

Expand Down Expand Up @@ -215,6 +236,36 @@ def test_helpme_starts(node_factory):
l1.start()


def test_watchtower_ping_offline(node_factory):
global mocked_return

mocked_return = "offline"

l1 = node_factory.get_node(options={"plugin": plugin_path})
r = l1.rpc.pingtower(tower_netaddr, tower_port)
assert r.get("alive") is False


def test_watchtower_ping_online(node_factory):
global mocked_return

mocked_return = "online"

l1 = node_factory.get_node(options={"plugin": plugin_path})
r = l1.rpc.pingtower(tower_netaddr, tower_port)
assert r.get("alive") is True


def test_watchtower_ping_error(node_factory):
global mocked_return

mocked_return = "error"

l1 = node_factory.get_node(options={"plugin": plugin_path})
r = l1.rpc.pingtower(tower_netaddr, tower_port)
assert r.get("alive") is False


def test_watchtower(node_factory):
""" Tests sending data to a single tower with short connection issue"""

Expand Down
36 changes: 35 additions & 1 deletion watchtower-plugin/watchtower.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from tower_info import TowerInfo
from towers_dbm import TowersDBM
from keys import generate_keys, load_keys
from net.http import post_request, process_post_response, add_appointment
from net.http import get_request, post_request, process_post_response, add_appointment


DATA_DIR = os.getenv("TOWERS_DATA_DIR", os.path.expanduser("~/.watchtower/"))
Expand Down Expand Up @@ -178,6 +178,40 @@ def init(options, configuration, plugin):
raise IOError(error)


@plugin.method("pingtower", desc="Pings a tower to check if its online")
def ping(plugin, host, port=None):
"""
Pings a tower to check if its online
Args:
plugin (:obj:`Plugin`): this plugin.
host (:obj:`str`): the ip or hostname to connect to.
port (:obj:`int`): the port to connect to, optional.
"""

response = {"alive": True}

try:
tower_netaddr = arg_parser.parse_ping_arguments(host, port, plugin.wt_client.config)

# Defaulting to http hosts for now
if not tower_netaddr.startswith("http"):
tower_netaddr = "http://" + tower_netaddr

# Send request to the server.
ping_endpoint = f"{tower_netaddr}/ping"
plugin.log(f"Pinging the Eye of Satoshi at {tower_netaddr}")
get_request(ping_endpoint)
plugin.log(f"The Eye of Satoshi is alive")

except (InvalidParameter, TowerConnectionError, TowerResponseError) as e:
plugin.log(str(e), level="warn")
response["alive"] = False
response["reason"] = str(e)

return response


@plugin.method("registertower", desc="Register your public key (user id) with the tower.")
def register(plugin, tower_id, host=None, port=None):
"""
Expand Down

0 comments on commit 78e5eac

Please sign in to comment.