From 4f1547a61bc90db8723243f2aa4ed51962255f2b Mon Sep 17 00:00:00 2001 From: Julian Lobbes Date: Sat, 3 Sep 2022 00:36:30 +0200 Subject: [PATCH] feat: implement update script --- dnsupdate.py | 90 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 4 deletions(-) mode change 100644 => 100755 dnsupdate.py diff --git a/dnsupdate.py b/dnsupdate.py old mode 100644 new mode 100755 index 7227cd7..b5b76e9 --- a/dnsupdate.py +++ b/dnsupdate.py @@ -11,16 +11,98 @@ except ImportError: from yaml import Loader +_API_ENDPOINT_URL = "https://api.hosting.ionos.com/dns/v1/dyndns" + + +class InvalidConfigurationError(RuntimeError): + """Raised when the configuration file's contents are invalid.""" + + def main(): path_to_config_file = Path("config.yml").resolve() - # TODO Check if config file exists + # Check if config file exists + if not path_to_config_file.is_file(): + raise FileNotFoundError( + f"No such file: '{path_to_config_file}'." + ) - # TODO parse config file + # Load config file + with open(path_to_config_file, 'r') as config_file: + config = load(config_file, Loader=Loader) - # TODO retrieve DNS update URL + # Validate config file + for top_level_key in ["apikey", "domains"]: + if not top_level_key in config: + raise InvalidConfigurationError( + f"Failed to locate key '{top_level_key}' in config." + ) + # Validate 'apikey' values + for key in ["prefix", "secret"]: + if not key in config["apikey"]: + raise InvalidConfigurationError( + f"Expected key 'apikey.{key}' in config:" + ) + if not isinstance(config["apikey"][key], str): + raise InvalidConfigurationError( + f"Expected type string as the value for 'apikey.{key}'." + ) + # Validate 'domains' values + if not isinstance(config["domains"], list): + raise InvalidConfigurationError( + "Expected type list as the value for 'domains'." + ) + for value in config["domains"]: + if not isinstance(value, str): + raise InvalidConfigurationError( + f"Expected only strings in the 'domains' list." + ) + # Validate 'description' value + if "description" in config: + if not isinstance(config["description"], str): + raise InvalidConfigurationError( + "Expected type string as value for 'description'." + ) - # TODO send update + # Parse config + apikey_prefix = config["apikey"]["prefix"] + apikey_secret = config["apikey"]["secret"] + apikey = f"{apikey_prefix}.{apikey_secret}" + domains = config["domains"] + description = config["description"] if "description" in config else "Dynamic DNS update." + + # Make an API call to retrieve the DNS update URL + response = requests.post( + _API_ENDPOINT_URL, + headers={"X-API-Key": apikey}, + data={ + "domains": domains, + "description": description, + } + ) + + if not response.status_code == 200: + try: + error_message = f"{response.status_code}: {response.json()}" + except requests.exceptions.JSONDecodeError: + error_message = f"{response.status_code}" + raise RuntimeError( + f"API request failed and returned: {error_message}" + ) + + update_url = response.json()["updateUrl"] + print(update_url) # DEBUG + + # Send a dynamic DNS update using the retrieved update URL + response = requests.get(update_url) + if not response.status_code == 200: + try: + error_message = f"{response.status_code}: {response.json()}" + except requests.exceptions.JSONDecodeError: + error_message = f"{response.status_code}" + raise RuntimeError( + f"DNS update request failed and returned: {error_message}" + ) if __name__ == "__main__":