diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index dc3f2b21ed..13381a85d4 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -23,6 +23,7 @@ ## Fixes and improvements * The `snow app run` command now allows upgrading to unversioned mode from a versioned or release mode application installation +* The `snow app teardown` command now allows dropping a package with versions when the `--force` flag is provided # v2.6.0 ## Backward incompatibility diff --git a/src/snowflake/cli/plugins/nativeapp/exceptions.py b/src/snowflake/cli/plugins/nativeapp/exceptions.py index e01324af24..8cefd4133a 100644 --- a/src/snowflake/cli/plugins/nativeapp/exceptions.py +++ b/src/snowflake/cli/plugins/nativeapp/exceptions.py @@ -79,14 +79,14 @@ def __init__(self, identifier: str): class CouldNotDropApplicationPackageWithVersions(ClickException): """Application package could not be dropped as it has versions associated with it.""" - def __init__(self): + def __init__(self, additional_msg: str = ""): super().__init__( dedent( f""" {self.__doc__} - Versions must be dropped first using “snow app version drop”. + {additional_msg} """ - ) + ).strip() ) diff --git a/src/snowflake/cli/plugins/nativeapp/teardown_processor.py b/src/snowflake/cli/plugins/nativeapp/teardown_processor.py index 585611465a..4b27178c42 100644 --- a/src/snowflake/cli/plugins/nativeapp/teardown_processor.py +++ b/src/snowflake/cli/plugins/nativeapp/teardown_processor.py @@ -226,8 +226,13 @@ def drop_package(self, auto_yes: bool): ) if show_versions_cursor.rowcount is None: raise SnowflakeSQLExecutionError(show_versions_query) + if show_versions_cursor.rowcount > 0: - raise CouldNotDropApplicationPackageWithVersions() + # allow dropping a package with versions when --force is set + if not auto_yes: + raise CouldNotDropApplicationPackageWithVersions( + "Drop versions first, or use --force to override." + ) # 4. Check distribution of the existing application package actual_distribution = self.get_app_pkg_distribution_in_snowflake diff --git a/tests/nativeapp/test_teardown_processor.py b/tests/nativeapp/test_teardown_processor.py index 965200f912..8afd5162c0 100644 --- a/tests/nativeapp/test_teardown_processor.py +++ b/tests/nativeapp/test_teardown_processor.py @@ -505,15 +505,10 @@ def test_drop_package_incorrect_owner( @mock.patch(TEARDOWN_PROCESSOR_GET_EXISTING_APP_PKG_INFO) @mock.patch(TEARDOWN_PROCESSOR_IS_CORRECT_OWNER, return_value=True) @mock.patch(NATIVEAPP_MANAGER_EXECUTE) -@pytest.mark.parametrize( - "auto_yes_param", - [True, False], # This should have no effect on the test -) def test_show_versions_failure_w_exception( mock_execute, mock_is_correct_owner, mock_get_existing_app_pkg_info, - auto_yes_param, temp_dir, mock_cursor, ): @@ -547,7 +542,7 @@ def test_show_versions_failure_w_exception( teardown_processor = _get_na_teardown_processor() with pytest.raises(CouldNotDropApplicationPackageWithVersions): - teardown_processor.drop_package(auto_yes_param) + teardown_processor.drop_package(auto_yes=False) mock_is_correct_owner.assert_called_once() mock_get_existing_app_pkg_info.assert_called_once() diff --git a/tests_integration/nativeapp/test_init_run.py b/tests_integration/nativeapp/test_init_run.py index 8a1bfb8837..c5b6cb0a25 100644 --- a/tests_integration/nativeapp/test_init_run.py +++ b/tests_integration/nativeapp/test_init_run.py @@ -622,13 +622,13 @@ def test_nativeapp_run_orphan( [ ([], []), ([], ["--version", "v1"]), - # ([], ["--from-release-directive"]), + ([], ["--from-release-directive"]), (["--version", "v1"], []), (["--version", "v1"], ["--version", "v1"]), - # (["--version", "v1"], ["--from-release-directive"]), - # (["--from-release-directive"], []), - # (["--from-release-directive"], ["--version", "v1"]), - # (["--from-release-directive"], ["--from-release-directive"]), + (["--version", "v1"], ["--from-release-directive"]), + (["--from-release-directive"], []), + (["--from-release-directive"], ["--version", "v1"]), + (["--from-release-directive"], ["--from-release-directive"]), ], ) def test_nativeapp_force_cross_upgrade( @@ -657,15 +657,15 @@ def test_nativeapp_force_cross_upgrade( assert result.exit_code == 0 # Set default release directive - # result = runner.invoke_with_connection_json( - # [ - # "sql", - # "-q", - # f"alter application package {pkg_name} set default release directive version = v1 patch = 0", - # ], - # env=TEST_ENV, - # ) - # assert result.exit_code == 0 + result = runner.invoke_with_connection( + [ + "sql", + "-q", + f"alter application package {pkg_name} set default release directive version = v1 patch = 0", + ], + env=TEST_ENV, + ) + assert result.exit_code == 0 # Initial run result = runner.invoke_with_connection( @@ -685,18 +685,6 @@ def test_nativeapp_force_cross_upgrade( assert f"Dropping application object {app_name}." in result.output finally: - # Drop the application (so it doesn't block dropping the version) - runner.invoke_with_connection( - ["sql", "-q", f"drop application if exists {app_name}"], - env=TEST_ENV, - ) - - # Drop version - runner.invoke_with_connection( - ["app", "version", "drop", "v1", "--force"], - env=TEST_ENV, - ) - # Drop the package result = runner.invoke_with_connection( ["app", "teardown", "--force"], diff --git a/tests_integration/nativeapp/test_teardown.py b/tests_integration/nativeapp/test_teardown.py index 649c801784..6463e59f43 100644 --- a/tests_integration/nativeapp/test_teardown.py +++ b/tests_integration/nativeapp/test_teardown.py @@ -225,3 +225,75 @@ def test_nativeapp_teardown_unowned_app( env=TEST_ENV, ) assert result.exit_code == 0 + + +@pytest.mark.integration +@pytest.mark.parametrize("default_release_directive", [True, False]) +def test_nativeapp_teardown_pkg_versions( + runner, + snowflake_session, + temporary_working_directory, + default_release_directive, +): + project_name = "myapp" + pkg_name = f"{project_name}_pkg_{USER_NAME}" + + result = runner.invoke_json( + ["app", "init", project_name], + env=TEST_ENV, + ) + assert result.exit_code == 0 + + with pushd(Path(os.getcwd(), project_name)): + result = runner.invoke_with_connection( + ["app", "version", "create", "v1"], + env=TEST_ENV, + ) + assert result.exit_code == 0 + + try: + # when setting a release directive, we will not have the ability to drop the version later + if default_release_directive: + result = runner.invoke_with_connection( + [ + "sql", + "-q", + f"alter application package {pkg_name} set default release directive version = v1 patch = 0", + ], + env=TEST_ENV, + ) + assert result.exit_code == 0 + + # try to teardown; fail because we have a version + result = runner.invoke_with_connection( + ["app", "teardown"], + env=TEST_ENV, + ) + assert result.exit_code == 1 + assert f"Drop versions first, or use --force to override." in result.output + + teardown_args = [] + if not default_release_directive: + # if we didn't set a release directive, we can drop the version and try again + result = runner.invoke_with_connection( + ["app", "version", "drop", "v1", "--force"], + env=TEST_ENV, + ) + assert result.exit_code == 0 + else: + # if we did set a release directive, we need --force for teardown to work + teardown_args = ["--force"] + + # either way, we can now tear down the application package + result = runner.invoke_with_connection( + ["app", "teardown"] + teardown_args, + env=TEST_ENV, + ) + assert result.exit_code == 0 + + finally: + result = runner.invoke_with_connection_json( + ["app", "teardown", "--force"], + env=TEST_ENV, + ) + assert result.exit_code == 0