diff --git a/src/snowflake/cli/_plugins/apps/commands.py b/src/snowflake/cli/_plugins/apps/commands.py index feaff49e94..09627fa2f1 100644 --- a/src/snowflake/cli/_plugins/apps/commands.py +++ b/src/snowflake/cli/_plugins/apps/commands.py @@ -728,7 +728,7 @@ def _url_is_ready(d: dict) -> bool: ), timeout_message=( f"Upgrade timed out. Check logs:\n" - f" CALL SYSTEM$GET_APPLICATION_SERVICE_LOGS('{app_name}')" + f" CALL SYSTEM$GET_APPLICATION_SERVICE_LOGS('{service_fqn.identifier}')" ), ) else: diff --git a/src/snowflake/cli/_plugins/apps/manager.py b/src/snowflake/cli/_plugins/apps/manager.py index c419e58838..944ce484e7 100644 --- a/src/snowflake/cli/_plugins/apps/manager.py +++ b/src/snowflake/cli/_plugins/apps/manager.py @@ -717,8 +717,10 @@ def get_service_status(self, service_fqn: FQN) -> str: def get_service_logs(self, service_fqn: FQN, last: int = 500) -> str: """Fetch recent log output from an application service.""" + from snowflake.cli.api.project.util import to_string_literal + cursor = self.execute_query( - f"CALL SYSTEM$GET_APPLICATION_SERVICE_LOGS('{service_fqn.identifier}', {last})" + f"CALL SYSTEM$GET_APPLICATION_SERVICE_LOGS({to_string_literal(service_fqn.identifier)}, {last})" ) row = cursor.fetchone() return row[0] if row else "" @@ -1001,7 +1003,7 @@ def describe_app_service(self, service_fqn: FQN) -> Dict[str, Any]: Returns an empty dict when the DESCRIBE returns no rows. """ cursor = self.execute_query( - f"DESCRIBE APPLICATION SERVICE {service_fqn.identifier}", + f"DESCRIBE APPLICATION SERVICE {service_fqn.sql_identifier}", cursor_class=DictCursor, ) row = cursor.fetchone() diff --git a/tests/apps/test_commands.py b/tests/apps/test_commands.py index 03fefc021c..3de5b790b3 100644 --- a/tests/apps/test_commands.py +++ b/tests/apps/test_commands.py @@ -1034,7 +1034,7 @@ def test_describe_app_service_normalises_keys(self, mock_execute): assert desc["url"] == "my-app.snowflakecomputing.app" assert desc["is_upgrading"] == "false" query = mock_execute.call_args[0][0] - assert "DESCRIBE APPLICATION SERVICE DB.SCHEMA.my_app" in query + assert "DESCRIBE APPLICATION SERVICE IDENTIFIER('DB.SCHEMA.my_app')" in query @patch(EXECUTE_QUERY) def test_describe_app_service_empty(self, mock_execute): @@ -1275,7 +1275,8 @@ def test_get_service_endpoint_url(self, mock_execute): assert url == "https://my-endpoint.snowflakecomputing.app" mock_execute.assert_called_once() assert ( - mock_execute.call_args[0][0] == "DESCRIBE APPLICATION SERVICE DB.SCHEMA.SVC" + mock_execute.call_args[0][0] + == "DESCRIBE APPLICATION SERVICE IDENTIFIER('DB.SCHEMA.SVC')" ) @patch(EXECUTE_QUERY) @@ -1302,7 +1303,8 @@ def test_get_service_endpoint_url_not_found(self, mock_execute): assert url is None mock_execute.assert_called_once() assert ( - "DESCRIBE APPLICATION SERVICE DB.SCHEMA.SVC" in mock_execute.call_args[0][0] + "DESCRIBE APPLICATION SERVICE IDENTIFIER('DB.SCHEMA.SVC')" + in mock_execute.call_args[0][0] ) @patch(EXECUTE_QUERY) @@ -3970,6 +3972,82 @@ def test_deploy_only_runs_deploy_and_stops( mock_mgr.build_app_artifact_repo.assert_not_called() mock_mgr.stage_exists.assert_not_called() + @patch("snowflake.cli._plugins.apps.commands._poll_until") + @patch("snowflake.cli._plugins.apps.commands.SnowflakeAppManager") + @patch( + RESOLVE_DEPLOY_DEFAULTS, + return_value={ + "query_warehouse": "WH", + "build_compute_pool": None, + "service_compute_pool": "SVC_POOL", + "build_eai": None, + "database": "TEST_DB", + "schema": "TEST_SCHEMA", + "artifact_repository": "MY_APP_REPO", + "artifact_repo_database": "TEST_DB", + "artifact_repo_schema": "TEST_SCHEMA", + }, + ) + @patch("snowflake.cli._plugins.apps.commands._get_entity") + @patch( + "snowflake.cli._plugins.apps.commands._resolve_entity_id", + return_value="my_app", + ) + def test_upgrade_timeout_hint_uses_fully_qualified_service_name( + self, + mock_resolve, + mock_get_entity, + mock_defaults, + mock_manager_cls, + mock_poll, + runner, + tmp_path, + ): + entity = Mock() + fqn = Mock() + fqn.name = "MY_APP" + fqn.database = "TEST_DB" + fqn.schema = "TEST_SCHEMA" + entity.fqn = fqn + entity.code_stage = None + entity.code_workspace = None + entity.artifacts = [] + entity.meta = None + entity.artifact_repository = None + mock_get_entity.return_value = entity + + already_exists_error = ProgrammingError("already exists") + already_exists_error.errno = 2002 + + mock_mgr = mock_manager_cls.return_value + mock_mgr.is_managed_compute_pool_enabled.return_value = False + mock_mgr.is_managed_compute_pool_fallback_enabled.return_value = False + mock_mgr.create_app_service.side_effect = already_exists_error + mock_mgr.resolve_application_service_url_from_describe.return_value = ( + "https://my-app.snowflakecomputing.app" + ) + mock_poll.return_value = { + "url": "my-app.snowflakecomputing.app", + "is_upgrading": "false", + } + + from tests_common import change_directory + + with change_directory(tmp_path): + _write_snowflake_app_yml(tmp_path) + result = runner.invoke(["app", "deploy", "--deploy-only"]) + assert result.exit_code == 0, result.output + + mock_mgr.upgrade_app_service.assert_called_once_with( + service_fqn=FQN(database="TEST_DB", schema="TEST_SCHEMA", name="MY_APP"), + version="LATEST", + ) + _, poll_kwargs = mock_poll.call_args + assert ( + "CALL SYSTEM$GET_APPLICATION_SERVICE_LOGS('TEST_DB.TEST_SCHEMA.MY_APP')" + in poll_kwargs["timeout_message"] + ) + @patch("snowflake.cli._plugins.apps.commands.StageManager") @patch("snowflake.cli._plugins.apps.commands.perform_bundle") @patch("snowflake.cli._plugins.apps.commands.SnowflakeAppManager")