Skip to content

Commit

Permalink
SNOW-1653357 Remove deploy_to_scratch_stage_fn() and call deploy() di…
Browse files Browse the repository at this point in the history
…rectly (#1621)

Now that `ApplicationPackageEntity` has a static `deploy()` method, we can just call that instead of having to pass a callback from the `NativeAppManager` to deploy the app files to the scratch stage when doing setup script validation. There's a bit of a an explosion in required params, but it's temporary and it'll be cleaned up when the v1 commands are made to operate on v2 entities directly.
  • Loading branch information
sfc-gh-fcampbell authored Sep 26, 2024
1 parent 9a6e37a commit 091947c
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 59 deletions.
125 changes: 99 additions & 26 deletions src/snowflake/cli/_plugins/nativeapp/entities/application_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from contextlib import contextmanager
from pathlib import Path
from textwrap import dedent
from typing import Callable, List, Literal, Optional, Union
from typing import List, Literal, Optional, Union

import typer
from click import BadOptionUsage, ClickException
Expand Down Expand Up @@ -229,32 +229,35 @@ def action_validate(
):
model = self._entity_model
package_name = model.fqn.identifier
stage_fqn = f"{package_name}.{model.stage}"
if model.meta and model.meta.role:
package_role = model.meta.role
if force:
policy = AllowAlwaysPolicy()
elif interactive:
policy = AskAlwaysPolicy()
else:
package_role = ctx.default_role

def deploy_to_scratch_stage_fn():
self.action_deploy(
ctx=ctx,
prune=True,
recursive=True,
paths=[],
validate=False,
stage_fqn=f"{package_name}.{model.scratch_stage}",
interactive=interactive,
force=force,
)
policy = DenyAlwaysPolicy()

self.validate_setup_script(
console=ctx.console,
project_root=ctx.project_root,
deploy_root=Path(model.deploy_root),
bundle_root=Path(model.bundle_root),
generated_root=Path(model.generated_root),
artifacts=model.artifacts,
package_name=package_name,
package_role=package_role,
stage_fqn=stage_fqn,
package_role=(model.meta and model.meta.role) or ctx.default_role,
package_distribution=model.distribution,
prune=True,
recursive=True,
paths=[],
stage_fqn=f"{package_name}.{model.stage}",
package_warehouse=(
(model.meta and model.meta.warehouse) or ctx.default_warehouse
),
post_deploy_hooks=model.meta and model.meta.post_deploy,
package_scripts=[], # Package scripts are not supported in PDFv2
policy=policy,
use_scratch_stage=True,
scratch_stage_fqn=f"{package_name}.{model.scratch_stage}",
deploy_to_scratch_stage_fn=deploy_to_scratch_stage_fn,
)
ctx.console.message("Setup script is valid")

Expand Down Expand Up @@ -439,12 +442,24 @@ def deploy(
if validate:
cls.validate_setup_script(
console=console,
project_root=project_root,
deploy_root=deploy_root,
bundle_root=bundle_root,
generated_root=generated_root,
artifacts=artifacts,
package_name=package_name,
package_role=package_role,
package_distribution=package_distribution,
prune=prune,
recursive=recursive,
paths=paths,
stage_fqn=stage_fqn,
package_warehouse=package_warehouse,
post_deploy_hooks=post_deploy_hooks,
package_scripts=package_scripts,
policy=policy,
use_scratch_stage=False,
scratch_stage_fqn="",
deploy_to_scratch_stage_fn=lambda *args: None,
)

return diff
Expand Down Expand Up @@ -1154,23 +1169,47 @@ def execute_post_deploy_hooks(
def validate_setup_script(
cls,
console: AbstractConsole,
project_root: Path,
deploy_root: Path,
bundle_root: Path,
generated_root: Path,
artifacts: list[PathMapping],
package_name: str,
package_role: str,
package_distribution: str,
package_warehouse: str | None,
prune: bool,
recursive: bool,
paths: List[Path] | None,
stage_fqn: str,
post_deploy_hooks: list[PostDeployHook] | None,
package_scripts: List[str],
policy: PolicyBase,
use_scratch_stage: bool,
scratch_stage_fqn: str,
deploy_to_scratch_stage_fn: Callable,
):
"""Validates Native App setup script SQL."""
with console.phase(f"Validating Snowflake Native App setup script."):
validation_result = cls.get_validation_result(
console=console,
project_root=project_root,
deploy_root=deploy_root,
bundle_root=bundle_root,
generated_root=generated_root,
artifacts=artifacts,
package_name=package_name,
package_role=package_role,
package_distribution=package_distribution,
prune=prune,
recursive=recursive,
paths=paths,
stage_fqn=stage_fqn,
package_warehouse=package_warehouse,
post_deploy_hooks=post_deploy_hooks,
package_scripts=package_scripts,
policy=policy,
use_scratch_stage=use_scratch_stage,
scratch_stage_fqn=scratch_stage_fqn,
deploy_to_scratch_stage_fn=deploy_to_scratch_stage_fn,
)

# First print warnings, regardless of the outcome of validation
Expand All @@ -1187,20 +1226,54 @@ def validate_setup_script(
if validation_result["status"] == "FAIL":
raise SetupScriptFailedValidation()

@staticmethod
@classmethod
def get_validation_result(
cls,
console: AbstractConsole,
project_root: Path,
deploy_root: Path,
bundle_root: Path,
generated_root: Path,
artifacts: list[PathMapping],
package_name: str,
package_role: str,
package_distribution: str,
package_warehouse: str | None,
prune: bool,
recursive: bool,
paths: List[Path] | None,
stage_fqn: str,
post_deploy_hooks: list[PostDeployHook] | None,
package_scripts: List[str],
policy: PolicyBase,
use_scratch_stage: bool,
scratch_stage_fqn: str,
deploy_to_scratch_stage_fn: Callable,
):
"""Call system$validate_native_app_setup() to validate deployed Native App setup script."""
if use_scratch_stage:
stage_fqn = scratch_stage_fqn
deploy_to_scratch_stage_fn()
cls.deploy(
console=console,
project_root=project_root,
deploy_root=deploy_root,
bundle_root=bundle_root,
generated_root=generated_root,
artifacts=artifacts,
bundle_map=None,
package_name=package_name,
package_role=package_role,
package_distribution=package_distribution,
prune=prune,
recursive=recursive,
paths=paths,
print_diff=False,
validate=False,
stage_fqn=stage_fqn,
package_warehouse=package_warehouse,
post_deploy_hooks=post_deploy_hooks,
package_scripts=package_scripts,
policy=policy,
)
prefixed_stage_fqn = StageManager.get_standard_stage_prefix(stage_fqn)
sql_executor = get_sql_executor()
try:
Expand Down
42 changes: 27 additions & 15 deletions src/snowflake/cli/_plugins/nativeapp/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,38 +318,50 @@ def deploy(
policy=policy,
)

def deploy_to_scratch_stage_fn(self):
bundle_map = self.build_bundle()
self.deploy(
bundle_map=bundle_map,
prune=True,
recursive=True,
stage_fqn=self.scratch_stage_fqn,
validate=False,
print_diff=False,
policy=AllowAlwaysPolicy(),
)

def validate(self, use_scratch_stage: bool = False):
return ApplicationPackageEntity.validate_setup_script(
console=cc,
project_root=self.project_root,
deploy_root=self.deploy_root,
bundle_root=self.bundle_root,
generated_root=self.generated_root,
artifacts=self.artifacts,
package_name=self.package_name,
package_role=self.package_role,
package_distribution=self.package_distribution,
prune=True,
recursive=True,
paths=[],
stage_fqn=self.stage_fqn,
package_warehouse=self.package_warehouse,
post_deploy_hooks=self.package_post_deploy_hooks,
package_scripts=self.package_scripts,
policy=AllowAlwaysPolicy(),
use_scratch_stage=use_scratch_stage,
scratch_stage_fqn=self.scratch_stage_fqn,
deploy_to_scratch_stage_fn=self.deploy_to_scratch_stage_fn,
)

def get_validation_result(self, use_scratch_stage: bool):
def get_validation_result(self, use_scratch_stage: bool = False):
return ApplicationPackageEntity.get_validation_result(
console=cc,
project_root=self.project_root,
deploy_root=self.deploy_root,
bundle_root=self.bundle_root,
generated_root=self.generated_root,
artifacts=self.artifacts,
package_name=self.package_name,
package_role=self.package_role,
package_distribution=self.package_distribution,
prune=True,
recursive=True,
paths=[],
stage_fqn=self.stage_fqn,
package_warehouse=self.package_warehouse,
post_deploy_hooks=self.package_post_deploy_hooks,
package_scripts=self.package_scripts,
policy=AllowAlwaysPolicy(),
use_scratch_stage=use_scratch_stage,
scratch_stage_fqn=self.scratch_stage_fqn,
deploy_to_scratch_stage_fn=self.deploy_to_scratch_stage_fn,
)

def get_events( # type: ignore
Expand Down
56 changes: 38 additions & 18 deletions tests/nativeapp/test_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
DiffResult,
StagePath,
)
from snowflake.cli.api.console import cli_console as cc
from snowflake.cli.api.entities.utils import _get_stage_paths_to_sync
from snowflake.cli.api.errno import (
DOES_NOT_EXIST_OR_NOT_AUTHORIZED,
Expand All @@ -60,11 +61,10 @@
)
from tests.nativeapp.utils import (
APP_ENTITY_GET_ACCOUNT_EVENT_TABLE,
APP_PACKAGE_ENTITY_DEPLOY,
APP_PACKAGE_ENTITY_GET_EXISTING_APP_PKG_INFO,
APP_PACKAGE_ENTITY_IS_DISTRIBUTION_SAME,
ENTITIES_UTILS_MODULE,
NATIVEAPP_MANAGER_BUILD_BUNDLE,
NATIVEAPP_MANAGER_DEPLOY,
NATIVEAPP_MODULE,
SQL_EXECUTOR_EXECUTE,
mock_execute_helper,
Expand Down Expand Up @@ -1173,12 +1173,9 @@ def test_validate_not_deployed(mock_execute, temp_dir, mock_cursor):
assert mock_execute.mock_calls == expected


@mock.patch(NATIVEAPP_MANAGER_BUILD_BUNDLE)
@mock.patch(NATIVEAPP_MANAGER_DEPLOY)
@mock.patch(APP_PACKAGE_ENTITY_DEPLOY)
@mock.patch(SQL_EXECUTOR_EXECUTE)
def test_validate_use_scratch_stage(
mock_execute, mock_deploy, mock_build_bundle, temp_dir, mock_cursor
):
def test_validate_use_scratch_stage(mock_execute, mock_deploy, temp_dir, mock_cursor):
create_named_file(
file_name="snowflake.yml",
dir_name=temp_dir,
Expand Down Expand Up @@ -1213,24 +1210,35 @@ def test_validate_use_scratch_stage(
native_app_manager = _get_na_manager()
native_app_manager.validate(use_scratch_stage=True)

mock_build_bundle.assert_called_once()
mock_deploy.assert_called_with(
bundle_map=mock_build_bundle.return_value,
console=cc,
project_root=native_app_manager.project_root,
deploy_root=native_app_manager.deploy_root,
bundle_root=native_app_manager.bundle_root,
generated_root=native_app_manager.generated_root,
artifacts=native_app_manager.artifacts,
bundle_map=None,
package_name=native_app_manager.package_name,
package_role=native_app_manager.package_role,
package_distribution=native_app_manager.package_distribution,
prune=True,
recursive=True,
stage_fqn=native_app_manager.scratch_stage_fqn,
validate=False,
paths=[],
print_diff=False,
validate=False,
stage_fqn=native_app_manager.scratch_stage_fqn,
package_warehouse=native_app_manager.package_warehouse,
post_deploy_hooks=native_app_manager.package_post_deploy_hooks,
package_scripts=native_app_manager.package_scripts,
policy=AllowAlwaysPolicy(),
)
assert mock_execute.mock_calls == expected


@mock.patch(NATIVEAPP_MANAGER_BUILD_BUNDLE)
@mock.patch(NATIVEAPP_MANAGER_DEPLOY)
@mock.patch(APP_PACKAGE_ENTITY_DEPLOY)
@mock.patch(SQL_EXECUTOR_EXECUTE)
def test_validate_failing_drops_scratch_stage(
mock_execute, mock_deploy, mock_build_bundle, temp_dir, mock_cursor
mock_execute, mock_deploy, temp_dir, mock_cursor
):
create_named_file(
file_name="snowflake.yml",
Expand Down Expand Up @@ -1280,14 +1288,26 @@ def test_validate_failing_drops_scratch_stage(
):
native_app_manager.validate(use_scratch_stage=True)

mock_build_bundle.assert_called_once()
mock_deploy.assert_called_with(
bundle_map=mock_build_bundle.return_value,
console=cc,
project_root=native_app_manager.project_root,
deploy_root=native_app_manager.deploy_root,
bundle_root=native_app_manager.bundle_root,
generated_root=native_app_manager.generated_root,
artifacts=native_app_manager.artifacts,
bundle_map=None,
package_name=native_app_manager.package_name,
package_role=native_app_manager.package_role,
package_distribution=native_app_manager.package_distribution,
prune=True,
recursive=True,
stage_fqn=native_app_manager.scratch_stage_fqn,
validate=False,
paths=[],
print_diff=False,
validate=False,
stage_fqn=native_app_manager.scratch_stage_fqn,
package_warehouse=native_app_manager.package_warehouse,
post_deploy_hooks=native_app_manager.package_post_deploy_hooks,
package_scripts=native_app_manager.package_scripts,
policy=AllowAlwaysPolicy(),
)
assert mock_execute.mock_calls == expected
Expand Down
1 change: 1 addition & 0 deletions tests/nativeapp/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
APP_ENTITY_GET_ACCOUNT_EVENT_TABLE = f"{APP_ENTITY}.get_account_event_table"

APP_PACKAGE_ENTITY = "snowflake.cli._plugins.nativeapp.entities.application_package.ApplicationPackageEntity"
APP_PACKAGE_ENTITY_DEPLOY = f"{APP_PACKAGE_ENTITY}.deploy"
APP_PACKAGE_ENTITY_DISTRIBUTION_IN_SF = (
f"{APP_PACKAGE_ENTITY}.get_app_pkg_distribution_in_snowflake"
)
Expand Down

0 comments on commit 091947c

Please sign in to comment.