diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml deleted file mode 100644 index ee1e3d7..0000000 --- a/.github/workflows/publish.yaml +++ /dev/null @@ -1,26 +0,0 @@ -name: Upload Python Package - -on: - release: - types: [published] - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install build - - name: Build package - run: python setup.py sdist - - name: Publish package - uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 - with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml deleted file mode 100644 index b36aa48..0000000 --- a/.github/workflows/test.yaml +++ /dev/null @@ -1,23 +0,0 @@ -name: Run tests - -on: - pull_request: - -jobs: - test: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] - - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - pip install -r requirements/base.txt - pip install tox tox-gh-actions - - name: Test with tox - run: tox diff --git a/.gitignore b/.gitignore deleted file mode 100644 index ffd4289..0000000 --- a/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -# python -__pycache__ -*.pyc -*.egg-info -.eggs -/dist -/pip-selfcheck.json - -# virtualenv -/.venv -/.virtualenv -/include -/lib -/lib64 -/share -/venv - -# misc -.idea -.tox diff --git a/CODEOWNERS b/CODEOWNERS deleted file mode 100644 index 1b08999..0000000 --- a/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -* @vdloo @alejandrogarza @tdgroot @martijneichelsheim diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 30b5e15..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Hypernode - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index 48bd049..71f99d5 100644 --- a/README.md +++ b/README.md @@ -1,113 +1,44 @@ -# Hypernode API Python Client +# Hypernode Backup Script -_**Please note: this project is still in its early stages and the API may be subject to change.**_ +## Overview +This backup script is designed specifically for use on the Hypernode platform. It facilitates secure backup of files and MySQL databases and can be configured to store backups locally and/or send to a remote server via SSH. -## Installation - -```bash -git clone https://github.com/byteinternet/hypernode-api-python.git -cd hypernode-api-python -python3 -m venv venv -. venv/bin/activate -pip install -r requirements/development.txt -``` - -## Usage - -### Acquiring an API token - -Each Hypernode has an API token associated with it, you can use that to talk to the API directly. You can find the token in `/etc/hypernode/hypernode_api_token`. For API tokens with special permissions please contact support@hypernode.com. Not all functionality in the API is currently generally available but if you'd like to start automating and have an interesting use-case we'd love to hear from you. +## Features +- Automated backup of files and databases. +- Support for daily, weekly, and monthly backup schedules. +- Cleanup functionality to remove old backups based on retention policy. +- Option to disable local backup storage and SSH/FTP transfer. +- Detailed logging of all backup activities. -## Using the library from the commandline +## Configuration +Customize the `backup_config.json` file to suit your backup needs. This is where you can set: +- SSH and MySQL connection details. +- Backup paths and schedules. +- Retention policy for backups. -In the `bin/` directory you'll find entry points to interact with the API directly from the commandline. - -See for example: -```bash -$ export PYTHONPATH=. - -$ ./bin/get_slas --help -usage: get_slas [-h] - -List all available SLAs. - -Example: -$ ./bin/get_slas -[ - { - "id": 123, - "code": "sla-standard", - "name": "SLA Standard", - "price": 1234, - "billing_period": 1, - "billing_period_unit": "month" - }, - ... -] - -optional arguments: - -h, --help show this help message and exit -``` +## Installation +1. Clone the repository to your Hypernode server. +2. Install any required external commands (`tar`, `rsync`, `mysqldump`). +3. Configure your backup preferences in `backup_config.json`. +4. Set up a cron job to run the script according to your schedule. -### Installing the library in your project +## Usage +The script is intended to run as a cron job but can be started manually with: -First make sure your project has the library installed: ```bash -pip install -e git+https://github.com/byteinternet/hypernode-api-python.git@master#egg=hypernode_api_python -``` -Of course you might want to put that in a `requirements.txt` file in your project instead of installing it manually. - -Alternatively, you can also install the [hypernode-api-python library from PyPI](https://pypi.org/project/hypernode-api-python/): -``` -$ python3 -m venv venv -$ . venv/bin/activate -$ pip install hypernode-api-python -$ pip freeze | grep hypernode-api-python -hypernode-api-python==0.0.6 -``` - -### Performing API calls - -Then to use the API client you can test out an example request in your Python repl: -```python -from hypernode_api_python.client import HypernodeAPIPython - -client = HypernodeAPIPython(token='yoursecrettoken') - -response = client.get_app_flavor('yourhypernodeappname') - -response.json() -{'name': '2CPU/8GB/60GB (Falcon S 202202)', 'redis_size': '1024'} -``` - -Using the Hypernode-API you can automate all kinds of cool things like configuring settings: -```python -client.set_app_setting('yourhypernodeappname', 'php_version', '8.1').json() -{'name': 'yourhypernodeappname', 'type': 'persistent', 'php_version': '8.1', ...} -``` - -You can even perform acts of cloud automation, like scaling to the first next larger plan: -```python -client.xgrade( - 'yourhypernodeappname', - data={ - 'product': client.get_next_best_plan_for_app_or_404( - 'yourhypernodeappname' - ).json()['code'] - } -) +python3 backup_script.py ``` +## Cron Job Setup +To schedule the backup script to run automatically, add the following line to your crontab: +Make sure to adjust the paths to match the location of your script and working directory. -## Development - -To run the unit tests you can use `tox`: -```bash -tox -r +```cron +0 * * * * sleep 7; cd /data/web/backup/ && /usr/bin/python3 /data/web/backup/backup_script.py # noflock ``` -## Related projects +## Disclaimer +This script is not officially supported by the Hypernode support team. Users should implement this script at their own risk and are responsible for properly configuring and securing their backup processes. -- The official [Hypernode API PHP Client](https://github.com/byteinternet/hypernode-api-php) -- The official [Hypernode Deploy](https://github.com/byteinternet/hypernode-deploy-configuration) tool -- The official [Hypernode Docker](https://github.com/byteinternet/hypernode-docker) image +## License +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. \ No newline at end of file diff --git a/backup_config.json b/backup_config.json new file mode 100644 index 0000000..50384de --- /dev/null +++ b/backup_config.json @@ -0,0 +1,37 @@ +{ + "ssh": { + "host": "example.com", + "port": 22, + "user": "username", + "location": "/path/on/remote/server" + }, + "local": { + "source_folder": "/path/to/source/folder", + "backup_folder": "/path/to/store/backups" + }, + "mysql": { + "host": "db.example.com", + "user": "dbuser", + "password": "dbpassword", + "database": "dbname" + }, + "schedule": { + "daily": [3, 8, 13, 22], + "weekly": { + "weekday": 4, + "hour": 17 + }, + "monthly": { + "weekday": 0, + "day_range": [1, 7], + "hour": 17 + } + }, + "retention": { + "daily": 4, + "weekly": 30, + "monthly": 3 + }, + "disable_local_save": false, + "disable_ssh_ftp": false +} diff --git a/backup_script.py b/backup_script.py new file mode 100644 index 0000000..2ee7a25 --- /dev/null +++ b/backup_script.py @@ -0,0 +1,132 @@ +import os +import subprocess +import datetime +import json +import logging +import glob + +# Load configuration from file +with open('backup_config.json', 'r') as config_file: + config = json.load(config_file) + +# Ensure backup folder exists +if not os.path.exists(config["local"]["backup_folder"]): + os.makedirs(config["local"]["backup_folder"]) + +# Setup logging +log_file = os.path.join(config["local"]["backup_folder"], "backup_log.txt") +logging.basicConfig(filename=log_file, level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + +def backup_files(backup_type): + if config.get('disable_local_save'): + logging.info("Local backup saving is disabled.") + return None + + try: + backup_name = f"{backup_type}_{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}.tar.gz" + backup_path = os.path.join(config["local"]["backup_folder"], backup_name) + + subprocess.run(['tar', '-czf', backup_path, config["local"]["source_folder"]]) + + if not config.get('disable_ssh_ftp'): + rsync_command = [ + 'rsync', + '-avz', + '-e', f'ssh -p {config["ssh"]["port"]}', + backup_path, + f'{config["ssh"]["user"]}@{config["ssh"]["host"]}:{config["ssh"]["location"]}' + ] + subprocess.run(rsync_command) + + logging.info(f"{backup_type} file backup-up successful.") + return backup_name + + except Exception as e: + logging.error(f"Error during {backup_type} file backup: {str(e)}") + return None + +def backup_mysql(backup_type): + if config.get('disable_local_save'): + logging.info("Local backup saving is disabled.") + return None + + try: + backup_name = f"{backup_type}_db_{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}.sql.gz" + backup_path = os.path.join(config["local"]["backup_folder"], backup_name) + + dump_command = [ + 'mysqldump', + '--single-transaction', + '-h', config["mysql"]["host"], + '-u', config["mysql"]["user"], + f'--password={config["mysql"]["password"]}', + config["mysql"]["database"], + '|', 'gzip', '>', backup_path + ] + subprocess.run(' '.join(dump_command), shell=True) + + if not config.get('disable_ssh_ftp'): + rsync_command = [ + 'rsync', + '-avz', + '-e', f'ssh -p {config["ssh"]["port"]}', + backup_path, + f'{config["ssh"]["user"]}@{config["ssh"]["host"]}:{config["ssh"]["location"]}' + ] + subprocess.run(rsync_command) + + logging.info(f"{backup_type} database backup-up successful.") + return backup_name + + except Exception as e: + logging.error(f"Error during {backup_type} database backup: {str(e)}") + return None + +def cleanup_backups(config): + try: + for backup_type, retention_period in config["retention"].items(): + file_backup_pattern = os.path.join(config["local"]["backup_folder"], f"{backup_type}_*.tar.gz") + db_backup_pattern = os.path.join(config["local"]["backup_folder"], f"{backup_type}_db_*.sql.gz") + + file_backups = sorted(glob.glob(file_backup_pattern)) + db_backups = sorted(glob.glob(db_backup_pattern)) + + delete_file_count = max(0, len(file_backups) - retention_period) + delete_db_count = max(0, len(db_backups) - retention_period) + + for i in range(delete_file_count): + os.remove(file_backups[i]) + logging.info(f"Cleaned up old file backup: {file_backups[i]}") + + for i in range(delete_db_count): + os.remove(db_backups[i]) + logging.info(f"Cleaned up old database backup: {db_backups[i]}") + + except Exception as e: + logging.error(f"Error during cleanup: {str(e)}") + +def main(): + now = datetime.datetime.now() + + if now.hour in config["schedule"]["daily"]: + backup_type = "daily" + elif now.weekday() == config["schedule"]["weekly"]["weekday"] and now.hour == config["schedule"]["weekly"]["hour"]: + backup_type = "weekly" + elif (config["schedule"]["monthly"]["day_range"][0] <= now.day <= config["schedule"]["monthly"]["day_range"][1]) and now.weekday() == config["schedule"]["monthly"]["weekday"] and now.hour == config["schedule"]["monthly"]["hour"]: + backup_type = "monthly" + else: + logging.info("No Backups for this time frame.") + return + + logging.info(f"Starting {backup_type} file back-up...") + backup_files(backup_type) + + logging.info(f"Starting {backup_type} database back-up...") + backup_mysql(backup_type) + + logging.info("Starting cleanup old back-ups...") + cleanup_backups(config) + logging.info("Cleanup successful.") + +if __name__ == "__main__": + main() diff --git a/bin/block_attack b/bin/block_attack deleted file mode 120000 index e42252a..0000000 --- a/bin/block_attack +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/check_payment_information_for_app b/bin/check_payment_information_for_app deleted file mode 120000 index e42252a..0000000 --- a/bin/check_payment_information_for_app +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/check_xgrade b/bin/check_xgrade deleted file mode 120000 index e42252a..0000000 --- a/bin/check_xgrade +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/command b/bin/command deleted file mode 100755 index 3d67aa5..0000000 --- a/bin/command +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python3 -import os -import sys -from hypernode_api_python import commands - - -if __name__ == '__main__': - command = os.path.basename(__file__) - - if hasattr(commands, command): - sys.exit(getattr(commands, command)() or 0) - else: - sys.stderr.write("Command '{}' not found in hypernode_api_python.commands\n".format(command)) - sys.exit(1) diff --git a/bin/create_brancher b/bin/create_brancher deleted file mode 120000 index e42252a..0000000 --- a/bin/create_brancher +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/destroy_brancher b/bin/destroy_brancher deleted file mode 120000 index e42252a..0000000 --- a/bin/destroy_brancher +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/get_active_branchers b/bin/get_active_branchers deleted file mode 120000 index e42252a..0000000 --- a/bin/get_active_branchers +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/get_active_products b/bin/get_active_products deleted file mode 120000 index e42252a..0000000 --- a/bin/get_active_products +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/get_app_configurations b/bin/get_app_configurations deleted file mode 120000 index e42252a..0000000 --- a/bin/get_app_configurations +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/get_app_flavor b/bin/get_app_flavor deleted file mode 120000 index e42252a..0000000 --- a/bin/get_app_flavor +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/get_app_info b/bin/get_app_info deleted file mode 120000 index e42252a..0000000 --- a/bin/get_app_info +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/get_available_backups_for_app b/bin/get_available_backups_for_app deleted file mode 120000 index e42252a..0000000 --- a/bin/get_available_backups_for_app +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/get_block_attack_descriptions b/bin/get_block_attack_descriptions deleted file mode 120000 index e42252a..0000000 --- a/bin/get_block_attack_descriptions +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/get_cluster_relations b/bin/get_cluster_relations deleted file mode 120000 index e42252a..0000000 --- a/bin/get_cluster_relations +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/get_current_product_for_app b/bin/get_current_product_for_app deleted file mode 120000 index e42252a..0000000 --- a/bin/get_current_product_for_app +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/get_eav_description b/bin/get_eav_description deleted file mode 120000 index e42252a..0000000 --- a/bin/get_eav_description +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/get_flows b/bin/get_flows deleted file mode 120000 index e42252a..0000000 --- a/bin/get_flows +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/get_fpm_status b/bin/get_fpm_status deleted file mode 120000 index e42252a..0000000 --- a/bin/get_fpm_status +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/get_next_best_plan_for_app b/bin/get_next_best_plan_for_app deleted file mode 120000 index e42252a..0000000 --- a/bin/get_next_best_plan_for_app +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/get_product_info b/bin/get_product_info deleted file mode 120000 index e42252a..0000000 --- a/bin/get_product_info +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/get_sla b/bin/get_sla deleted file mode 120000 index e42252a..0000000 --- a/bin/get_sla +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/get_slas b/bin/get_slas deleted file mode 120000 index e42252a..0000000 --- a/bin/get_slas +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/get_whitelist_options b/bin/get_whitelist_options deleted file mode 120000 index e42252a..0000000 --- a/bin/get_whitelist_options +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/get_whitelist_rules b/bin/get_whitelist_rules deleted file mode 120000 index e42252a..0000000 --- a/bin/get_whitelist_rules +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/validate_app_name b/bin/validate_app_name deleted file mode 120000 index e42252a..0000000 --- a/bin/validate_app_name +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/bin/xgrade b/bin/xgrade deleted file mode 120000 index e42252a..0000000 --- a/bin/xgrade +++ /dev/null @@ -1 +0,0 @@ -command \ No newline at end of file diff --git a/hypernode_api_python/__init__.py b/hypernode_api_python/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/hypernode_api_python/client.py b/hypernode_api_python/client.py deleted file mode 100644 index 942f38e..0000000 --- a/hypernode_api_python/client.py +++ /dev/null @@ -1,884 +0,0 @@ -from requests import Session - -DEFAULT_USER_AGENT = "hypernode-api-python" -HYPERNODE_API_URL = "https://api.hypernode.com" -HYPERNODE_API_ADDON_LIST_ENDPOINT = "/v2/addon/" -HYPERNODE_API_ADDON_SLA_LIST_ENDPOINT = "/v2/addon/slas/" -HYPERNODE_API_APP_CHECK_PAYMENT_INFORMATION = "/v2/app/{}/check-payment-information/" -HYPERNODE_API_APP_CONFIGURATION_ENDPOINT = "/v2/configuration/" -HYPERNODE_API_APP_CLUSTER_RELATIONS = "/v2/app/{}/relations/" -HYPERNODE_API_APP_DETAIL_ENDPOINT = "/v2/app/{}/?destroyed=false" -HYPERNODE_API_APP_DETAIL_WITH_ADDONS_ENDPOINT = "/v2/app/{}/with_addons?destroyed=false" -HYPERNODE_API_APP_EAV_DESCRIPTION_ENDPOINT = "/v2/app/eav_descriptions/" -HYPERNODE_API_APP_FLAVOR_ENDPOINT = "/v2/app/{}/flavor/" -HYPERNODE_API_APP_FLOWS_ENDPOINT = "/logbook/v1/logbooks/{}/flows" -HYPERNODE_API_APP_NEXT_BEST_PLAN_ENDPOINT = "/v2/app/{}/next_best_plan/" -HYPERNODE_API_APP_ORDER_ENDPOINT = "/v2/app/order/" -HYPERNODE_API_APP_PRODUCT_LIST_ENDPOINT = "/v2/product/app/{}/" -HYPERNODE_API_APP_XGRADE_CHECK_ENDPOINT = "/v2/app/xgrade/{}/check/{}/" -HYPERNODE_API_APP_XGRADE_ENDPOINT = "/v2/app/xgrade/{}/" -HYPERNODE_API_BACKUPS_ENDPOINT = "/v2/app/{}/backup/" -HYPERNODE_API_BLOCK_ATTACK_ENDPOINT = "/v2/app/{}/block_attack/" -HYPERNODE_API_BLOCK_ATTACK_DESCRIPTION_ENDPOINT = "/v2/app/block_attack_descriptions/" -HYPERNODE_API_BRANCHER_APP_ENDPOINT = "/v2/brancher/app/{}/" -HYPERNODE_API_FPM_STATUS_APP_ENDPOINT = "/v2/nats/{}/hypernode.show-fpm-status" -HYPERNODE_API_BRANCHER_ENDPOINT = "/v2/brancher/{}/" -HYPERNODE_API_PRODUCT_APP_DETAIL_ENDPOINT = "/v2/product/app/{}/current/" -HYPERNODE_API_PRODUCT_LIST_ENDPOINT = "/v2/product/" -HYPERNODE_API_PRODUCT_PRICE_DETAIL_ENDPOINT = "/v2/product/{}/with_price/" -HYPERNODE_API_VALIDATE_APP_NAME_ENDPOINT = "/v2/app/name/validate/" -HYPERNODE_API_WHITELIST_ENDPOINT = "/v2/whitelist/{}/" - - -class HypernodeAPIPython: - def __init__(self, token, api_url=None, user_agent=None): - """ - The official Hypernode API client for Python - - :param str token: The API token you're using to talk to the API - Check out /etc/hypernode/hypernode_api_token on the Hypernode to - find your token. - :param str api_url: The URL of the API. Leave None if you wish - to use the 'real' Hypernode API. For local development you might - want to override this (if you want to talk to a different API, or - no real API at all). - :param str user_agent: The user agent to use when doing the API - requests. If you leave this None it will default to DEFAULT_USER_AGENT, - but you may want to enter the name of your application here. - """ - self.session = Session() - self.token = token - self.api_url = api_url if api_url else HYPERNODE_API_URL - self.authorization_header = "Token {}".format(self.token) - self.user_agent = user_agent if user_agent else DEFAULT_USER_AGENT - - def requests(self, method, path, *args, **kwargs): - """ - Some default requests settings. You can override this entire - method if you wish to set some different defaults. - """ - kwargs.setdefault("headers", {}).update( - { - "Accept": "application/json", - "Authorization": self.authorization_header, - "Accept-Language": "en-US", - "User-Agent": self.user_agent, - } - ) - return self.session.request( - method, HYPERNODE_API_URL.rstrip("/") + path, *args, **kwargs - ) - - def _get_app_endpoint_or_404(self, app_name, endpoint, error_to_raise=None): - """ - Get the app endpoint and return the response, but if the status_code is - 404 then raise the specified exception. Raises RuntimeError by default - but if you're using this in for example a Django application you might - want to raise a django.http.response.Http404 instead. - - :param str app_name: The name of the app to get the endpoint of - :param str endpoint: The endpoint to get - :param obj error_to_raise: What error to raise if the name is not - valid or available. By default, this will be RuntimeError. - :return obj response: The request response object - """ - error_to_raise = error_to_raise if error_to_raise else RuntimeError - response = self.requests("GET", endpoint.format(app_name)) - - if response.status_code == 404: - raise error_to_raise("App {} not found.".format(app_name)) - return response - - def get_app_info_or_404(self, app_name): - """ - Get information about the specified app. Raises if the status returns 404. - Example: - > client.get_app_info_or_404('yourhypernodeappname').json() - > { - > 'account_user': - > { - > 'email': 'email@example.com', - > 'first_name': 'Firstname', - > 'id': 0, - > 'last_name': 'Lastname', - > 'username': 'email@example.com' - > }, - > 'destroyed': False, - > 'domainname': 'yourhypernodeappname.hypernode.io', - > 'flavor': {'name': '2CPU/8GB/60GB (Falcon S 202202)', 'redis_size': '1024'}, - > 'in_production': True, - > 'ip': '1.2.3.4', - > 'mysql_version': '8.0', - > 'name': 'yourhypernodeappname', - > 'type': 'persistent', - > ... - > } - - :param str app_name: The name of the app to get information about - :return obj response: The request response object - """ - return self._get_app_endpoint_or_404( - app_name, HYPERNODE_API_APP_DETAIL_ENDPOINT - ) - - def get_next_best_plan_for_app_or_404(self, app_name): - """ - Ask the API for what the next plan would be for this app - if it was looking to upgrade. In case you want to implement - some sort of auto-scaling you need a way to query the API for - the product code (and product name) of the plan next in line. - For example, if you are on the Falcon S, doing this API call - would return you information about the Falcon M. If the information - for the specified app_name can not be queried due to a 404, this - method will raise. - Example: - > client.get_next_best_plan_for_app_or_404('yourhypernodeappname').json() - > {'name': 'Falcon M', 'code': 'FALCON_M_202203'} - - :param str app_name: The name of the app to get the next best plan for - :return obj response: The request response object - """ - return self._get_app_endpoint_or_404( - app_name, HYPERNODE_API_APP_NEXT_BEST_PLAN_ENDPOINT - ) - - def validate_app_name(self, app_name, error_to_raise=None): - """ - Check if an app name is valid and available. Before you create - a new Hypernode you could check if the name is available. - - :param str app_name: The name of the app to check for availability - :param obj error_to_raise: What error to raise if the name is not - valid or available. By default, this will be RuntimeError. - :return NoneType None: If it's valid, we don't raise, but we don't - return anything either. - """ - error_to_raise = error_to_raise if error_to_raise else RuntimeError - response = self.requests( - "GET", HYPERNODE_API_VALIDATE_APP_NAME_ENDPOINT, params={"name": app_name} - ) - if response.content and response.content.decode() != "[]": - data = response.json() - if isinstance(data, dict): - raise error_to_raise(data["errors"]) - raise error_to_raise(data) - - def get_app_flavor(self, app_name): - """ - Get the flavor of an app - Example: - > client.get_app_flavor('yourhypernodeappname').json() - > {'name': '2CPU/8GB/60GB (Falcon S 202202)', 'redis_size': '1024'} - - :param str app_name: The name of the app to get the flavor for - :return obj response: The request response object - """ - return self.requests("GET", HYPERNODE_API_APP_FLAVOR_ENDPOINT.format(app_name)) - - def get_flows(self, app_name): - """ - List the flows for an app. Take note that this result is paginated and will not - retrieve all flows. If you are looking to retrieve all flows and not those available - on just the first page, look at the get_all_flows method instead. - - Example: - > client.get_flows('yourhypernodeappname').json() - > {'count': 2, - > 'next': None, - > 'previous': None, - > 'results': [{'uuid': '1d1b8437-c8c0-4e96-a28a-26cc311c1f4c', - > 'state': 'success', - > 'name': 'update_node', - > 'created_at': '2023-03-04T14:42:24Z', - > 'updated_at': '2023-03-04T14:42:57Z', - > 'progress': {'running': [], 'total': 8, 'completed': 8}, - > 'logbook': 'yourhypernodeappname', - > 'tracker': {'uuid': '6012bf2c-952d-4cb0-97ff-1022614b50b7', - > 'description': None}}, - > {'uuid': 'fe696417-024e-4a57-8442-4967f6df24a3', - > 'state': 'success', - > 'name': 'create_backup', - > 'created_at': '2023-03-04T14:13:00Z', - > 'updated_at': '2023-03-04T14:13:12Z', - > 'progress': {'running': [], 'total': 2, 'completed': 2}, - > 'logbook': 'yourhypernodeappname', - > 'tracker': {'uuid': None, 'description': None}}] - > } - - - :param str app_name: The name of the app to get the flows for - :return obj response: The request response object - """ - return self.requests("GET", HYPERNODE_API_APP_FLOWS_ENDPOINT.format(app_name)) - - def get_all_flows(self, app_name, limit=None): - """ - List all the flows for an app (paginate over the results), or - retrieve results until we have enough (if limit is specified). - Note that this method does not return a response object but a - list of dicts instead. - Example: - > client.get_all_flows('yourhypernodeappname') - > [{'uuid': '03bd6e10-5493-4ee8-92fc-cc429faebead', - > 'state': None, - > 'name': 'update_node', - > 'created_at': '2023-03-05T14:01:56Z', - > 'updated_at': None, - > 'progress': {'running': [], 'total': 0, 'completed': 0}, - > 'logbook': 'yourhypernodeappname', - > 'tracker': {'uuid': '0dd83d83-6b9b-4fb5-9665-79fcf8235069', - > 'description': None}}, - > {'uuid': 'ace36ab9-323e-4a95-a0c6-46f96945b623', - > 'state': None, - > 'name': 'update_node', - > 'created_at': '2023-03-05T14:01:55Z', - > 'updated_at': None, - > 'progress': {'running': [], 'total': 0, 'completed': 0}, - > 'logbook': 'yourhypernodeappname', - > 'tracker': {'uuid': '0006206e-df48-4af4-8e7b-b3db86d35bb0', - > 'description': None}}, - > ... - > ] - - :param str app_name: The name of the app to get all the flows for - :param int limit: How many flows to get. If None is specified all - available flows will be retrieved. - :return list flows: A list of all flows - """ - flows = [] - next_url = HYPERNODE_API_APP_FLOWS_ENDPOINT.format(app_name) - while next_url: - if limit and len(flows) >= limit: - break - response = self.requests("GET", next_url.replace(HYPERNODE_API_URL, "")) - response_data = response.json() - results = response_data["results"] - flows.extend(results) - next_url = response_data["next"] - return flows[:limit] - - def get_slas(self): - """ - List the available SLAs - Example: - > client.get_slas().json() - > [{'billing_period': 1, - > 'billing_period_unit': 'month', - > 'code': 'sla-standard', - > 'id': 123, - > 'name': 'SLA Standard', - > 'price': 1234}, - > ..] - - :return obj response: The request response object - """ - return self.requests("GET", HYPERNODE_API_ADDON_SLA_LIST_ENDPOINT) - - def get_sla(self, sla_code): - """ - List a specific SLA - Example: - > client.get_sla("sla-standard").json() - > {'billing_period': 1, - > 'billing_period_unit': 'month', - > 'code': 'sla-standard', - > 'id': 123, - > 'name': 'SLA Standard', - > 'price': 1234} - - :return obj response: The request response object - """ - return self.requests("GET", HYPERNODE_API_ADDON_LIST_ENDPOINT + sla_code + "/") - - def get_available_backups_for_app(self, app_name): - """ - Lists the available backups for the specified app - Example: - > client.get_available_backups_for_app('yourhypernodeappname').json() - > { - > 'count': 11, - > 'next': None, - > 'previous': None, - > 'results': [ - > { - > 'backup_created_at': '2022-09-30T13:26:01+02:00', - > 'backup_id': '12341234-1234-1234-1234-123412341234', - > 'expired_at': '2022-10-28T13:26:01+02:00', - > 'type': 'periodic' - > }, - > ... - > ] - > } - - :param str app_name: The app to look up the available backups for - :return obj response: The request response object - """ - return self.requests("GET", HYPERNODE_API_BACKUPS_ENDPOINT.format(app_name)) - - def get_app_eav_description(self): - """ - List all the available EAV settings that are available. These are - the same settings as you'd be able to configure using the command-line - tool on the Hypernode itself (hypernode-systemctl settings). - Example: - > client.get_app_eav_description().json() - > { - > 'varnish_enabled': [True, False], - > 'php_version': ['8.0', '8.1'], - > ... - > } - - :return obj response: The request response object - """ - return self.requests("GET", HYPERNODE_API_APP_EAV_DESCRIPTION_ENDPOINT) - - # TODO: add entrypoint for this method in bin/ and commands.py - def set_app_setting(self, app_name, attribute, value): - """ - Update a setting on the app, like the PHP or MySQL version. See - get_app_eav_description for all possible values. This is similar - to hypernode-systemctl settings. - Example: - > client.get_app_eav_description().json() - > { - > 'varnish_enabled': [True, False], - > 'php_version': ['8.0', '8.1'], - > ... - > } - - :param str app_name: The app to configure the setting for - :param str attribute: The setting to configure, like 'php_version' - :param str || bool value: The value to set it to, like '8.1' - :return obj response: The request response object - """ - data = {attribute: value} - return self.requests( - "PATCH", HYPERNODE_API_APP_DETAIL_ENDPOINT.format(app_name), data=data - ) - - def get_app_configurations(self): - """ - List all the available app configurations. These are the available - configurations you can select when ordering a new Hypernode. For example - if you'd use the Magento 2 app configuration you'd get a certain PHP version - and specific Magento 2 NGINX configurations. - Example: - > client.get_app_configurations().json() - > { - > 'count': 2, - > 'next': None, - > 'previous': None, - > 'results': [ - > { - > 'allow_preinstall': True, - > 'composer_version': '2.x', - > 'name': 'Akeneo 4.0', - > 'elasticsearch_enabled': True, - > 'elasticsearch_version': '7.x', - > ... - > }, - > ... - > ] - > } - - :return obj response: The request response object - """ - return self.requests("GET", HYPERNODE_API_APP_CONFIGURATION_ENDPOINT) - - def get_cluster_relations(self, app_name): - """ - List all relations for the specified app. This will return all the - relations that are currently configured for the specified app. - - Example: - > client.get_cluster_relations('mytestappweb').json() - > {'children': [], - > 'parents': [{'child': 'mytestappweb', - > 'cluster_description': None, - > 'id': 182, - > 'parent': 'mytestappdb', - > 'relation_type': 'mysql'}, - > {'child': 'mytestappweb', - > 'cluster_description': None, - > 'id': 180, - > 'parent': 'mytestapp', - > 'relation_type': 'loadbalancer'}, - > {'child': 'mytestappweb', - > 'cluster_description': None, - > 'id': 181, - > 'parent': 'mytestapp', - > 'relation_type': 'nfs'}]} - """ - return self.requests( - "GET", HYPERNODE_API_APP_CLUSTER_RELATIONS.format(app_name) - ) - - def get_product_info_with_price(self, product_code, error_to_raise=None): - """ - Get information about a specific product - :param str product_code: The code of the product to get information of - :param obj error_to_raise: What error to raise if the product does not - exist. By default this will be RuntimeError. - Example: - > client.get_product_info_with_price('MAGG201909').json() - > { - > 'backups_enabled': True, - > 'code': 'FALCON_S_202203', - > 'is_development': False, - > 'name': 'Grow', - > 'price': 1234, - > 'provider': 'combell', - > 'related_product': { - > 'backups_enabled': True, - > 'code': 'FALCON_S_202203DEV', - > 'is_development': True, - > 'name': 'Grow Development', - > 'price': 4321, - > 'provider': 'combell', - > 'storage_size_in_gb': 44, - > 'supports_sla': False, - > 'varnish_supported': False - > }, - > 'storage_size_in_gb': 44, - > 'supports_sla': True, - > 'varnish_supported': False - > } - - :return obj response: The request response object - """ - error_to_raise = error_to_raise if error_to_raise else RuntimeError - response = self.requests( - "GET", HYPERNODE_API_PRODUCT_PRICE_DETAIL_ENDPOINT.format(product_code) - ) - if response.status_code == 404: - raise error_to_raise - return response - - def get_block_attack_descriptions(self): - """ - Get the block attack descriptions. These can be used to block attacks - by automatically placing an NGINX snippet in /data/web/nginx if the - specified block is compatible with the current NGINX configuration. - Example: - > client.get_block_attack_descriptions().json() - > { - > 'BlockSqliBruteForce': 'Attempts to deploy NGINX rules to block suspected (blind) SQL injection attack', - > ... - > } - - :return obj response: The request response object - """ - return self.requests("GET", HYPERNODE_API_BLOCK_ATTACK_DESCRIPTION_ENDPOINT) - - def block_attack(self, app_name, attack_name): - """ - Attempts to deploy one of the block attack rules. The list of rules can be retrieved with the - get_block_attack_descriptions method. This will place an NGINX snippet to block the specified attack - in /data/web/nginx. If the nginx-config-validator detects that the new config is valid, it will leave - the newly placed config. Otherwise it will be removed again. - - Example: - > client.block_attack('yourhypernodeappname', 'BlockSqliBruteForce').ok - > True - - :param str app_name: The name of the Hypernode you want to deploy the block attack nginx configuration on - :param str attack_name: The specific attack you want to block. See get_block_attack_descriptions for options. - :return obj response: The request response object - """ - return self.requests( - "POST", - HYPERNODE_API_BLOCK_ATTACK_ENDPOINT.format(app_name), - data={"attack_name": attack_name}, - ) - - def get_whitelist_options(self, app_name): - """ - Get whitelist options for app. Retrieve the options for specifying - whitelist information for this Hypernode. See hypernode-systemctl - for an implementation example of how this can be used to configure - the firewall using the Hypernode API. - Example: - > client.get_whitelist_options('yourhypernodeappname').json()) - > {'actions': - > {'POST': - > {'description': - > { - > 'label': 'Description', - > 'read_only': False, - > 'required': False, - > 'type': 'string' - > }, - > 'ip': { - > 'label': 'Ip', - > 'read_only': False, - > 'required': True, - > 'type': 'string' - > }, - > 'type': { - > 'choices': [ - > { - > 'display_name': 'waf', - > 'value': 'waf' - > }, - > { - > 'display_name': 'database', - > 'value': 'database' - > }, - > { - > 'display_name': 'ftp', - > 'value': 'ftp' - > } - > ], - > 'label': 'Type', - > 'read_only': False, - > 'required': True, - > 'type': 'choice' - > } - > } - > }, - > 'description': '', - > 'name': 'Whitelist', - > 'parses': [ - > 'application/json', - > 'application/x-www-form-urlencoded', - > 'multipart/form-data' - > ], - > 'renders': ['application/json'] - > } - - :param str app_name: The name of the app to get the whitelist information for - :return obj response: The request response object - """ - return self.requests( - "OPTIONS", HYPERNODE_API_WHITELIST_ENDPOINT.format(app_name) - ) - - def get_whitelist_rules(self, app_name, filter_data=None): - """ - Get the whitelist rules currently configured for the specified app - Example: - > client.get_whitelist_rules('yourhypernodeappname').json()) - > [ - > { - > 'created': '2022-10-23T14:49:06Z', - > 'description': '', - > 'domainname': 'yourhypernodeappname.hypernode.io', - > 'id': 1234, - > 'ip': '1.2.3.4', - > 'type': 'waf' - > }, - > ... - > ] - - :param str app_name: The name of the Hypernode you want to get the - currently configured whitelist information for. - :param dict filter_data: Filter the results based on this filter - An example filter to specify could be: {'type': 'waf'} - :return obj response: The request response object - """ - filter_data = filter_data or {} - return self.requests( - "GET", HYPERNODE_API_WHITELIST_ENDPOINT.format(app_name), filter_data - ) - - def get_current_product_for_app(self, app_name): - """ - Retrieve information about the product the specified App is currently on. - Example: - > client.get_current_product_for_app('yourhypernodeappname').json() - > { - > 'backups_enabled': True, - > 'code': 'FALCON_S_202203', - > 'flavor': { - > 'name': '2CPU/8GB/60GB (Falcon S 202202)', 'redis_size': '1024' - > }, - > 'is_active': True, - > 'is_development': False, - > 'name': 'Falcon S', - > 'price': 1234, - > 'provider_flavors': [ - > { - > 'disk_size_in_gb': 1234, - > 'exact_disk_size_in_kb': 1234, - > 'inodes': 1234, - > 'provider': { - > 'display_name': 'Combell OpenStack', - > 'name': 'combell' - > }, - > 'ram_in_mb': 8192, - > 'vcpus': 2 - > } - > ], - > 'related_product': { - > 'backups_enabled': True, - > 'code': 'FALCON_S_202203DEV', - > 'flavor': { - > 'name': '2CPU/8GB/60GB (Falcon S 202202)', - > 'redis_size': '1024' - > }, - > 'is_active': True, - > 'is_development': True, - > 'name': 'Falcon S Development', - > 'price': 1234, - > 'provider_flavors': [ - > { - > 'disk_size_in_gb': 1234, - > 'exact_disk_size_in_kb': 1234, - > 'inodes': 1234, - > 'provider': { - > 'display_name': 'Combell ' - > 'OpenStack', - > 'name': 'combell' - > }, - > 'ram_in_mb': 8192, - > 'vcpus': 2 - > } - > ], - > 'supports_sla': False, - > 'varnish_supported': True - > }, - > 'supports_sla': True, - > 'varnish_supported': True - > } - - :param str app_name: The name of the Hypernode to retrieve the product - information for. - :return obj response: The request response object - """ - return self.requests( - "GET", HYPERNODE_API_PRODUCT_APP_DETAIL_ENDPOINT.format(app_name) - ) - - def check_payment_information_for_app(self, app_name): - """ - Get the payment information that is currently configured for this Hypernode - Example: - > client.check_payment_information_for_app('yourhypernodeappname').json() - > { - > 'has_valid_vat_number': True, - > 'has_valid_payment_method': True, - > } - - :param str app_name: The Hypernode to check the payment information for - :return obj response: The request response object - """ - return self.requests( - "GET", HYPERNODE_API_APP_CHECK_PAYMENT_INFORMATION.format(app_name) - ) - - def get_active_products(self): - """ - Retrieve the list of products that are currently available. You can - change the plan of your Hypernode to these products. Doing so would - start a migration and change your subscription to the specified plan. - Example: - > client.get_active_products().json() - > [ - > {'backups_enabled': True, - > 'code': 'FALCON_XS_202203', - > 'flavor': { - > 'name': '2CPU/4GB/60GB (Falcon XS 202202)', 'redis_size': '768' - > }, - > 'is_active': True, - > 'is_development': False, - > 'name': 'Falcon XS', - > 'price': 1234, - > 'provider_flavors': [ - > {'disk_size_in_gb': 1234, - > 'exact_disk_size_in_kb': 1234, - > 'inodes': 1234, - > 'provider': { - > 'display_name': 'Combell OpenStack', - > 'name': 'combell' - > }, - > 'ram_in_mb': 4096, - > 'vcpus': 2 - > } - > ], - > 'related_product': { - > 'backups_enabled': True, - > 'code': 'FALCON_XS_202203DEV', - > 'flavor': { - > 'name': '2CPU/4GB/60GB (Falcon XS 202202)', - > 'redis_size': '768' - > }, - > 'is_active': True, - > 'is_development': True, - > 'name': 'Falcon XS Development', - > 'price': 1234, - > 'provider_flavors': [ - > { - > 'disk_size_in_gb': 1234, - > 'exact_disk_size_in_kb': 1234, - > 'inodes': 1234, - > 'provider': { - > 'display_name': 'Combell ' - > 'OpenStack', - > 'name': 'combell' - > }, - > 'ram_in_mb': 4096, - > 'vcpus': 2 - > } - > ], - > 'supports_sla': False, - > 'varnish_supported': False - > }, - > 'supports_sla': True, - > 'varnish_supported': False - > }, - > ... - > ] - - :return obj response: The request response object - """ - return self.requests("GET", HYPERNODE_API_PRODUCT_LIST_ENDPOINT) - - def check_xgrade(self, app_name, product_code): - """ - Checks if the Hypernode 'is going to fit' on the new product. Retrieves some - information about what a plan change to the specified product would look like for - the specified app. If it does not fit because the disk space currently in use is too - high, then we can find out here. Also, whether the IP will change, or if the volume - would entail a rsync migration or a more sophisticated volume swap cloud action which - would mean a significantly shorter migration time (depending on how much space is used). - Example: - > client.check_xgrade('yourhypernodeappname', 'FALCON_S_202203').json() - > { - > 'has_valid_vat_number': True, - > 'has_valid_payment_method': True, - > 'will_change_ip': False, - > 'will_do_volswap': True, - > 'will_disk_fit': True - > } - - :param str app_name: The name of the Hypernode to check the xgrade for - :param str product_code: The destination product we're checking. So if we'd xgrade - to this product, what would that mean? The response will tell us. - :return obj response: The request response object - """ - return self.requests( - "GET", - HYPERNODE_API_APP_XGRADE_CHECK_ENDPOINT.format(app_name, product_code), - ) - - def xgrade(self, app_name, data): - """ - Change the product of a Hypernode to a different plan. This will initiate - a migration of the Hypernode to a larger or smaller Hypernode, depending on - what product you specify. Progress of the migration can be tracked on the - hypernode by running hypernode-log (or getting that information from the API - directly as well). This API call does not result any output, on success the - response.text will be an empty string and the status_code will be 200. - - :param str app_name: The name of the Hypernode to change the product of - :param dict data: Data regarding what product the Hypernode should be changed - to. An example could be: {'product': 'FALCON_S_202203'}. You can also specify a - scheduled_at time in case you want to perform the migration at a scheduled - moment. That could look something like this: - {'product': 'FALCON_S_202203', 'scheduled_at': '2022-11-25T01:00:00+03:00'} - :return obj response: The request response object - """ - return self.requests( - "PATCH", HYPERNODE_API_APP_XGRADE_ENDPOINT.format(app_name), data=data - ) - - # TODO: add entrypoint for this method in bin/ and commands.py - def order_hypernode(self, data): - """ - Orders a new Hypernode. Note that you can not do this with the API permissions - of the API token of a Hypernode. If you wish to programmatically order Hypernodes - please contact support@hypernode.com, and we'll set up an API token with appropriate - permissions for you. Also, if this is something you're actively working on, we'd love - to hear about your use-case. - - :param dict data: Data regarding the Hypernode that should be newly created - This should be something like: - { - 'app_name': 'mynewhypernodeappnameofmax16chars', - 'product': 'FALCON_S_202203', - 'initial_app_configuration': 'magento_2', - } - :return obj response: The request response object - """ - return self.requests("POST", HYPERNODE_API_APP_ORDER_ENDPOINT, data=data) - - def get_active_branchers(self, app_name): - """ - List all active brancher nodes of your Hypernode. - Example: - > client.get_active_branchers("yourhypernodeappname").json() - > { - > "monthly_total_time": 6397, - > "monthly_total_cost": 109, - > "branchers": [ - > { - > "id": 21, - > "name": "yourhypernodeappname-eph123456", - > "cost": 131, - > "created": "2019-08-24T14:15:22Z" - > "ip": "127.0.0.1", - > "end_time": None, - > "elapsed_time": 7824, - > "labels": {"description": "php upgrade test", "label without equal sign": None}, - > }, - > { - > "id": 22, - > "name": "yourhypernodeappname-eph654321", - > "cost": 121, - > "ip": "52.68.96.58", - > "created": "2019-08-24T14:15:22Z" - > "end_time": None, - > "elapsed_time": 7224, - > "labels": {}, - > } - > ] - > } - - :param str app_name: The name of the Hypernode to get your active branchers for - :return obj response: The request response object - """ - return self.requests( - "GET", HYPERNODE_API_BRANCHER_APP_ENDPOINT.format(app_name) - ) - - def get_fpm_status(self, app_name): - """ - Get the status of the FPM service for the specified app - Example: - > client.get_fpm_status("yourhypernodeappname").json() - { - "message": null, - "data": "50570 IDLE 0.0s - phpfpm 127.0.0.1 GET magweb/status.php (python-requests/2.28.1)\n50571 IDLE 0.0s - phpfpm 127.0.0.1 GET magweb/status.php (python-requests/2.28.1)\n", - "status": 200 - } - - :param str app_name: The name of the Hypernode to get the FPM status for - :return obj response: The request response object - """ - return self.requests( - "POST", HYPERNODE_API_FPM_STATUS_APP_ENDPOINT.format(app_name) - ) - - def create_brancher(self, app_name, data): - """ - Create a new branch (server replica) of your Hypernode. - - :param str app_name: The name of the Hypernode to create the branch from - :param dict data: Data regarding the branch to be created. An example could be: - {'clear_services': ['mysql', 'cron']}. - :return obj response: The request response object - """ - return self.requests( - "POST", HYPERNODE_API_BRANCHER_APP_ENDPOINT.format(app_name), data=data - ) - - def destroy_brancher(self, brancher_name): - """ - Destroy an existing brancher node of your Hypernode. - - :param str brancher_name: The name of the brancher node to destroy. - :return obj response: The request response object - """ - return self.requests( - "DELETE", HYPERNODE_API_BRANCHER_ENDPOINT.format(brancher_name) - ) diff --git a/hypernode_api_python/commands.py b/hypernode_api_python/commands.py deleted file mode 100644 index 7f96f6b..0000000 --- a/hypernode_api_python/commands.py +++ /dev/null @@ -1,724 +0,0 @@ -import json -from os import environ, EX_OK, EX_UNAVAILABLE -from argparse import ArgumentParser, RawTextHelpFormatter -from hypernode_api_python.client import HypernodeAPIPython - - -def get_client(): - """ - Instantiates the HypernodeAPIPython client with the token from the environment. - :return obj HypernodeAPIPython: The instantiated client - """ - api_token = environ.get("HYPERNODE_API_TOKEN") - if not api_token: - raise ValueError( - "HYPERNODE_API_TOKEN environment variable not set. " - "Try running `export HYPERNODE_API_TOKEN=yourapitoken`" - ) - client = HypernodeAPIPython(environ["HYPERNODE_API_TOKEN"]) - return client - - -def get_app_name(): - """ - Gets the app name from the environment. - :return str app_name: The app name - """ - app_name = environ.get("HYPERNODE_APP_NAME") - if not app_name: - raise ValueError( - "HYPERNODE_APP_NAME environment variable not set. " - "Try running `export HYPERNODE_APP_NAME=yourhypernodeappname`" - ) - return app_name - - -def print_response(response): - """ - Pretty prints the JSON response. - :param obj response: The response object - :return NoneType None: None - """ - print(json.dumps(response.json(), indent=2)) - - -def get_app_info(args=None): - parser = ArgumentParser( - description=""" -Get information about the Hypernode app. - -Example: -$ ./bin/get_app_info -{ - "name": "yourhypernodeappname", - "type": "persistent", - "product": { - "code": "FALCON_M_202203", - "name": "Falcon M", - "backups_enabled": true, - "storage_size_in_gb": 75, - "price": 1234, - "is_development": false, - "provider": "combell", - "varnish_supported": true, - "supports_sla": true - }, - "domainname": "yourhypernodeappname.hypernode.io", - ... -} -""", - formatter_class=RawTextHelpFormatter, - ) - parser.parse_args(args=args) - client = get_client() - app_name = get_app_name() - print_response(client.get_app_info_or_404(app_name)) - - -def get_next_best_plan_for_app(args=None): - parser = ArgumentParser( - description=""" -Get the plan that is the first bigger plan than the current plan for this Hypernode. -This is convenient for if you want to upgrade your Hypernode to a bigger plan using the -xgrade feature and you need to know which plan to upgrade to. - -Example: -$ ./bin/get_next_best_plan_for_app -{ - "code": "FALCON_L_202203", - "name": "Falcon L" -} -""", - formatter_class=RawTextHelpFormatter, - ) - parser.parse_args(args=args) - client = get_client() - app_name = get_app_name() - print_response(client.get_next_best_plan_for_app_or_404(app_name)) - - -def validate_app_name(args=None): - parser = ArgumentParser( - description=""" -Check if the specified app_name is valid and available. An app name can not be -registered if it's already taken. Also there are certain restrictions on the -app name, like it can't contain certain characters or exceed a certain length. - -Examples: -$ ./bin/validate_app_name hypernode -App name 'hypernode' is valid. - -$ ./bin/validate_app_name hyper_node -App name 'hyper_node' is invalid: ["This value can only contain non-capital letters 'a' through 'z' or digits 0 through 9."] -""", - formatter_class=RawTextHelpFormatter, - ) - parser.add_argument( - "app_name", - help="The app name to validate", - ) - args = parser.parse_args(args=args) - client = get_client() - try: - client.validate_app_name(args.app_name) - print("App name '{}' is valid.".format(args.app_name)) - exit(EX_OK) - except Exception as e: - print("App name '{}' is invalid: {}".format(args.app_name, e)) - exit(EX_UNAVAILABLE) - - -def get_app_flavor(args=None): - parser = ArgumentParser( - description=""" -Get the current flavor of the Hypernode app. - -Example: -$ ./bin/get_app_flavor -{ - "name": "3CPU/16GB/80GB (Falcon M 202202)", - "redis_size": "2048" -} -""", - formatter_class=RawTextHelpFormatter, - ) - parser.parse_args(args=args) - client = get_client() - app_name = get_app_name() - print_response(client.get_app_flavor(app_name)) - - -def get_flows(args=None): - parser = ArgumentParser( - description=""" -Llist the flows for the Hypernode app. This is a history of all the -Hypernode automation jobs that have been ran for this Hypernode. - -Example: -$ ./bin/get_flows -{ - "count": 36, - "next": null, - "previous": null, - "results": [ - { - "uuid": "ad3b520e-a424-4bcb-a6fb-7d1fc055c3fa", - "state": "success", - "name": "create_backup", - "created_at": "2024-05-25T10:01:25Z", - "updated_at": "2024-05-25T10:01:57Z", - "progress": { - "running": [], - "total": 2, - "completed": 2 - }, - "logbook": "myhypernodeappname", - "tracker": { - "uuid": null, - "description": null - } - }, - ... -} -""", - formatter_class=RawTextHelpFormatter, - ) - parser.parse_args(args=args) - client = get_client() - app_name = get_app_name() - print_response(client.get_flows(app_name)) - - -def get_slas(args=None): - parser = ArgumentParser( - description=""" -List all available SLAs. - -Example: -$ ./bin/get_slas -[ - { - "id": 123, - "code": "sla-standard", - "name": "SLA Standard", - "price": 1234, - "billing_period": 1, - "billing_period_unit": "month" - }, - ... -] -""", - formatter_class=RawTextHelpFormatter, - ) - parser.parse_args(args=args) - client = get_client() - print_response(client.get_slas()) - - -def get_sla(args=None): - parser = ArgumentParser( - description=""" -Get a specific SLA. - -$ ./bin/get_sla sla-standard -{ - "id": 123, - "code": "sla-standard", - "name": "SLA Standard", - "price": 1234, - "billing_period": 1, - "billing_period_unit": "month" -} -""", - formatter_class=RawTextHelpFormatter, - ) - parser.add_argument("sla_code", help="The code of the SLA to get") - args = parser.parse_args(args=args) - client = get_client() - print_response(client.get_sla(args.sla_code)) - - -def get_available_backups_for_app(args=None): - parser = ArgumentParser( - description=""" -List the available backups for the Hypernode - -Example: -$ ./bin/get_available_backups_for_app -{ - "count": 10, - "next": null, - "previous": null, - "results": [ - { - "backup_created_at": "2024-05-02T12:00:33+02:00", - "type": "periodic", - "backup_id": "1169e792-8b05-449c-a7b1-7d52cf43153a", - "expired_at": "2024-05-30T12:00:33+02:00" - }, - ... -] -""", - formatter_class=RawTextHelpFormatter, - ) - parser.parse_args(args=args) - client = get_client() - app_name = get_app_name() - print_response(client.get_available_backups_for_app(app_name)) - - -def get_eav_description(args=None): - parser = ArgumentParser( - description=""" -List all available EAV settings and their descriptions. - -Example: -$ ./bin/get_eav_description -{ - "supervisor_enabled": [ - true, - false - ], - "redis_eviction_policy": [ - "noeviction", - "allkeys-lru", - "allkeys-lfu", - "volatile-lru", - "volatile-lfu", - "allkeys-random", - "volatile-random", - "volatile-ttl" - ], - ... -} -""", - formatter_class=RawTextHelpFormatter, - ) - parser.parse_args(args=args) - client = get_client() - print_response(client.get_app_eav_description()) - - -def get_app_configurations(args=None): - parser = ArgumentParser( - description=""" -List all the available configurations that can be selected when ordering a new Hypernode. - -Example: -$ ./bin/get_app_configurations -{ - "count": 7, - "next": null, - "previous": null, - "results": [ - { - "name": "Akeneo 6.0", - "configuration_id": "akeneo_6_0", - "php_version": "8.0", - "mysql_version": "8.0", - ... - }, - ... -} -""", - formatter_class=RawTextHelpFormatter, - ) - parser.parse_args(args=args) - client = get_client() - print_response(client.get_app_configurations()) - - -def get_cluster_relations(args=None): - parser = ArgumentParser( - description=""" -List all the cluster relations for the Hypernode. - -Example: -$ ./bin/get_cluster_relations -{ - "parents": [ - { - "id": 182, - "parent": "mytestappdb", - "child": "mytestappweb", - "relation_type": "mysql", - "cluster_description": null - }, - { - "id": 180, - "parent": "mytestapp", - "child": "mytestappweb", - "relation_type": "loadbalancer", - "cluster_description": null - }, - ... -} -""", - formatter_class=RawTextHelpFormatter, - ) - parser.parse_args(args=args) - client = get_client() - app_name = get_app_name() - print_response(client.get_cluster_relations(app_name)) - - -def get_product_info(args=None): - parser = ArgumentParser( - description=""" -Gets the product info for the specified product - -$ ./bin/get_product_info FALCON_S_202203 -{ - "code": "FALCON_S_202203", - "name": "Falcon S", - "backups_enabled": true, - "storage_size_in_gb": 57, - "price": 1234, - ... -} -""", - formatter_class=RawTextHelpFormatter, - ) - client = get_client() - parser.add_argument( - "product_code", - help="The code of the product to get", - choices=[p["code"] for p in client.get_active_products().json()], - ) - args = parser.parse_args(args=args) - print_response(client.get_product_info_with_price(args.product_code)) - - -def get_block_attack_descriptions(args=None): - parser = ArgumentParser( - description=""" -List all attack blocking strategies and their descriptions. - -Example: -$ ./bin/get_block_attack_descriptions -{ - "BlockSqliBruteForce": "Attempts to deploy NGINX rules to block suspected (blind) SQL injection attacks", - ... -} -""", - formatter_class=RawTextHelpFormatter, - ) - parser.parse_args(args=args) - client = get_client() - print_response(client.get_block_attack_descriptions()) - - -def block_attack(args=None): - parser = ArgumentParser( - description=""" -Block a specific attack based on a pre-defined attack blocking strategy. - -$ ./bin/block_attack BlockSqliBruteForce -A job to block the 'BlockSqliBruteForce' attack has been posted. -""", - formatter_class=RawTextHelpFormatter, - ) - client = get_client() - choices = client.get_block_attack_descriptions().json().keys() - parser.add_argument("attack_name", help="The attack to block", choices=choices) - args = parser.parse_args(args=args) - app_name = get_app_name() - output = client.block_attack(app_name, args.attack_name).content - if output: - print(output) - else: - print( - "A job to block the '{}' attack has been posted.".format(args.attack_name) - ) - - -def get_whitelist_options(args=None): - parser = ArgumentParser( - description=""" -List all available WAF whitelist options for the Hypernode. - -Example: -$ ./bin/get_whitelist_options -{ - "name": "Whitelist", - "description": "", - "renders": [ - "application/json" - ], - ... -} -""", - formatter_class=RawTextHelpFormatter, - ) - parser.parse_args(args=args) - client = get_client() - app_name = get_app_name() - print_response(client.get_whitelist_options(app_name)) - - -def get_whitelist_rules(args=None): - parser = ArgumentParser( - description=""" -List all currently configured WAF whitelist rules for the Hypernode. - -Example: -$ ./bin/get_whitelist_rules -[ - { - "id": 1234, - "created": "2024-05-25T13:39:48Z", - "domainname": "yourhypernodeappname.hypernode.io", - "ip": "1.2.3.4", - "type": "database", - "description": "my description" - }, - ... -] -""", - formatter_class=RawTextHelpFormatter, - ) - parser.parse_args(args=args) - client = get_client() - app_name = get_app_name() - print_response(client.get_whitelist_rules(app_name)) - - -def get_current_product_for_app(args=None): - parser = ArgumentParser( - description=""" -Gets the current product for the specified app. - -Example: -$ ./bin/get_current_product_for_app -{ - "code": "FALCON_M_202203", - "name": "Falcon M", - "backups_enabled": true, - "is_development": false, - "varnish_supported": true, - "supports_sla": true, - "provider_flavors": [ - { - "vcpus": 3, - "ram_in_mb": 16384, - ... - }, - ... -} -""", - formatter_class=RawTextHelpFormatter, - ) - parser.parse_args(args=args) - client = get_client() - app_name = get_app_name() - print_response(client.get_current_product_for_app(app_name)) - - -def check_payment_information_for_app(args=None): - parser = ArgumentParser( - description=""" -Shows the payment information for the specified app. - -Example: -$ ./bin/check_payment_information_for_app -{ - "has_valid_vat_number": true, - "has_valid_payment_method": true -} -""", - formatter_class=RawTextHelpFormatter, - ) - parser.parse_args(args=args) - client = get_client() - app_name = get_app_name() - print_response(client.check_payment_information_for_app(app_name)) - - -def get_active_products(args=None): - parser = ArgumentParser( - description=""" -Lists all available products. - -Example: -$ ./bin/get_active_products -[ - { - "code": "JACKAL_S_202301", - "name": "Jackal S", - "backups_enabled": true, - "is_development": false, - ... - }, - ... -] -""", - formatter_class=RawTextHelpFormatter, - ) - parser.parse_args(args=args) - client = get_client() - print_response(client.get_active_products()) - - -def check_xgrade(args=None): - parser = ArgumentParser( - description=""" -Verify that the specified app can be upgraded to the specified product. -This checks if there is enough disk space available. The output will also -show whether or not there will be an IP change and if a volume swap xgrade -would be performed instead of an rsync xgrade. - -Example: -$ ./bin/check_xgrade FALCON_L_202203 -{ - "has_valid_vat_number": true, - "has_valid_payment_method": true, - "will_change_ip": false, - "will_do_volswap": false, - "will_disk_fit": true -} -""", - formatter_class=RawTextHelpFormatter, - ) - client = get_client() - parser.add_argument( - "product_code", - help="The code of the product to check", - choices=[p["code"] for p in client.get_active_products().json()], - ) - args = parser.parse_args(args=args) - app_name = get_app_name() - print_response(client.check_xgrade(app_name, args.product_code)) - - -def xgrade(args=None): - parser = ArgumentParser( - description=""" -Change the plan of your Hypernode. - -Example: -$ ./bin/xgrade FALCON_L_202203 -The job to xgrade Hypernode 'yourappname' to product 'FALCON_L_202203' has been posted -""", - formatter_class=RawTextHelpFormatter, - ) - client = get_client() - parser.add_argument( - "product_code", - help="The code of the product to check", - choices=[p["code"] for p in client.get_active_products().json()], - ) - args = parser.parse_args(args=args) - app_name = get_app_name() - data = {"product": args.product_code} - output = client.xgrade(app_name, data=data).content - if output: - print(output) - else: - print( - "The job to xgrade Hypernode '{}' to product '{}' " - "has been posted".format(app_name, args.product_code) - ) - - -def get_active_branchers(args=None): - parser = ArgumentParser( - description=""" -List all active branchers - -Example: -$ ./bin/get_active_branchers -{ - "monthly_total_time": 0, - "total_minutes_elapsed": 0, - "actual_monthly_total_cost": 0, - "monthly_total_cost": 0, - "branchers": [] -} -""", - formatter_class=RawTextHelpFormatter, - ) - parser.parse_args(args=args) - client = get_client() - app_name = get_app_name() - print_response(client.get_active_branchers(app_name)) - - -def create_brancher(args=None): - parser = ArgumentParser( - description=""" -Create a Brancher Hypernode from the specified app. -Outputs the app_info of the brancher to be created.. - -Example: -$ ./bin/create_brancher -{ - "name": "yourappname-ephoj82yb", - "parent": "yourappname", - "type": "brancher", - "product": "FALCON_M_202203", - "domainname": "yourappname-ephoj82yb.hypernode.io", - ... -} -""", - formatter_class=RawTextHelpFormatter, - ) - parser.parse_args(args=args) - client = get_client() - app_name = get_app_name() - data = {} - print_response(client.create_brancher(app_name, data=data)) - - -def destroy_brancher(args=None): - parser = ArgumentParser( - description=""" -Destroy a Brancher Hypernode. - -Examples: -$ ./bin/destroy_brancher yourbrancherappname-eph12345 -A job has been posted to cancel the 'yourbrancherappname-eph12345' brancher app. -""", - formatter_class=RawTextHelpFormatter, - ) - parser.add_argument( - "brancher_app_name", - help="The name of the brancher to destroy. See ./bin/get_active_branchers", - ) - args = parser.parse_args(args=args) - client = get_client() - try: - client.destroy_brancher(args.brancher_app_name) - print( - "A job has been posted to cancel the '{}' brancher app.".format( - args.brancher_app_name - ) - ) - exit(EX_OK) - except Exception as e: - print( - "Brancher app '{}' failed to be cancelled: {}".format( - args.brancher_app_name, e - ) - ) - exit(EX_UNAVAILABLE) - - -def get_fpm_status(args=None): - parser = ArgumentParser( - description=""" -Show the status of the PHP-FPM workers. - -Example: -$ ./bin/get_fpm_status -{ - "message": null, - "data": "50570 IDLE 0.0s - phpfpm 127.0.0.1 GET magweb/status.php (python-requests/2.28.1)\n50571 IDLE 0.0s - phpfpm 127.0.0.1 GET magweb/status.php (python-requests/2.28.1)\n", - "status": 200 -} -""", - formatter_class=RawTextHelpFormatter, - ) - parser.parse_args(args=args) - client = get_client() - app_name = get_app_name() - print_response(client.get_fpm_status(app_name)) diff --git a/pycodestyle.ini b/pycodestyle.ini deleted file mode 100644 index a32fd0c..0000000 --- a/pycodestyle.ini +++ /dev/null @@ -1,7 +0,0 @@ -[pycodestyle] -count = False -max-line-length = 160 -statistics = True -exclude = migrations,build,tasks,.git,.tox,__pycache__,help -# @TODO: try to shorten the list of ignored errors -ignore = E305,E501,E731,E741,W292,W391,W503,W504,W605 diff --git a/requirements/base.txt b/requirements/base.txt deleted file mode 100644 index 06afaed..0000000 --- a/requirements/base.txt +++ /dev/null @@ -1,2 +0,0 @@ -requests==2.28.1 - diff --git a/requirements/development.txt b/requirements/development.txt deleted file mode 100644 index 66f78c7..0000000 --- a/requirements/development.txt +++ /dev/null @@ -1,6 +0,0 @@ --r base.txt - -black==22.10.0 -pytest==7.1.3 -pytest-xdist==2.5.0 -pycodestyle==2.9.1 diff --git a/setup.py b/setup.py deleted file mode 100644 index 6728a59..0000000 --- a/setup.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python -import os -from pathlib import Path - -from setuptools import find_packages, setup - -# allow setup.py to be run from any path -os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) - - -this_directory = Path(__file__).parent -long_description = (this_directory / "README.md").read_text() -requirements = """ -pip -requests -""" - - -setup( - name="hypernode_api_python", - version="0.0.6", - description='"Hypernode API Client for Python"', - url="https://github.com/ByteInternet/hypernode_api_python", - packages=find_packages( - include=["hypernode_api_python", "requirements/base.txt"], exclude=["tests"] - ), - author="Hypernode Team", - author_email="support@hypernode.com", - install_requires=requirements.split("\n"), - python_requires=">=3.7", - long_description=long_description, - long_description_content_type="text/markdown", - license="MIT", -) diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/client/__init__.py b/tests/client/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/client/test_block_attack.py b/tests/client/test_block_attack.py deleted file mode 100644 index aef443e..0000000 --- a/tests/client/test_block_attack.py +++ /dev/null @@ -1,39 +0,0 @@ -from unittest import TestCase -from unittest.mock import Mock - -from hypernode_api_python.client import ( - HYPERNODE_API_BLOCK_ATTACK_ENDPOINT, - HypernodeAPIPython, -) - - -class TestBlockAttack(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="my_token") - self.mock_request = Mock() - self.client.requests = self.mock_request - self.app_name = "my_app" - - def test_block_attack_endpoint_is_correct(self): - self.assertEqual( - "/v2/app/{}/block_attack/", HYPERNODE_API_BLOCK_ATTACK_ENDPOINT - ) - - def test_calls_block_attack_endpoint_correctly(self): - self.client.block_attack(self.app_name, "BlockSqliBruteForce") - - self.mock_request.assert_called_once_with( - "POST", - f"/v2/app/{self.app_name}/block_attack/", - data={"attack_name": "BlockSqliBruteForce"}, - ) - - def test_returns_block_attack_data(self): - response = Mock() - response.status_code = 202 - self.mock_request.return_value = response - - self.assertEqual( - self.client.block_attack(self.app_name, "BlockSqliBruteForce"), - self.mock_request.return_value, - ) diff --git a/tests/client/test_check_payment_information_for_app.py b/tests/client/test_check_payment_information_for_app.py deleted file mode 100644 index 55f9722..0000000 --- a/tests/client/test_check_payment_information_for_app.py +++ /dev/null @@ -1,38 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_APP_CHECK_PAYMENT_INFORMATION, -) - - -class TestCheckPaymentInformationForApp(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="mytoken") - self.mock_request = Mock() - self.client.requests = self.mock_request - - def test_check_app_payment_info_endpoint_is_correct(self): - self.assertEqual( - "/v2/app/{}/check-payment-information/", - HYPERNODE_API_APP_CHECK_PAYMENT_INFORMATION, - ) - - def test_calls_check_app_payment_info_endpoint_properly(self): - self.client.check_payment_information_for_app("yourhypernodeappname") - - self.mock_request.assert_called_once_with( - "GET", - HYPERNODE_API_APP_CHECK_PAYMENT_INFORMATION.format("yourhypernodeappname"), - ) - - def test_returns_check_app_payment_info_data(self): - response = Mock() - response.status_code = 200 - self.mock_request.return_value = response - - self.assertEqual( - self.client.check_payment_information_for_app("yourhypernodeappname"), - self.mock_request.return_value, - ) diff --git a/tests/client/test_check_xgrade.py b/tests/client/test_check_xgrade.py deleted file mode 100644 index 5f53752..0000000 --- a/tests/client/test_check_xgrade.py +++ /dev/null @@ -1,36 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_APP_XGRADE_CHECK_ENDPOINT, -) - - -class TestCheckXGrade(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="mytoken") - self.mock_request = Mock() - self.client.requests = self.mock_request - - def test_xgrade_check_endpoint_is_correct(self): - self.assertEqual( - "/v2/app/xgrade/{}/check/{}/", HYPERNODE_API_APP_XGRADE_CHECK_ENDPOINT - ) - - def test_calls_xgrade_check_endpoint_properly(self): - self.client.check_xgrade("yourhypernodeappname", "MAGG201908") - - self.mock_request.assert_called_once_with( - "GET", "/v2/app/xgrade/yourhypernodeappname/check/MAGG201908/" - ) - - def test_returns_check_xgrade_data(self): - response = Mock() - response.status_code = 200 - self.mock_request.return_value = response - - self.assertEqual( - self.client.check_xgrade("yourhypernodeappname", "MAGG201908"), - self.mock_request.return_value, - ) diff --git a/tests/client/test_create_brancher.py b/tests/client/test_create_brancher.py deleted file mode 100644 index f980681..0000000 --- a/tests/client/test_create_brancher.py +++ /dev/null @@ -1,36 +0,0 @@ -from unittest import TestCase -from unittest.mock import Mock - -from hypernode_api_python.client import ( - HYPERNODE_API_BRANCHER_APP_ENDPOINT, - HypernodeAPIPython, -) - - -class TestCreateBrancher(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="my_token") - self.mock_request = Mock() - self.client.requests = self.mock_request - self.app_name = "my_app" - - def test_brancher_endpoint_is_correct(self): - self.assertEqual("/v2/brancher/app/{}/", HYPERNODE_API_BRANCHER_APP_ENDPOINT) - - def test_calls_create_brancher_endpoint_properly(self): - data = {"clear_services": ["mysql"]} - self.client.create_brancher(self.app_name, data) - - self.mock_request.assert_called_once_with( - "POST", f"/v2/brancher/app/{self.app_name}/", data=data - ) - - def test_returns_create_brancher_data(self): - response = Mock() - response.status_code = 200 - self.mock_request.return_value = response - - self.assertEqual( - self.client.create_brancher(self.app_name, {}), - self.mock_request.return_value, - ) diff --git a/tests/client/test_destroy_brancher.py b/tests/client/test_destroy_brancher.py deleted file mode 100644 index 698ef4a..0000000 --- a/tests/client/test_destroy_brancher.py +++ /dev/null @@ -1,35 +0,0 @@ -from unittest import TestCase -from unittest.mock import Mock - -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_BRANCHER_ENDPOINT, -) - - -class TestDestroyBrancher(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="my_token") - self.mock_request = Mock() - self.client.requests = self.mock_request - self.brancher_name = "app-branchermobyname" - - def test_brancher_endpoint_is_correct(self): - self.assertEqual("/v2/brancher/{}/", HYPERNODE_API_BRANCHER_ENDPOINT) - - def test_calls_destroy_brancher_endpoint_properly(self): - self.client.destroy_brancher(self.brancher_name) - - self.mock_request.assert_called_once_with( - "DELETE", HYPERNODE_API_BRANCHER_ENDPOINT.format(self.brancher_name) - ) - - def test_returns_destroy_brancher_data(self): - response = Mock() - response.status_code = 204 - self.mock_request.return_value = response - - self.assertEqual( - self.client.destroy_brancher(self.brancher_name), - self.mock_request.return_value, - ) diff --git a/tests/client/test_get_active_branchers.py b/tests/client/test_get_active_branchers.py deleted file mode 100644 index bb71fc9..0000000 --- a/tests/client/test_get_active_branchers.py +++ /dev/null @@ -1,35 +0,0 @@ -from unittest import TestCase -from unittest.mock import Mock - -from hypernode_api_python.client import ( - HYPERNODE_API_BRANCHER_APP_ENDPOINT, - HypernodeAPIPython, -) - - -class TestGetActiveBranchers(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="my_token") - self.mock_request = Mock() - self.client.requests = self.mock_request - self.app_name = "my_app" - - def test_brancher_endpoint_is_correct(self): - self.assertEqual("/v2/brancher/app/{}/", HYPERNODE_API_BRANCHER_APP_ENDPOINT) - - def test_calls_get_active_branchers_endpoint_properly(self): - self.client.get_active_branchers(self.app_name) - - self.mock_request.assert_called_once_with( - "GET", f"/v2/brancher/app/{self.app_name}/" - ) - - def test_returns_active_branchers_data(self): - response = Mock() - response.status_code = 200 - self.mock_request.return_value = response - - self.assertEqual( - self.client.get_active_branchers(self.app_name), - self.mock_request.return_value, - ) diff --git a/tests/client/test_get_active_products.py b/tests/client/test_get_active_products.py deleted file mode 100644 index 17c82d2..0000000 --- a/tests/client/test_get_active_products.py +++ /dev/null @@ -1,31 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_PRODUCT_LIST_ENDPOINT, -) - - -class TestGetActiveProducts(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="mytoken") - self.mock_request = Mock() - self.client.requests = self.mock_request - - def test_product_endpoint_is_correct(self): - self.assertEqual("/v2/product/", HYPERNODE_API_PRODUCT_LIST_ENDPOINT) - - def test_calls_product_list_endpoint_properly(self): - self.client.get_active_products() - - self.mock_request.assert_called_once_with("GET", "/v2/product/") - - def test_returns_product_data(self): - response = Mock() - response.status_code = 200 - self.mock_request.return_value = response - - self.assertEqual( - self.client.get_active_products(), self.mock_request.return_value - ) diff --git a/tests/client/test_get_all_flows.py b/tests/client/test_get_all_flows.py deleted file mode 100644 index ea87878..0000000 --- a/tests/client/test_get_all_flows.py +++ /dev/null @@ -1,146 +0,0 @@ -from unittest.mock import Mock, call - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, -) - - -class TestGetAllFlows(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="mytoken") - self.mock_request = Mock() - self.client.requests = self.mock_request - self.flow1 = { - "uuid": "e1db2b60-882d-4b43-8910-ce6d38ca5393", - "state": None, - "name": "create_backup", - "created_at": "2023-03-05T14:13:21Z", - "updated_at": None, - "progress": {"running": [], "total": 0, "completed": 0}, - "logbook": "my_app", - "tracker": {"uuid": None, "description": None}, - } - self.flow2 = { - "uuid": "03bd6e10-5493-4ee8-92fc-cc429faebead", - "state": None, - "name": "update_node", - "created_at": "2023-03-05T14:01:56Z", - "updated_at": None, - "progress": {"running": [], "total": 0, "completed": 0}, - "logbook": "my_app", - "tracker": { - "uuid": "0dd83d83-6b9b-4fb5-9665-79fcf8235069", - "description": None, - }, - } - - def test_get_all_flows_returns_flows_if_only_one_page(self): - self.mock_request.return_value.json.return_value = { - "count": 2, - "next": None, - "previous": None, - "results": [self.flow1, self.flow2], - } - - ret = self.client.get_all_flows("my_app") - - expected_calls = [ - call("GET", "/logbook/v1/logbooks/my_app/flows"), - call().json(), - ] - self.assertEqual(expected_calls, self.mock_request.mock_calls) - expected_results = [self.flow1, self.flow2] - self.assertEqual(expected_results, ret) - - def test_get_all_flows_returns_flows_if_only_one_page_but_limited_results_requested( - self, - ): - self.mock_request.return_value.json.return_value = { - "count": 2, - "next": None, - "previous": None, - "results": [self.flow1, self.flow2], - } - - ret = self.client.get_all_flows("my_app", limit=1) - - expected_calls = [ - call("GET", "/logbook/v1/logbooks/my_app/flows"), - call().json(), - ] - self.assertEqual(expected_calls, self.mock_request.mock_calls) - expected_results = [self.flow1] - self.assertEqual(expected_results, ret) - - def test_get_all_flows_returns_flows_if_more_than_one_page(self): - self.mock_request.return_value.json.side_effect = [ - { - "count": 101, - "next": "https://api.hypernode.com/logbook/v1/logbooks/my_app/flows/?limit=50&offset=50", - "previous": None, - "results": [self.flow1, self.flow2] * 25, - }, - { - "count": 101, - "next": "https://api.hypernode.com/logbook/v1/logbooks/my_app/flows/?limit=50&offset=100", - "previous": "https://api.hypernode.com/logbook/v1/logbooks/my_app/flows/?limit=50&offset=50", - "results": [self.flow1, self.flow2] * 25, - }, - { - "count": 101, - "next": None, - "previous": "https://api.hypernode.com/logbook/v1/logbooks/my_app/flows/?limit=50&offset=100", - "results": [self.flow1], - }, - ] - - ret = self.client.get_all_flows("my_app") - - expected_calls = [ - call("GET", "/logbook/v1/logbooks/my_app/flows"), - call().json(), - call("GET", "/logbook/v1/logbooks/my_app/flows/?limit=50&offset=50"), - call().json(), - call("GET", "/logbook/v1/logbooks/my_app/flows/?limit=50&offset=100"), - call().json(), - ] - self.assertEqual(expected_calls, self.mock_request.mock_calls) - expected_results = [self.flow1, self.flow2] * 50 + [self.flow1] - self.assertEqual(expected_results, ret) - - def test_get_all_flows_returns_flows_if_more_than_one_page_but_limited_results_requested( - self, - ): - self.mock_request.return_value.json.side_effect = [ - { - "count": 101, - "next": "https://api.hypernode.com/logbook/v1/logbooks/my_app/flows/?limit=50&offset=50", - "previous": None, - "results": [self.flow1, self.flow2] * 25, - }, - { - "count": 101, - "next": "https://api.hypernode.com/logbook/v1/logbooks/my_app/flows/?limit=50&offset=100", - "previous": "https://api.hypernode.com/logbook/v1/logbooks/my_app/flows/?limit=50&offset=50", - "results": [self.flow1, self.flow2] * 25, - }, - { - "count": 101, - "next": None, - "previous": "https://api.hypernode.com/logbook/v1/logbooks/my_app/flows/?limit=50&offset=100", - "results": [self.flow1], - }, - ] - - ret = self.client.get_all_flows("my_app", limit=51) - - expected_calls = [ - call("GET", "/logbook/v1/logbooks/my_app/flows"), - call().json(), - call("GET", "/logbook/v1/logbooks/my_app/flows/?limit=50&offset=50"), - call().json(), - ] - self.assertEqual(expected_calls, self.mock_request.mock_calls) - expected_results = [self.flow1, self.flow2] * 25 + [self.flow1] - self.assertEqual(expected_results, ret) diff --git a/tests/client/test_get_app_configuration.py b/tests/client/test_get_app_configuration.py deleted file mode 100644 index e3719ca..0000000 --- a/tests/client/test_get_app_configuration.py +++ /dev/null @@ -1,33 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_APP_CONFIGURATION_ENDPOINT, -) - - -class TestGetAppConfiguration(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="mytoken") - self.mock_request = Mock() - self.client.requests = self.mock_request - - def test_api_app_configuration_list_endpoint_is_correct(self): - self.assertEqual("/v2/configuration/", HYPERNODE_API_APP_CONFIGURATION_ENDPOINT) - - def test_calls_app_configuration_endpoint_properly(self): - self.client.get_app_configurations() - - self.mock_request.assert_called_once_with( - "GET", HYPERNODE_API_APP_CONFIGURATION_ENDPOINT - ) - - def test_returns_app_configuration_data(self): - response = Mock() - response.status_code = 200 - self.mock_request.return_value = response - - self.assertEqual( - self.client.get_app_configurations(), self.mock_request.return_value - ) diff --git a/tests/client/test_get_app_eav_description.py b/tests/client/test_get_app_eav_description.py deleted file mode 100644 index 70fec7b..0000000 --- a/tests/client/test_get_app_eav_description.py +++ /dev/null @@ -1,27 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, -) - - -class TestGetAppDescription(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="mytoken") - self.mock_request = Mock() - self.client.requests = self.mock_request - - def test_calls_hypernode_api_app_eav_description_endpoint(self): - self.client.get_app_eav_description() - - self.mock_request.assert_called_once_with("GET", "/v2/app/eav_descriptions/") - - def test_returns_response_json_if_hypernode_api_returns_description(self): - response = Mock() - response.status_code = 200 - self.mock_request.return_value = response - - ret = self.client.get_app_eav_description() - - self.assertEqual(ret, self.mock_request.return_value) diff --git a/tests/client/test_get_app_endpoint_or_404.py b/tests/client/test_get_app_endpoint_or_404.py deleted file mode 100644 index ac13106..0000000 --- a/tests/client/test_get_app_endpoint_or_404.py +++ /dev/null @@ -1,45 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, -) - - -class TestGetAppEndpointOr404(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="mytoken") - self.mock_request = Mock() - self.client.requests = self.mock_request - - def test_get_app_endpoint_or_404_gets_endpoint(self): - self.client._get_app_endpoint_or_404("yourhypernodeappname", "/v2/app/{}/") - - self.mock_request.assert_called_once_with( - "GET", "/v2/app/yourhypernodeappname/" - ) - - def test_raises_runtime_error_if_hypernode_api_returns_404(self): - response = Mock() - response.status_code = 404 - self.mock_request.return_value = response - - with self.assertRaises(RuntimeError): - self.client._get_app_endpoint_or_404("yourhypernodeappname", "/v2/app/{}/") - - def test_raises_specified_error_if_hypernode_api_returns_404(self): - response = Mock() - response.status_code = 404 - self.mock_request.return_value = response - - with self.assertRaises(OSError): - self.client._get_app_endpoint_or_404( - "yourhypernodeappname", "/v2/app/{}/", error_to_raise=OSError - ) - - def test_get_app_endpoint_or_404_returns_response(self): - ret = self.client._get_app_endpoint_or_404( - "yourhypernodeappname", "/v2/app/{}/" - ) - - self.assertEqual(ret, self.mock_request.return_value) diff --git a/tests/client/test_get_app_flavor.py b/tests/client/test_get_app_flavor.py deleted file mode 100644 index 3cdaa33..0000000 --- a/tests/client/test_get_app_flavor.py +++ /dev/null @@ -1,27 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_APP_FLAVOR_ENDPOINT, -) - - -class TestGetAppFlavor(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="mytoken") - self.mock_request = Mock() - self.client.requests = self.mock_request - - def test_app_flavor_endpoint_is_correct(self): - self.assertEqual("/v2/app/{}/flavor/", HYPERNODE_API_APP_FLAVOR_ENDPOINT) - - def test_calls_app_flavor_endpoint_properly(self): - self.client.get_app_flavor("my_app") - - self.mock_request.assert_called_once_with("GET", "/v2/app/my_app/flavor/") - - def test_returns_app_flavor_data(self): - self.assertEqual( - self.client.get_app_flavor("my_app"), self.mock_request.return_value - ) diff --git a/tests/client/test_get_app_info_or_404.py b/tests/client/test_get_app_info_or_404.py deleted file mode 100644 index f43a6ae..0000000 --- a/tests/client/test_get_app_info_or_404.py +++ /dev/null @@ -1,37 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, -) - - -class TestGetAppInfoOr404(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="mytoken") - self.mock_request = Mock() - self.client.requests = self.mock_request - - def test_calls_hypernode_api_app_endpoint_with_app_name(self): - self.client.get_app_info_or_404("yourhypernodeappname") - - self.mock_request.assert_called_once_with( - "GET", "/v2/app/yourhypernodeappname/?destroyed=false" - ) - - def test_raises_if_hypernode_api_returns_404(self): - response = Mock() - response.status_code = 404 - self.mock_request.return_value = response - - with self.assertRaises(RuntimeError): - self.client.get_app_info_or_404("yourhypernodeappname") - - def test_returns_response_json_if_hypernode_api_returns_app_info(self): - response = Mock() - response.status_code = 200 - self.mock_request.return_value = response - - ret = self.client.get_app_info_or_404("yourhypernodeappname") - - self.assertEqual(ret, self.mock_request.return_value) diff --git a/tests/client/test_get_available_backups_for_app.py b/tests/client/test_get_available_backups_for_app.py deleted file mode 100644 index d15f721..0000000 --- a/tests/client/test_get_available_backups_for_app.py +++ /dev/null @@ -1,30 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_BACKUPS_ENDPOINT, -) - - -class TestGetAvailableBackupsForApp(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="mytoken") - self.mock_request = Mock() - self.client.requests = self.mock_request - - def test_api_available_backups_endpoint_is_correct(self): - self.assertEqual("/v2/app/{}/backup/", HYPERNODE_API_BACKUPS_ENDPOINT) - - def test_calls_available_backups_endpoint_properly(self): - self.client.get_available_backups_for_app("myhypernodeappname") - - self.mock_request.assert_called_once_with( - "GET", "/v2/app/myhypernodeappname/backup/" - ) - - def test_returns_available_backups_data(self): - self.assertEqual( - self.client.get_available_backups_for_app("myhypernodeappname"), - self.mock_request.return_value, - ) diff --git a/tests/client/test_get_block_attack_descriptions.py b/tests/client/test_get_block_attack_descriptions.py deleted file mode 100644 index ebd3275..0000000 --- a/tests/client/test_get_block_attack_descriptions.py +++ /dev/null @@ -1,37 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_BLOCK_ATTACK_DESCRIPTION_ENDPOINT, -) - - -class TestGetBlockAttackDescriptions(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="mytoken") - self.mock_request = Mock() - self.client.requests = self.mock_request - - def test_block_attack_descriptions_endpoint_is_correct(self): - self.assertEqual( - "/v2/app/block_attack_descriptions/", - HYPERNODE_API_BLOCK_ATTACK_DESCRIPTION_ENDPOINT, - ) - - def test_calls_block_attack_descriptions_detail_endpoint_properly(self): - self.client.get_block_attack_descriptions() - - self.mock_request.assert_called_once_with( - "GET", "/v2/app/block_attack_descriptions/" - ) - - def test_returns_block_attack_descriptions(self): - response = Mock() - response.status_code = 200 - self.mock_request.return_value = response - - self.assertEqual( - self.client.get_block_attack_descriptions(), - self.mock_request.return_value, - ) diff --git a/tests/client/test_get_cluster_relations.py b/tests/client/test_get_cluster_relations.py deleted file mode 100644 index 4b5b7ec..0000000 --- a/tests/client/test_get_cluster_relations.py +++ /dev/null @@ -1,28 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_APP_CLUSTER_RELATIONS, -) - - -class TestGetClusterRelations(TestCase): - def setUp(self): - self.mock_request = Mock() - self.client = HypernodeAPIPython(token="mytoken") - self.client.requests = self.mock_request - - def test_calls_hypernode_api_cluster_relations_endpoint_with_correct_parameters( - self, - ): - self.client.get_cluster_relations("yourhypernodeappname") - - self.mock_request.assert_called_once_with( - "GET", HYPERNODE_API_APP_CLUSTER_RELATIONS.format("yourhypernodeappname") - ) - - def test_returns_result_for_hypernode_api_cluster_relations(self): - ret = self.client.get_cluster_relations("yourhypernodeappname") - - self.assertEqual(ret, self.mock_request.return_value) diff --git a/tests/client/test_get_current_product_for_app.py b/tests/client/test_get_current_product_for_app.py deleted file mode 100644 index 4d155bb..0000000 --- a/tests/client/test_get_current_product_for_app.py +++ /dev/null @@ -1,36 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_PRODUCT_APP_DETAIL_ENDPOINT, -) - - -class TestGetCurrentProductForApp(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="mytoken") - self.mock_request = Mock() - self.client.requests = self.mock_request - - def test_product_app_endpoint_is_correct(self): - self.assertEqual( - "/v2/product/app/{}/current/", HYPERNODE_API_PRODUCT_APP_DETAIL_ENDPOINT - ) - - def test_calls_product_app_detail_endpoint_properly(self): - self.client.get_current_product_for_app("yourhypernodeappname") - - self.mock_request.assert_called_once_with( - "GET", "/v2/product/app/yourhypernodeappname/current/" - ) - - def test_returns_product_data(self): - response = Mock() - response.status_code = 200 - self.mock_request.return_value = response - - self.assertEqual( - self.client.get_current_product_for_app("yourhypernodeappname"), - self.mock_request.return_value, - ) diff --git a/tests/client/test_get_flows.py b/tests/client/test_get_flows.py deleted file mode 100644 index 8fcb2d4..0000000 --- a/tests/client/test_get_flows.py +++ /dev/null @@ -1,31 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_APP_FLOWS_ENDPOINT, -) - - -class TestGetFlows(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="mytoken") - self.mock_request = Mock() - self.client.requests = self.mock_request - - def test_flows_endpoint_is_correct(self): - self.assertEqual( - "/logbook/v1/logbooks/{}/flows", HYPERNODE_API_APP_FLOWS_ENDPOINT - ) - - def test_calls_flows_endpoint_properly(self): - self.client.get_flows("my_app") - - self.mock_request.assert_called_once_with( - "GET", "/logbook/v1/logbooks/my_app/flows" - ) - - def test_returns_flows_data(self): - self.assertEqual( - self.client.get_flows("my_app"), self.mock_request.return_value - ) diff --git a/tests/client/test_get_fpm_status.py b/tests/client/test_get_fpm_status.py deleted file mode 100644 index 8f54034..0000000 --- a/tests/client/test_get_fpm_status.py +++ /dev/null @@ -1,39 +0,0 @@ -from unittest import TestCase -from unittest.mock import Mock - -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_FPM_STATUS_APP_ENDPOINT, -) - - -class TestGetFPMStatus(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="my_token") - self.mock_request = Mock() - self.client.requests = self.mock_request - self.app_name = "my_app" - - def test_get_fpm_status_endpoint_is_correct(self): - self.assertEqual( - "/v2/nats/{}/hypernode.show-fpm-status", - HYPERNODE_API_FPM_STATUS_APP_ENDPOINT, - ) - - def test_calls_fpm_status_endpoint_properly(self): - self.client.get_fpm_status(self.app_name) - - self.mock_request.assert_called_once_with( - "POST", - f"/v2/nats/{self.app_name}/hypernode.show-fpm-status", - ) - - def test_returns_fpm_status_data(self): - response = Mock() - response.status_code = 200 - self.mock_request.return_value = response - - self.assertEqual( - self.client.get_fpm_status(self.app_name), - self.mock_request.return_value, - ) diff --git a/tests/client/test_get_next_best_plan_for_app_or_404.py b/tests/client/test_get_next_best_plan_for_app_or_404.py deleted file mode 100644 index cb2b97f..0000000 --- a/tests/client/test_get_next_best_plan_for_app_or_404.py +++ /dev/null @@ -1,37 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, -) - - -class TestGetNextBestPlanForAppOr404(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="mytoken") - self.mock_request = Mock() - self.client.requests = self.mock_request - - def test_calls_hypernode_api_app_next_best_plan_endpoint_with_app_name(self): - self.client.get_next_best_plan_for_app_or_404("yourhypernodeappname") - - self.mock_request.assert_called_once_with( - "GET", "/v2/app/yourhypernodeappname/next_best_plan/" - ) - - def test_raises_if_hypernode_api_returns_404_for_next_best_plan_app(self): - response = Mock() - response.status_code = 404 - self.mock_request.return_value = response - - with self.assertRaises(RuntimeError): - self.client.get_next_best_plan_for_app_or_404("yourhypernodeappname") - - def test_returns_response_json_if_hypernode_api_returns_next_best_plan(self): - response = Mock() - response.status_code = 200 - self.mock_request.return_value = response - - ret = self.client.get_next_best_plan_for_app_or_404("yourhypernodeappname") - - self.assertEqual(ret, self.mock_request.return_value) diff --git a/tests/client/test_get_product_info_with_price.py b/tests/client/test_get_product_info_with_price.py deleted file mode 100644 index 1774df4..0000000 --- a/tests/client/test_get_product_info_with_price.py +++ /dev/null @@ -1,38 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_PRODUCT_PRICE_DETAIL_ENDPOINT, -) - - -class TestGetProductInfoWithPrice(TestCase): - def setUp(self): - self.mock_request = Mock() - self.client = HypernodeAPIPython(token="mytoken") - self.client.requests = self.mock_request - - def test_calls_hypernode_api_product_endpoint_with_correct_parameters(self): - self.client.get_product_info_with_price("MAGS") - - self.mock_request.assert_called_once_with( - "GET", HYPERNODE_API_PRODUCT_PRICE_DETAIL_ENDPOINT.format("MAGS") - ) - - def test_returns_json_result(self): - ret = self.client.get_product_info_with_price("MAGS") - - self.assertEqual(ret, self.mock_request.return_value) - - def test_raises_runtime_error_when_request_returns_404(self): - self.mock_request.return_value = Mock(status_code=404) - - with self.assertRaises(RuntimeError): - self.client.get_product_info_with_price("MAGS") - - def test_raises_specified_error_when_request_returns_404(self): - self.mock_request.return_value = Mock(status_code=404) - - with self.assertRaises(OSError): - self.client.get_product_info_with_price("MAGS", error_to_raise=OSError) diff --git a/tests/client/test_get_sla.py b/tests/client/test_get_sla.py deleted file mode 100644 index 02a0487..0000000 --- a/tests/client/test_get_sla.py +++ /dev/null @@ -1,26 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_ADDON_LIST_ENDPOINT, -) - - -class TestGetSla(TestCase): - def setUp(self): - self.mock_request = Mock() - self.client = HypernodeAPIPython(token="mytoken") - self.client.requests = self.mock_request - - def test_calls_hypernode_api_endpoint_with_correct_parameters(self): - self.client.get_sla("sla-standard") - - self.mock_request.assert_called_once_with( - "GET", HYPERNODE_API_ADDON_LIST_ENDPOINT + "sla-standard/" - ) - - def test_returns_json_result(self): - ret = self.client.get_sla("sla-standard") - - self.assertEqual(ret, self.mock_request.return_value) diff --git a/tests/client/test_get_slas.py b/tests/client/test_get_slas.py deleted file mode 100644 index 0e186f8..0000000 --- a/tests/client/test_get_slas.py +++ /dev/null @@ -1,26 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_ADDON_SLA_LIST_ENDPOINT, -) - - -class TestGetSlas(TestCase): - def setUp(self): - self.mock_request = Mock() - self.client = HypernodeAPIPython(token="mytoken") - self.client.requests = self.mock_request - - def test_calls_hypernode_api_endpoint_with_correct_parameters(self): - self.client.get_slas() - - self.mock_request.assert_called_once_with( - "GET", HYPERNODE_API_ADDON_SLA_LIST_ENDPOINT - ) - - def test_returns_json_result(self): - ret = self.client.get_slas() - - self.assertEqual(ret, self.mock_request.return_value) diff --git a/tests/client/test_get_whitelist_options.py b/tests/client/test_get_whitelist_options.py deleted file mode 100644 index f2e8453..0000000 --- a/tests/client/test_get_whitelist_options.py +++ /dev/null @@ -1,28 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_WHITELIST_ENDPOINT, -) - - -class TestGetWhitelistOptions(TestCase): - def setUp(self): - self.mock_request = Mock() - self.client = HypernodeAPIPython(token="mytoken") - self.client.requests = self.mock_request - - def test_calls_hypernode_api_whitelist_options_endpoint_with_correct_parameters( - self, - ): - self.client.get_whitelist_options("yourhypernodeappname") - - self.mock_request.assert_called_once_with( - "OPTIONS", HYPERNODE_API_WHITELIST_ENDPOINT.format("yourhypernodeappname") - ) - - def test_returns_json_result_for_hypernode_api_whitelist_options(self): - ret = self.client.get_whitelist_options("yourhypernodeappname") - - self.assertEqual(ret, self.mock_request.return_value) diff --git a/tests/client/test_get_whitelist_rules.py b/tests/client/test_get_whitelist_rules.py deleted file mode 100644 index 93f4d94..0000000 --- a/tests/client/test_get_whitelist_rules.py +++ /dev/null @@ -1,41 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_WHITELIST_ENDPOINT, -) - - -class TestGetWhitelistRules(TestCase): - def setUp(self): - self.mock_request = Mock() - self.client = HypernodeAPIPython(token="mytoken") - self.client.requests = self.mock_request - - def test_calls_hypernode_api_whitelist_rules_endpoint_with_correct_parameters( - self, - ): - self.client.get_whitelist_rules("yourhypernodeappname") - - self.mock_request.assert_called_once_with( - "GET", HYPERNODE_API_WHITELIST_ENDPOINT.format("yourhypernodeappname"), {} - ) - - def test_calls_hypernode_api_whitelist_rules_endpoint_with_correct_parameters_if_filter_specified( - self, - ): - self.client.get_whitelist_rules( - "yourhypernodeappname", filter_data={"type": "waf"} - ) - - self.mock_request.assert_called_once_with( - "GET", - HYPERNODE_API_WHITELIST_ENDPOINT.format("yourhypernodeappname"), - {"type": "waf"}, - ) - - def test_returns_json_result_for_hypernode_api_whitelist_rules(self): - ret = self.client.get_whitelist_rules("yourhypernodeappname") - - self.assertEqual(ret, self.mock_request.return_value) diff --git a/tests/client/test_order_hypernode.py b/tests/client/test_order_hypernode.py deleted file mode 100644 index d3fbf85..0000000 --- a/tests/client/test_order_hypernode.py +++ /dev/null @@ -1,36 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_APP_ORDER_ENDPOINT, -) - - -class TestOrderHypernode(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="mytoken") - self.mock_request = Mock() - self.client.requests = self.mock_request - - def test_order_hypernode_endpoint_is_correct(self): - self.assertEqual("/v2/app/order/", HYPERNODE_API_APP_ORDER_ENDPOINT) - - def test_calls_order_hypernode_endpoint_properly(self): - data = { - "app_name": "mynewhypernodeappnameofmax16chars", - "product": "FALCON_S_202203", - "initial_app_configuration": "magento_2", - } - self.client.order_hypernode(data) - - self.mock_request.assert_called_once_with("POST", "/v2/app/order/", data=data) - - def test_returns_hypernode_data(self): - response = Mock() - response.status_code = 200 - self.mock_request.return_value = response - - self.assertEqual( - self.client.order_hypernode({}), self.mock_request.return_value - ) diff --git a/tests/client/test_set_app_setting.py b/tests/client/test_set_app_setting.py deleted file mode 100644 index cd6c7fe..0000000 --- a/tests/client/test_set_app_setting.py +++ /dev/null @@ -1,37 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_APP_DETAIL_ENDPOINT, -) - - -class TestSetAppSetting(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="mytoken") - self.mock_request = Mock() - self.client.requests = self.mock_request - - def test_set_app_setting_endpoint_is_correct(self): - self.assertEqual( - "/v2/app/{}/?destroyed=false", HYPERNODE_API_APP_DETAIL_ENDPOINT - ) - - def test_calls_set_app_setting_endpoint_properly(self): - self.client.set_app_setting("yourhypernodeappname", "php_version", "8.1") - - expected_data = {"php_version": "8.1"} - self.mock_request.assert_called_once_with( - "PATCH", "/v2/app/yourhypernodeappname/?destroyed=false", data=expected_data - ) - - def test_returns_set_app_setting_data(self): - response = Mock() - response.status_code = 200 - self.mock_request.return_value = response - - self.assertEqual( - self.client.set_app_setting("yourhypernodeappname", "php_version", "8.1"), - self.mock_request.return_value, - ) diff --git a/tests/client/test_validate_app_name.py b/tests/client/test_validate_app_name.py deleted file mode 100644 index 33fa3fa..0000000 --- a/tests/client/test_validate_app_name.py +++ /dev/null @@ -1,68 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_VALIDATE_APP_NAME_ENDPOINT, -) - - -class TestValidateAppName(TestCase): - def setUp(self): - self.mock_request = Mock() - self.client = HypernodeAPIPython(token="mytoken") - self.client.requests = self.mock_request - - def test_hypernode_api_validate_app_name_endpoint_is_correct(self): - self.assertEqual( - HYPERNODE_API_VALIDATE_APP_NAME_ENDPOINT, "/v2/app/name/validate/" - ) - - def test_calls_validate_appname_endpoint_with_correct_parameters(self): - response = Mock() - response.content = "" - self.mock_request.return_value = response - - self.client.validate_app_name("yourhypernodeappname") - - self.mock_request.assert_called_once_with( - "GET", - HYPERNODE_API_VALIDATE_APP_NAME_ENDPOINT, - params={"name": "yourhypernodeappname"}, - ) - - def test_raises_runtime_error_if_non_empty_response(self): - with self.assertRaises(RuntimeError): - self.client.validate_app_name("yourhypernodeappname") - - def test_raises_specified_error_if_non_empty_response_and_error_to_raise_specified( - self, - ): - with self.assertRaises(OSError): - self.client.validate_app_name( - "yourhypernodeappname", error_to_raise=OSError - ) - - def test_raises_runtime_errors_if_json_result_is_dict(self): - response = Mock() - response.json.return_value = {"errors": ["fake-error"]} - self.mock_request.return_value = response - - with self.assertRaises(RuntimeError): - self.client.validate_app_name("yourhypernodeappname") - - def test_does_not_raise_runtime_error_if_no_content_in_response(self): - response = Mock() - response.content = None - self.mock_request.return_value = response - - # Should not raise - self.client.validate_app_name("yourhypernodeappname") - - def test_does_not_raise_runtime_error_if_empty_list_in_response(self): - response = Mock() - response.content.decode.return_value = "[]" - self.mock_request.return_value = response - - # Should not raise - self.client.validate_app_name("yourhypernodeappname") diff --git a/tests/client/test_xgrade.py b/tests/client/test_xgrade.py deleted file mode 100644 index 0e79d1f..0000000 --- a/tests/client/test_xgrade.py +++ /dev/null @@ -1,35 +0,0 @@ -from unittest.mock import Mock - -from tests.testcase import TestCase -from hypernode_api_python.client import ( - HypernodeAPIPython, - HYPERNODE_API_APP_XGRADE_ENDPOINT, -) - - -class TestXGrade(TestCase): - def setUp(self): - self.client = HypernodeAPIPython(token="mytoken") - self.mock_request = Mock() - self.client.requests = self.mock_request - - def test_xgrade_endpoint_is_correct(self): - self.assertEqual("/v2/app/xgrade/{}/", HYPERNODE_API_APP_XGRADE_ENDPOINT) - - def test_calls_xgrade_endpoint_properly(self): - data = {"test": "data"} - self.client.xgrade("yourhypernodeappname", data) - - self.mock_request.assert_called_once_with( - "PATCH", "/v2/app/xgrade/yourhypernodeappname/", data=data - ) - - def test_returns_xgrade_data(self): - response = Mock() - response.status_code = 200 - self.mock_request.return_value = response - - self.assertEqual( - self.client.xgrade("yourhypernodeappname", {}), - self.mock_request.return_value, - ) diff --git a/tests/commands/__init__.py b/tests/commands/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/commands/test_block_attack.py b/tests/commands/test_block_attack.py deleted file mode 100644 index 270ca49..0000000 --- a/tests/commands/test_block_attack.py +++ /dev/null @@ -1,54 +0,0 @@ -from unittest.mock import Mock - -from hypernode_api_python.commands import block_attack -from tests.testcase import TestCase - - -class TestBlockAttack(TestCase): - def setUp(self): - self.print = self.set_up_patch("hypernode_api_python.commands.print") - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - self.get_app_name = self.set_up_patch( - "hypernode_api_python.commands.get_app_name" - ) - self.get_app_name.return_value = "myappname" - self.client.get_block_attack_descriptions.return_value = Mock( - json=lambda: {"BlockSqliBruteForce": "", "BlockDownloaderBruteForce": ""} - ) - self.client.block_attack.return_value = Mock(content="") - - def test_block_attack_gets_client(self): - block_attack(["BlockSqliBruteForce"]) - - self.get_client.assert_called_once_with() - - def test_block_attack_blocks_attack(self): - block_attack(["BlockSqliBruteForce"]) - - self.client.block_attack.assert_called_once_with( - "myappname", "BlockSqliBruteForce" - ) - - def test_block_attack_prints_output_on_success(self): - block_attack(["BlockSqliBruteForce"]) - - self.print.assert_called_once_with( - "A job to block the 'BlockSqliBruteForce' attack has been posted." - ) - - def test_block_attack_prints_output_on_failure(self): - self.client.block_attack.return_value = Mock( - content='{"attack_name":["\\"BlockDownloaderBruteForce\\" is not a valid choice."]}' - ) - - block_attack(["BlockDownloaderBruteForce"]) - - self.print.assert_called_once_with( - '{"attack_name":["\\"BlockDownloaderBruteForce\\" is not a valid choice."]}' - ) - - def test_block_attack_raises_on_invalid_choice(self): - with self.assertRaises(SystemExit): - # We get the valid choices from get_block_attack_descriptions - block_attack(["DoesNotExist"]) diff --git a/tests/commands/test_check_payment_information_for_app.py b/tests/commands/test_check_payment_information_for_app.py deleted file mode 100644 index 114cb39..0000000 --- a/tests/commands/test_check_payment_information_for_app.py +++ /dev/null @@ -1,39 +0,0 @@ -from hypernode_api_python.commands import check_payment_information_for_app -from tests.testcase import TestCase - - -class TestCheckPaymentInformationForApp(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - self.get_app_name = self.set_up_patch( - "hypernode_api_python.commands.get_app_name" - ) - self.get_app_name.return_value = "myappname" - - def test_check_payment_information_for_app_gets_client(self): - check_payment_information_for_app([]) - - self.get_client.assert_called_once_with() - - def test_check_payment_information_for_app_gets_app_name(self): - check_payment_information_for_app([]) - - self.get_app_name.assert_called_once_with() - - def test_check_payment_information_for_app_gets_current_product_for_app(self): - check_payment_information_for_app([]) - - self.client.check_payment_information_for_app.assert_called_once_with( - "myappname" - ) - - def test_check_payment_information_for_app_prints_current_product_for_app(self): - check_payment_information_for_app([]) - - self.print_response.assert_called_once_with( - self.client.check_payment_information_for_app.return_value - ) diff --git a/tests/commands/test_check_xgrade.py b/tests/commands/test_check_xgrade.py deleted file mode 100644 index 8ee612e..0000000 --- a/tests/commands/test_check_xgrade.py +++ /dev/null @@ -1,49 +0,0 @@ -from hypernode_api_python.commands import check_xgrade -from tests.testcase import TestCase - - -class TestCheckXgrade(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - self.get_app_name = self.set_up_patch( - "hypernode_api_python.commands.get_app_name" - ) - self.get_app_name.return_value = "myappname" - self.client.get_active_products.return_value.json.return_value = [ - { - "code": "FALCON_S_202203", - }, - { - "code": "JACKAL_M_202201", - }, - ] - - def test_check_xgrade_gets_client(self): - check_xgrade(["FALCON_S_202203"]) - - self.get_client.assert_called_once_with() - - def test_check_xgrade_gets_app_name(self): - check_xgrade(["FALCON_S_202203"]) - - self.get_app_name.assert_called_once_with() - - def test_check_xgrade_gets_product_info(self): - check_xgrade(["FALCON_S_202203"]) - - self.client.check_xgrade.assert_called_once_with("myappname", "FALCON_S_202203") - - def test_check_xgrade_prints_product(self): - check_xgrade(["FALCON_S_202203"]) - - self.print_response.assert_called_once_with( - self.client.check_xgrade.return_value - ) - - def test_check_xgrade_raises_if_product_not_found(self): - with self.assertRaises(SystemExit): - check_xgrade(["ProductThatDoesNotExist"]) diff --git a/tests/commands/test_create_brancher.py b/tests/commands/test_create_brancher.py deleted file mode 100644 index d198775..0000000 --- a/tests/commands/test_create_brancher.py +++ /dev/null @@ -1,40 +0,0 @@ -from hypernode_api_python.commands import create_brancher -from tests.testcase import TestCase - - -class TestCreateBrancher(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - self.get_app_name = self.set_up_patch( - "hypernode_api_python.commands.get_app_name" - ) - self.get_app_name.return_value = "myappname" - - def test_create_brancher_gets_client(self): - create_brancher([]) - - self.get_client.assert_called_once_with() - - def test_create_brancher_gets_app_name(self): - create_brancher([]) - - self.get_app_name.assert_called_once_with() - - def test_create_brancher_creates_brancher(self): - create_brancher([]) - - expected_data = {} - self.client.create_brancher.assert_called_once_with( - "myappname", data=expected_data - ) - - def test_create_brancher_prints_response(self): - create_brancher([]) - - self.print_response.assert_called_once_with( - self.client.create_brancher.return_value - ) diff --git a/tests/commands/test_destroy_brancher.py b/tests/commands/test_destroy_brancher.py deleted file mode 100644 index 56bfc83..0000000 --- a/tests/commands/test_destroy_brancher.py +++ /dev/null @@ -1,53 +0,0 @@ -from os import EX_UNAVAILABLE, EX_OK - -from hypernode_api_python.commands import destroy_brancher -from tests.testcase import TestCase - - -class TestDestroyBrancher(TestCase): - def setUp(self): - self.exit = self.set_up_patch("hypernode_api_python.commands.exit") - self.print = self.set_up_patch("hypernode_api_python.commands.print") - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - - def test_destroy_brancher_gets_client(self): - destroy_brancher(["myappname-eph1234"]) - - self.get_client.assert_called_once_with() - - def test_destroy_brancher_destroys_brancher(self): - destroy_brancher(["myappname-eph1234"]) - - self.client.destroy_brancher.assert_called_once_with("myappname-eph1234") - - def test_destroy_brancher_prints_app_name_is_valid(self): - destroy_brancher(["myappname-eph1234"]) - - self.print.assert_called_once_with( - "A job has been posted to cancel the 'myappname-eph1234' brancher app." - ) - - def test_destroy_brancher_prints_app_name_is_invalid(self): - self.client.destroy_brancher.side_effect = MemoryError("Killed") - - destroy_brancher(["myappname-eph1234"]) - - self.print.assert_called_once_with( - "Brancher app 'myappname-eph1234' failed to be cancelled: Killed" - ) - - def test_destroy_brancher_exits_zero_when_cancel_job_posted(self): - destroy_brancher(["myappname-eph1234"]) - - self.exit.assert_called_once_with(EX_OK) - - def test_destroy_brancher_exits_nonzero_when_unexpected_exception_occurs(self): - self.client.destroy_brancher.side_effect = RuntimeError - - destroy_brancher(["myappname-eph1234"]) - - self.exit.assert_called_once_with(EX_UNAVAILABLE) diff --git a/tests/commands/test_get_active_branchers.py b/tests/commands/test_get_active_branchers.py deleted file mode 100644 index c4b2316..0000000 --- a/tests/commands/test_get_active_branchers.py +++ /dev/null @@ -1,37 +0,0 @@ -from hypernode_api_python.commands import get_active_branchers -from tests.testcase import TestCase - - -class TestGetActiveBranchers(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - self.get_app_name = self.set_up_patch( - "hypernode_api_python.commands.get_app_name" - ) - self.get_app_name.return_value = "myappname" - - def test_get_active_branchers_gets_client(self): - get_active_branchers([]) - - self.get_client.assert_called_once_with() - - def test_get_active_branchers_gets_app_name(self): - get_active_branchers([]) - - self.get_app_name.assert_called_once_with() - - def test_get_active_branchers_gets_active_branchers(self): - get_active_branchers([]) - - self.client.get_active_branchers.assert_called_once_with("myappname") - - def test_get_active_branchers_prints_active_branchers(self): - get_active_branchers([]) - - self.print_response.assert_called_once_with( - self.client.get_active_branchers.return_value - ) diff --git a/tests/commands/test_get_active_products.py b/tests/commands/test_get_active_products.py deleted file mode 100644 index 284fb39..0000000 --- a/tests/commands/test_get_active_products.py +++ /dev/null @@ -1,28 +0,0 @@ -from hypernode_api_python.commands import get_active_products -from tests.testcase import TestCase - - -class TestGetActiveProducts(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - - def test_get_active_products_gets_client(self): - get_active_products([]) - - self.get_client.assert_called_once_with() - - def test_get_active_products_gets_active_products(self): - get_active_products([]) - - self.client.get_active_products.assert_called_once_with() - - def test_get_active_products_prints_active_products(self): - get_active_products([]) - - self.print_response.assert_called_once_with( - self.client.get_active_products.return_value - ) diff --git a/tests/commands/test_get_app_configurations.py b/tests/commands/test_get_app_configurations.py deleted file mode 100644 index e0dff12..0000000 --- a/tests/commands/test_get_app_configurations.py +++ /dev/null @@ -1,28 +0,0 @@ -from hypernode_api_python.commands import get_app_configurations -from tests.testcase import TestCase - - -class TestGetAppConfigurations(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - - def test_get_app_configurations_gets_client(self): - get_app_configurations([]) - - self.get_client.assert_called_once_with() - - def test_get_app_configurations_gets_slas(self): - get_app_configurations([]) - - self.client.get_app_configurations.assert_called_once_with() - - def test_get_app_configurations_prints_slas(self): - get_app_configurations([]) - - self.print_response.assert_called_once_with( - self.client.get_app_configurations.return_value - ) diff --git a/tests/commands/test_get_app_flavor.py b/tests/commands/test_get_app_flavor.py deleted file mode 100644 index 9493448..0000000 --- a/tests/commands/test_get_app_flavor.py +++ /dev/null @@ -1,37 +0,0 @@ -from hypernode_api_python.commands import get_app_flavor -from tests.testcase import TestCase - - -class TestGetAppFlavor(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - self.get_app_name = self.set_up_patch( - "hypernode_api_python.commands.get_app_name" - ) - self.get_app_name.return_value = "myappname" - - def test_get_app_flavor_gets_client(self): - get_app_flavor([]) - - self.get_client.assert_called_once_with() - - def test_get_app_flavor_gets_app_name(self): - get_app_flavor([]) - - self.get_app_name.assert_called_once_with() - - def test_get_app_flavor_gets_app_flavor(self): - get_app_flavor([]) - - self.client.get_app_flavor.assert_called_once_with("myappname") - - def test_get_app_flavor_prints_app_flavor(self): - get_app_flavor([]) - - self.print_response.assert_called_once_with( - self.client.get_app_flavor.return_value - ) diff --git a/tests/commands/test_get_app_info.py b/tests/commands/test_get_app_info.py deleted file mode 100644 index e83ae99..0000000 --- a/tests/commands/test_get_app_info.py +++ /dev/null @@ -1,37 +0,0 @@ -from hypernode_api_python.commands import get_app_info -from tests.testcase import TestCase - - -class TestGetAppInfo(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - self.get_app_name = self.set_up_patch( - "hypernode_api_python.commands.get_app_name" - ) - self.get_app_name.return_value = "myappname" - - def test_get_app_info_gets_client(self): - get_app_info([]) - - self.get_client.assert_called_once_with() - - def test_get_app_info_gets_app_name(self): - get_app_info([]) - - self.get_app_name.assert_called_once_with() - - def test_get_app_info_gets_app_info(self): - get_app_info([]) - - self.client.get_app_info_or_404.assert_called_once_with("myappname") - - def test_get_app_info_prints_app_info(self): - get_app_info([]) - - self.print_response.assert_called_once_with( - self.client.get_app_info_or_404.return_value - ) diff --git a/tests/commands/test_get_app_name.py b/tests/commands/test_get_app_name.py deleted file mode 100644 index 3b2321a..0000000 --- a/tests/commands/test_get_app_name.py +++ /dev/null @@ -1,29 +0,0 @@ -from hypernode_api_python.commands import get_app_name -from tests.testcase import TestCase - - -class TestGetAppName(TestCase): - def setUp(self): - self.expected_environment = { - "HYPERNODE_APP_NAME": "myappname", - } - self.set_up_patch( - "hypernode_api_python.commands.environ", self.expected_environment - ) - - def test_get_app_name_returns_app_name(self): - ret = get_app_name() - - self.assertEqual(ret, "myappname") - - def test_get_app_name_raises_value_error_if_no_app_name(self): - del self.expected_environment["HYPERNODE_APP_NAME"] - - with self.assertRaises(ValueError): - get_app_name() - - def test_get_app_name_raises_value_error_if_empty_app_name(self): - self.expected_environment["HYPERNODE_APP_NAME"] = "" - - with self.assertRaises(ValueError): - get_app_name() diff --git a/tests/commands/test_get_available_backups_for_app.py b/tests/commands/test_get_available_backups_for_app.py deleted file mode 100644 index 3a8d0e8..0000000 --- a/tests/commands/test_get_available_backups_for_app.py +++ /dev/null @@ -1,37 +0,0 @@ -from hypernode_api_python.commands import get_available_backups_for_app -from tests.testcase import TestCase - - -class TestGetAvailableBackupsForApp(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - self.get_app_name = self.set_up_patch( - "hypernode_api_python.commands.get_app_name" - ) - self.get_app_name.return_value = "myappname" - - def test_get_available_backups_for_app_gets_client(self): - get_available_backups_for_app([]) - - self.get_client.assert_called_once_with() - - def test_get_available_backups_for_app_gets_app_name(self): - get_available_backups_for_app([]) - - self.get_app_name.assert_called_once_with() - - def test_get_available_backups_for_app_gets_available_backups_for_app(self): - get_available_backups_for_app([]) - - self.client.get_available_backups_for_app.assert_called_once_with("myappname") - - def test_get_available_backups_for_app_prints_available_backups_for_app(self): - get_available_backups_for_app([]) - - self.print_response.assert_called_once_with( - self.client.get_available_backups_for_app.return_value - ) diff --git a/tests/commands/test_get_block_attack_descriptions.py b/tests/commands/test_get_block_attack_descriptions.py deleted file mode 100644 index e9a161c..0000000 --- a/tests/commands/test_get_block_attack_descriptions.py +++ /dev/null @@ -1,28 +0,0 @@ -from hypernode_api_python.commands import get_block_attack_descriptions -from tests.testcase import TestCase - - -class TestGetBlockAttackDescriptions(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - - def test_get_block_attack_descriptions_gets_client(self): - get_block_attack_descriptions([]) - - self.get_client.assert_called_once_with() - - def test_get_block_attack_descriptions_gets_slas(self): - get_block_attack_descriptions([]) - - self.client.get_block_attack_descriptions.assert_called_once_with() - - def test_get_block_attack_descriptions_prints_slas(self): - get_block_attack_descriptions([]) - - self.print_response.assert_called_once_with( - self.client.get_block_attack_descriptions.return_value - ) diff --git a/tests/commands/test_get_client.py b/tests/commands/test_get_client.py deleted file mode 100644 index 81e4d51..0000000 --- a/tests/commands/test_get_client.py +++ /dev/null @@ -1,37 +0,0 @@ -from hypernode_api_python.commands import get_client -from tests.testcase import TestCase - - -class TestGetClient(TestCase): - def setUp(self): - self.hypernode_api_python = self.set_up_patch( - "hypernode_api_python.commands.HypernodeAPIPython" - ) - self.expected_environment = { - "HYPERNODE_API_TOKEN": "mytoken", - } - self.set_up_patch( - "hypernode_api_python.commands.environ", self.expected_environment - ) - - def test_get_client_instantiates_client(self): - get_client() - - self.hypernode_api_python.assert_called_once_with("mytoken") - - def test_get_client_returns_instantiated_client(self): - ret = get_client() - - self.assertEqual(ret, self.hypernode_api_python.return_value) - - def test_get_client_raises_value_error_if_no_token(self): - del self.expected_environment["HYPERNODE_API_TOKEN"] - - with self.assertRaises(ValueError): - get_client() - - def test_get_client_raises_value_error_if_empty_token(self): - self.expected_environment["HYPERNODE_API_TOKEN"] = "" - - with self.assertRaises(ValueError): - get_client() diff --git a/tests/commands/test_get_cluster_relations.py b/tests/commands/test_get_cluster_relations.py deleted file mode 100644 index c77cb00..0000000 --- a/tests/commands/test_get_cluster_relations.py +++ /dev/null @@ -1,37 +0,0 @@ -from hypernode_api_python.commands import get_cluster_relations -from tests.testcase import TestCase - - -class TestGetClusterRelations(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - self.get_app_name = self.set_up_patch( - "hypernode_api_python.commands.get_app_name" - ) - self.get_app_name.return_value = "myappname" - - def test_get_cluster_relations_gets_client(self): - get_cluster_relations([]) - - self.get_client.assert_called_once_with() - - def test_get_cluster_relations_gets_app_name(self): - get_cluster_relations([]) - - self.get_app_name.assert_called_once_with() - - def test_get_cluster_relations_gets_cluster_relations(self): - get_cluster_relations([]) - - self.client.get_cluster_relations.assert_called_once_with("myappname") - - def test_get_cluster_relations_prints_cluster_relations(self): - get_cluster_relations([]) - - self.print_response.assert_called_once_with( - self.client.get_cluster_relations.return_value - ) diff --git a/tests/commands/test_get_current_product_for_app.py b/tests/commands/test_get_current_product_for_app.py deleted file mode 100644 index e19d9d6..0000000 --- a/tests/commands/test_get_current_product_for_app.py +++ /dev/null @@ -1,37 +0,0 @@ -from hypernode_api_python.commands import get_current_product_for_app -from tests.testcase import TestCase - - -class TestGetCurrentProductForApp(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - self.get_app_name = self.set_up_patch( - "hypernode_api_python.commands.get_app_name" - ) - self.get_app_name.return_value = "myappname" - - def test_get_current_product_for_app_gets_client(self): - get_current_product_for_app([]) - - self.get_client.assert_called_once_with() - - def test_get_current_product_for_app_gets_app_name(self): - get_current_product_for_app([]) - - self.get_app_name.assert_called_once_with() - - def test_get_current_product_for_app_gets_current_product_for_app(self): - get_current_product_for_app([]) - - self.client.get_current_product_for_app.assert_called_once_with("myappname") - - def test_get_current_product_for_app_prints_current_product_for_app(self): - get_current_product_for_app([]) - - self.print_response.assert_called_once_with( - self.client.get_current_product_for_app.return_value - ) diff --git a/tests/commands/test_get_eav_description.py b/tests/commands/test_get_eav_description.py deleted file mode 100644 index b73fa63..0000000 --- a/tests/commands/test_get_eav_description.py +++ /dev/null @@ -1,28 +0,0 @@ -from hypernode_api_python.commands import get_eav_description -from tests.testcase import TestCase - - -class TestGetEavDescription(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - - def test_get_eav_description_gets_client(self): - get_eav_description([]) - - self.get_client.assert_called_once_with() - - def test_get_eav_description_gets_slas(self): - get_eav_description([]) - - self.client.get_app_eav_description.assert_called_once_with() - - def test_get_eav_description_prints_slas(self): - get_eav_description([]) - - self.print_response.assert_called_once_with( - self.client.get_app_eav_description.return_value - ) diff --git a/tests/commands/test_get_flows.py b/tests/commands/test_get_flows.py deleted file mode 100644 index ed01b13..0000000 --- a/tests/commands/test_get_flows.py +++ /dev/null @@ -1,35 +0,0 @@ -from hypernode_api_python.commands import get_flows -from tests.testcase import TestCase - - -class TestGetFlows(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - self.get_app_name = self.set_up_patch( - "hypernode_api_python.commands.get_app_name" - ) - self.get_app_name.return_value = "myappname" - - def test_get_flows_gets_client(self): - get_flows([]) - - self.get_client.assert_called_once_with() - - def test_get_flows_gets_app_name(self): - get_flows([]) - - self.get_app_name.assert_called_once_with() - - def test_get_flows_gets_flows(self): - get_flows([]) - - self.client.get_flows.assert_called_once_with("myappname") - - def test_get_flows_prints_flows(self): - get_flows([]) - - self.print_response.assert_called_once_with(self.client.get_flows.return_value) diff --git a/tests/commands/test_get_fpm_status.py b/tests/commands/test_get_fpm_status.py deleted file mode 100644 index 88a19a2..0000000 --- a/tests/commands/test_get_fpm_status.py +++ /dev/null @@ -1,32 +0,0 @@ -from hypernode_api_python.commands import get_fpm_status -from tests.testcase import TestCase - - -class TestGetFPMStatus(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - self.get_app_name = self.set_up_patch( - "hypernode_api_python.commands.get_app_name" - ) - self.get_app_name.return_value = "myappname" - - def test_get_fpm_status_gets_client(self): - get_fpm_status([]) - - self.get_client.assert_called_once_with() - - def test_get_fpm_status_gets_fpm_status(self): - get_fpm_status([]) - - self.client.get_fpm_status.assert_called_once_with("myappname") - - def test_get_fpm_status_prints_fpm_status(self): - get_fpm_status([]) - - self.print_response.assert_called_once_with( - self.client.get_fpm_status.return_value - ) diff --git a/tests/commands/test_get_next_best_plan.py b/tests/commands/test_get_next_best_plan.py deleted file mode 100644 index 75809c5..0000000 --- a/tests/commands/test_get_next_best_plan.py +++ /dev/null @@ -1,39 +0,0 @@ -from hypernode_api_python.commands import get_next_best_plan_for_app -from tests.testcase import TestCase - - -class TestGetNextBestPlan(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - self.get_app_name = self.set_up_patch( - "hypernode_api_python.commands.get_app_name" - ) - self.get_app_name.return_value = "myappname" - - def test_get_next_best_plan_for_app_gets_client(self): - get_next_best_plan_for_app([]) - - self.get_client.assert_called_once_with() - - def test_get_next_best_plan_for_app_gets_app_name(self): - get_next_best_plan_for_app([]) - - self.get_app_name.assert_called_once_with() - - def test_get_next_best_plan_for_app_gets_next_best_plan_for_app(self): - get_next_best_plan_for_app([]) - - self.client.get_next_best_plan_for_app_or_404.assert_called_once_with( - "myappname" - ) - - def test_get_next_best_plan_for_app_prints_next_best_plan_for_app(self): - get_next_best_plan_for_app([]) - - self.print_response.assert_called_once_with( - self.client.get_next_best_plan_for_app_or_404.return_value - ) diff --git a/tests/commands/test_get_product_info.py b/tests/commands/test_get_product_info.py deleted file mode 100644 index e3a6156..0000000 --- a/tests/commands/test_get_product_info.py +++ /dev/null @@ -1,42 +0,0 @@ -from hypernode_api_python.commands import get_product_info -from tests.testcase import TestCase - - -class TestGetProductInfo(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - self.client.get_active_products.return_value.json.return_value = [ - { - "code": "FALCON_S_202203", - }, - { - "code": "JACKAL_M_202201", - }, - ] - - def test_get_product_info_gets_client(self): - get_product_info(["FALCON_S_202203"]) - - self.get_client.assert_called_once_with() - - def test_get_product_info_gets_product_info_with_price(self): - get_product_info(["FALCON_S_202203"]) - - self.client.get_product_info_with_price.assert_called_once_with( - "FALCON_S_202203" - ) - - def test_get_product_info_prints_product(self): - get_product_info(["FALCON_S_202203"]) - - self.print_response.assert_called_once_with( - self.client.get_product_info_with_price.return_value - ) - - def test_get_product_raises_if_product_not_found(self): - with self.assertRaises(SystemExit): - get_product_info(["ProductThatDoesNotExist"]) diff --git a/tests/commands/test_get_sla.py b/tests/commands/test_get_sla.py deleted file mode 100644 index abbb026..0000000 --- a/tests/commands/test_get_sla.py +++ /dev/null @@ -1,26 +0,0 @@ -from hypernode_api_python.commands import get_sla -from tests.testcase import TestCase - - -class TestGetSla(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - - def test_get_sla_gets_client(self): - get_sla(["sla-standard"]) - - self.get_client.assert_called_once_with() - - def test_get_sla_gets_sla(self): - get_sla(["sla-standard"]) - - self.client.get_sla.assert_called_once_with("sla-standard") - - def test_get_sla_prints_sla(self): - get_sla(["sla-standard"]) - - self.print_response.assert_called_once_with(self.client.get_sla.return_value) diff --git a/tests/commands/test_get_slas.py b/tests/commands/test_get_slas.py deleted file mode 100644 index d545f13..0000000 --- a/tests/commands/test_get_slas.py +++ /dev/null @@ -1,26 +0,0 @@ -from hypernode_api_python.commands import get_slas -from tests.testcase import TestCase - - -class TestGetSlas(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - - def test_get_slas_gets_client(self): - get_slas([]) - - self.get_client.assert_called_once_with() - - def test_get_slas_gets_slas(self): - get_slas([]) - - self.client.get_slas.assert_called_once_with() - - def test_get_slas_prints_slas(self): - get_slas([]) - - self.print_response.assert_called_once_with(self.client.get_slas.return_value) diff --git a/tests/commands/test_get_whitelist_options.py b/tests/commands/test_get_whitelist_options.py deleted file mode 100644 index 6b989d6..0000000 --- a/tests/commands/test_get_whitelist_options.py +++ /dev/null @@ -1,37 +0,0 @@ -from hypernode_api_python.commands import get_whitelist_options -from tests.testcase import TestCase - - -class TestGetWhitelistOptions(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - self.get_app_name = self.set_up_patch( - "hypernode_api_python.commands.get_app_name" - ) - self.get_app_name.return_value = "myappname" - - def test_get_whitelist_options_gets_client(self): - get_whitelist_options([]) - - self.get_client.assert_called_once_with() - - def test_get_whitelist_options_gets_app_name(self): - get_whitelist_options([]) - - self.get_app_name.assert_called_once_with() - - def test_get_whitelist_options_gets_whitelist_options(self): - get_whitelist_options([]) - - self.client.get_whitelist_options.assert_called_once_with("myappname") - - def test_get_whitelist_options_prints_whitelist_options(self): - get_whitelist_options([]) - - self.print_response.assert_called_once_with( - self.client.get_whitelist_options.return_value - ) diff --git a/tests/commands/test_get_whitelist_rules.py b/tests/commands/test_get_whitelist_rules.py deleted file mode 100644 index daca133..0000000 --- a/tests/commands/test_get_whitelist_rules.py +++ /dev/null @@ -1,37 +0,0 @@ -from hypernode_api_python.commands import get_whitelist_rules -from tests.testcase import TestCase - - -class TestGetWhitelistRules(TestCase): - def setUp(self): - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - self.get_app_name = self.set_up_patch( - "hypernode_api_python.commands.get_app_name" - ) - self.get_app_name.return_value = "myappname" - - def test_get_whitelist_rules_gets_client(self): - get_whitelist_rules([]) - - self.get_client.assert_called_once_with() - - def test_get_whitelist_rules_gets_app_name(self): - get_whitelist_rules([]) - - self.get_app_name.assert_called_once_with() - - def test_get_whitelist_rules_gets_whitelist_rules(self): - get_whitelist_rules([]) - - self.client.get_whitelist_rules.assert_called_once_with("myappname") - - def test_get_whitelist_rules_prints_whitelist_rules(self): - get_whitelist_rules([]) - - self.print_response.assert_called_once_with( - self.client.get_whitelist_rules.return_value - ) diff --git a/tests/commands/test_print_response.py b/tests/commands/test_print_response.py deleted file mode 100644 index d72a606..0000000 --- a/tests/commands/test_print_response.py +++ /dev/null @@ -1,32 +0,0 @@ -from unittest.mock import Mock - -from hypernode_api_python.commands import print_response -from tests.testcase import TestCase - - -class TestPrintResponse(TestCase): - def setUp(self): - self.response = Mock() - self.response.json.return_value = { - "id": 123, - "code": "sla-standard", - "name": "SLA Standard", - "price": 1234, - "billing_period": 1, - "billing_period_unit": "month", - } - - self.print = self.set_up_patch("hypernode_api_python.commands.print") - - def test_print_response_prints_pretty_response(self): - print_response(self.response) - - self.print.assert_called_once_with( - '{\n "id": 123,\n "code": "sla-standard",\n "name": "SLA Standard",\n ' - '"price": 1234,\n "billing_period": 1,\n "billing_period_unit": "month"\n}' - ) - - def test_print_response_returns_none(self): - ret = print_response(self.response) - - self.assertIsNone(ret) diff --git a/tests/commands/test_validate_app_name.py b/tests/commands/test_validate_app_name.py deleted file mode 100644 index b0c3b73..0000000 --- a/tests/commands/test_validate_app_name.py +++ /dev/null @@ -1,55 +0,0 @@ -from os import EX_UNAVAILABLE, EX_OK - -from hypernode_api_python.commands import validate_app_name -from tests.testcase import TestCase - - -class TestValidateAppName(TestCase): - def setUp(self): - self.exit = self.set_up_patch("hypernode_api_python.commands.exit") - self.print = self.set_up_patch("hypernode_api_python.commands.print") - self.print_response = self.set_up_patch( - "hypernode_api_python.commands.print_response" - ) - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - - def test_validate_app_name_gets_client(self): - validate_app_name(["myappname"]) - - self.get_client.assert_called_once_with() - - def test_validate_app_name_validates_app_name(self): - validate_app_name(["myappname"]) - - self.client.validate_app_name.assert_called_once_with("myappname") - - def test_validate_app_name_prints_app_name_is_valid(self): - validate_app_name(["myappname"]) - - self.print.assert_called_once_with("App name 'myappname' is valid.") - - def test_validate_app_name_prints_app_name_is_invalid(self): - self.client.validate_app_name.side_effect = RuntimeError( - "[\"This value can only contain non-capital letters 'a' through 'z' or digits " - '0 through 9."]' - ) - - validate_app_name(["myappname"]) - - self.print.assert_called_once_with( - "App name 'myappname' is invalid: [\"This value can only contain non-capital letters " - "'a' through 'z' or digits 0 through 9.\"]" - ) - - def test_validate_app_name_exits_zero_when_valid(self): - validate_app_name(["myappname"]) - - self.exit.assert_called_once_with(EX_OK) - - def test_validate_app_name_exits_nonzero_when_invalid(self): - self.client.validate_app_name.side_effect = RuntimeError - - validate_app_name(["myappname"]) - - self.exit.assert_called_once_with(EX_UNAVAILABLE) diff --git a/tests/commands/test_xgrade.py b/tests/commands/test_xgrade.py deleted file mode 100644 index cad88ce..0000000 --- a/tests/commands/test_xgrade.py +++ /dev/null @@ -1,62 +0,0 @@ -from unittest.mock import Mock - -from hypernode_api_python.commands import xgrade -from tests.testcase import TestCase - - -class TestXgrade(TestCase): - def setUp(self): - self.print = self.set_up_patch("hypernode_api_python.commands.print") - self.get_client = self.set_up_patch("hypernode_api_python.commands.get_client") - self.client = self.get_client.return_value - self.get_app_name = self.set_up_patch( - "hypernode_api_python.commands.get_app_name" - ) - self.get_app_name.return_value = "myappname" - self.client.get_xgrade_descriptions.return_value = Mock( - json=lambda: {"FALCON_L_202203": "", "BlockDownloaderBruteForce": ""} - ) - self.client.xgrade.return_value = Mock(content="") - self.client.get_active_products.return_value.json.return_value = [ - { - "code": "FALCON_L_202203", - }, - { - "code": "FALCON_6XL_202203", - }, - ] - - def test_xgrade_gets_client(self): - xgrade(["FALCON_L_202203"]) - - self.get_client.assert_called_once_with() - - def test_xgrade_performs_xgrade(self): - xgrade(["FALCON_L_202203"]) - - self.client.xgrade.assert_called_once_with( - "myappname", data={"product": "FALCON_L_202203"} - ) - - def test_xgrade_prints_output_on_success(self): - xgrade(["FALCON_L_202203"]) - - self.print.assert_called_once_with( - "The job to xgrade Hypernode 'myappname' to product 'FALCON_L_202203' has been posted" - ) - - def test_xgrade_prints_output_on_failure(self): - self.client.xgrade.return_value = Mock( - content='{"product":["Object with code=FALCON_6XL_202203 does not exist."]}' - ) - - xgrade(["FALCON_6XL_202203"]) - - self.print.assert_called_once_with( - '{"product":["Object with code=FALCON_6XL_202203 does not exist."]}' - ) - - def test_xgrade_raises_on_invalid_choice(self): - with self.assertRaises(SystemExit): - # We get the valid choices from get_xgrade_descriptions - xgrade(["DoesNotExist"]) diff --git a/tests/testcase.py b/tests/testcase.py deleted file mode 100644 index 47683b2..0000000 --- a/tests/testcase.py +++ /dev/null @@ -1,18 +0,0 @@ -import unittest -from unittest.mock import Mock, mock_open, patch - - -class TestCase(unittest.TestCase): - def set_up_patch(self, patch_target, mock_target=None, **kwargs): - patcher = patch(patch_target, mock_target or Mock(**kwargs)) - self.addCleanup(patcher.stop) - return patcher.start() - - def set_up_context_manager_patch(self, topatch, themock=None, **kwargs): - patcher = self.set_up_patch(topatch, themock=themock, **kwargs) - patcher.return_value.__exit__ = lambda a, b, c, d: None - patcher.return_value.__enter__ = lambda x: None - return patcher - - def set_up_mock_open(self, read_value=""): - return self.set_up_patch("builtins.open", mock_open(read_data=read_value)) diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 54ee538..0000000 --- a/tox.ini +++ /dev/null @@ -1,27 +0,0 @@ -[tox] -envlist = py{3.7,3.8,3.9,3.10,3.11} -skipsdist = True -skip_missing_interpreters = True - - -[testenv] -basepython = - py3.7: python3.7 - py3.8: python3.8 - py3.9: python3.9 - py3.10: python3.10 - py3.11: python3.11 - -deps = -rrequirements/development.txt - -commands = pytest --numprocesses=auto {posargs:--quiet tests/} - black --check . - pycodestyle --config=pycodestyle.ini --exclude=venv,.tox -setenv = PYTHONPATH = {toxinidir} - PYTHONDONTWRITEBYTECODE = 1 - - -[testenv:jenkins] -commands = pytest --cache-clear --numprocesses=auto --maxprocesses=4 --cov-report=term-missing --cov=kamikaze3 - black --check . - pycodestyle --config=pycodestyle.ini