Skip to content

Commit

Permalink
feedback round 2 + necessary rebase for a failing test
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-bgoel committed Jul 10, 2024
1 parent a82dca1 commit 0664030
Show file tree
Hide file tree
Showing 6 changed files with 296 additions and 118 deletions.
76 changes: 57 additions & 19 deletions src/snowflake/cli/plugins/nativeapp/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
from snowflake.cli.api.project.schemas.native_app.path_mapping import PathMapping
from snowflake.cli.api.project.util import (
identifier_for_url,
to_identifier,
unquote_identifier,
)
from snowflake.cli.api.sql_execution import SqlExecutionMixin
Expand Down Expand Up @@ -267,19 +268,58 @@ def use_warehouse(self, new_wh: Optional[str]):
wh_result = self._execute_query(
f"select current_warehouse()", cursor_class=DictCursor
).fetchone()
# If user has an assigned default warehouse, prev_wh will contain a value even if the warehouse is suspended.
prev_wh = wh_result["CURRENT_WAREHOUSE()"]
is_different_wh = (new_wh is not None) and (new_wh.lower() != prev_wh.lower())
if is_different_wh:

if new_wh is not None:
new_wh_id = to_identifier(new_wh)

if prev_wh is None and new_wh is None:
raise ClickException(
"Could not find both the connection and the requested warehouse in the Snowflake account for this user."
)

elif prev_wh is None:
self._log.debug("Using warehouse: %s", new_wh_id)
self.use(object_type=ObjectType.WAREHOUSE, name=new_wh_id)
try:
yield
finally:
self._log.debug(
"Continuing to use warehouse unless requested otherwise: %s",
new_wh_id,
)

elif new_wh is None:
self._log.debug(
"Temporarily switching to a different warehouse: %s", new_wh
"Requested warehouse is empty, continuing to use the connection warehouse: %s",
prev_wh,
)
self.use(object_type=ObjectType.WAREHOUSE, name=new_wh)
try:
yield
finally:
# Activate a suspended warehouse, will be a no-op will already active
self.use(object_type=ObjectType.WAREHOUSE, name=prev_wh)
try:
yield
finally:
self._log.debug(
"Continuing to use warehouse unless requested otherwise: %s",
prev_wh,
)

else:
is_different_wh = new_wh_id != prev_wh
self._log.debug("Using warehouse: %s", new_wh_id)
if is_different_wh:
self._log.debug("Switching back to the original warehouse: %s", prev_wh)
# Activate the new warehouse
self.use(object_type=ObjectType.WAREHOUSE, name=new_wh_id)
else:
# Activate a suspended warehouse, will be a no-op will already active
self.use(object_type=ObjectType.WAREHOUSE, name=prev_wh)
try:
yield
finally:
if is_different_wh:
self._log.debug("Switching back to warehouse: %s", prev_wh)
self.use(object_type=ObjectType.WAREHOUSE, name=prev_wh)

@cached_property
def get_app_pkg_distribution_in_snowflake(self) -> str:
Expand Down Expand Up @@ -585,17 +625,15 @@ def _apply_package_scripts(self) -> None:
raise InvalidPackageScriptError(relpath, e)

# once we're sure all the templates expanded correctly, execute all of them
try:
if self.package_warehouse:
self._execute_query(f"use warehouse {self.package_warehouse}")

for i, queries in enumerate(queued_queries):
cc.step(f"Applying package script: {self.package_scripts[i]}")
self._execute_queries(queries)
except ProgrammingError as err:
generic_sql_error_handler(
err, role=self.package_role, warehouse=self.package_warehouse
)
with self.use_warehouse(self.package_warehouse):
try:
for i, queries in enumerate(queued_queries):
cc.step(f"Applying package script: {self.package_scripts[i]}")
self._execute_queries(queries)
except ProgrammingError as err:
generic_sql_error_handler(
err, role=self.package_role, warehouse=self.package_warehouse
)

