From 46cfcab6c2476370248f55d539c595b69c98ed24 Mon Sep 17 00:00:00 2001 From: Zhiwei Liang <121905282+zliang-akamai@users.noreply.github.com> Date: Wed, 5 Apr 2023 11:38:29 -0400 Subject: [PATCH 01/10] Better test temp file cleanup (#432) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📝 Description Let the tempfile directory cleanup the test files, instead of clean them up in the test case function. ## ✔️ How to Test `pytest tests/integration/test_obj_plugin.py` --- tests/integration/test_obj_plugin.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/integration/test_obj_plugin.py b/tests/integration/test_obj_plugin.py index 2fd42a69..030d9c3a 100644 --- a/tests/integration/test_obj_plugin.py +++ b/tests/integration/test_obj_plugin.py @@ -1,7 +1,6 @@ import logging import subprocess from dataclasses import dataclass -from pathlib import Path from typing import Callable, List, Optional import pytest @@ -147,18 +146,15 @@ def test_obj_single_file_single_bucket( assert str(file_size) in output assert file_path.name in output - downloaded_file_path = Path.cwd() / f"downloaded_{file_path.name}" + downloaded_file_path = file_path.parent / f"downloaded_{file_path.name}" process = exec_test_command( BASE_CMD - + ["get", bucket_name, file_path.name, downloaded_file_path.name] + + ["get", bucket_name, file_path.name, str(downloaded_file_path)] ) output = process.stdout.decode() with open(downloaded_file_path) as f2, open(file_path) as f1: assert f1.read() == f2.read() - downloaded_file_path.unlink() - file_path.unlink() - def test_multi_files_multi_bucket( create_bucket: Callable[[Optional[str]], str], @@ -178,8 +174,6 @@ def test_multi_files_multi_bucket( output = process.stdout.decode() assert "100.0%" in output assert "Done" in output - for file in file_paths: - file.unlink() def test_modify_access_control( From 49b18a782e5322254f62ef0cd1ceb3580cde6463 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Apr 2023 11:38:40 -0400 Subject: [PATCH 02/10] Bump pylint from 2.17.1 to 2.17.2 (#431) Bumps [pylint](https://github.com/PyCQA/pylint) from 2.17.1 to 2.17.2.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pylint&package-manager=pip&previous-version=2.17.1&new-version=2.17.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 596ce221..88c7e3ad 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,4 @@ -pylint==2.17.1 +pylint==2.17.2 pytest==7.2.2 black>=23.1.0 isort>=5.12.0 From d23dbe0a34740553d50e85e7575e1bb0b7705a63 Mon Sep 17 00:00:00 2001 From: Zhiwei Liang <121905282+zliang-akamai@users.noreply.github.com> Date: Wed, 5 Apr 2023 11:39:03 -0400 Subject: [PATCH 03/10] Move objects and buckets methods to separate files (#429) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📝 Description Make them more readable. ## ✔️ How to Test `pytest tests/integration/test_obj_plugin.py` --- .github/workflows/pull-request.yml | 3 +- linodecli/plugins/obj/__init__.py | 232 +---------------------------- linodecli/plugins/obj/buckets.py | 77 ++++++++++ linodecli/plugins/obj/objects.py | 180 ++++++++++++++++++++++ 4 files changed, 265 insertions(+), 227 deletions(-) create mode 100644 linodecli/plugins/obj/buckets.py create mode 100644 linodecli/plugins/obj/objects.py diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 09f692e8..1b779a18 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -13,7 +13,8 @@ jobs: uses: actions/setup-python@v4 with: python-version: '3.x' - + - name: install boto3 + run: pip3 install boto3 - name: install dependencies run: pip3 install -r requirements-dev.txt -r requirements.txt diff --git a/linodecli/plugins/obj/__init__.py b/linodecli/plugins/obj/__init__.py index f0210c54..ab8bb6f0 100644 --- a/linodecli/plugins/obj/__init__.py +++ b/linodecli/plugins/obj/__init__.py @@ -24,6 +24,7 @@ from linodecli.configuration.helpers import _default_thing_input from linodecli.helpers import expand_globs from linodecli.plugins import PluginContext, inherit_plugin_args +from linodecli.plugins.obj.buckets import create_bucket, delete_bucket from linodecli.plugins.obj.config import ( BASE_URL_TEMPLATE, BASE_WEBSITE_TEMPLATE, @@ -47,6 +48,11 @@ _progress, restricted_int_arg_type, ) +from linodecli.plugins.obj.objects import ( + delete_object, + get_object, + upload_object, +) try: import boto3 @@ -133,232 +139,6 @@ def list_objects_or_buckets( sys.exit(0) -def create_bucket(get_client, args): - """ - Creates a new bucket - """ - parser = inherit_plugin_args(ArgumentParser(PLUGIN_BASE + " mb")) - - parser.add_argument( - "name", - metavar="NAME", - type=str, - help="The name of the bucket to create.", - ) - - parsed = parser.parse_args(args) - client = get_client() - - client.create_bucket(Bucket=parsed.name) - - print(f"Bucket {parsed.name} created") - sys.exit(0) - - -def delete_bucket(get_client, args): - """ - Deletes a bucket - """ - parser = inherit_plugin_args(ArgumentParser(PLUGIN_BASE + " rb")) - - parser.add_argument( - "name", - metavar="NAME", - type=str, - help="The name of the bucket to remove.", - ) - parser.add_argument( - "--recursive", - action="store_true", - help="If given, force removal of non-empty buckets by deleting " - "all objects in the bucket before deleting the bucket. For " - "large buckets, this may take a while.", - ) - - parsed = parser.parse_args(args) - client = get_client() - bucket_name = parsed.name - - if parsed.recursive: - objects = [ - {"Key": obj.get("Key")} - for obj in client.list_objects_v2(Bucket=bucket_name).get( - "Contents", [] - ) - if obj.get("Key") - ] - client.delete_objects( - Bucket=bucket_name, - Delete={ - "Objects": objects, - "Quiet": False, - }, - ) - - client.delete_bucket(Bucket=bucket_name) - - print(f"Bucket {parsed.name} removed") - - sys.exit(0) - - -def upload_object(get_client, args): # pylint: disable=too-many-locals - """ - Uploads an object to object storage - """ - parser = inherit_plugin_args(ArgumentParser(PLUGIN_BASE + " put")) - - parser.add_argument( - "file", metavar="FILE", type=str, nargs="+", help="The files to upload." - ) - parser.add_argument( - "bucket", - metavar="BUCKET", - type=str, - help="The bucket to put a file in.", - ) - parser.add_argument( - "--acl-public", - action="store_true", - help="If set, the new object can be downloaded without " - "authentication.", - ) - parser.add_argument( - "--chunk-size", - type=restricted_int_arg_type(5120), - default=MULTIPART_UPLOAD_CHUNK_SIZE_DEFAULT, - help="The size of file chunks when uploading large files, in MB.", - ) - - # TODO: - # 1. Allow user specified key (filename on cloud) - # 2. As below: - # parser.add_argument('--recursive', action='store_true', - # help="If set, upload directories recursively.") - - parsed = parser.parse_args(args) - client = get_client() - - to_upload: List[Path] = [] - files = list(parsed.file) - for f in files: - # Windows doesn't natively expand globs, so we should implement it here - if platform.system() == "Windows" and "*" in f: - results = expand_globs(f) - files.extend(results) - continue - - for f in files: - file_path = Path(f).resolve() - if not file_path.is_file(): - sys.exit(f"No file {file_path}") - - to_upload.append(file_path) - - chunk_size = 1024 * 1024 * parsed.chunk_size - - upload_options = { - "Bucket": parsed.bucket, - "Config": TransferConfig(multipart_chunksize=chunk_size * MB), - } - - if parsed.acl_public: - upload_options["ExtraArgs"] = {"ACL": "public-read"} - - for file_path in to_upload: - print(f"Uploading {file_path.name}:") - upload_options["Filename"] = str(file_path.resolve()) - upload_options["Key"] = file_path.name - upload_options["Callback"] = ProgressPercentage( - file_path.stat().st_size, PROGRESS_BAR_WIDTH - ) - try: - client.upload_file(**upload_options) - except S3UploadFailedError as e: - sys.exit(e) - - print("Done.") - - -def get_object(get_client, args): - """ - Retrieves an uploaded object and writes it to a file - """ - parser = inherit_plugin_args(ArgumentParser(PLUGIN_BASE + " get")) - - parser.add_argument( - "bucket", metavar="BUCKET", type=str, help="The bucket the file is in." - ) - parser.add_argument( - "file", metavar="OBJECT", type=str, help="The object to retrieve." - ) - parser.add_argument( - "destination", - metavar="LOCAL_FILE", - type=str, - nargs="?", - help="The destination file. If omitted, uses the object " - "name and saves to the current directory.", - ) - - parsed = parser.parse_args(args) - client = get_client() - - # find destination file - destination = parsed.destination - - if destination is None: - destination = parsed.file - - destination = Path(destination).resolve() - - # download the file - bucket = parsed.bucket - key = parsed.file - - response = client.head_object( - Bucket=bucket, - Key=key, - ) - - client.download_file( - Bucket=bucket, - Key=key, - Filename=str(destination), - Callback=ProgressPercentage( - response.get("ContentLength", 0), PROGRESS_BAR_WIDTH - ), - ) - - print("Done.") - - -def delete_object(get_client, args): - """ - Removes a file from a bucket - """ - parser = inherit_plugin_args(ArgumentParser(PLUGIN_BASE + " del")) - - parser.add_argument( - "bucket", metavar="BUCKET", type=str, help="The bucket to delete from." - ) - parser.add_argument( - "file", metavar="OBJECT", type=str, help="The object to remove." - ) - - parsed = parser.parse_args(args) - client = get_client() - bucket = parsed.bucket - key = parsed.file - - client.delete_object( - Bucket=bucket, - Key=key, - ) - - print(f"{parsed.file} removed from {parsed.bucket}") - - def generate_url(get_client, args): """ Generates a URL to an object diff --git a/linodecli/plugins/obj/buckets.py b/linodecli/plugins/obj/buckets.py new file mode 100644 index 00000000..e4ff8da5 --- /dev/null +++ b/linodecli/plugins/obj/buckets.py @@ -0,0 +1,77 @@ +""" +The bucket manipulation module of CLI Plugin for handling object storage +""" +import sys +from argparse import ArgumentParser + +from linodecli.plugins import inherit_plugin_args +from linodecli.plugins.obj.config import PLUGIN_BASE + + +def create_bucket(get_client, args): + """ + Creates a new bucket + """ + parser = inherit_plugin_args(ArgumentParser(PLUGIN_BASE + " mb")) + + parser.add_argument( + "name", + metavar="NAME", + type=str, + help="The name of the bucket to create.", + ) + + parsed = parser.parse_args(args) + client = get_client() + + client.create_bucket(Bucket=parsed.name) + + print(f"Bucket {parsed.name} created") + sys.exit(0) + + +def delete_bucket(get_client, args): + """ + Deletes a bucket + """ + parser = inherit_plugin_args(ArgumentParser(PLUGIN_BASE + " rb")) + + parser.add_argument( + "name", + metavar="NAME", + type=str, + help="The name of the bucket to remove.", + ) + parser.add_argument( + "--recursive", + action="store_true", + help="If given, force removal of non-empty buckets by deleting " + "all objects in the bucket before deleting the bucket. For " + "large buckets, this may take a while.", + ) + + parsed = parser.parse_args(args) + client = get_client() + bucket_name = parsed.name + + if parsed.recursive: + objects = [ + {"Key": obj.get("Key")} + for obj in client.list_objects_v2(Bucket=bucket_name).get( + "Contents", [] + ) + if obj.get("Key") + ] + client.delete_objects( + Bucket=bucket_name, + Delete={ + "Objects": objects, + "Quiet": False, + }, + ) + + client.delete_bucket(Bucket=bucket_name) + + print(f"Bucket {parsed.name} removed") + + sys.exit(0) diff --git a/linodecli/plugins/obj/objects.py b/linodecli/plugins/obj/objects.py new file mode 100644 index 00000000..a5ae5316 --- /dev/null +++ b/linodecli/plugins/obj/objects.py @@ -0,0 +1,180 @@ +""" +The object manipulation module of CLI Plugin for handling object storage +""" +import platform +import sys +from argparse import ArgumentParser +from pathlib import Path +from typing import List + +from boto3.exceptions import S3UploadFailedError +from boto3.s3.transfer import MB, TransferConfig + +from linodecli.helpers import expand_globs +from linodecli.plugins import inherit_plugin_args +from linodecli.plugins.obj.config import ( + MULTIPART_UPLOAD_CHUNK_SIZE_DEFAULT, + PLUGIN_BASE, + PROGRESS_BAR_WIDTH, +) +from linodecli.plugins.obj.helpers import ( + ProgressPercentage, + restricted_int_arg_type, +) + + +def upload_object(get_client, args): # pylint: disable=too-many-locals + """ + Uploads an object to object storage + """ + parser = inherit_plugin_args(ArgumentParser(PLUGIN_BASE + " put")) + + parser.add_argument( + "file", metavar="FILE", type=str, nargs="+", help="The files to upload." + ) + parser.add_argument( + "bucket", + metavar="BUCKET", + type=str, + help="The bucket to put a file in.", + ) + parser.add_argument( + "--acl-public", + action="store_true", + help="If set, the new object can be downloaded without " + "authentication.", + ) + parser.add_argument( + "--chunk-size", + type=restricted_int_arg_type(5120), + default=MULTIPART_UPLOAD_CHUNK_SIZE_DEFAULT, + help="The size of file chunks when uploading large files, in MB.", + ) + + # TODO: + # 1. Allow user specified key (filename on cloud) + # 2. As below: + # parser.add_argument('--recursive', action='store_true', + # help="If set, upload directories recursively.") + + parsed = parser.parse_args(args) + client = get_client() + + to_upload: List[Path] = [] + files = list(parsed.file) + for f in files: + # Windows doesn't natively expand globs, so we should implement it here + if platform.system() == "Windows" and "*" in f: + results = expand_globs(f) + files.extend(results) + continue + + for f in files: + file_path = Path(f).resolve() + if not file_path.is_file(): + sys.exit(f"No file {file_path}") + + to_upload.append(file_path) + + chunk_size = 1024 * 1024 * parsed.chunk_size + + upload_options = { + "Bucket": parsed.bucket, + "Config": TransferConfig(multipart_chunksize=chunk_size * MB), + } + + if parsed.acl_public: + upload_options["ExtraArgs"] = {"ACL": "public-read"} + + for file_path in to_upload: + print(f"Uploading {file_path.name}:") + upload_options["Filename"] = str(file_path.resolve()) + upload_options["Key"] = file_path.name + upload_options["Callback"] = ProgressPercentage( + file_path.stat().st_size, PROGRESS_BAR_WIDTH + ) + try: + client.upload_file(**upload_options) + except S3UploadFailedError as e: + sys.exit(e) + + print("Done.") + + +def get_object(get_client, args): + """ + Retrieves an uploaded object and writes it to a file + """ + parser = inherit_plugin_args(ArgumentParser(PLUGIN_BASE + " get")) + + parser.add_argument( + "bucket", metavar="BUCKET", type=str, help="The bucket the file is in." + ) + parser.add_argument( + "file", metavar="OBJECT", type=str, help="The object to retrieve." + ) + parser.add_argument( + "destination", + metavar="LOCAL_FILE", + type=str, + nargs="?", + help="The destination file. If omitted, uses the object " + "name and saves to the current directory.", + ) + + parsed = parser.parse_args(args) + client = get_client() + + # find destination file + destination = parsed.destination + + if destination is None: + destination = parsed.file + + destination = Path(destination).resolve() + + # download the file + bucket = parsed.bucket + key = parsed.file + + response = client.head_object( + Bucket=bucket, + Key=key, + ) + + client.download_file( + Bucket=bucket, + Key=key, + Filename=str(destination), + Callback=ProgressPercentage( + response.get("ContentLength", 0), PROGRESS_BAR_WIDTH + ), + ) + + print("Done.") + + +def delete_object(get_client, args): + """ + Removes a file from a bucket + """ + parser = inherit_plugin_args(ArgumentParser(PLUGIN_BASE + " del")) + + parser.add_argument( + "bucket", metavar="BUCKET", type=str, help="The bucket to delete from." + ) + parser.add_argument( + "file", metavar="OBJECT", type=str, help="The object to remove." + ) + + parsed = parser.parse_args(args) + client = get_client() + bucket = parsed.bucket + key = parsed.file + + client.delete_object( + Bucket=bucket, + Key=key, + ) + + print(f"{parsed.file} removed from {parsed.bucket}") From fc3d64e73d70844ecee75c2c66eb7b43102ad057 Mon Sep 17 00:00:00 2001 From: Ye Chen <127243817+yec-akamai@users.noreply.github.com> Date: Mon, 10 Apr 2023 16:41:32 -0400 Subject: [PATCH 04/10] Add default values for database engines (#427) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📝 Description Add the default values for database engines in CLI config. https://jira.linode.com/browse/TPT-1676 ## ✔️ How to Test `make testunit` Create MySQL and PostgreSQL database separately use default values including engine: ![Pasted Graphic](https://user-images.githubusercontent.com/127243817/230231195-29948195-a102-44ba-9888-82b7436c9ff7.jpg) Overwrite the engine value: ![image](https://user-images.githubusercontent.com/127243817/230231180-95d7b7b3-e3b0-4a98-bfd6-73bc7e94ef26.png) --- README.rst | 6 ++-- linodecli/api_request.py | 4 ++- linodecli/configuration/__init__.py | 38 ++++++++++++++++++-- linodecli/configuration/helpers.py | 16 ++++++++- tests/unit/configuration.py | 56 ++++++++++++++++++++++++++--- tests/unit/conftest.py | 1 + tests/unit/test_api_request.py | 15 +++++--- 7 files changed, 120 insertions(+), 16 deletions(-) diff --git a/README.rst b/README.rst index 00843401..dc9b8b50 100644 --- a/README.rst +++ b/README.rst @@ -180,8 +180,8 @@ Suppressing Defaults """""""""""""""""""" If you configured default values for ``image``, ``authorized_users``, ``region``, -and Linode ``type``, they will be sent for all requests that accept them if you -do not specify a different value. If you want to send a request *without* these +database ``engine``, and Linode ``type``, they will be sent for all requests that accept them +if you do not specify a different value. If you want to send a request *without* these arguments, you must invoke the CLI with the ``--no-defaults`` option. For example, to create a Linode with no ``image`` after a default Image has been @@ -474,7 +474,7 @@ added to Linode's OpenAPI spec: |x-linode-cli-skip | path | If present and truthy, this method will not be available in the CLI. | +-----------------------------+-------------+-------------------------------------------------------------------------------------------+ +x-linode-cli-allowed-defaults| requestBody | Tells the CLI what configured defaults apply to this request. Valid defaults are "region",| -+ | | "image", "authorized_users", and "type". | ++ | | "image", "authorized_users", "engine", and "type". | +-----------------------------+-------------+-------------------------------------------------------------------------------------------+ +x-linode-cli-nested-list | content-type| Tells the CLI to flatten a single object into multiple table rows based on the keys | | | | included in this value. Values should be comma-delimited JSON paths, and must all be | diff --git a/linodecli/api_request.py b/linodecli/api_request.py index 7c62d82a..9a5fa0f0 100644 --- a/linodecli/api_request.py +++ b/linodecli/api_request.py @@ -100,7 +100,9 @@ def _build_request_body(ctx, operation, parsed_args) -> Optional[str]: # Merge defaults into body if applicable if ctx.defaults: - parsed_args = ctx.config.update(parsed_args, operation.allowed_defaults) + parsed_args = ctx.config.update( + parsed_args, operation.allowed_defaults, operation.action + ) to_json = {k: v for k, v in vars(parsed_args).items() if v is not None} diff --git a/linodecli/configuration/__init__.py b/linodecli/configuration/__init__.py index 5a53ff15..81360ce3 100644 --- a/linodecli/configuration/__init__.py +++ b/linodecli/configuration/__init__.py @@ -212,7 +212,7 @@ def plugin_get_value(self, key): # might be better to move this to argparsing during refactor and just have # configuration return defaults or keys or something def update( - self, namespace, allowed_defaults + self, namespace, allowed_defaults, action=None ): # pylint: disable=too-many-branches """ This updates a Namespace (as returned by ArgumentParser) with config values @@ -242,6 +242,17 @@ def update( continue if self.config.has_option(username, key): value = self.config.get(username, key) + # different types of database creation use different endpoints, + # so we need to set the default engine value based on the type + elif key == "engine": + if action == "mysql-create" and self.config.has_option( + username, "mysql_engine" + ): + value = self.config.get(username, "mysql_engine") + elif action == "postgresql-create" and self.config.has_option( + username, "postgresql_engine" + ): + value = self.config.get(username, "postgresql_engine") else: value = ns_dict[key] if not value: @@ -275,7 +286,7 @@ def write_config(self): def configure( self, - ): # pylint: disable=too-many-branches,too-many-statements + ): # pylint: disable=too-many-branches,too-many-statements,too-many-locals """ This assumes we're running interactively, and prompts the user for a series of defaults in order to make future CLI calls @@ -336,6 +347,15 @@ def configure( images = [ i["id"] for i in _do_get_request(self.base_url, "/images")["data"] ] + engines_list = _do_get_request(self.base_url, "/databases/engines")[ + "data" + ] + mysql_engines = [ + e["id"] for e in engines_list if e["engine"] == "mysql" + ] + postgresql_engines = [ + e["id"] for e in engines_list if e["engine"] == "postgresql" + ] is_full_access = _check_full_access(self.base_url, token) @@ -378,6 +398,20 @@ def configure( "Please select a valid Image, or press Enter to skip", ) + config["mysql_engine"] = _default_thing_input( + "Default Engine to create a Managed MySQL Database.", + mysql_engines, + "Default Engine (Optional): ", + "Please select a valid MySQL Database Engine, or press Enter to skip", + ) + + config["postgresql_engine"] = _default_thing_input( + "Default Engine to create a Managed PostgreSQL Database.", + postgresql_engines, + "Default Engine (Optional): ", + "Please select a valid PostgreSQL Database Engine, or press Enter to skip", + ) + if auth_users: config["authorized_users"] = _default_thing_input( "Select the user that should be given default SSH access to new Linodes.", diff --git a/linodecli/configuration/helpers.py b/linodecli/configuration/helpers.py index 5a5c4d79..96db226c 100644 --- a/linodecli/configuration/helpers.py +++ b/linodecli/configuration/helpers.py @@ -118,7 +118,7 @@ def _default_thing_input( return ret -def _handle_no_default_user(self): +def _handle_no_default_user(self): # pylint: disable=too-many-branches """ Handle the case that there is no default user in the config """ @@ -167,6 +167,20 @@ def _handle_no_default_user(self): username, "image", self.config.get("DEFAULT", "image") ) + if self.config.has_option("DEFAULT", "mysql_engine"): + self.config.set( + username, + "mysql_engine", + self.config.get("DEFAULT", "mysql_engine"), + ) + + if self.config.has_option("DEFAULT", "postgresql_engine"): + self.config.set( + username, + "postgresql_engine", + self.config.get("DEFAULT", "postgresql_engine"), + ) + if self.config.has_option("DEFAULT", "authorized_keys"): self.config.set( username, diff --git a/tests/unit/configuration.py b/tests/unit/configuration.py index 82c9419d..3f97b5c6 100644 --- a/tests/unit/configuration.py +++ b/tests/unit/configuration.py @@ -32,13 +32,15 @@ class ConfigurationTests(unittest.TestCase): image = linode/alpine3.16 plugin-testplugin-testkey = plugin-test-value authorized_users = cli-dev +mysql_engine = mysql/8.0.26 [cli-dev2] token = {test_token}2 region = us-east type = g6-nanode-1 image = linode/alpine3.16 -authorized_users = cli-dev2""" +authorized_users = cli-dev2 +mysql_engine = mysql/8.0.26""" def _build_test_config(self, config=mock_config_file, base_url=base_url): """ @@ -179,7 +181,10 @@ def test_update(self): parser.add_argument("--testkey") parser.add_argument("--authorized_users") parser.add_argument("--plugin-testplugin-testkey") - ns = parser.parse_args(["--testkey", "testvalue"]) + parser.add_argument("--engine") + ns = parser.parse_args( + ["--testkey", "testvalue", "--engine", "mysql/new-test-engine"] + ) conf.username = "tester" conf.config.add_section("tester") @@ -187,11 +192,13 @@ def test_update(self): conf.config.set("tester", "newkey", "newvalue") conf.config.set("tester", "authorized_users", "tester") conf.config.set("tester", "plugin-testplugin-testkey", "plugin-value") - allowed_defaults = { + conf.config.set("tester", "mysql_engine", "mysql/default-test-engine") + allowed_defaults = [ "newkey", "authorized_users", "plugin-testplugin-testkey", - } + "engine", + ] f = io.StringIO() with contextlib.redirect_stdout(f): @@ -211,6 +218,13 @@ def test_update(self): self.assertFalse("--no-defaults" in f.getvalue()) + # test that update default engine value correctly when creating database + create_db_action = "mysql-create" + f = io.StringIO() + with contextlib.redirect_stdout(f): + result = vars(conf.update(ns, allowed_defaults, create_db_action)) + self.assertEqual(result.get("engine"), "mysql/new-test-engine") + def test_write_config(self): """ Test CLIConfig.write_config() @@ -258,6 +272,18 @@ def mock_input(prompt): m.get( f"{self.base_url}/images", json={"data": [{"id": "test-image"}]} ) + m.get( + f"{self.base_url}/databases/engines", + json={ + "data": [ + {"id": "mysql/test-engine", "engine": "mysql"}, + { + "id": "postgresql/test-engine", + "engine": "postgresql", + }, + ] + }, + ) m.get( f"{self.base_url}/account/users", json={"data": [{"username": "cli-dev", "ssh_keys": "testkey"}]}, @@ -269,6 +295,11 @@ def mock_input(prompt): self.assertEqual(conf.get_value("image"), "test-image") self.assertEqual(conf.get_value("region"), "test-region") self.assertEqual(conf.get_value("authorized_users"), "cli-dev") + # make sure that we set the default engine value according to type of database + self.assertEqual(conf.get_value("mysql_engine"), "mysql/test-engine") + self.assertEqual( + conf.get_value("postgresql_engine"), "postgresql/test-engine" + ) def test_configure_default_terminal(self): """ @@ -305,6 +336,18 @@ def mock_input(prompt): m.get( f"{self.base_url}/images", json={"data": [{"id": "test-image"}]} ) + m.get( + f"{self.base_url}/databases/engines", + json={ + "data": [ + {"id": "mysql/test-engine", "engine": "mysql"}, + { + "id": "postgresql/test-engine", + "engine": "postgresql", + }, + ] + }, + ) m.get( f"{self.base_url}/account/users", json={"data": [{"username": "cli-dev", "ssh_keys": "testkey"}]}, @@ -316,3 +359,8 @@ def mock_input(prompt): self.assertEqual(conf.get_value("region"), "test-region") self.assertEqual(conf.get_value("authorized_users"), "cli-dev") self.assertEqual(conf.config.get("DEFAULT", "default-user"), "DEFAULT") + # make sure that we set the default engine value according to type of database + self.assertEqual(conf.get_value("mysql_engine"), "mysql/test-engine") + self.assertEqual( + conf.get_value("postgresql_engine"), "postgresql/test-engine" + ) diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index 59985417..b42e581e 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -16,6 +16,7 @@ image = linode/ubuntu21.10 token = notafaketoken type = g6-nanode-1 +mysql_engine = mysql/8.0.26 """ diff --git a/tests/unit/test_api_request.py b/tests/unit/test_api_request.py index d7c6c57d..29507cee 100644 --- a/tests/unit/test_api_request.py +++ b/tests/unit/test_api_request.py @@ -54,16 +54,21 @@ def test_request_debug_info(self): assert "> " in output def test_build_request_body(self, mock_cli, create_operation): - create_operation.allowed_defaults = ["region"] - + create_operation.allowed_defaults = ["region", "engine"] + create_operation.action = "mysql-create" result = api_request._build_request_body( mock_cli, create_operation, - SimpleNamespace(generic_arg="foo", region=None), + SimpleNamespace(generic_arg="foo", region=None, engine=None), ) - assert ( - json.dumps({"generic_arg": "foo", "region": "us-southeast"}) + json.dumps( + { + "generic_arg": "foo", + "region": "us-southeast", + "engine": "mysql/8.0.26", + } + ) == result ) From 0e5208c8b3ce89eb3da020a320124bc4df709b72 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Apr 2023 12:21:17 -0400 Subject: [PATCH 05/10] Bump pytest from 7.2.2 to 7.3.0 (#434) Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.2.2 to 7.3.0.
Release notes

Sourced from pytest's releases.

7.3.0

pytest 7.3.0 (2023-04-08)

Features

  • #10525: Test methods decorated with @classmethod can now be discovered as tests, following the same rules as normal methods. This fills the gap that static methods were discoverable as tests but not class methods.
  • #10755: console_output_style{.interpreted-text role="confval"} now supports progress-even-when-capture-no to force the use of the progress output even when capture is disabled. This is useful in large test suites where capture may have significant performance impact.
  • #7431: --log-disable CLI option added to disable individual loggers.
  • #8141: Added tmp_path_retention_count{.interpreted-text role="confval"} and tmp_path_retention_policy{.interpreted-text role="confval"} configuration options to control how directories created by the tmp_path{.interpreted-text role="fixture"} fixture are kept.

Improvements

  • #10226: If multiple errors are raised in teardown, we now re-raise an ExceptionGroup of them instead of discarding all but the last.
  • #10658: Allow -p arguments to include spaces (eg: -p no:logging instead of -pno:logging). Mostly useful in the addopts section of the configuration file.
  • #10710: Added start and stop timestamps to TestReport objects.
  • #10727: Split the report header for rootdir, config file and testpaths so each has its own line.
  • #10840: pytest should no longer crash on AST with pathological position attributes, for example testing AST produced by [Hylang <https://github.com/hylang/hy>__]{.title-ref}.
  • #6267: The full output of a test is no longer truncated if the truncation message would be longer than the hidden text. The line number shown has also been fixed.

Bug Fixes

  • #10743: The assertion rewriting mechanism now works correctly when assertion expressions contain the walrus operator.
  • #10765: Fixed tmp_path{.interpreted-text role="fixture"} fixture always raising OSError{.interpreted-text role="class"} on emscripten platform due to missing os.getuid{.interpreted-text role="func"}.
  • #1904: Correctly handle __tracebackhide__ for chained exceptions.

Improved Documentation

  • #10782: Fixed the minimal example in goodpractices{.interpreted-text role="ref"}: pip install -e . requires a version entry in pyproject.toml to run successfully.

Trivial/Internal Changes

  • #10669: pytest no longer depends on the [attrs]{.title-ref} package (don't worry, nice diffs for attrs classes are still supported).
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pytest&package-manager=pip&previous-version=7.2.2&new-version=7.3.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 88c7e3ad..8effee4a 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ pylint==2.17.2 -pytest==7.2.2 +pytest==7.3.0 black>=23.1.0 isort>=5.12.0 autoflake>=2.0.1 From f1a0cf08593dcef7f2fa18aa8ac174f8ff656dcd Mon Sep 17 00:00:00 2001 From: ykim-1 <126618609+ykim-1@users.noreply.github.com> Date: Tue, 18 Apr 2023 07:58:37 -0700 Subject: [PATCH 06/10] Fix/teardown pytest (#433) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📝 Description Fixing teardowns in cli test suites ## ✔️ How to Test make testint ## 📷 Preview **If applicable, include a screenshot or code snippet of this change. Otherwise, please remove this section.** --- tests/integration/domains/helpers_domains.py | 70 ++++ .../domains/test_domain_records.py | 26 +- .../integration/domains/test_domains_tags.py | 53 ++- ...ster-domains.py => test_master_domains.py} | 42 +-- .../integration/domains/test_slave_domains.py | 30 +- tests/integration/events/test_events.py | 20 +- tests/integration/helpers.py | 5 + tests/integration/linodes/helpers_linodes.py | 196 ++++++++++- tests/integration/linodes/test_backups.py | 113 ++---- tests/integration/linodes/test_linodes.py | 122 ++----- .../integration/linodes/test_power_status.py | 20 +- tests/integration/linodes/test_rebuild.py | 6 +- tests/integration/linodes/test_resize.py | 6 +- .../integration/networking/test_networking.py | 19 +- .../nodebalancers/helpers_nodebalancers.py | 34 ++ .../nodebalancers/test_node_balancers.py | 327 +++++++++--------- tests/integration/ssh/test_ssh.py | 9 +- tests/integration/volumes/test_volumes.py | 16 +- .../volumes/test_volumes_resize.py | 3 +- 19 files changed, 585 insertions(+), 532 deletions(-) create mode 100644 tests/integration/domains/helpers_domains.py rename tests/integration/domains/{test_master-domains.py => test_master_domains.py} (81%) create mode 100644 tests/integration/nodebalancers/helpers_nodebalancers.py diff --git a/tests/integration/domains/helpers_domains.py b/tests/integration/domains/helpers_domains.py new file mode 100644 index 00000000..6949775d --- /dev/null +++ b/tests/integration/domains/helpers_domains.py @@ -0,0 +1,70 @@ +import time + +import pytest + +from tests.integration.helpers import delete_target_id, exec_test_command + +BASE_CMD = ["linode-cli", "domains"] + + +# test helper specific to domain test suite +@pytest.fixture +def create_master_domain(): + timestamp = str(int(time.time())) + + domain_id = ( + exec_test_command( + BASE_CMD + + [ + "create", + "--type", + "master", + "--domain", + timestamp + "example.com", + "--soa_email", + "pthiel_test@linode.com", + "--text", + "--no-header", + "--format", + "id", + ] + ) + .stdout.decode() + .rstrip() + ) + + yield domain_id + + delete_target_id("domains", id=domain_id) + + +# test helper specific to domain test suite +@pytest.fixture +def create_slave_domain(): + timestamp = str(int(time.time())) + + domain_id = ( + exec_test_command( + BASE_CMD + + [ + "create", + "--type", + "slave", + "--domain", + timestamp + "-example.com", + "--master_ips", + "1.1.1.1", + "--text", + "--no-header", + "--delimiter", + ",", + "--format=id", + ] + ) + .stdout.decode() + .rstrip() + ) + + yield domain_id + + delete_target_id("domains", domain_id) diff --git a/tests/integration/domains/test_domain_records.py b/tests/integration/domains/test_domain_records.py index e7d9b302..24832325 100644 --- a/tests/integration/domains/test_domain_records.py +++ b/tests/integration/domains/test_domain_records.py @@ -6,7 +6,7 @@ from tests.integration.helpers import ( SUCCESS_STATUS_CODE, - delete_all_domains, + delete_target_id, exec_test_command, ) @@ -72,14 +72,12 @@ def domain_records_setup(): yield domain_id, record_id try: - delete_all_domains() + delete_target_id(target="domains", id=domain_id) except: logging.exception("Failed to delete all domains") -def test_create_a_domain(): - timestamp = str(int(time.time())) - +def test_create_a_domain(create_master_domain): # Current domain list process = exec_test_command( BASE_CMD + ["list", '--format="id"', "--text", "--no-header"] @@ -87,19 +85,7 @@ def test_create_a_domain(): output_current = process.stdout.decode() # Create domain - exec_test_command( - BASE_CMD - + [ - "create", - "--type", - "master", - "--domain", - timestamp + "example.com", - "--soa_email=pthiel@linode.com", - "--text", - "--no-header", - ] - ) + domain_id = create_master_domain process = exec_test_command( BASE_CMD + ["list", "--format=id", "--text", "--no-header"] @@ -219,7 +205,3 @@ def test_delete_a_domain_record(domain_records_setup): # Assert on status code returned from deleting domain assert process.returncode == SUCCESS_STATUS_CODE - - -def test_delete_all_domains(): - delete_all_domains() diff --git a/tests/integration/domains/test_domains_tags.py b/tests/integration/domains/test_domains_tags.py index 451ab871..51b5de52 100644 --- a/tests/integration/domains/test_domains_tags.py +++ b/tests/integration/domains/test_domains_tags.py @@ -1,12 +1,9 @@ -import logging import re import time -import pytest - from tests.integration.helpers import ( - delete_all_domains, delete_tag, + delete_target_id, exec_failing_test_command, exec_test_command, ) @@ -14,15 +11,6 @@ BASE_CMD = ["linode-cli", "domains"] -@pytest.fixture(scope="session", autouse=True) -def domain_tags_setup(): - yield "setup" - try: - delete_all_domains() - except: - logging.exception("Failed to delete all domains") - - # @pytest.mark.skip(reason="BUG 943") def test_fail_to_create_master_domain_with_invalid_tags(): timestamp = str(int(time.time())) @@ -91,16 +79,43 @@ def test_create_master_domain_with_tags(): tag, ] ) - output = process.stdout.decode() + output = process.stdout.decode().rstrip() assert re.search("[0-9]+,[0-9]+-example.com,master,active," + tag, output) + res_arr = output.split(",") + domain_id = res_arr[0] + delete_target_id(target="domains", id=domain_id) + # @pytest.mark.skip(reason="BUG 943") def test_delete_domain_and_tag(): + timestamp = str(int(time.time())) + tag = "zoo" + + domain_id = ( + exec_test_command( + BASE_CMD + + [ + "create", + "--type", + "master", + "--domain", + timestamp + "-example.com", + "--soa_email=" + timestamp + "pthiel@linode.com", + "--text", + "--no-header", + "--delimiter=,", + "--format=id", + "--tag", + tag, + ] + ) + .stdout.decode() + .rstrip() + ) # need to check if tag foo is still present while running this test result = exec_test_command(["linode-cli", "tags", "list"]).stdout.decode() - if "foo" in result: - delete_tag("foo") - delete_all_domains() - else: - delete_all_domains() + + if "zoo" in result: + delete_tag("zoo") + delete_target_id(target="domains", id=domain_id) diff --git a/tests/integration/domains/test_master-domains.py b/tests/integration/domains/test_master_domains.py similarity index 81% rename from tests/integration/domains/test_master-domains.py rename to tests/integration/domains/test_master_domains.py index bd7cb0ea..0b4891a1 100644 --- a/tests/integration/domains/test_master-domains.py +++ b/tests/integration/domains/test_master_domains.py @@ -7,7 +7,7 @@ from tests.integration.helpers import ( FAILED_STATUS_CODE, - delete_all_domains, + delete_target_id, exec_test_command, ) @@ -44,9 +44,9 @@ def setup_master_domains(): logging.exception("Failed to create master domain in setup") yield master_domain_id try: - delete_all_domains() + delete_target_id(target="domains", id=master_domain_id) except: - logging.exception("Failed to delete all domains") + logging.exception("Failed to delete domain with id:" + master_domain_id) def test_create_domain_fails_without_spcified_type(): @@ -111,34 +111,9 @@ def test_create_master_domain_fails_without_soa_email(): assert "soa_email soa_email required when type=master" in result -def test_create_master_domain(): - timestamp = str(int(time.time())) - - result = exec_test_command( - BASE_CMD - + [ - "create", - "--type", - "master", - "--domain", - "BC" + timestamp + "-example.com", - "--soa_email=pthiel" + timestamp + "@linode.com", - "--text", - "--no-header", - "--delimiter", - ",", - "--format=id,domain,type,status,soa_email", - ] - ).stdout.decode() - - assert re.search( - "[0-9]+,BC" - + timestamp - + "-example.com,master,active,pthiel" - + timestamp - + "@linode.com", - result, - ) +def test_create_master_domain(create_master_domain): + domain_id = create_master_domain + assert re.search("[0-9]+", domain_id) def test_update_master_domain_soa_email(setup_master_domains): @@ -198,8 +173,3 @@ def test_show_domain_detail(setup_master_domains): ).stdout.decode() assert re.search("[0-9]+,BC[0-9]+-example.com,master,active", result) - - -# This test actually gets covered by the teardown method in @fixture -def test_delete_all_master_domains(): - delete_all_domains() diff --git a/tests/integration/domains/test_slave_domains.py b/tests/integration/domains/test_slave_domains.py index 485eae42..57ab5d56 100644 --- a/tests/integration/domains/test_slave_domains.py +++ b/tests/integration/domains/test_slave_domains.py @@ -8,7 +8,7 @@ from tests.integration.helpers import ( FAILED_STATUS_CODE, SUCCESS_STATUS_CODE, - delete_all_domains, + delete_target_id, exec_test_command, ) @@ -46,7 +46,7 @@ def slave_domain_setup(): logging.exception("Failed to create slave domain in setup") yield slave_domain_id try: - delete_all_domains() + delete_target_id(target="domains", id=slave_domain_id) except: logging.exception("Failed to delete all domains") @@ -66,29 +66,9 @@ def test_create_slave_domain_fails_without_master_dns_server(): ) -def test_create_slave_domain(): - new_timestamp = str(int(time.time())) - result = exec_test_command( - BASE_CMD - + [ - "create", - "--type", - "slave", - "--domain", - new_timestamp + "-example.com", - "--master_ips", - "1.1.1.1", - "--text", - "--no-header", - "--delimiter", - ",", - "--format=id,domain,type,status", - ] - ).stdout.decode() - - assert re.search( - "[0-9]+," + new_timestamp + "-example.com,slave,active", result - ) +def test_create_slave_domain(create_slave_domain): + domain_id = create_slave_domain + assert re.search("[0-9]+", domain_id) def test_list_slave_domain(): diff --git a/tests/integration/events/test_events.py b/tests/integration/events/test_events.py index 733dbf2a..6b8997f6 100644 --- a/tests/integration/events/test_events.py +++ b/tests/integration/events/test_events.py @@ -5,7 +5,7 @@ import pytest -from tests.integration.helpers import delete_all_domains, exec_test_command +from tests.integration.helpers import delete_target_id, exec_test_command BASE_CMD = ["linode-cli", "events"] @@ -43,27 +43,11 @@ def events_setup(): yield domain_id try: - delete_all_domains() + delete_target_id(target="domains", id=domain_id) except: logging.exception("Failed to delete all domains") -def get_domain_id(): - process = exec_test_command( - [ - "linode-cli", - "domains", - "list", - "--format=id", - "--text", - "--no-header", - ] - ) - output = process.stdout.decode() - domain_id_arr = output.splitlines() - return domain_id_arr[0] - - def test_print_events_usage_information(): process = exec_test_command(BASE_CMD) output = process.stdout.decode() diff --git a/tests/integration/helpers.py b/tests/integration/helpers.py index e0a00f3a..818b2b38 100644 --- a/tests/integration/helpers.py +++ b/tests/integration/helpers.py @@ -90,6 +90,11 @@ def remove_lke_clusters(): exec_test_command(["linode-cli", "lke", "cluster-delete", id]) +def delete_target_id(target: str, id: str): + result = exec_test_command(["linode-cli", target, "delete", id]) + assert result.returncode == SUCCESS_STATUS_CODE + + def remove_all(target: str): entity_ids = "" if target == "stackscripts": diff --git a/tests/integration/linodes/helpers_linodes.py b/tests/integration/linodes/helpers_linodes.py index d85917ca..baaf3407 100644 --- a/tests/integration/linodes/helpers_linodes.py +++ b/tests/integration/linodes/helpers_linodes.py @@ -1,7 +1,9 @@ import os import time -from tests.integration.helpers import exec_test_command +import pytest + +from tests.integration.helpers import delete_target_id, exec_test_command DEFAULT_RANDOM_PASS = ( exec_test_command(["openssl", "rand", "-base64", "32"]) @@ -203,3 +205,195 @@ def create_linode_and_wait( ) return linode_id + + +@pytest.fixture +def create_linode_with_label(): + result = ( + exec_test_command( + BASE_CMD + + [ + "create", + "--type", + "g6-standard-2", + "--region", + "us-east", + "--image", + DEFAULT_TEST_IMAGE, + "--label", + "cli-1", + "--root_pass", + DEFAULT_RANDOM_PASS, + "--text", + "--delimiter", + ",", + "--no-headers", + "--format", + "label,region,type,image,id", + "--no-defaults", + ] + ) + .stdout.decode() + .rstrip() + ) + + yield result + + res_arr = result.split(",") + linode_id = res_arr[4] + delete_target_id(target="linodes", id=linode_id) + + +@pytest.fixture +def create_linode_min_req(): + result = ( + exec_test_command( + BASE_CMD + + [ + "create", + "--type", + "g6-standard-2", + "--region", + "us-east", + "--root_pass", + DEFAULT_RANDOM_PASS, + "--no-defaults", + "--text", + "--delimiter", + ",", + "--no-headers", + "--format", + "id,region,type", + ] + ) + .stdout.decode() + .rstrip() + ) + + yield result + + res_arr = result.split(",") + linode_id = res_arr[0] + delete_target_id(target="linodes", id=linode_id) + + +@pytest.fixture +def create_linode_wo_image(): + linode_type = ( + os.popen( + "linode-cli linodes types --text --no-headers --format=id | xargs | awk '{ print $1 }'" + ) + .read() + .rstrip() + ) + linode_region = ( + os.popen( + "linode-cli regions list --format=id --text --no-headers | xargs | awk '{ print $1 }'" + ) + .read() + .rstrip() + ) + + exec_test_command( + BASE_CMD + + [ + "create", + "--no-defaults", + "--label", + "cli-2", + "--type", + linode_type, + "--region", + linode_region, + "--root_pass", + DEFAULT_RANDOM_PASS, + ] + ).stdout.decode() + + linode_id = ( + exec_test_command( + BASE_CMD + + [ + "list", + "--label", + "cli-2", + "--text", + "--no-headers", + "--format", + "id", + ] + ) + .stdout.decode() + .rstrip() + ) + + yield linode_id + + delete_target_id(target="linodes", id=linode_id) + + +@pytest.fixture +def create_linode_backup_enabled(): + linode_type = ( + os.popen( + "linode-cli linodes types --text --no-headers --format='id' | xargs | awk '{ print $1 }'" + ) + .read() + .rstrip() + ) + + # create linode with backups enabled + linode_id = ( + exec_test_command( + [ + "linode-cli", + "linodes", + "create", + "--backups_enabled", + "true", + "--type", + linode_type, + "--region", + DEFAULT_REGION, + "--image", + DEFAULT_TEST_IMAGE, + "--root_pass", + DEFAULT_RANDOM_PASS, + "--text", + "--no-headers", + "--format=id", + ] + ) + .stdout.decode() + .rstrip() + ) + + yield linode_id + + delete_target_id("linodes", linode_id) + + +@pytest.fixture +def take_snapshot_of_linode(): + timestamp = str(time.time()) + # get linode id after creation and wait for "running" status + linode_id = create_linode_and_wait() + new_snapshot_label = "test_snapshot" + timestamp + + result = exec_test_command( + BASE_CMD + + [ + "snapshot", + linode_id, + "--label", + new_snapshot_label, + "--text", + "--delimiter", + ",", + "--no-headers", + ] + ).stdout.decode() + + yield linode_id, new_snapshot_label + + delete_target_id("linodes", linode_id) diff --git a/tests/integration/linodes/test_backups.py b/tests/integration/linodes/test_backups.py index 0b6ed539..49ae804f 100755 --- a/tests/integration/linodes/test_backups.py +++ b/tests/integration/linodes/test_backups.py @@ -1,18 +1,13 @@ -import logging import os import re import pytest -from tests.integration.helpers import exec_test_command +from tests.integration.helpers import delete_target_id, exec_test_command from tests.integration.linodes.helpers_linodes import ( BASE_CMD, - DEFAULT_RANDOM_PASS, - DEFAULT_REGION, - DEFAULT_TEST_IMAGE, create_linode, create_linode_and_wait, - remove_linodes, ) # ################################################################## @@ -22,26 +17,17 @@ snapshot_label = "test_snapshot1" -@pytest.fixture(scope="session", autouse=True) -def setup_backup(): - # skip all test if TEST_ENVIRONMENT variable is "test" or "dev" - if "test" == os.environ.get( - "TEST_ENVIRONMENT", None - ) or "dev" == os.environ.get("TEST_ENVIRONMENT", None): - pytest.skip(allow_module_level=True) - else: - # create linode with back up disabled - linode_id = create_linode() +@pytest.fixture +def create_linode_setup(): + linode_id = create_linode() + yield linode_id - # teadown clean up (delete all linodes) - try: - remove_linodes() - except: - logging.exception("Fail to remove all linodes..") + + delete_target_id("linodes", linode_id) -def test_create_linode_with_backup_disabled(): - new_linode_id = create_linode() +def test_create_linode_with_backup_disabled(create_linode_setup): + linode_id = create_linode_setup result = exec_test_command( BASE_CMD + [ @@ -54,12 +40,12 @@ def test_create_linode_with_backup_disabled(): ] ).stdout.decode() - assert re.search(new_linode_id + ",False", result) + assert re.search(linode_id + ",False", result) -def test_enable_backups(setup_backup): +def test_enable_backups(create_linode_setup): # get linode id - linode_id = setup_backup + linode_id = create_linode_setup # enable backup exec_test_command( @@ -81,41 +67,8 @@ def test_enable_backups(setup_backup): assert re.search(linode_id + ",True", result) -def test_create_backup_with_backup_enabled(): - linode_type = ( - os.popen( - "linode-cli linodes types --text --no-headers --format='id' | xargs | awk '{ print $1 }'" - ) - .read() - .rstrip() - ) - - # create linode with backups enabled - linode_id = ( - exec_test_command( - [ - "linode-cli", - "linodes", - "create", - "--backups_enabled", - "true", - "--type", - linode_type, - "--region", - DEFAULT_REGION, - "--image", - DEFAULT_TEST_IMAGE, - "--root_pass", - DEFAULT_RANDOM_PASS, - "--text", - "--no-headers", - "--format=id", - ] - ) - .stdout.decode() - .rstrip() - ) - +def test_create_backup_with_backup_enabled(create_linode_backup_enabled): + linode_id = create_linode_backup_enabled result = exec_test_command( BASE_CMD + [ @@ -163,29 +116,10 @@ def test_take_snapshot_of_linode(): os.environ.get("RUN_LONG_TESTS", None) != "TRUE", reason="Skipping long-running Test, to run set RUN_LONG_TESTS=TRUE", ) -def test_view_the_snapshot(): +def test_view_the_snapshot(take_snapshot_of_linode): # get linode id after creation and wait for "running" status - linode_id = create_linode_and_wait() - new_snapshot_label = "test_snapshot2" - - result = exec_test_command( - BASE_CMD - + [ - "snapshot", - linode_id, - "--label", - new_snapshot_label, - "--text", - "--delimiter", - ",", - "--no-headers", - ] - ).stdout.decode() - assert re.search( - "[0-9]+,pending,snapshot,[0-9]+-[0-9]+-[0-9]+T[0-9]+:[0-9]+:[0-9]+," - + new_snapshot_label, - result, - ) + linode_id = take_snapshot_of_linode[0] + new_snapshot_label = take_snapshot_of_linode[1] result = exec_test_command( BASE_CMD @@ -204,23 +138,16 @@ def test_view_the_snapshot(): + new_snapshot_label, result, ) - # BUG outputs the backup as json, assertion below asserts that outputs the expected. - # assert(re.search("'status':.*'pending", result)) - # assert(re.search("'finished':.*None", result)) - # assert(re.search("'type':.*'snapshot'", result)) - # assert(re.search("'label':.*"+new_snapshot_label, result)) - # assert(re.search("'region':.*'us-east'", result)) - # assert(re.search("'id':.*[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]", result)) @pytest.mark.skipif( os.environ.get("RUN_LONG_TESTS", None) != "TRUE", reason="Skipping long-running Test, to run set RUN_LONG_TESTS=TRUE", ) -def test_cancel_backups(): +def test_cancel_backups(take_snapshot_of_linode): # get linode id after creation and wait for "running" status - linode_id = create_linode_and_wait() - new_snapshot_label = "test_snapshot3" + linode_id = take_snapshot_of_linode[0] + new_snapshot_label = take_snapshot_of_linode[1] result = exec_test_command( BASE_CMD diff --git a/tests/integration/linodes/test_linodes.py b/tests/integration/linodes/test_linodes.py index c085bcea..511b992c 100644 --- a/tests/integration/linodes/test_linodes.py +++ b/tests/integration/linodes/test_linodes.py @@ -1,11 +1,11 @@ import logging -import os import re import time import pytest from tests.integration.helpers import ( + delete_target_id, exec_failing_test_command, exec_test_command, ) @@ -14,10 +14,12 @@ DEFAULT_LABEL, DEFAULT_RANDOM_PASS, DEFAULT_TEST_IMAGE, - remove_linodes, wait_until, ) +timestamp = str(time.time()) +linode_label = DEFAULT_LABEL + timestamp + @pytest.fixture(scope="session", autouse=True) def setup_linodes(): @@ -35,7 +37,7 @@ def setup_linodes(): "--image", DEFAULT_TEST_IMAGE, "--label", - DEFAULT_LABEL, + linode_label, "--root_pass", DEFAULT_RANDOM_PASS, "--text", @@ -43,7 +45,7 @@ def setup_linodes(): ",", "--no-headers", "--format", - "label,region,type,image", + "id", "--no-defaults", "--format", "id", @@ -54,12 +56,15 @@ def setup_linodes(): ) except: logging.exception("Failed to create default linode in setup..") + yield linode_id + try: # clean up - remove_linodes() + delete_target_id(target="linodes", id=linode_id) + except: - logging.exception("Failed removing all linodes..") + logging.exception("Failed removing linode..") def test_update_linode_with_a_image(): @@ -68,32 +73,9 @@ def test_update_linode_with_a_image(): assert "--image" not in result -def test_create_linodes_with_a_label(): - result = exec_test_command( - BASE_CMD - + [ - "create", - "--type", - "g6-standard-2", - "--region", - "us-east", - "--image", - DEFAULT_TEST_IMAGE, - "--label", - "cli-1", - "--root_pass", - DEFAULT_RANDOM_PASS, - "--text", - "--delimiter", - ",", - "--no-headers", - "--format", - "label,region,type,image", - "--no-defaults", - ] - ).stdout.decode() +def test_create_linodes_with_a_label(create_linode_with_label): + result = create_linode_with_label - print(result) assert re.search( "cli-1,us-east,g6-standard-2," + DEFAULT_TEST_IMAGE, result ) @@ -119,33 +101,15 @@ def test_view_linode_configuration(setup_linodes): assert re.search( linode_id + "," - + DEFAULT_LABEL + + linode_label + ",us-east,g6-standard-2," + DEFAULT_TEST_IMAGE, result, ) -def test_create_linode_with_min_required_props(): - result = exec_test_command( - BASE_CMD - + [ - "create", - "--type", - "g6-standard-2", - "--region", - "us-east", - "--root_pass", - DEFAULT_RANDOM_PASS, - "--no-defaults", - "--text", - "--delimiter", - ",", - "--no-headers", - "--format", - "id,region,type", - ] - ).stdout.decode() +def test_create_linode_with_min_required_props(create_linode_min_req): + result = create_linode_min_req assert re.search("[0-9]+,us-east,g6-standard-2", result) @@ -168,54 +132,8 @@ def test_create_linodes_fails_without_a_root_pass(): assert "root_pass root_pass is required" in result -def test_create_linode_without_image_and_not_boot(): - linode_type = ( - os.popen( - "linode-cli linodes types --text --no-headers --format=id | xargs | awk '{ print $1 }'" - ) - .read() - .rstrip() - ) - linode_region = ( - os.popen( - "linode-cli regions list --format=id --text --no-headers | xargs | awk '{ print $1 }'" - ) - .read() - .rstrip() - ) - - exec_test_command( - BASE_CMD - + [ - "create", - "--no-defaults", - "--label", - "cli-2", - "--type", - linode_type, - "--region", - linode_region, - "--root_pass", - DEFAULT_RANDOM_PASS, - ] - ).stdout.decode() - - linode_id = ( - exec_test_command( - BASE_CMD - + [ - "list", - "--label", - "cli-2", - "--text", - "--no-headers", - "--format", - "id", - ] - ) - .stdout.decode() - .rstrip() - ) +def test_create_linode_without_image_and_not_boot(create_linode_wo_image): + linode_id = create_linode_wo_image wait_until(linode_id=linode_id, timeout=180, status="offline") @@ -231,12 +149,12 @@ def test_list_linodes(setup_linodes): result = exec_test_command( BASE_CMD + ["list", "--format", "label", "--text", "--no-headers"] ).stdout.decode() - assert DEFAULT_LABEL in result + assert linode_label in result def test_add_tag_to_linode(setup_linodes): linode_id = setup_linodes - unique_tag = str(int(time.time())) + "tag" + unique_tag = "tag" + str(int(time.time())) result = exec_test_command( BASE_CMD diff --git a/tests/integration/linodes/test_power_status.py b/tests/integration/linodes/test_power_status.py index d0c75e1d..5e93c1ad 100644 --- a/tests/integration/linodes/test_power_status.py +++ b/tests/integration/linodes/test_power_status.py @@ -2,12 +2,11 @@ import pytest -from tests.integration.helpers import exec_test_command +from tests.integration.helpers import delete_target_id, exec_test_command from tests.integration.linodes.helpers_linodes import ( BASE_CMD, create_linode, create_linode_and_wait, - remove_linodes, wait_until, ) @@ -22,9 +21,18 @@ def setup_power_status(): yield linode_id try: # clean up - remove_linodes() + delete_target_id(target="linodes", id=linode_id) except: - logging.exception("Failed removing all linodes..") + logging.exception("Failed removing linode..") + + +@pytest.fixture(scope="session") +def create_linode_in_running_state(): + linode_id = create_linode_and_wait() + + yield linode_id + + delete_target_id("linodes", linode_id) def test_create_linode_and_boot(setup_power_status): @@ -36,9 +44,9 @@ def test_create_linode_and_boot(setup_power_status): assert result, "Linode status has not changed to running from provisioning" -def test_reboot_linode(): +def test_reboot_linode(create_linode_in_running_state): # create linode and wait until it is in "running" state - linode_id = create_linode_and_wait() + linode_id = create_linode_in_running_state # reboot linode from "running" status exec_test_command( diff --git a/tests/integration/linodes/test_rebuild.py b/tests/integration/linodes/test_rebuild.py index 289e48f2..dab518df 100644 --- a/tests/integration/linodes/test_rebuild.py +++ b/tests/integration/linodes/test_rebuild.py @@ -4,6 +4,7 @@ import pytest from tests.integration.helpers import ( + delete_target_id, exec_failing_test_command, exec_test_command, ) @@ -11,7 +12,6 @@ BASE_CMD, DEFAULT_RANDOM_PASS, create_linode_and_wait, - remove_linodes, wait_until, ) @@ -26,9 +26,9 @@ def setup_rebuild(): yield linode_id try: # clean up - remove_linodes() + delete_target_id(target="linodes", id=linode_id) except: - logging.exception("Failed removing all linodes..") + logging.exception("Failed removing linode..") def test_rebuild_fails_without_image(setup_rebuild): diff --git a/tests/integration/linodes/test_resize.py b/tests/integration/linodes/test_resize.py index 89521dec..28509589 100644 --- a/tests/integration/linodes/test_resize.py +++ b/tests/integration/linodes/test_resize.py @@ -4,13 +4,13 @@ import pytest from tests.integration.helpers import ( + delete_target_id, exec_failing_test_command, exec_test_command, ) from tests.integration.linodes.helpers_linodes import ( BASE_CMD, create_linode_and_wait, - remove_linodes, wait_until, ) @@ -32,9 +32,9 @@ def setup_resize(): yield linode_id try: # clean up - remove_linodes() + delete_target_id(target="linodes", id=linode_id) except: - logging.exception("Failed removing all linodes..") + logging.exception("Failed removing linode..") def test_resize_fails_to_the_same_plan(setup_resize): diff --git a/tests/integration/networking/test_networking.py b/tests/integration/networking/test_networking.py index cc512092..c1bf8272 100644 --- a/tests/integration/networking/test_networking.py +++ b/tests/integration/networking/test_networking.py @@ -3,11 +3,8 @@ import pytest -from tests.integration.helpers import exec_test_command -from tests.integration.linodes.helpers_linodes import ( - create_linode_and_wait, - remove_linodes, -) +from tests.integration.helpers import delete_target_id, exec_test_command +from tests.integration.linodes.helpers_linodes import create_linode_and_wait BASE_CMD = ["linode-cli", "networking"] @@ -17,9 +14,9 @@ def setup_test_networking(): linode_id = create_linode_and_wait() yield linode_id try: - remove_linodes() + delete_target_id(target="linodes", id=linode_id) except: - logging.exception("Failed to remove all linodes in teardown..") + logging.exception("Failed to remove linode in teardown..") def test_display_ips_for_available_linodes(setup_test_networking): @@ -95,11 +92,3 @@ def test_allocate_additional_private_ipv4_address(setup_test_networking): assert re.search( "ipv4,False,.*,[0-9][0-9][0-9][0-9][0-9][0-9][0-9]*", result ) - - -def test_list_empty_output_for_linode_account_without_linodes(): - remove_linodes() - result = exec_test_command( - BASE_CMD + ["ips-list", "--text", "--no-headers"] - ).stdout.decode() - assert "" == result diff --git a/tests/integration/nodebalancers/helpers_nodebalancers.py b/tests/integration/nodebalancers/helpers_nodebalancers.py new file mode 100644 index 00000000..295ea348 --- /dev/null +++ b/tests/integration/nodebalancers/helpers_nodebalancers.py @@ -0,0 +1,34 @@ +import pytest + +from tests.integration.helpers import delete_target_id, exec_test_command + +BASE_CMD = ["linode-cli", "nodebalancers"] + + +@pytest.fixture +def create_nodebalancer_with_default_conf(): + result = ( + exec_test_command( + BASE_CMD + + [ + "create", + "--region", + "us-east", + "--text", + "--delimiter", + ",", + "--format", + "id,label,region,hostname,client_conn_throttle", + "--suppress-warnings", + "--no-headers", + ] + ) + .stdout.decode() + .rstrip() + ) + + yield result + + res_arr = result.split(",") + nodebalancer_id = res_arr[0] + delete_target_id(target="nodebalancers", id=nodebalancer_id) diff --git a/tests/integration/nodebalancers/test_node_balancers.py b/tests/integration/nodebalancers/test_node_balancers.py index d6d3827a..214817df 100644 --- a/tests/integration/nodebalancers/test_node_balancers.py +++ b/tests/integration/nodebalancers/test_node_balancers.py @@ -1,12 +1,11 @@ -import os import re import pytest from tests.integration.helpers import ( + delete_target_id, exec_failing_test_command, exec_test_command, - remove_all, ) from tests.integration.linodes.helpers_linodes import DEFAULT_TEST_IMAGE @@ -17,143 +16,151 @@ @pytest.fixture(scope="session", autouse=True) def setup_test_node_balancers(): # create a default nodebalancer - exec_test_command( - BASE_CMD - + [ - "create", - "--region", - "us-east", - "--text", - "--delimiter", - ",", - "--format", - "id,label,region,hostname,client_conn_throttle", - ] - ).stdout.decode() - # create one standard config - nodebalancer_id = get_nodebalancer_id() - exec_test_command( - BASE_CMD - + [ - "config-create", - nodebalancer_id, - "--delimiter", - ",", - "--text", - "--no-headers", - ] - ).stdout.decode() - - # create a default node - node_ip = ( - os.popen( - "linode-cli linodes create --root_pass aComplex@Password --booted true --region us-east --type g6-standard-2 --private_ip true --image " - + DEFAULT_TEST_IMAGE - + ' --text --no-headers --format "ip_address" | egrep -o "192.168.[0-9]{1,3}.[0-9]{1,3}"' - ) - .read() - .rstrip() - ) - node_label = "defaultnode1" - config_id = get_config_id(nodebalancer_id=nodebalancer_id) - - exec_test_command( - BASE_CMD - + [ - "node-create", - "--address", - node_ip + ":80", - "--label", - node_label, - "--weight", - "100", - "--text", - "--no-headers", - "--delimiter", - ",", - nodebalancer_id, - config_id, - ] - ).stdout.decode() - - yield "setup" - try: - remove_all(target="nodebalancers") - remove_all(target="linodes") - except: - "Failed to remove all linodes/nodebalancers in teardown.." - - -# get helpers -def get_nodebalancer_id(): - nb_id = ( + nodebalancer_id = ( exec_test_command( - BASE_CMD + ["list", "--format", "id", "--text", "--no-headers"] + BASE_CMD + + [ + "create", + "--region", + "us-east", + "--text", + "--delimiter", + ",", + "--format", + "id", + "--no-headers", + ] ) .stdout.decode() - .split() + .rstrip() ) - return nb_id[0] - - -def get_config_id(nodebalancer_id: str): - conf_id = ( + # create one standard config + config_id = ( exec_test_command( BASE_CMD + [ - "configs-list", + "config-create", nodebalancer_id, - "--format", - "id", + "--delimiter", + ",", "--text", "--no-headers", + "--format", + "id", ] ) .stdout.decode() - .split() + .rstrip() ) - return conf_id[0] + linode_create = ( + exec_test_command( + [ + "linode-cli", + "linodes", + "create", + "--root_pass", + "aComplex@Password", + "--booted", + "true", + "--region", + "us-east", + "--type", + "g6-standard-2", + "--private_ip", + "true", + "--image", + DEFAULT_TEST_IMAGE, + "--text", + "--delimiter", + ",", + "--format", + "id,ipv4", + "--no-header", + "--suppress-warnings", + ] + ) + .stdout.decode() + .rstrip() + ) + linode_arr = linode_create.split(",") + linode_id = linode_arr[0] + ip_arr = linode_arr[1].split(" ") + node_ip = ip_arr[1] + node_label = "defaultnode1" -def get_node_id(nodebalancer_id: str, config_id: str): node_id = ( exec_test_command( BASE_CMD + [ - "nodes-list", - nodebalancer_id, - config_id, + "node-create", + "--address", + node_ip + ":80", + "--label", + node_label, + "--weight", + "100", "--text", "--no-headers", + "--delimiter", + ",", + nodebalancer_id, + config_id, "--format", "id", ] ) .stdout.decode() - .split() + .rstrip() ) - return node_id[0] + yield nodebalancer_id, config_id, node_id, node_ip + + try: + delete_target_id(target="nodebalancers", id=nodebalancer_id) + delete_target_id(target="linodes", id=linode_id) + except: + "Failed to remove all linodes/nodebalancers in teardown.." -def get_node_ip(nodebalancer_id: str, config_id: str, node_id: str): - node_ip = ( + +@pytest.fixture +def create_linode_to_add(): + linode = ( exec_test_command( - BASE_CMD - + [ - "node-view", - nodebalancer_id, - config_id, - node_id, - "--format", - "address", + [ + "linode-cli", + "linodes", + "create", + "--root_pass", + "aComplex@Password", + "--booted", + "true", + "--region", + "us-east", + "--type", + "g6-standard-2", + "--private_ip", + "true", + "--image", + DEFAULT_TEST_IMAGE, "--text", - "--no-headers", + "--delimiter", + ",", + "--format", + "id,ipv4", + "--no-header", + "--suppress-warnings", ] ) .stdout.decode() - .split() + .rstrip() ) - return node_ip[0] + + yield linode + + linode_arr = linode.split(",") + linode_id = linode_arr[0] + delete_target_id("linodes", linode_id) def test_fail_to_create_nodebalancer_without_region(): @@ -164,20 +171,10 @@ def test_fail_to_create_nodebalancer_without_region(): assert "region region is required" in result -def test_create_nodebalancer_with_default_conf(): - result = exec_test_command( - BASE_CMD - + [ - "create", - "--region", - "us-east", - "--text", - "--delimiter", - ",", - "--format", - "id,label,region,hostname,client_conn_throttle", - ] - ).stdout.decode() +def test_create_nodebalancer_with_default_conf( + create_nodebalancer_with_default_conf, +): + result = create_nodebalancer_with_default_conf assert re.search(nodebalancer_created, result) @@ -197,8 +194,8 @@ def test_list_nodebalancers_and_status(): assert re.search(nodebalancer_created, result) -def test_display_public_ipv4_for_nodebalancer(): - nodebalancer_id = get_nodebalancer_id() +def test_display_public_ipv4_for_nodebalancer(setup_test_node_balancers): + nodebalancer_id = setup_test_node_balancers[0] result = exec_test_command( BASE_CMD @@ -222,8 +219,8 @@ def test_fail_to_view_nodebalancer_with_invalid_id(): assert "Request failed: 404" in result -def test_create_standard_configuration_profile(): - nodebalancer_id = get_nodebalancer_id() +def test_create_standard_configuration_profile(setup_test_node_balancers): + nodebalancer_id = setup_test_node_balancers[0] result = exec_test_command( BASE_CMD @@ -243,9 +240,9 @@ def test_create_standard_configuration_profile(): ) -def test_view_configuration_profile(): - nodebalancer_id = get_nodebalancer_id() - config_id = get_config_id(nodebalancer_id=nodebalancer_id) +def test_view_configuration_profile(setup_test_node_balancers): + nodebalancer_id = setup_test_node_balancers[0] + config_id = setup_test_node_balancers[1] result = exec_test_command( BASE_CMD @@ -264,19 +261,17 @@ def test_view_configuration_profile(): ) -def test_add_node_to_conf_profile(): - node_ip = ( - os.popen( - "linode-cli linodes create --root_pass aComplex@Password --booted true --region us-east --type g6-standard-2 --private_ip true --image " - + DEFAULT_TEST_IMAGE - + ' --text --no-headers --format "ip_address" | egrep -o "192.168.[0-9]{1,3}.[0-9]{1,3}"' - ) - .read() - .rstrip() - ) +def test_add_node_to_conf_profile( + setup_test_node_balancers, create_linode_to_add +): + linode_create = create_linode_to_add + linode_arr = linode_create.split(",") + ip_arr = linode_arr[1].split(" ") + node_ip = ip_arr[1] + node_label = "testnode1" - nodebalancer_id = get_nodebalancer_id() - config_id = get_config_id(nodebalancer_id=nodebalancer_id) + nodebalancer_id = setup_test_node_balancers[0] + config_id = setup_test_node_balancers[1] result = exec_test_command( BASE_CMD @@ -303,13 +298,11 @@ def test_add_node_to_conf_profile(): ) -def test_update_node_label(): - nodebalancer_id = get_nodebalancer_id() - config_id = get_config_id(nodebalancer_id=nodebalancer_id) - node_id = get_node_id(nodebalancer_id=nodebalancer_id, config_id=config_id) - node_ip = get_node_ip( - nodebalancer_id=nodebalancer_id, config_id=config_id, node_id=node_id - ) +def test_update_node_label(setup_test_node_balancers): + nodebalancer_id = setup_test_node_balancers[0] + config_id = setup_test_node_balancers[1] + node_id = setup_test_node_balancers[2] + node_ip = setup_test_node_balancers[3] new_label = "testnode1-edited" result = exec_test_command( @@ -329,21 +322,20 @@ def test_update_node_label(): ).stdout.decode() assert re.search( - "[0-9]+," + new_label + "," + node_ip + ",Unknown,100,accept", result + "[0-9]+," + new_label + "," + node_ip + ":80" + ",Unknown,100,accept", + result, ) -def test_update_node_port(): - nodebalancer_id = get_nodebalancer_id() - config_id = get_config_id(nodebalancer_id=nodebalancer_id) - node_id = get_node_id(nodebalancer_id=nodebalancer_id, config_id=config_id) - node_ip = get_node_ip( - nodebalancer_id=nodebalancer_id, config_id=config_id, node_id=node_id - ) +def test_update_node_port(setup_test_node_balancers): + nodebalancer_id = setup_test_node_balancers[0] + config_id = setup_test_node_balancers[1] + node_id = setup_test_node_balancers[2] + node_ip = setup_test_node_balancers[3] updated_port = ":23" - new_address = node_ip.replace(":80", updated_port) + new_address = node_ip + updated_port result = exec_test_command( BASE_CMD @@ -364,10 +356,10 @@ def test_update_node_port(): assert ("[0-9]+,.," + new_address + ",Unknown,100,accept", result) -def test_fail_to_update_node_to_public_ipv4_address(): - nodebalancer_id = get_nodebalancer_id() - config_id = get_config_id(nodebalancer_id=nodebalancer_id) - node_id = get_node_id(nodebalancer_id=nodebalancer_id, config_id=config_id) +def test_fail_to_update_node_to_public_ipv4_address(setup_test_node_balancers): + nodebalancer_id = setup_test_node_balancers[0] + config_id = setup_test_node_balancers[1] + node_id = setup_test_node_balancers[2] public_ip = "8.8.8.8:80" @@ -391,11 +383,10 @@ def test_fail_to_update_node_to_public_ipv4_address(): assert "Must begin with 192.168" in result -# @pytest.mark.dependency(test_update_node_port()) -def test_remove_node_from_configuration_profile(): - nodebalancer_id = get_nodebalancer_id() - config_id = get_config_id(nodebalancer_id=nodebalancer_id) - node_id = get_node_id(nodebalancer_id=nodebalancer_id, config_id=config_id) +def test_remove_node_from_configuration_profile(setup_test_node_balancers): + nodebalancer_id = setup_test_node_balancers[0] + config_id = setup_test_node_balancers[1] + node_id = setup_test_node_balancers[2] exec_test_command( BASE_CMD + ["node-delete", nodebalancer_id, config_id, node_id] @@ -404,9 +395,9 @@ def test_remove_node_from_configuration_profile(): # Test below this needs to be ran last and in order @pytest.fixture(scope="session") -def test_update_the_port_of_a_configuration_profile(): - nodebalancer_id = get_nodebalancer_id() - config_id = get_config_id(nodebalancer_id=nodebalancer_id) +def test_update_the_port_of_a_configuration_profile(setup_test_node_balancers): + nodebalancer_id = setup_test_node_balancers[0] + config_id = setup_test_node_balancers[1] result = exec_test_command( BASE_CMD @@ -431,8 +422,8 @@ def test_update_the_port_of_a_configuration_profile(): @pytest.fixture(scope="session") -def test_add_additional_configuration_profile(): - nodebalancer_id = get_nodebalancer_id() +def test_add_additional_configuration_profile(setup_test_node_balancers): + nodebalancer_id = setup_test_node_balancers[0] result = exec_test_command( BASE_CMD @@ -457,8 +448,8 @@ def test_add_additional_configuration_profile(): "test_add_additional_configuration_profile", "test_update_the_port_of_a_configuration_profile", ) -def test_list_multiple_configuration_profile(): - nodebalancer_id = get_nodebalancer_id() +def test_list_multiple_configuration_profile(setup_test_node_balancers): + nodebalancer_id = setup_test_node_balancers[0] result = exec_test_command( BASE_CMD diff --git a/tests/integration/ssh/test_ssh.py b/tests/integration/ssh/test_ssh.py index 000445d5..69218644 100644 --- a/tests/integration/ssh/test_ssh.py +++ b/tests/integration/ssh/test_ssh.py @@ -4,11 +4,8 @@ import pytest -from tests.integration.helpers import exec_test_command -from tests.integration.linodes.helpers_linodes import ( - create_linode_and_wait, - remove_linodes, -) +from tests.integration.helpers import delete_target_id, exec_test_command +from tests.integration.linodes.helpers_linodes import create_linode_and_wait BASE_CMD = ["linode-cli", "ssh"] NUM_OF_RETRIES = 10 @@ -42,7 +39,7 @@ def test_create_a_linode_in_running_state(ssh_key_pair_generator): ) yield linode_id - remove_linodes() + delete_target_id(target="linodes", id=linode_id) def test_display_ssh_plugin_usage_info(): diff --git a/tests/integration/volumes/test_volumes.py b/tests/integration/volumes/test_volumes.py index b71f93eb..5089b90b 100644 --- a/tests/integration/volumes/test_volumes.py +++ b/tests/integration/volumes/test_volumes.py @@ -4,10 +4,10 @@ import pytest from tests.integration.helpers import ( + delete_target_id, exec_failing_test_command, exec_test_command, os, - remove_all, ) BASE_CMD = ["linode-cli", "volumes"] @@ -40,19 +40,7 @@ def setup_test_volumes(): .rstrip() ) yield volume_id - remove_all(target="tags") - remove_all(target="volumes") - - -def get_volume_id(): - volume_id = ( - os.popen( - 'linode-cli volumes list --text --no-headers --delimiter="," --format="id" | head -n1' - ) - .read() - .rstrip() - ) - return volume_id + delete_target_id(target="volumes", id=volume_id) def test_fail_to_create_volume_under_10gb(): diff --git a/tests/integration/volumes/test_volumes_resize.py b/tests/integration/volumes/test_volumes_resize.py index 36791f39..d519ca52 100644 --- a/tests/integration/volumes/test_volumes_resize.py +++ b/tests/integration/volumes/test_volumes_resize.py @@ -4,6 +4,7 @@ import pytest from tests.integration.helpers import ( + delete_target_id, exec_failing_test_command, exec_test_command, ) @@ -38,7 +39,7 @@ def setup_test_volumes_resize(): .rstrip() ) yield volume_id - exec_test_command(BASE_CMD + ["delete", volume_id]) + delete_target_id(target="volumes", id=volume_id) def test_resize_fails_to_smaller_volume(setup_test_volumes_resize): From ac55dc114cc1c14a2dcab5e8c368ff0bcde73e09 Mon Sep 17 00:00:00 2001 From: Zhiwei Liang <121905282+zliang-akamai@users.noreply.github.com> Date: Tue, 18 Apr 2023 17:28:48 -0400 Subject: [PATCH 07/10] GH workflows update (#428) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📝 Description 1. Pin GitHub official actions to version number to avoid deprecating warnings and potential issues. --- .github/workflows/oci-build.yml | 4 ++-- .github/workflows/publish-pypi.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/oci-build.yml b/.github/workflows/oci-build.yml index 94eeeaa1..79529a2b 100644 --- a/.github/workflows/oci-build.yml +++ b/.github/workflows/oci-build.yml @@ -9,10 +9,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Clone Repository - uses: actions/checkout@e2f20e631ae6d7dd3b768f56a5d2af784dd54791 # pin@v2 + uses: actions/checkout@v3 - name: setup python 3 - uses: actions/setup-python@75f3110429a8c05be0e1bf360334e4cced2b63fa # pin@v2 + uses: actions/setup-python@v4 with: python-version: '3.x' diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index ae8e54aa..c564210d 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # pin@v3 + uses: actions/checkout@v3 - name: Update system packages run: sudo apt-get update -y @@ -17,7 +17,7 @@ jobs: run: sudo apt-get install -y build-essential - name: Setup Python - uses: actions/setup-python@75f3110429a8c05be0e1bf360334e4cced2b63fa # pin@v2 + uses: actions/setup-python@v4 with: python-version: '3.x' From 8a2bc454a05be06396a0b91f1e2103d34c0689c9 Mon Sep 17 00:00:00 2001 From: ykim-1 <126618609+ykim-1@users.noreply.github.com> Date: Wed, 19 Apr 2023 10:21:21 -0700 Subject: [PATCH 08/10] Fixing integration failures (#439) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📝 Description **What does this PR do and why is this change necessary?** ## ✔️ How to Test **What are the steps to reproduce the issue or verify the changes?** **How do I run the relevant unit/integration tests?** ## 📷 Preview **If applicable, include a screenshot or code snippet of this change. Otherwise, please remove this section.** --- tests/integration/conftest.py | 302 +++++++++++++++++- tests/integration/domains/helpers_domains.py | 70 ---- .../domains/test_domain_records.py | 97 +++--- .../domains/test_master_domains.py | 52 ++- .../integration/domains/test_slave_domains.py | 54 ++-- tests/integration/events/test_events.py | 53 ++- tests/integration/firewalls/test_firewalls.py | 48 ++- .../firewalls/test_firewalls_rules.py | 51 ++- tests/integration/linodes/helpers_linodes.py | 196 +----------- tests/integration/linodes/test_linodes.py | 68 ++-- .../integration/linodes/test_power_status.py | 16 +- tests/integration/linodes/test_rebuild.py | 15 +- tests/integration/linodes/test_resize.py | 26 +- tests/integration/lke/test_clusters.py | 7 +- .../integration/networking/test_networking.py | 8 +- .../nodebalancers/helpers_nodebalancers.py | 34 -- .../nodebalancers/test_node_balancers.py | 7 +- tests/integration/ssh/test_ssh.py | 16 +- .../stackscripts/test_stackscripts.py | 19 +- tests/integration/volumes/test_volumes.py | 4 +- .../volumes/test_volumes_resize.py | 2 + 21 files changed, 515 insertions(+), 630 deletions(-) delete mode 100644 tests/integration/domains/helpers_domains.py delete mode 100644 tests/integration/nodebalancers/helpers_nodebalancers.py diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 46aafe72..9dfb22aa 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -4,6 +4,7 @@ import os import subprocess import tempfile +import time from collections import defaultdict from itertools import count from pathlib import Path @@ -13,7 +14,21 @@ import pytest from linodecli import ENV_TOKEN_NAME -from tests.integration.helpers import get_random_text +from tests.integration.helpers import ( + delete_target_id, + exec_test_command, + get_random_text, +) +from tests.integration.linodes.helpers_linodes import ( + DEFAULT_RANDOM_PASS, + DEFAULT_REGION, + DEFAULT_TEST_IMAGE, + create_linode_and_wait, +) + +DOMAIN_BASE_CMD = ["linode-cli", "domains"] +LINODE_BASE_CMD = ["linode-cli", "linodes"] +NODEBALANCER_BASE_CMD = ["linode-cli", "nodebalancers"] @pytest.fixture(scope="session") @@ -102,3 +117,288 @@ def _generate_test_files( return file_paths return _generate_test_files + + +# test helper specific to Domains test suite +@pytest.fixture +def create_master_domain(): + timestamp = str(int(time.time())) + + domain_id = ( + exec_test_command( + DOMAIN_BASE_CMD + + [ + "create", + "--type", + "master", + "--domain", + timestamp + "example.com", + "--soa_email", + "pthiel_test@linode.com", + "--text", + "--no-header", + "--format", + "id", + ] + ) + .stdout.decode() + .rstrip() + ) + + yield domain_id + + delete_target_id("domains", id=domain_id) + + +@pytest.fixture +def create_slave_domain(): + timestamp = str(int(time.time())) + + domain_id = ( + exec_test_command( + DOMAIN_BASE_CMD + + [ + "create", + "--type", + "slave", + "--domain", + timestamp + "-example.com", + "--master_ips", + "1.1.1.1", + "--text", + "--no-header", + "--delimiter", + ",", + "--format=id", + ] + ) + .stdout.decode() + .rstrip() + ) + + yield domain_id + + delete_target_id("domains", domain_id) + + +# Test helpers specific to Linodes test suite +@pytest.fixture +def create_linode_with_label(): + result = ( + exec_test_command( + LINODE_BASE_CMD + + [ + "create", + "--type", + "g6-standard-2", + "--region", + "us-east", + "--image", + DEFAULT_TEST_IMAGE, + "--label", + "cli-1", + "--root_pass", + DEFAULT_RANDOM_PASS, + "--text", + "--delimiter", + ",", + "--no-headers", + "--format", + "label,region,type,image,id", + "--no-defaults", + ] + ) + .stdout.decode() + .rstrip() + ) + + yield result + + res_arr = result.split(",") + linode_id = res_arr[4] + delete_target_id(target="linodes", id=linode_id) + + +@pytest.fixture +def create_linode_min_req(): + result = ( + exec_test_command( + LINODE_BASE_CMD + + [ + "create", + "--type", + "g6-standard-2", + "--region", + "us-east", + "--root_pass", + DEFAULT_RANDOM_PASS, + "--no-defaults", + "--text", + "--delimiter", + ",", + "--no-headers", + "--format", + "id,region,type", + ] + ) + .stdout.decode() + .rstrip() + ) + + yield result + + res_arr = result.split(",") + linode_id = res_arr[0] + delete_target_id(target="linodes", id=linode_id) + + +@pytest.fixture +def create_linode_wo_image(): + linode_type = ( + os.popen( + "linode-cli linodes types --text --no-headers --format=id | xargs | awk '{ print $1 }'" + ) + .read() + .rstrip() + ) + linode_region = ( + os.popen( + "linode-cli regions list --format=id --text --no-headers | xargs | awk '{ print $1 }'" + ) + .read() + .rstrip() + ) + + exec_test_command( + LINODE_BASE_CMD + + [ + "create", + "--no-defaults", + "--label", + "cli-2", + "--type", + linode_type, + "--region", + linode_region, + "--root_pass", + DEFAULT_RANDOM_PASS, + ] + ).stdout.decode() + + linode_id = ( + exec_test_command( + LINODE_BASE_CMD + + [ + "list", + "--label", + "cli-2", + "--text", + "--no-headers", + "--format", + "id", + ] + ) + .stdout.decode() + .rstrip() + ) + + yield linode_id + + delete_target_id(target="linodes", id=linode_id) + + +@pytest.fixture +def create_linode_backup_enabled(): + linode_type = ( + os.popen( + "linode-cli linodes types --text --no-headers --format='id' | xargs | awk '{ print $1 }'" + ) + .read() + .rstrip() + ) + + # create linode with backups enabled + linode_id = ( + exec_test_command( + [ + "linode-cli", + "linodes", + "create", + "--backups_enabled", + "true", + "--type", + linode_type, + "--region", + DEFAULT_REGION, + "--image", + DEFAULT_TEST_IMAGE, + "--root_pass", + DEFAULT_RANDOM_PASS, + "--text", + "--no-headers", + "--format=id", + ] + ) + .stdout.decode() + .rstrip() + ) + + yield linode_id + + delete_target_id("linodes", linode_id) + + +@pytest.fixture +def take_snapshot_of_linode(): + timestamp = str(time.time()) + # get linode id after creation and wait for "running" status + linode_id = create_linode_and_wait() + new_snapshot_label = "test_snapshot" + timestamp + + result = exec_test_command( + LINODE_BASE_CMD + + [ + "snapshot", + linode_id, + "--label", + new_snapshot_label, + "--text", + "--delimiter", + ",", + "--no-headers", + ] + ).stdout.decode() + + yield linode_id, new_snapshot_label + + delete_target_id("linodes", linode_id) + + +# Test helpers specific to Nodebalancers test suite +@pytest.fixture +def create_nodebalancer_with_default_conf(): + result = ( + exec_test_command( + NODEBALANCER_BASE_CMD + + [ + "create", + "--region", + "us-east", + "--text", + "--delimiter", + ",", + "--format", + "id,label,region,hostname,client_conn_throttle", + "--suppress-warnings", + "--no-headers", + ] + ) + .stdout.decode() + .rstrip() + ) + + yield result + + res_arr = result.split(",") + nodebalancer_id = res_arr[0] + delete_target_id(target="nodebalancers", id=nodebalancer_id) diff --git a/tests/integration/domains/helpers_domains.py b/tests/integration/domains/helpers_domains.py deleted file mode 100644 index 6949775d..00000000 --- a/tests/integration/domains/helpers_domains.py +++ /dev/null @@ -1,70 +0,0 @@ -import time - -import pytest - -from tests.integration.helpers import delete_target_id, exec_test_command - -BASE_CMD = ["linode-cli", "domains"] - - -# test helper specific to domain test suite -@pytest.fixture -def create_master_domain(): - timestamp = str(int(time.time())) - - domain_id = ( - exec_test_command( - BASE_CMD - + [ - "create", - "--type", - "master", - "--domain", - timestamp + "example.com", - "--soa_email", - "pthiel_test@linode.com", - "--text", - "--no-header", - "--format", - "id", - ] - ) - .stdout.decode() - .rstrip() - ) - - yield domain_id - - delete_target_id("domains", id=domain_id) - - -# test helper specific to domain test suite -@pytest.fixture -def create_slave_domain(): - timestamp = str(int(time.time())) - - domain_id = ( - exec_test_command( - BASE_CMD - + [ - "create", - "--type", - "slave", - "--domain", - timestamp + "-example.com", - "--master_ips", - "1.1.1.1", - "--text", - "--no-header", - "--delimiter", - ",", - "--format=id", - ] - ) - .stdout.decode() - .rstrip() - ) - - yield domain_id - - delete_target_id("domains", domain_id) diff --git a/tests/integration/domains/test_domain_records.py b/tests/integration/domains/test_domain_records.py index 24832325..010c30f0 100644 --- a/tests/integration/domains/test_domain_records.py +++ b/tests/integration/domains/test_domain_records.py @@ -1,4 +1,3 @@ -import logging import re import time @@ -15,66 +14,54 @@ @pytest.fixture(scope="session", autouse=True) def domain_records_setup(): - # Create one domain for some tests in this suite - try: - timestamp = str(int(time.time())) - # Create domain - domain_id = ( - exec_test_command( - BASE_CMD - + [ - "create", - "--type", - "master", - "--domain", - timestamp + "example.com", - "--soa_email=pthiel@linode.com", - "--text", - "--no-header", - "--format=id", - ] - ) - .stdout.decode() - .rstrip() + timestamp = str(int(time.time())) + # Create domain + domain_id = ( + exec_test_command( + BASE_CMD + + [ + "create", + "--type", + "master", + "--domain", + timestamp + "example.com", + "--soa_email=pthiel@linode.com", + "--text", + "--no-header", + "--format=id", + ] ) + .stdout.decode() + .rstrip() + ) - except: - logging.exception("Failed creating domain in setup") - - try: - # Create record - record_id = ( - exec_test_command( - BASE_CMD - + [ - "records-create", - "--protocol=tcp", - "--type=SRV", - "--port=23", - "--priority=4", - "--service=telnet", - "--target=record-setup", - "--weight=4", - "--text", - "--no-header", - "--delimiter=,", - "--format=id", - domain_id, - ] - ) - .stdout.decode() - .rstrip() + # Create record + record_id = ( + exec_test_command( + BASE_CMD + + [ + "records-create", + "--protocol=tcp", + "--type=SRV", + "--port=23", + "--priority=4", + "--service=telnet", + "--target=record-setup", + "--weight=4", + "--text", + "--no-header", + "--delimiter=,", + "--format=id", + domain_id, + ] ) - - except: - logging.exception("Failed creating record in setup") + .stdout.decode() + .rstrip() + ) yield domain_id, record_id - try: - delete_target_id(target="domains", id=domain_id) - except: - logging.exception("Failed to delete all domains") + delete_target_id(target="domains", id=domain_id) def test_create_a_domain(create_master_domain): diff --git a/tests/integration/domains/test_master_domains.py b/tests/integration/domains/test_master_domains.py index 0b4891a1..9998adad 100644 --- a/tests/integration/domains/test_master_domains.py +++ b/tests/integration/domains/test_master_domains.py @@ -1,4 +1,3 @@ -import logging import os import re import time @@ -17,36 +16,31 @@ @pytest.fixture(scope="session", autouse=True) def setup_master_domains(): timestamp = str(int(time.time())) - # create one master domain for some tests in this suite - try: - # Create domain - master_domain_id = ( - exec_test_command( - BASE_CMD - + [ - "create", - "--type", - "master", - "--domain", - "BC" + timestamp + "-example.com", - "--soa_email=pthiel" + timestamp + "@linode.com", - "--text", - "--no-header", - "--delimiter", - ",", - "--format=id", - ] - ) - .stdout.decode() - .rstrip() + # Create domain + master_domain_id = ( + exec_test_command( + BASE_CMD + + [ + "create", + "--type", + "master", + "--domain", + "BC" + timestamp + "-example.com", + "--soa_email=pthiel" + timestamp + "@linode.com", + "--text", + "--no-header", + "--delimiter", + ",", + "--format=id", + ] ) - except: - logging.exception("Failed to create master domain in setup") + .stdout.decode() + .rstrip() + ) + yield master_domain_id - try: - delete_target_id(target="domains", id=master_domain_id) - except: - logging.exception("Failed to delete domain with id:" + master_domain_id) + + delete_target_id(target="domains", id=master_domain_id) def test_create_domain_fails_without_spcified_type(): diff --git a/tests/integration/domains/test_slave_domains.py b/tests/integration/domains/test_slave_domains.py index 57ab5d56..30fef387 100644 --- a/tests/integration/domains/test_slave_domains.py +++ b/tests/integration/domains/test_slave_domains.py @@ -1,4 +1,3 @@ -import logging import os import re import time @@ -18,37 +17,32 @@ @pytest.fixture(scope="session", autouse=True) def slave_domain_setup(): - # create one slave domain for some tests in this suite - try: - # Create domain - slave_domain_id = ( - exec_test_command( - BASE_CMD - + [ - "create", - "--type", - "slave", - "--domain", - timestamp + "-example.com", - "--master_ips", - "1.1.1.1", - "--text", - "--no-header", - "--delimiter", - ",", - "--format=id", - ] - ) - .stdout.decode() - .rstrip() + # Create domain + slave_domain_id = ( + exec_test_command( + BASE_CMD + + [ + "create", + "--type", + "slave", + "--domain", + timestamp + "-example.com", + "--master_ips", + "1.1.1.1", + "--text", + "--no-header", + "--delimiter", + ",", + "--format=id", + ] ) - except: - logging.exception("Failed to create slave domain in setup") + .stdout.decode() + .rstrip() + ) + yield slave_domain_id - try: - delete_target_id(target="domains", id=slave_domain_id) - except: - logging.exception("Failed to delete all domains") + + delete_target_id(target="domains", id=slave_domain_id) def test_create_slave_domain_fails_without_master_dns_server(): diff --git a/tests/integration/events/test_events.py b/tests/integration/events/test_events.py index 6b8997f6..9c854708 100644 --- a/tests/integration/events/test_events.py +++ b/tests/integration/events/test_events.py @@ -1,4 +1,3 @@ -import logging import os import re import time @@ -12,40 +11,32 @@ @pytest.fixture(scope="session", autouse=True) def events_setup(): - # Create one domain for some tests in this suite - try: - timestamp = str(int(time.time())) - # Create domain - domain_id = ( - exec_test_command( - [ - "linode-cli", - "domains", - "create", - "--type", - "master", - "--domain", - "A" + timestamp + "example.com", - "--soa_email=developer-test@linode.com", - "--text", - "--no-header", - "--format", - "id", - ] - ) - .stdout.decode() - .rstrip() + timestamp = str(int(time.time())) + # Create domain + domain_id = ( + exec_test_command( + [ + "linode-cli", + "domains", + "create", + "--type", + "master", + "--domain", + "A" + timestamp + "example.com", + "--soa_email=developer-test@linode.com", + "--text", + "--no-header", + "--format", + "id", + ] ) - - except: - logging.exception("Failed creating domain in setup") + .stdout.decode() + .rstrip() + ) yield domain_id - try: - delete_target_id(target="domains", id=domain_id) - except: - logging.exception("Failed to delete all domains") + delete_target_id(target="domains", id=domain_id) def test_print_events_usage_information(): diff --git a/tests/integration/firewalls/test_firewalls.py b/tests/integration/firewalls/test_firewalls.py index f0681da0..90a92132 100644 --- a/tests/integration/firewalls/test_firewalls.py +++ b/tests/integration/firewalls/test_firewalls.py @@ -1,4 +1,3 @@ -import logging import re import time @@ -17,37 +16,30 @@ @pytest.fixture(scope="session", autouse=True) def firewalls_setup(): # Create one domain for some tests in this suite - try: - # Create domain - firewall_id = ( - exec_test_command( - BASE_CMD - + [ - "create", - "--label", - FIREWALL_LABEL, - "--rules.outbound_policy", - "ACCEPT", - "--rules.inbound_policy", - "DROP", - "--text", - "--no-headers", - "--format", - "id", - ] - ) - .stdout.decode() - .rstrip() + firewall_id = ( + exec_test_command( + BASE_CMD + + [ + "create", + "--label", + FIREWALL_LABEL, + "--rules.outbound_policy", + "ACCEPT", + "--rules.inbound_policy", + "DROP", + "--text", + "--no-headers", + "--format", + "id", + ] ) - except: - logging.exception("Failed creating domain in setup") + .stdout.decode() + .rstrip() + ) yield firewall_id # teardown - delete all firewalls - try: - delete_target_id(target="firewalls", id=firewall_id) - except: - logging.exception("Failed to delete all firewalls") + delete_target_id(target="firewalls", id=firewall_id) def test_view_firewall(firewalls_setup): diff --git a/tests/integration/firewalls/test_firewalls_rules.py b/tests/integration/firewalls/test_firewalls_rules.py index 49ae1f9e..f0d32d1f 100644 --- a/tests/integration/firewalls/test_firewalls_rules.py +++ b/tests/integration/firewalls/test_firewalls_rules.py @@ -1,4 +1,3 @@ -import logging import re import time @@ -12,39 +11,31 @@ @pytest.fixture(scope="session", autouse=True) def create_firewall(): - # Create one domain for some tests in this suite - try: - # Create domain - firewall_id = ( - exec_test_command( - [ - "linode-cli", - "firewalls", - "create", - "--label", - FIREWALL_LABEL, - "--rules.outbound_policy", - "ACCEPT", - "--rules.inbound_policy", - "DROP", - "--text", - "--no-headers", - "--format", - "id", - ] - ) - .stdout.decode() - .rstrip() + firewall_id = ( + exec_test_command( + [ + "linode-cli", + "firewalls", + "create", + "--label", + FIREWALL_LABEL, + "--rules.outbound_policy", + "ACCEPT", + "--rules.inbound_policy", + "DROP", + "--text", + "--no-headers", + "--format", + "id", + ] ) - except: - logging.exception("Failed creating domain in setup") + .stdout.decode() + .rstrip() + ) yield firewall_id # teardown - delete all firewalls - try: - delete_target_id(target="firewalls", id=firewall_id) - except: - logging.exception("Failed to delete all firewalls") + delete_target_id(target="firewalls", id=firewall_id) def test_add_rule_to_existing_firewall(create_firewall): diff --git a/tests/integration/linodes/helpers_linodes.py b/tests/integration/linodes/helpers_linodes.py index baaf3407..d85917ca 100644 --- a/tests/integration/linodes/helpers_linodes.py +++ b/tests/integration/linodes/helpers_linodes.py @@ -1,9 +1,7 @@ import os import time -import pytest - -from tests.integration.helpers import delete_target_id, exec_test_command +from tests.integration.helpers import exec_test_command DEFAULT_RANDOM_PASS = ( exec_test_command(["openssl", "rand", "-base64", "32"]) @@ -205,195 +203,3 @@ def create_linode_and_wait( ) return linode_id - - -@pytest.fixture -def create_linode_with_label(): - result = ( - exec_test_command( - BASE_CMD - + [ - "create", - "--type", - "g6-standard-2", - "--region", - "us-east", - "--image", - DEFAULT_TEST_IMAGE, - "--label", - "cli-1", - "--root_pass", - DEFAULT_RANDOM_PASS, - "--text", - "--delimiter", - ",", - "--no-headers", - "--format", - "label,region,type,image,id", - "--no-defaults", - ] - ) - .stdout.decode() - .rstrip() - ) - - yield result - - res_arr = result.split(",") - linode_id = res_arr[4] - delete_target_id(target="linodes", id=linode_id) - - -@pytest.fixture -def create_linode_min_req(): - result = ( - exec_test_command( - BASE_CMD - + [ - "create", - "--type", - "g6-standard-2", - "--region", - "us-east", - "--root_pass", - DEFAULT_RANDOM_PASS, - "--no-defaults", - "--text", - "--delimiter", - ",", - "--no-headers", - "--format", - "id,region,type", - ] - ) - .stdout.decode() - .rstrip() - ) - - yield result - - res_arr = result.split(",") - linode_id = res_arr[0] - delete_target_id(target="linodes", id=linode_id) - - -@pytest.fixture -def create_linode_wo_image(): - linode_type = ( - os.popen( - "linode-cli linodes types --text --no-headers --format=id | xargs | awk '{ print $1 }'" - ) - .read() - .rstrip() - ) - linode_region = ( - os.popen( - "linode-cli regions list --format=id --text --no-headers | xargs | awk '{ print $1 }'" - ) - .read() - .rstrip() - ) - - exec_test_command( - BASE_CMD - + [ - "create", - "--no-defaults", - "--label", - "cli-2", - "--type", - linode_type, - "--region", - linode_region, - "--root_pass", - DEFAULT_RANDOM_PASS, - ] - ).stdout.decode() - - linode_id = ( - exec_test_command( - BASE_CMD - + [ - "list", - "--label", - "cli-2", - "--text", - "--no-headers", - "--format", - "id", - ] - ) - .stdout.decode() - .rstrip() - ) - - yield linode_id - - delete_target_id(target="linodes", id=linode_id) - - -@pytest.fixture -def create_linode_backup_enabled(): - linode_type = ( - os.popen( - "linode-cli linodes types --text --no-headers --format='id' | xargs | awk '{ print $1 }'" - ) - .read() - .rstrip() - ) - - # create linode with backups enabled - linode_id = ( - exec_test_command( - [ - "linode-cli", - "linodes", - "create", - "--backups_enabled", - "true", - "--type", - linode_type, - "--region", - DEFAULT_REGION, - "--image", - DEFAULT_TEST_IMAGE, - "--root_pass", - DEFAULT_RANDOM_PASS, - "--text", - "--no-headers", - "--format=id", - ] - ) - .stdout.decode() - .rstrip() - ) - - yield linode_id - - delete_target_id("linodes", linode_id) - - -@pytest.fixture -def take_snapshot_of_linode(): - timestamp = str(time.time()) - # get linode id after creation and wait for "running" status - linode_id = create_linode_and_wait() - new_snapshot_label = "test_snapshot" + timestamp - - result = exec_test_command( - BASE_CMD - + [ - "snapshot", - linode_id, - "--label", - new_snapshot_label, - "--text", - "--delimiter", - ",", - "--no-headers", - ] - ).stdout.decode() - - yield linode_id, new_snapshot_label - - delete_target_id("linodes", linode_id) diff --git a/tests/integration/linodes/test_linodes.py b/tests/integration/linodes/test_linodes.py index 511b992c..6d4c814e 100644 --- a/tests/integration/linodes/test_linodes.py +++ b/tests/integration/linodes/test_linodes.py @@ -1,4 +1,3 @@ -import logging import re import time @@ -23,48 +22,39 @@ @pytest.fixture(scope="session", autouse=True) def setup_linodes(): - try: - # create one linode with default label for some tests - linode_id = ( - exec_test_command( - BASE_CMD - + [ - "create", - "--type", - "g6-standard-2", - "--region", - "us-east", - "--image", - DEFAULT_TEST_IMAGE, - "--label", - linode_label, - "--root_pass", - DEFAULT_RANDOM_PASS, - "--text", - "--delimiter", - ",", - "--no-headers", - "--format", - "id", - "--no-defaults", - "--format", - "id", - ] - ) - .stdout.decode() - .rstrip() + linode_id = ( + exec_test_command( + BASE_CMD + + [ + "create", + "--type", + "g6-standard-2", + "--region", + "us-east", + "--image", + DEFAULT_TEST_IMAGE, + "--label", + linode_label, + "--root_pass", + DEFAULT_RANDOM_PASS, + "--text", + "--delimiter", + ",", + "--no-headers", + "--format", + "id", + "--no-defaults", + "--format", + "id", + ] ) - except: - logging.exception("Failed to create default linode in setup..") + .stdout.decode() + .rstrip() + ) yield linode_id - try: - # clean up - delete_target_id(target="linodes", id=linode_id) - - except: - logging.exception("Failed removing linode..") + delete_target_id(target="linodes", id=linode_id) def test_update_linode_with_a_image(): diff --git a/tests/integration/linodes/test_power_status.py b/tests/integration/linodes/test_power_status.py index 5e93c1ad..c9aac35c 100644 --- a/tests/integration/linodes/test_power_status.py +++ b/tests/integration/linodes/test_power_status.py @@ -1,5 +1,3 @@ -import logging - import pytest from tests.integration.helpers import delete_target_id, exec_test_command @@ -13,17 +11,11 @@ @pytest.fixture(scope="session", autouse=True) def setup_power_status(): - # create linode - try: - linode_id = create_linode() - except: - logging.exception("Failed in creating linode in setup..") + linode_id = create_linode() + yield linode_id - try: - # clean up - delete_target_id(target="linodes", id=linode_id) - except: - logging.exception("Failed removing linode..") + + delete_target_id(target="linodes", id=linode_id) @pytest.fixture(scope="session") diff --git a/tests/integration/linodes/test_rebuild.py b/tests/integration/linodes/test_rebuild.py index dab518df..9d617d56 100644 --- a/tests/integration/linodes/test_rebuild.py +++ b/tests/integration/linodes/test_rebuild.py @@ -1,4 +1,3 @@ -import logging import os import pytest @@ -18,17 +17,11 @@ @pytest.fixture(scope="session", autouse=True) def setup_rebuild(): - # create linode - try: - linode_id = create_linode_and_wait() - except: - logging.exception("Failed in creating linode in setup..") + linode_id = create_linode_and_wait() + yield linode_id - try: - # clean up - delete_target_id(target="linodes", id=linode_id) - except: - logging.exception("Failed removing linode..") + + delete_target_id(target="linodes", id=linode_id) def test_rebuild_fails_without_image(setup_rebuild): diff --git a/tests/integration/linodes/test_resize.py b/tests/integration/linodes/test_resize.py index 28509589..62363628 100644 --- a/tests/integration/linodes/test_resize.py +++ b/tests/integration/linodes/test_resize.py @@ -1,4 +1,3 @@ -import logging import os import pytest @@ -18,23 +17,18 @@ @pytest.fixture(scope="session", autouse=True) def setup_resize(): # create linode - try: - plan = ( - os.popen( - 'linode-cli linodes types --format="id" --text --no-headers | sed -n 2p' - ) - .read() - .rstrip() + plan = ( + os.popen( + 'linode-cli linodes types --format="id" --text --no-headers | sed -n 2p' ) - linode_id = create_linode_and_wait(test_plan=plan) - except: - logging.exception("Failed in creating linode in setup..") + .read() + .rstrip() + ) + linode_id = create_linode_and_wait(test_plan=plan) + yield linode_id - try: - # clean up - delete_target_id(target="linodes", id=linode_id) - except: - logging.exception("Failed removing linode..") + + delete_target_id(target="linodes", id=linode_id) def test_resize_fails_to_the_same_plan(setup_resize): diff --git a/tests/integration/lke/test_clusters.py b/tests/integration/lke/test_clusters.py index 4cd57b95..d616a151 100644 --- a/tests/integration/lke/test_clusters.py +++ b/tests/integration/lke/test_clusters.py @@ -1,5 +1,3 @@ -import logging - import pytest from tests.integration.helpers import exec_test_command, os, remove_lke_clusters @@ -11,10 +9,7 @@ def setup_test_clusters(): yield "setup" # just clean up method required for this test suite - try: - remove_lke_clusters() - except: - logging.exception("Failed the remove lke cluster..") + remove_lke_clusters() def test_deploy_an_lke_cluster(): diff --git a/tests/integration/networking/test_networking.py b/tests/integration/networking/test_networking.py index c1bf8272..97c4bde5 100644 --- a/tests/integration/networking/test_networking.py +++ b/tests/integration/networking/test_networking.py @@ -1,4 +1,3 @@ -import logging import re import pytest @@ -12,11 +11,10 @@ @pytest.fixture(scope="session", autouse=True) def setup_test_networking(): linode_id = create_linode_and_wait() + yield linode_id - try: - delete_target_id(target="linodes", id=linode_id) - except: - logging.exception("Failed to remove linode in teardown..") + + delete_target_id(target="linodes", id=linode_id) def test_display_ips_for_available_linodes(setup_test_networking): diff --git a/tests/integration/nodebalancers/helpers_nodebalancers.py b/tests/integration/nodebalancers/helpers_nodebalancers.py deleted file mode 100644 index 295ea348..00000000 --- a/tests/integration/nodebalancers/helpers_nodebalancers.py +++ /dev/null @@ -1,34 +0,0 @@ -import pytest - -from tests.integration.helpers import delete_target_id, exec_test_command - -BASE_CMD = ["linode-cli", "nodebalancers"] - - -@pytest.fixture -def create_nodebalancer_with_default_conf(): - result = ( - exec_test_command( - BASE_CMD - + [ - "create", - "--region", - "us-east", - "--text", - "--delimiter", - ",", - "--format", - "id,label,region,hostname,client_conn_throttle", - "--suppress-warnings", - "--no-headers", - ] - ) - .stdout.decode() - .rstrip() - ) - - yield result - - res_arr = result.split(",") - nodebalancer_id = res_arr[0] - delete_target_id(target="nodebalancers", id=nodebalancer_id) diff --git a/tests/integration/nodebalancers/test_node_balancers.py b/tests/integration/nodebalancers/test_node_balancers.py index 214817df..0dd7b4b8 100644 --- a/tests/integration/nodebalancers/test_node_balancers.py +++ b/tests/integration/nodebalancers/test_node_balancers.py @@ -116,11 +116,8 @@ def setup_test_node_balancers(): yield nodebalancer_id, config_id, node_id, node_ip - try: - delete_target_id(target="nodebalancers", id=nodebalancer_id) - delete_target_id(target="linodes", id=linode_id) - except: - "Failed to remove all linodes/nodebalancers in teardown.." + delete_target_id(target="nodebalancers", id=nodebalancer_id) + delete_target_id(target="linodes", id=linode_id) @pytest.fixture diff --git a/tests/integration/ssh/test_ssh.py b/tests/integration/ssh/test_ssh.py index 69218644..5983d634 100644 --- a/tests/integration/ssh/test_ssh.py +++ b/tests/integration/ssh/test_ssh.py @@ -44,20 +44,8 @@ def test_create_a_linode_in_running_state(ssh_key_pair_generator): def test_display_ssh_plugin_usage_info(): result = exec_test_command(BASE_CMD + ["-h"]).stdout.decode() - assert "usage: linode-cli ssh [-h] [-6] [[USERNAME@]LABEL]" in result - assert "positional arguments:" in result - assert ( - "[USERNAME@]LABEL The label of the Linode to SSH into, optionally with a" - in result - ) - assert "username before it in USERNAME@LABEL format. If no" in result - assert "username is given, defaults to the current user." in result - assert "option" in result - assert "-h, --help show this help message and exit" in result - assert ( - "-6 If given, uses the Linode's SLAAC address for SSH." - in result - ) + assert "[USERNAME@]LABEL" in result + assert "uses the Linode's SLAAC address for SSH" in result def test_fail_to_ssh_to_nonexistent_linode(): diff --git a/tests/integration/stackscripts/test_stackscripts.py b/tests/integration/stackscripts/test_stackscripts.py index f175100a..6338da6d 100644 --- a/tests/integration/stackscripts/test_stackscripts.py +++ b/tests/integration/stackscripts/test_stackscripts.py @@ -7,21 +7,11 @@ exec_failing_test_command, exec_test_command, ) -from tests.integration.linodes.helpers_linodes import ( - DEFAULT_RANDOM_PASS, - remove_linodes, -) +from tests.integration.linodes.helpers_linodes import DEFAULT_RANDOM_PASS BASE_CMD = ["linode-cli", "stackscripts"] -@pytest.fixture(scope="session", autouse=True) -def setup_test_stackscripts(): - remove_linodes() - yield "setup" - delete_stackscript_and_teardown_linode() - - def get_linode_image_lists(): images = os.popen( 'LINODE_CLI_TOKEN=$LINODE_CLI_TOKEN linode-cli images list --format id --text --no-headers | egrep "linode\/.*"' @@ -51,13 +41,6 @@ def get_private_stackscript(): return private_stackscript.splitlines() -def delete_stackscript_and_teardown_linode(): - private_stackscript = get_private_stackscript() - for sc_id in private_stackscript: - exec_test_command(BASE_CMD + ["delete", sc_id]) - remove_linodes() - - @pytest.fixture(scope="session", autouse=True) def test_create_stackscript(): result = exec_test_command( diff --git a/tests/integration/volumes/test_volumes.py b/tests/integration/volumes/test_volumes.py index 5089b90b..481e46b0 100644 --- a/tests/integration/volumes/test_volumes.py +++ b/tests/integration/volumes/test_volumes.py @@ -1,3 +1,4 @@ +import os import re import time @@ -7,7 +8,6 @@ delete_target_id, exec_failing_test_command, exec_test_command, - os, ) BASE_CMD = ["linode-cli", "volumes"] @@ -39,7 +39,9 @@ def setup_test_volumes(): .stdout.decode() .rstrip() ) + yield volume_id + delete_target_id(target="volumes", id=volume_id) diff --git a/tests/integration/volumes/test_volumes_resize.py b/tests/integration/volumes/test_volumes_resize.py index d519ca52..8cd1cbe4 100644 --- a/tests/integration/volumes/test_volumes_resize.py +++ b/tests/integration/volumes/test_volumes_resize.py @@ -38,7 +38,9 @@ def setup_test_volumes_resize(): .stdout.decode() .rstrip() ) + yield volume_id + delete_target_id(target="volumes", id=volume_id) From 13d4f27f99edc30d9274c0fbbe650ce4383f20cb Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Wed, 19 Apr 2023 21:25:35 +0400 Subject: [PATCH 09/10] release: inc requirements.txt for pypi source tarball file (#435) release: inc requirements.txt for pypi source tarball file (#435) relates to #416 Signed-off-by: Rui Chen --- MANIFEST.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 2e9c70de..a89873bb 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ include linodecli/data-3 include linodecli/oauth-landing-page.html include linode-cli.sh -include baked_version \ No newline at end of file +include baked_version +include requirements.txt From aefee9af79804cf6e040b7d125e9e2ee5adbd5fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Apr 2023 13:27:38 -0400 Subject: [PATCH 10/10] Bump pytest from 7.3.0 to 7.3.1 (#437) Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.3.0 to 7.3.1. Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 8effee4a..088b86cd 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ pylint==2.17.2 -pytest==7.3.0 +pytest==7.3.1 black>=23.1.0 isort>=5.12.0 autoflake>=2.0.1