From a304c3456e340c76c0275404d757bdc065c730bb Mon Sep 17 00:00:00 2001 From: Derek Graeber Date: Wed, 4 Dec 2024 15:44:30 -0500 Subject: [PATCH 1/6] limit extraction dir to one on archives --- CHANGELOG.md | 1 + seedfarmer/mgmt/archive_support.py | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ecb6f6..6ba95b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a Ch - Update session manager to pass toolchain role region to sts ### Fixes +- correct archive extraction when there is only one nested path (issue 749) ## v5.0.0 (2024-08-16) diff --git a/seedfarmer/mgmt/archive_support.py b/seedfarmer/mgmt/archive_support.py index a36a966..a48e288 100644 --- a/seedfarmer/mgmt/archive_support.py +++ b/seedfarmer/mgmt/archive_support.py @@ -72,12 +72,26 @@ def _download_archive(archive_url: str, secret_name: Optional[str]) -> Response: def _extract_archive(archive_name: str, extracted_dir_path: str) -> str: if archive_name.endswith(".tar.gz"): with tarfile.open(archive_name, "r:gz") as tar_file: - embedded_dir = os.path.commonprefix(tar_file.getnames()) - tar_file.extractall(extracted_dir_path) + all_members = tar_file.getmembers() + top_level_dirs = set(member.name.split("/")[0] for member in all_members) + if len(top_level_dirs) == 1: + embedded_dir = top_level_dirs.pop() + else: + embedded_dir = "" + members_to_extract = [m for m in all_members] + tar_file.extractall(extracted_dir_path, members=members_to_extract) else: with ZipFile(archive_name, "r") as zip_file: - embedded_dir = os.path.commonprefix(zip_file.namelist()) - zip_file.extractall(extracted_dir_path) + all_files = zip_file.namelist() + top_level_dirs = set(name.split("/")[0] for name in all_files if name.endswith("/")) + if len(top_level_dirs) == 1: + embedded_dir = top_level_dirs.pop() + else: + embedded_dir = "" + files_to_extract = [f for f in all_files] # ] + + for file in files_to_extract: + zip_file.extract(file, path=extracted_dir_path) return embedded_dir From c994c67f069a2c30dab62d411585b4be999b9974 Mon Sep 17 00:00:00 2001 From: Derek Graeber Date: Wed, 4 Dec 2024 21:58:13 -0500 Subject: [PATCH 2/6] augment test cases --- seedfarmer/mgmt/archive_support.py | 18 +- test/unit-test/test_mgmt_archive_support.py | 202 +++++++++++++++++++- 2 files changed, 209 insertions(+), 11 deletions(-) diff --git a/seedfarmer/mgmt/archive_support.py b/seedfarmer/mgmt/archive_support.py index a48e288..877d3ac 100644 --- a/seedfarmer/mgmt/archive_support.py +++ b/seedfarmer/mgmt/archive_support.py @@ -74,7 +74,11 @@ def _extract_archive(archive_name: str, extracted_dir_path: str) -> str: with tarfile.open(archive_name, "r:gz") as tar_file: all_members = tar_file.getmembers() top_level_dirs = set(member.name.split("/")[0] for member in all_members) - if len(top_level_dirs) == 1: + if len(top_level_dirs) > 1: + raise InvalidConfigurationError( + f"the archive {archive_name} can only have one directory at the root and no files" + ) + elif len(top_level_dirs) == 1: embedded_dir = top_level_dirs.pop() else: embedded_dir = "" @@ -83,16 +87,18 @@ def _extract_archive(archive_name: str, extracted_dir_path: str) -> str: else: with ZipFile(archive_name, "r") as zip_file: all_files = zip_file.namelist() - top_level_dirs = set(name.split("/")[0] for name in all_files if name.endswith("/")) - if len(top_level_dirs) == 1: + top_level_dirs = set(name.split("/")[0] for name in all_files) + if len(top_level_dirs) > 1: + raise InvalidConfigurationError( + f"the archive {archive_name} can only have one directory at the root and no files" + ) + elif len(top_level_dirs) == 1: embedded_dir = top_level_dirs.pop() else: embedded_dir = "" - files_to_extract = [f for f in all_files] # ] - + files_to_extract = [f for f in all_files] for file in files_to_extract: zip_file.extract(file, path=extracted_dir_path) - return embedded_dir diff --git a/test/unit-test/test_mgmt_archive_support.py b/test/unit-test/test_mgmt_archive_support.py index e18507d..2192b4b 100644 --- a/test/unit-test/test_mgmt_archive_support.py +++ b/test/unit-test/test_mgmt_archive_support.py @@ -80,10 +80,27 @@ def parent_dir_prepare(): example_archive_files = [ - ("modules/test-module/modulestack.yaml", io.BytesIO(b"111")), - ("modules/test-module/pyproject.toml", io.BytesIO(b"222")), - ("README.md", io.BytesIO(b"333")), - ("LICENSE", io.BytesIO(b"444")), + ("test-project/modules/test-module/modulestack.yaml", io.BytesIO(b"111")), + ("test-project/modules/test-module/pyproject.toml", io.BytesIO(b"222")), + ("test-project/README.md", io.BytesIO(b"333")), + ("test-project/LICENSE", io.BytesIO(b"444")), +] + +example_archive_files_single_module = [ + ("test-project/modules/test-module/deployspec.yaml", io.BytesIO(b"111")), + ("test-project/modules/test-module/something.yaml", io.BytesIO(b"111")), +] + +example_archive_files_no_nesting = [ + ("test-project/deployspec.yaml", io.BytesIO(b"111")), + ("test-project/app.py", io.BytesIO(b"111")), + ("test-project/stack.py", io.BytesIO(b"111")), +] + +example_archive_files_bad_structure = [ + ("deployspec.yaml", io.BytesIO(b"111")), + ("app.py", io.BytesIO(b"111")), + ("stack.py", io.BytesIO(b"111")), ] @@ -98,6 +115,39 @@ def zip_file_data() -> Tuple[bytes, str]: return zip_buffer.getvalue(), "zip" +@pytest.fixture(scope="function") +def zip_file_data_not_nested() -> Tuple[bytes, str]: + zip_buffer = io.BytesIO() + + with zipfile.ZipFile(zip_buffer, "a", zipfile.ZIP_DEFLATED) as zip_file: + for file_name, data in example_archive_files_no_nesting: + zip_file.writestr(file_name, data.getvalue()) + + return zip_buffer.getvalue(), "zip" + + +@pytest.fixture(scope="function") +def zip_file_data_single_module() -> Tuple[bytes, str]: + zip_buffer = io.BytesIO() + + with zipfile.ZipFile(zip_buffer, "a", zipfile.ZIP_DEFLATED) as zip_file: + for file_name, data in example_archive_files_single_module: + zip_file.writestr(file_name, data.getvalue()) + + return zip_buffer.getvalue(), "zip" + + +@pytest.fixture(scope="function") +def zip_file_data_bad_structure() -> Tuple[bytes, str]: + zip_buffer = io.BytesIO() + + with zipfile.ZipFile(zip_buffer, "a", zipfile.ZIP_DEFLATED) as zip_file: + for file_name, data in example_archive_files_bad_structure: + zip_file.writestr(file_name, data.getvalue()) + + return zip_buffer.getvalue(), "zip" + + @pytest.fixture(scope="function") def tar_file_data() -> Tuple[bytes, str]: tar_buffer = io.BytesIO() @@ -109,6 +159,39 @@ def tar_file_data() -> Tuple[bytes, str]: return tar_buffer.getvalue(), "tar.gz" +@pytest.fixture(scope="function") +def tar_file_data_not_nested() -> Tuple[bytes, str]: + tar_buffer = io.BytesIO() + + with tarfile.open(fileobj=tar_buffer, mode="w:gz") as tar_file: + for file_name, data in example_archive_files_no_nesting: + tar_file.addfile(tarfile.TarInfo(name=file_name), fileobj=data) + + return tar_buffer.getvalue(), "tar.gz" + + +@pytest.fixture(scope="function") +def tar_file_data_single_module() -> Tuple[bytes, str]: + tar_buffer = io.BytesIO() + + with tarfile.open(fileobj=tar_buffer, mode="w:gz") as tar_file: + for file_name, data in example_archive_files_single_module: + tar_file.addfile(tarfile.TarInfo(name=file_name), fileobj=data) + + return tar_buffer.getvalue(), "tar.gz" + + +@pytest.fixture(scope="function") +def tar_file_data_bad_structure() -> Tuple[bytes, str]: + tar_buffer = io.BytesIO() + + with tarfile.open(fileobj=tar_buffer, mode="w:gz") as tar_file: + for file_name, data in example_archive_files_bad_structure: + tar_file.addfile(tarfile.TarInfo(name=file_name), fileobj=data) + + return tar_buffer.getvalue(), "tar.gz" + + @pytest.fixture(params=["zip_file_data", "tar_file_data"]) def archive_file_data( request: pytest.FixtureRequest, zip_file_data: Tuple[bytes, str], tar_file_data: Tuple[bytes, str] @@ -116,6 +199,33 @@ def archive_file_data( return request.getfixturevalue(request.param) +@pytest.fixture(params=["zip_file_data_not_nested", "tar_file_data_not_nested"]) +def archive_file_data_not_nested( + request: pytest.FixtureRequest, + zip_file_data_not_nested: Tuple[bytes, str], + tar_file_data_not_nested: Tuple[bytes, str], +) -> Tuple[bytes, str]: + return request.getfixturevalue(request.param) + + +@pytest.fixture(params=["zip_file_data_single_module", "tar_file_data_single_module"]) +def archive_file_data_single_module( + request: pytest.FixtureRequest, + zip_file_data_single_module: Tuple[bytes, str], + tar_file_data_single_module: Tuple[bytes, str], +) -> Tuple[bytes, str]: + return request.getfixturevalue(request.param) + + +@pytest.fixture(params=["zip_file_data_bad_structure", "tar_file_data_bad_structure"]) +def archive_file_data_single_bad_structure( + request: pytest.FixtureRequest, + zip_file_data_bad_structure: Tuple[bytes, str], + tar_file_data_bad_structure: Tuple[bytes, str], +) -> Tuple[bytes, str]: + return request.getfixturevalue(request.param) + + @pytest.mark.mgmt @pytest.mark.mgmt_archive_support def test_fetch_module_repo_dns_path(): @@ -195,7 +305,6 @@ def test_fetch_module_repo_from_s3( archive_path, _ = archive.fetch_archived_module(release_path=archive_path_test) archive.fetch_archived_module(release_path=archive_path_test) - mock_requests_get.assert_called_once() assert mock_requests_get.call_args.kwargs["url"] == s3_http_url @@ -208,6 +317,89 @@ def test_fetch_module_repo_from_s3( assert os.path.exists(os.path.join(archive_path, module_name, "modulestack.yaml")) +@pytest.mark.mgmt +@pytest.mark.mgmt_archive_support +@pytest.mark.parametrize( + "s3_bucket_http_url", ["testing-bucket.s3.amazonaws.com", "testing-bucket.s3.us-west-2.amazonaws.com"] +) +def test_fetch_module_repo_from_s3_non_nested( + session_manager: None, archive_file_data_not_nested: Tuple[bytes, str], s3_bucket_http_url: str +) -> None: + archive_bytes, archive_extension = archive_file_data_not_nested + + response_mock = MagicMock() + response_mock.status_code = 200 + response_mock.content = archive_bytes + + s3_http_url = f"https://{s3_bucket_http_url}/testing-modules.{archive_extension}" + module_name = "./" + + with patch("requests.get", return_value=response_mock) as mock_requests_get: + archive_path_test = f"archive::{s3_http_url}?module={module_name}" + archive_path, subdir = archive.fetch_archived_module(release_path=archive_path_test) + mock_requests_get.assert_called_once() + + assert mock_requests_get.call_args.kwargs["url"] == s3_http_url + # check that the sha256 header was added and that the request was signed + assert "x-amz-content-sha256" in mock_requests_get.call_args.kwargs["headers"] + assert "Authorization" in mock_requests_get.call_args.kwargs["headers"] + + assert os.path.exists(os.path.join(archive_path, "deployspec.yaml")) + + +@pytest.mark.mgmt +@pytest.mark.mgmt_archive_support +@pytest.mark.parametrize( + "s3_bucket_http_url", ["testing-bucket.s3.amazonaws.com", "testing-bucket.s3.us-west-2.amazonaws.com"] +) +def test_fetch_module_repo_from_s3_single_module( + session_manager: None, archive_file_data_single_module: Tuple[bytes, str], s3_bucket_http_url: str +) -> None: + archive_bytes, archive_extension = archive_file_data_single_module + + response_mock = MagicMock() + response_mock.status_code = 200 + response_mock.content = archive_bytes + + s3_http_url = f"https://{s3_bucket_http_url}/testing-modules.{archive_extension}" + module_name = "modules/test-module/" + + with patch("requests.get", return_value=response_mock) as mock_requests_get: + archive_path_test = f"archive::{s3_http_url}?module={module_name}" + archive_path, subdir = archive.fetch_archived_module(release_path=archive_path_test) + mock_requests_get.assert_called_once() + + assert mock_requests_get.call_args.kwargs["url"] == s3_http_url + # check that the sha256 header was added and that the request was signed + assert "x-amz-content-sha256" in mock_requests_get.call_args.kwargs["headers"] + assert "Authorization" in mock_requests_get.call_args.kwargs["headers"] + assert os.path.exists(os.path.join(archive_path, module_name, "deployspec.yaml")) + + +@pytest.mark.mgmt +@pytest.mark.mgmt_archive_support +@pytest.mark.parametrize( + "s3_bucket_http_url", ["testing-bucket.s3.amazonaws.com", "testing-bucket.s3.us-west-2.amazonaws.com"] +) +def test_fetch_module_repo_bad_archive_structure( + session_manager: None, archive_file_data_single_bad_structure: Tuple[bytes, str], s3_bucket_http_url: str +) -> None: + archive_bytes, archive_extension = archive_file_data_single_bad_structure + + response_mock = MagicMock() + response_mock.status_code = 200 + response_mock.content = archive_bytes + + s3_http_url = f"https://{s3_bucket_http_url}/testing-modules.{archive_extension}" + module_name = "modules/test-module/" + + with patch("requests.get", return_value=response_mock) as mock_requests_get: + archive_path_test = f"archive::{s3_http_url}?module={module_name}" + with pytest.raises(InvalidConfigurationError): + archive.fetch_archived_module(release_path=archive_path_test) + mock_requests_get.assert_called_once() + + @pytest.mark.mgmt @pytest.mark.mgmt_archive_support @pytest.mark.parametrize( From a71dc4f8d1ef4621ec2c7fa3cdf7d094f54e1391 Mon Sep 17 00:00:00 2001 From: Derek Graeber Date: Thu, 5 Dec 2024 08:59:26 -0500 Subject: [PATCH 3/6] allow hidden files at root, update tests --- seedfarmer/mgmt/archive_support.py | 4 +-- test/unit-test/test_mgmt_archive_support.py | 29 +++++++++++++-------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/seedfarmer/mgmt/archive_support.py b/seedfarmer/mgmt/archive_support.py index 877d3ac..9a48421 100644 --- a/seedfarmer/mgmt/archive_support.py +++ b/seedfarmer/mgmt/archive_support.py @@ -73,7 +73,7 @@ def _extract_archive(archive_name: str, extracted_dir_path: str) -> str: if archive_name.endswith(".tar.gz"): with tarfile.open(archive_name, "r:gz") as tar_file: all_members = tar_file.getmembers() - top_level_dirs = set(member.name.split("/")[0] for member in all_members) + top_level_dirs = set(member.name.split("/")[0] for member in all_members if "/" in member.name) if len(top_level_dirs) > 1: raise InvalidConfigurationError( f"the archive {archive_name} can only have one directory at the root and no files" @@ -87,7 +87,7 @@ def _extract_archive(archive_name: str, extracted_dir_path: str) -> str: else: with ZipFile(archive_name, "r") as zip_file: all_files = zip_file.namelist() - top_level_dirs = set(name.split("/")[0] for name in all_files) + top_level_dirs = set(name.split("/")[0] for name in all_files if "/" in name) if len(top_level_dirs) > 1: raise InvalidConfigurationError( f"the archive {archive_name} can only have one directory at the root and no files" diff --git a/test/unit-test/test_mgmt_archive_support.py b/test/unit-test/test_mgmt_archive_support.py index 2192b4b..4ae04fe 100644 --- a/test/unit-test/test_mgmt_archive_support.py +++ b/test/unit-test/test_mgmt_archive_support.py @@ -84,23 +84,30 @@ def parent_dir_prepare(): ("test-project/modules/test-module/pyproject.toml", io.BytesIO(b"222")), ("test-project/README.md", io.BytesIO(b"333")), ("test-project/LICENSE", io.BytesIO(b"444")), + (".DS_Store_local", io.BytesIO(b"555")), ] example_archive_files_single_module = [ - ("test-project/modules/test-module/deployspec.yaml", io.BytesIO(b"111")), - ("test-project/modules/test-module/something.yaml", io.BytesIO(b"111")), + ("test-project/modules/test-module/deployspec.yaml", io.BytesIO(b"666")), + ("test-project/modules/test-module/something.yaml", io.BytesIO(b"777")), + (".DS_Store", io.BytesIO(b"888")), + ("README.md", io.BytesIO(b"999")), ] example_archive_files_no_nesting = [ - ("test-project/deployspec.yaml", io.BytesIO(b"111")), - ("test-project/app.py", io.BytesIO(b"111")), - ("test-project/stack.py", io.BytesIO(b"111")), + ("deployspec.yaml", io.BytesIO(b"1010")), + ("app.py", io.BytesIO(b"1011")), + ("stack.py", io.BytesIO(b"1012")), + (".DS_Store", io.BytesIO(b"1013")), + ("README.md", io.BytesIO(b"1014")), ] example_archive_files_bad_structure = [ - ("deployspec.yaml", io.BytesIO(b"111")), - ("app.py", io.BytesIO(b"111")), - ("stack.py", io.BytesIO(b"111")), + ("test-project/deployspec.yaml", io.BytesIO(b"1200")), + ("test-project_additional/deployspec.yaml", io.BytesIO(b"1201")), + ("deployspec.yaml", io.BytesIO(b"1202")), + ("app.py", io.BytesIO(b"1203")), + ("stack.py", io.BytesIO(b"1204")), ] @@ -218,7 +225,7 @@ def archive_file_data_single_module( @pytest.fixture(params=["zip_file_data_bad_structure", "tar_file_data_bad_structure"]) -def archive_file_data_single_bad_structure( +def archive_file_data_bad_structure( request: pytest.FixtureRequest, zip_file_data_bad_structure: Tuple[bytes, str], tar_file_data_bad_structure: Tuple[bytes, str], @@ -382,9 +389,9 @@ def test_fetch_module_repo_from_s3_single_module( "s3_bucket_http_url", ["testing-bucket.s3.amazonaws.com", "testing-bucket.s3.us-west-2.amazonaws.com"] ) def test_fetch_module_repo_bad_archive_structure( - session_manager: None, archive_file_data_single_bad_structure: Tuple[bytes, str], s3_bucket_http_url: str + session_manager: None, archive_file_data_bad_structure: Tuple[bytes, str], s3_bucket_http_url: str ) -> None: - archive_bytes, archive_extension = archive_file_data_single_bad_structure + archive_bytes, archive_extension = archive_file_data_bad_structure response_mock = MagicMock() response_mock.status_code = 200 From 558d4fe7f466709e3e5b2acda8c53516b14de774 Mon Sep 17 00:00:00 2001 From: Derek Graeber Date: Fri, 6 Dec 2024 08:57:23 -0500 Subject: [PATCH 4/6] adding checks for subdir --- test/unit-test/test_mgmt_archive_support.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/unit-test/test_mgmt_archive_support.py b/test/unit-test/test_mgmt_archive_support.py index 4ae04fe..c9a3a47 100644 --- a/test/unit-test/test_mgmt_archive_support.py +++ b/test/unit-test/test_mgmt_archive_support.py @@ -310,8 +310,7 @@ def test_fetch_module_repo_from_s3( with patch("requests.get", return_value=response_mock) as mock_requests_get: archive_path_test = f"archive::{s3_http_url}?module={module_name}" - archive_path, _ = archive.fetch_archived_module(release_path=archive_path_test) - archive.fetch_archived_module(release_path=archive_path_test) + archive_path, subdir = archive.fetch_archived_module(release_path=archive_path_test) mock_requests_get.assert_called_once() assert mock_requests_get.call_args.kwargs["url"] == s3_http_url @@ -319,9 +318,9 @@ def test_fetch_module_repo_from_s3( # check that the sha256 header was added and that the request was signed assert "x-amz-content-sha256" in mock_requests_get.call_args.kwargs["headers"] assert "Authorization" in mock_requests_get.call_args.kwargs["headers"] - # Check that the module was extracted to the correct location assert os.path.exists(os.path.join(archive_path, module_name, "modulestack.yaml")) + assert subdir == "modules/test-module/" @pytest.mark.mgmt @@ -352,7 +351,7 @@ def test_fetch_module_repo_from_s3_non_nested( assert "Authorization" in mock_requests_get.call_args.kwargs["headers"] assert os.path.exists(os.path.join(archive_path, "deployspec.yaml")) - + assert subdir == "./" @pytest.mark.mgmt @pytest.mark.mgmt_archive_support @@ -381,6 +380,7 @@ def test_fetch_module_repo_from_s3_single_module( assert "x-amz-content-sha256" in mock_requests_get.call_args.kwargs["headers"] assert "Authorization" in mock_requests_get.call_args.kwargs["headers"] assert os.path.exists(os.path.join(archive_path, module_name, "deployspec.yaml")) + assert subdir=="modules/test-module/" @pytest.mark.mgmt From b3b6d27ddb4d442cc4cfa92ae02b89dea8b35834 Mon Sep 17 00:00:00 2001 From: Derek Graeber Date: Fri, 6 Dec 2024 08:59:33 -0500 Subject: [PATCH 5/6] adding checks for subdir - formatted --- test/unit-test/test_mgmt_archive_support.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unit-test/test_mgmt_archive_support.py b/test/unit-test/test_mgmt_archive_support.py index c9a3a47..455b16d 100644 --- a/test/unit-test/test_mgmt_archive_support.py +++ b/test/unit-test/test_mgmt_archive_support.py @@ -353,6 +353,7 @@ def test_fetch_module_repo_from_s3_non_nested( assert os.path.exists(os.path.join(archive_path, "deployspec.yaml")) assert subdir == "./" + @pytest.mark.mgmt @pytest.mark.mgmt_archive_support @pytest.mark.parametrize( @@ -380,7 +381,7 @@ def test_fetch_module_repo_from_s3_single_module( assert "x-amz-content-sha256" in mock_requests_get.call_args.kwargs["headers"] assert "Authorization" in mock_requests_get.call_args.kwargs["headers"] assert os.path.exists(os.path.join(archive_path, module_name, "deployspec.yaml")) - assert subdir=="modules/test-module/" + assert subdir == "modules/test-module/" @pytest.mark.mgmt From 2097e83a37d32545d643e2c794e5a813b9304157 Mon Sep 17 00:00:00 2001 From: Derek Graeber Date: Fri, 6 Dec 2024 10:04:13 -0500 Subject: [PATCH 6/6] removing generator, adding test comments --- seedfarmer/mgmt/archive_support.py | 6 ++---- test/unit-test/test_mgmt_archive_support.py | 10 ++++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/seedfarmer/mgmt/archive_support.py b/seedfarmer/mgmt/archive_support.py index 9a48421..8b6c475 100644 --- a/seedfarmer/mgmt/archive_support.py +++ b/seedfarmer/mgmt/archive_support.py @@ -82,8 +82,7 @@ def _extract_archive(archive_name: str, extracted_dir_path: str) -> str: embedded_dir = top_level_dirs.pop() else: embedded_dir = "" - members_to_extract = [m for m in all_members] - tar_file.extractall(extracted_dir_path, members=members_to_extract) + tar_file.extractall(extracted_dir_path, members=all_members) else: with ZipFile(archive_name, "r") as zip_file: all_files = zip_file.namelist() @@ -96,8 +95,7 @@ def _extract_archive(archive_name: str, extracted_dir_path: str) -> str: embedded_dir = top_level_dirs.pop() else: embedded_dir = "" - files_to_extract = [f for f in all_files] - for file in files_to_extract: + for file in all_files: zip_file.extract(file, path=extracted_dir_path) return embedded_dir diff --git a/test/unit-test/test_mgmt_archive_support.py b/test/unit-test/test_mgmt_archive_support.py index 455b16d..e4acb21 100644 --- a/test/unit-test/test_mgmt_archive_support.py +++ b/test/unit-test/test_mgmt_archive_support.py @@ -79,6 +79,9 @@ def parent_dir_prepare(): shutil.rmtree(parent_dir) +# This represents a proper archive with the project (test-project) at the root +# are are ignoring the hidden file at the root, and the other files +# under test-project are not used example_archive_files = [ ("test-project/modules/test-module/modulestack.yaml", io.BytesIO(b"111")), ("test-project/modules/test-module/pyproject.toml", io.BytesIO(b"222")), @@ -87,6 +90,8 @@ def parent_dir_prepare(): (".DS_Store_local", io.BytesIO(b"555")), ] +# This represents a proper archive with the project (test-project) at the root +# are are ignoring the hidden file at the root, and the other file is not used example_archive_files_single_module = [ ("test-project/modules/test-module/deployspec.yaml", io.BytesIO(b"666")), ("test-project/modules/test-module/something.yaml", io.BytesIO(b"777")), @@ -94,6 +99,9 @@ def parent_dir_prepare(): ("README.md", io.BytesIO(b"999")), ] +# This represents a an archive without a nested dir (project) +# This is not recommended as it is not a proper SF project, +# but can support due to customer ask example_archive_files_no_nesting = [ ("deployspec.yaml", io.BytesIO(b"1010")), ("app.py", io.BytesIO(b"1011")), @@ -102,6 +110,8 @@ def parent_dir_prepare(): ("README.md", io.BytesIO(b"1014")), ] +# This is a bad package as there are two dirs at the +# root level...that is not allowed example_archive_files_bad_structure = [ ("test-project/deployspec.yaml", io.BytesIO(b"1200")), ("test-project_additional/deployspec.yaml", io.BytesIO(b"1201")),