def deploy(
self,
Expand Down
8 changes: 4 additions & 4 deletions src/snowflake/cli/plugins/nativeapp/project_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,16 @@ def stage_schema(self) -> Optional[str]:
@cached_property
def package_warehouse(self) -> Optional[str]:
if self.definition.package and self.definition.package.warehouse:
return self.definition.package.warehouse
return to_identifier(self.definition.package.warehouse)
else:
return cli_context.connection.warehouse
return to_identifier(cli_context.connection.warehouse)

@cached_property
def application_warehouse(self) -> Optional[str]:
if self.definition.application and self.definition.application.warehouse:
return self.definition.application.warehouse
return to_identifier(self.definition.application.warehouse)
else:
return cli_context.connection.warehouse
return to_identifier(cli_context.connection.warehouse)

@cached_property
def project_identifier(self) -> str:
Expand Down
153 changes: 74 additions & 79 deletions src/snowflake/cli/plugins/nativeapp/run_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,15 +142,14 @@ def _execute_sql_script(self, sql_script_path):
"""
with open(sql_script_path) as f:
sql_script = f.read()
try:
if self.application_warehouse:
self._execute_query(f"use warehouse {self.application_warehouse}")
if self._conn.database:
self._execute_query(f"use database {self._conn.database}")
sql_script = snowflake_sql_jinja_render(content=sql_script)
self._execute_queries(sql_script)
except ProgrammingError as err:
generic_sql_error_handler(err)

try:
if self._conn.database:
self._execute_query(f"use database {self._conn.database}")
sql_script = snowflake_sql_jinja_render(content=sql_script)
self._execute_queries(sql_script)
except ProgrammingError as err:
generic_sql_error_handler(err)

def _execute_post_deploy_hooks(self):
post_deploy_script_hooks = self.app_post_deploy_hooks
Expand Down Expand Up @@ -267,89 +266,85 @@ def create_or_upgrade_app(
with self.use_role(self.app_role):

# 1. Need to use a warehouse to create an application object
try:
if self.application_warehouse:
self._execute_query(f"use warehouse {self.application_warehouse}")
except ProgrammingError as err:
generic_sql_error_handler(
err=err, role=self.app_role, warehouse=self.application_warehouse
)
with self.use_warehouse(self.application_warehouse):

# 2. Check for an existing application by the same name
show_app_row = self.get_existing_app_info()

# 2. Check for an existing application by the same name
show_app_row = self.get_existing_app_info()
# 3. If existing application is found, perform a few validations and upgrade the application object.
if show_app_row:

# 3. If existing application is found, perform a few validations and upgrade the application object.
if show_app_row:
install_method.ensure_app_usable(self._na_project, show_app_row)

install_method.ensure_app_usable(self._na_project, show_app_row)
# If all the above checks are in order, proceed to upgrade
try:
cc.step(
f"Upgrading existing application object {self.app_name}."
)
using_clause = install_method.using_clause(self._na_project)
self._execute_query(
f"alter application {self.app_name} upgrade {using_clause}"
)

if install_method.is_dev_mode:
# if debug_mode is present (controlled), ensure it is up-to-date
if self.debug_mode is not None:
self._execute_query(
f"alter application {self.app_name} set debug_mode = {self.debug_mode}"
)

# hooks always executed after a create or upgrade
self._execute_post_deploy_hooks()
return

except ProgrammingError as err:
if err.errno not in UPGRADE_RESTRICTION_CODES:
generic_sql_error_handler(err=err)
else: # The existing application object was created from a different process.
cc.warning(err.msg)
self.drop_application_before_upgrade(policy, is_interactive)

# 4. With no (more) existing application objects, create an application object using the release directives
cc.step(f"Creating new application object {self.app_name} in account.")

if self.app_role != self.package_role:
with self.use_role(self.package_role):
self._execute_query(
f"grant install, develop on application package {self.package_name} to role {self.app_role}"
)
self._execute_query(
f"grant usage on schema {self.package_name}.{self.stage_schema} to role {self.app_role}"
)
self._execute_query(
f"grant read on stage {self.stage_fqn} to role {self.app_role}"
)

# If all the above checks are in order, proceed to upgrade
try:
cc.step(f"Upgrading existing application object {self.app_name}.")
# by default, applications are created in debug mode when possible;
# this can be overridden in the project definition
debug_mode_clause = ""
if install_method.is_dev_mode:
initial_debug_mode = (
self.debug_mode if self.debug_mode is not None else True
)
debug_mode_clause = f"debug_mode = {initial_debug_mode}"

using_clause = install_method.using_clause(self._na_project)
self._execute_query(
f"alter application {self.app_name} upgrade {using_clause}"
dedent(
f"""\
create application {self.app_name}
from application package {self.package_name} {using_clause} {debug_mode_clause}
comment = {SPECIAL_COMMENT}
"""
)
)

if install_method.is_dev_mode:
# if debug_mode is present (controlled), ensure it is up-to-date
if self.debug_mode is not None:
self._execute_query(
f"alter application {self.app_name} set debug_mode = {self.debug_mode}"
)

# hooks always executed after a create or upgrade
self._execute_post_deploy_hooks()
return

except ProgrammingError as err:
if err.errno not in UPGRADE_RESTRICTION_CODES:
generic_sql_error_handler(err=err)
else: # The existing application object was created from a different process.
cc.warning(err.msg)
self.drop_application_before_upgrade(policy, is_interactive)

# 4. With no (more) existing application objects, create an application object using the release directives
cc.step(f"Creating new application object {self.app_name} in account.")

if self.app_role != self.package_role:
with self.use_role(self.package_role):
self._execute_query(
f"grant install, develop on application package {self.package_name} to role {self.app_role}"
)
self._execute_query(
f"grant usage on schema {self.package_name}.{self.stage_schema} to role {self.app_role}"
)
self._execute_query(
f"grant read on stage {self.stage_fqn} to role {self.app_role}"
)

try:
# by default, applications are created in debug mode when possible;
# this can be overridden in the project definition
debug_mode_clause = ""
if install_method.is_dev_mode:
initial_debug_mode = (
self.debug_mode if self.debug_mode is not None else True
)
debug_mode_clause = f"debug_mode = {initial_debug_mode}"

using_clause = install_method.using_clause(self._na_project)
self._execute_query(
dedent(
f"""\
create application {self.app_name}
from application package {self.package_name} {using_clause} {debug_mode_clause}
comment = {SPECIAL_COMMENT}
"""
)
)

# hooks always executed after a create or upgrade
self._execute_post_deploy_hooks()

except ProgrammingError as err:
generic_sql_error_handler(err)
generic_sql_error_handler(err)

def process(
self,
Expand Down
Loading

0 comments on commit 0664030

Please sign in to comment.