snowflake-cli-labs 3.0.0rc4__py3-none-any.whl → 3.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- README.md +21 -0
- {snowflake_cli_labs-3.0.0rc4.dist-info → snowflake_cli_labs-3.0.1.dist-info}/METADATA +6 -96
- snowflake_cli_labs-3.0.1.dist-info/RECORD +5 -0
- snowflake/cli/__about__.py +0 -17
- snowflake/cli/__init__.py +0 -13
- snowflake/cli/_app/__init__.py +0 -22
- snowflake/cli/_app/__main__.py +0 -31
- snowflake/cli/_app/api_impl/__init__.py +0 -13
- snowflake/cli/_app/api_impl/plugin/__init__.py +0 -13
- snowflake/cli/_app/api_impl/plugin/plugin_config_provider_impl.py +0 -66
- snowflake/cli/_app/cli_app.py +0 -252
- snowflake/cli/_app/commands_registration/__init__.py +0 -33
- snowflake/cli/_app/commands_registration/builtin_plugins.py +0 -50
- snowflake/cli/_app/commands_registration/command_plugins_loader.py +0 -169
- snowflake/cli/_app/commands_registration/commands_registration_with_callbacks.py +0 -105
- snowflake/cli/_app/commands_registration/exception_logging.py +0 -26
- snowflake/cli/_app/commands_registration/threadsafe.py +0 -48
- snowflake/cli/_app/commands_registration/typer_registration.py +0 -153
- snowflake/cli/_app/constants.py +0 -19
- snowflake/cli/_app/dev/__init__.py +0 -13
- snowflake/cli/_app/dev/commands_structure.py +0 -48
- snowflake/cli/_app/dev/docs/__init__.py +0 -13
- snowflake/cli/_app/dev/docs/commands_docs_generator.py +0 -118
- snowflake/cli/_app/dev/docs/generator.py +0 -35
- snowflake/cli/_app/dev/docs/project_definition_docs_generator.py +0 -58
- snowflake/cli/_app/dev/docs/project_definition_generate_json_schema.py +0 -227
- snowflake/cli/_app/dev/docs/template_utils.py +0 -23
- snowflake/cli/_app/dev/docs/templates/definition_description.rst.jinja2 +0 -38
- snowflake/cli/_app/dev/docs/templates/overview.rst.jinja2 +0 -9
- snowflake/cli/_app/dev/docs/templates/usage.rst.jinja2 +0 -67
- snowflake/cli/_app/dev/pycharm_remote_debug.py +0 -46
- snowflake/cli/_app/loggers.py +0 -199
- snowflake/cli/_app/main_typer.py +0 -62
- snowflake/cli/_app/printing.py +0 -181
- snowflake/cli/_app/secret.py +0 -9
- snowflake/cli/_app/snow_connector.py +0 -309
- snowflake/cli/_app/telemetry.py +0 -220
- snowflake/cli/_app/version_check.py +0 -74
- snowflake/cli/_plugins/__init__.py +0 -13
- snowflake/cli/_plugins/connection/__init__.py +0 -13
- snowflake/cli/_plugins/connection/commands.py +0 -353
- snowflake/cli/_plugins/connection/plugin_spec.py +0 -30
- snowflake/cli/_plugins/connection/util.py +0 -195
- snowflake/cli/_plugins/cortex/__init__.py +0 -13
- snowflake/cli/_plugins/cortex/commands.py +0 -332
- snowflake/cli/_plugins/cortex/constants.py +0 -17
- snowflake/cli/_plugins/cortex/manager.py +0 -189
- snowflake/cli/_plugins/cortex/plugin_spec.py +0 -30
- snowflake/cli/_plugins/cortex/types.py +0 -22
- snowflake/cli/_plugins/git/__init__.py +0 -13
- snowflake/cli/_plugins/git/commands.py +0 -358
- snowflake/cli/_plugins/git/manager.py +0 -151
- snowflake/cli/_plugins/git/plugin_spec.py +0 -30
- snowflake/cli/_plugins/helpers/__init__.py +0 -13
- snowflake/cli/_plugins/helpers/commands.py +0 -61
- snowflake/cli/_plugins/helpers/plugin_spec.py +0 -30
- snowflake/cli/_plugins/init/__init__.py +0 -13
- snowflake/cli/_plugins/init/commands.py +0 -248
- snowflake/cli/_plugins/init/plugin_spec.py +0 -30
- snowflake/cli/_plugins/nativeapp/__init__.py +0 -13
- snowflake/cli/_plugins/nativeapp/artifacts.py +0 -757
- snowflake/cli/_plugins/nativeapp/bundle_context.py +0 -31
- snowflake/cli/_plugins/nativeapp/codegen/__init__.py +0 -13
- snowflake/cli/_plugins/nativeapp/codegen/artifact_processor.py +0 -91
- snowflake/cli/_plugins/nativeapp/codegen/compiler.py +0 -149
- snowflake/cli/_plugins/nativeapp/codegen/sandbox.py +0 -306
- snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +0 -249
- snowflake/cli/_plugins/nativeapp/codegen/setup/setup_driver.py.source +0 -59
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/callback_source.py.jinja +0 -181
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/extension_function_utils.py +0 -217
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/models.py +0 -61
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +0 -523
- snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +0 -114
- snowflake/cli/_plugins/nativeapp/commands.py +0 -559
- snowflake/cli/_plugins/nativeapp/common_flags.py +0 -44
- snowflake/cli/_plugins/nativeapp/constants.py +0 -27
- snowflake/cli/_plugins/nativeapp/entities/__init__.py +0 -0
- snowflake/cli/_plugins/nativeapp/entities/application.py +0 -878
- snowflake/cli/_plugins/nativeapp/entities/application_package.py +0 -1392
- snowflake/cli/_plugins/nativeapp/exceptions.py +0 -113
- snowflake/cli/_plugins/nativeapp/feature_flags.py +0 -24
- snowflake/cli/_plugins/nativeapp/manager.py +0 -415
- snowflake/cli/_plugins/nativeapp/plugin_spec.py +0 -30
- snowflake/cli/_plugins/nativeapp/policy.py +0 -53
- snowflake/cli/_plugins/nativeapp/project_model.py +0 -211
- snowflake/cli/_plugins/nativeapp/run_processor.py +0 -184
- snowflake/cli/_plugins/nativeapp/same_account_install_method.py +0 -70
- snowflake/cli/_plugins/nativeapp/teardown_processor.py +0 -70
- snowflake/cli/_plugins/nativeapp/utils.py +0 -98
- snowflake/cli/_plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +0 -262
- snowflake/cli/_plugins/nativeapp/version/__init__.py +0 -13
- snowflake/cli/_plugins/nativeapp/version/commands.py +0 -141
- snowflake/cli/_plugins/nativeapp/version/version_processor.py +0 -98
- snowflake/cli/_plugins/notebook/__init__.py +0 -13
- snowflake/cli/_plugins/notebook/commands.py +0 -86
- snowflake/cli/_plugins/notebook/exceptions.py +0 -20
- snowflake/cli/_plugins/notebook/manager.py +0 -71
- snowflake/cli/_plugins/notebook/plugin_spec.py +0 -30
- snowflake/cli/_plugins/notebook/types.py +0 -15
- snowflake/cli/_plugins/object/__init__.py +0 -13
- snowflake/cli/_plugins/object/command_aliases.py +0 -95
- snowflake/cli/_plugins/object/commands.py +0 -180
- snowflake/cli/_plugins/object/common.py +0 -85
- snowflake/cli/_plugins/object/manager.py +0 -118
- snowflake/cli/_plugins/object/plugin_spec.py +0 -30
- snowflake/cli/_plugins/snowpark/__init__.py +0 -13
- snowflake/cli/_plugins/snowpark/commands.py +0 -450
- snowflake/cli/_plugins/snowpark/common.py +0 -268
- snowflake/cli/_plugins/snowpark/models.py +0 -150
- snowflake/cli/_plugins/snowpark/package/__init__.py +0 -13
- snowflake/cli/_plugins/snowpark/package/anaconda_packages.py +0 -199
- snowflake/cli/_plugins/snowpark/package/commands.py +0 -195
- snowflake/cli/_plugins/snowpark/package/manager.py +0 -44
- snowflake/cli/_plugins/snowpark/package/utils.py +0 -26
- snowflake/cli/_plugins/snowpark/package_utils.py +0 -354
- snowflake/cli/_plugins/snowpark/plugin_spec.py +0 -30
- snowflake/cli/_plugins/snowpark/snowpark_entity.py +0 -29
- snowflake/cli/_plugins/snowpark/snowpark_entity_model.py +0 -173
- snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +0 -109
- snowflake/cli/_plugins/snowpark/snowpark_shared.py +0 -59
- snowflake/cli/_plugins/snowpark/zipper.py +0 -89
- snowflake/cli/_plugins/spcs/__init__.py +0 -33
- snowflake/cli/_plugins/spcs/common.py +0 -99
- snowflake/cli/_plugins/spcs/compute_pool/__init__.py +0 -13
- snowflake/cli/_plugins/spcs/compute_pool/commands.py +0 -241
- snowflake/cli/_plugins/spcs/compute_pool/manager.py +0 -121
- snowflake/cli/_plugins/spcs/image_registry/__init__.py +0 -13
- snowflake/cli/_plugins/spcs/image_registry/commands.py +0 -65
- snowflake/cli/_plugins/spcs/image_registry/manager.py +0 -105
- snowflake/cli/_plugins/spcs/image_repository/__init__.py +0 -13
- snowflake/cli/_plugins/spcs/image_repository/commands.py +0 -202
- snowflake/cli/_plugins/spcs/image_repository/manager.py +0 -84
- snowflake/cli/_plugins/spcs/plugin_spec.py +0 -30
- snowflake/cli/_plugins/spcs/services/__init__.py +0 -13
- snowflake/cli/_plugins/spcs/services/commands.py +0 -345
- snowflake/cli/_plugins/spcs/services/manager.py +0 -208
- snowflake/cli/_plugins/sql/__init__.py +0 -13
- snowflake/cli/_plugins/sql/commands.py +0 -86
- snowflake/cli/_plugins/sql/manager.py +0 -92
- snowflake/cli/_plugins/sql/plugin_spec.py +0 -30
- snowflake/cli/_plugins/sql/snowsql_templating.py +0 -28
- snowflake/cli/_plugins/stage/__init__.py +0 -13
- snowflake/cli/_plugins/stage/commands.py +0 -264
- snowflake/cli/_plugins/stage/diff.py +0 -280
- snowflake/cli/_plugins/stage/manager.py +0 -582
- snowflake/cli/_plugins/stage/md5.py +0 -160
- snowflake/cli/_plugins/stage/plugin_spec.py +0 -30
- snowflake/cli/_plugins/stage/utils.py +0 -54
- snowflake/cli/_plugins/streamlit/__init__.py +0 -13
- snowflake/cli/_plugins/streamlit/commands.py +0 -195
- snowflake/cli/_plugins/streamlit/manager.py +0 -220
- snowflake/cli/_plugins/streamlit/plugin_spec.py +0 -30
- snowflake/cli/_plugins/streamlit/streamlit_entity.py +0 -12
- snowflake/cli/_plugins/streamlit/streamlit_entity_model.py +0 -66
- snowflake/cli/_plugins/workspace/__init__.py +0 -13
- snowflake/cli/_plugins/workspace/action_context.py +0 -18
- snowflake/cli/_plugins/workspace/commands.py +0 -306
- snowflake/cli/_plugins/workspace/manager.py +0 -74
- snowflake/cli/_plugins/workspace/plugin_spec.py +0 -30
- snowflake/cli/api/__init__.py +0 -48
- snowflake/cli/api/cli_global_context.py +0 -247
- snowflake/cli/api/commands/__init__.py +0 -13
- snowflake/cli/api/commands/alias.py +0 -23
- snowflake/cli/api/commands/common.py +0 -25
- snowflake/cli/api/commands/decorators.py +0 -369
- snowflake/cli/api/commands/execution_metadata.py +0 -40
- snowflake/cli/api/commands/experimental_behaviour.py +0 -18
- snowflake/cli/api/commands/flags.py +0 -561
- snowflake/cli/api/commands/overrideable_parameter.py +0 -143
- snowflake/cli/api/commands/snow_typer.py +0 -247
- snowflake/cli/api/commands/utils.py +0 -18
- snowflake/cli/api/config.py +0 -380
- snowflake/cli/api/connections.py +0 -216
- snowflake/cli/api/console/__init__.py +0 -17
- snowflake/cli/api/console/abc.py +0 -94
- snowflake/cli/api/console/console.py +0 -134
- snowflake/cli/api/console/enum.py +0 -17
- snowflake/cli/api/constants.py +0 -90
- snowflake/cli/api/entities/common.py +0 -56
- snowflake/cli/api/entities/utils.py +0 -370
- snowflake/cli/api/errno.py +0 -28
- snowflake/cli/api/exceptions.py +0 -190
- snowflake/cli/api/feature_flags.py +0 -54
- snowflake/cli/api/identifiers.py +0 -190
- snowflake/cli/api/metrics.py +0 -92
- snowflake/cli/api/output/__init__.py +0 -13
- snowflake/cli/api/output/formats.py +0 -20
- snowflake/cli/api/output/types.py +0 -118
- snowflake/cli/api/plugins/__init__.py +0 -13
- snowflake/cli/api/plugins/command/__init__.py +0 -72
- snowflake/cli/api/plugins/command/plugin_hook_specs.py +0 -21
- snowflake/cli/api/plugins/plugin_config.py +0 -32
- snowflake/cli/api/project/__init__.py +0 -13
- snowflake/cli/api/project/definition.py +0 -126
- snowflake/cli/api/project/definition_conversion.py +0 -395
- snowflake/cli/api/project/definition_manager.py +0 -145
- snowflake/cli/api/project/errors.py +0 -56
- snowflake/cli/api/project/project_verification.py +0 -23
- snowflake/cli/api/project/schemas/__init__.py +0 -13
- snowflake/cli/api/project/schemas/entities/__init__.py +0 -13
- snowflake/cli/api/project/schemas/entities/common.py +0 -153
- snowflake/cli/api/project/schemas/entities/entities.py +0 -61
- snowflake/cli/api/project/schemas/project_definition.py +0 -330
- snowflake/cli/api/project/schemas/template.py +0 -77
- snowflake/cli/api/project/schemas/updatable_model.py +0 -202
- snowflake/cli/api/project/schemas/v1/__init__.py +0 -0
- snowflake/cli/api/project/schemas/v1/identifier_model.py +0 -51
- snowflake/cli/api/project/schemas/v1/native_app/__init__.py +0 -0
- snowflake/cli/api/project/schemas/v1/native_app/application.py +0 -61
- snowflake/cli/api/project/schemas/v1/native_app/native_app.py +0 -93
- snowflake/cli/api/project/schemas/v1/native_app/package.py +0 -84
- snowflake/cli/api/project/schemas/v1/native_app/path_mapping.py +0 -65
- snowflake/cli/api/project/schemas/v1/snowpark/__init__.py +0 -0
- snowflake/cli/api/project/schemas/v1/snowpark/argument.py +0 -28
- snowflake/cli/api/project/schemas/v1/snowpark/callable.py +0 -69
- snowflake/cli/api/project/schemas/v1/snowpark/snowpark.py +0 -36
- snowflake/cli/api/project/schemas/v1/streamlit/__init__.py +0 -0
- snowflake/cli/api/project/schemas/v1/streamlit/streamlit.py +0 -47
- snowflake/cli/api/project/util.py +0 -278
- snowflake/cli/api/rendering/__init__.py +0 -13
- snowflake/cli/api/rendering/jinja.py +0 -118
- snowflake/cli/api/rendering/project_definition_templates.py +0 -43
- snowflake/cli/api/rendering/project_templates.py +0 -98
- snowflake/cli/api/rendering/sql_templates.py +0 -105
- snowflake/cli/api/rest_api.py +0 -178
- snowflake/cli/api/sanitizers.py +0 -43
- snowflake/cli/api/secure_path.py +0 -360
- snowflake/cli/api/secure_utils.py +0 -118
- snowflake/cli/api/sql_execution.py +0 -280
- snowflake/cli/api/utils/__init__.py +0 -13
- snowflake/cli/api/utils/cursor.py +0 -34
- snowflake/cli/api/utils/definition_rendering.py +0 -415
- snowflake/cli/api/utils/dict_utils.py +0 -73
- snowflake/cli/api/utils/error_handling.py +0 -23
- snowflake/cli/api/utils/graph.py +0 -97
- snowflake/cli/api/utils/models.py +0 -63
- snowflake/cli/api/utils/naming_utils.py +0 -13
- snowflake/cli/api/utils/path_utils.py +0 -36
- snowflake/cli/api/utils/templating_functions.py +0 -144
- snowflake/cli/api/utils/types.py +0 -35
- snowflake_cli_labs-3.0.0rc4.dist-info/RECORD +0 -242
- snowflake_cli_labs-3.0.0rc4.dist-info/entry_points.txt +0 -2
- {snowflake_cli_labs-3.0.0rc4.dist-info → snowflake_cli_labs-3.0.1.dist-info}/WHEEL +0 -0
- {snowflake_cli_labs-3.0.0rc4.dist-info → snowflake_cli_labs-3.0.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,878 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import time
|
|
4
|
-
from contextlib import contextmanager
|
|
5
|
-
from datetime import datetime
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from textwrap import dedent
|
|
8
|
-
from typing import Callable, Generator, List, Literal, Optional, TypedDict
|
|
9
|
-
|
|
10
|
-
import typer
|
|
11
|
-
from click import ClickException, UsageError
|
|
12
|
-
from pydantic import Field, field_validator
|
|
13
|
-
from snowflake.cli._plugins.connection.util import make_snowsight_url
|
|
14
|
-
from snowflake.cli._plugins.nativeapp.common_flags import (
|
|
15
|
-
ForceOption,
|
|
16
|
-
InteractiveOption,
|
|
17
|
-
ValidateOption,
|
|
18
|
-
)
|
|
19
|
-
from snowflake.cli._plugins.nativeapp.constants import (
|
|
20
|
-
ALLOWED_SPECIAL_COMMENTS,
|
|
21
|
-
COMMENT_COL,
|
|
22
|
-
NAME_COL,
|
|
23
|
-
OWNER_COL,
|
|
24
|
-
SPECIAL_COMMENT,
|
|
25
|
-
)
|
|
26
|
-
from snowflake.cli._plugins.nativeapp.entities.application_package import (
|
|
27
|
-
ApplicationPackageEntity,
|
|
28
|
-
ApplicationPackageEntityModel,
|
|
29
|
-
)
|
|
30
|
-
from snowflake.cli._plugins.nativeapp.exceptions import (
|
|
31
|
-
ApplicationPackageDoesNotExistError,
|
|
32
|
-
NoEventTableForAccount,
|
|
33
|
-
)
|
|
34
|
-
from snowflake.cli._plugins.nativeapp.policy import (
|
|
35
|
-
AllowAlwaysPolicy,
|
|
36
|
-
AskAlwaysPolicy,
|
|
37
|
-
DenyAlwaysPolicy,
|
|
38
|
-
PolicyBase,
|
|
39
|
-
)
|
|
40
|
-
from snowflake.cli._plugins.nativeapp.same_account_install_method import (
|
|
41
|
-
SameAccountInstallMethod,
|
|
42
|
-
)
|
|
43
|
-
from snowflake.cli._plugins.nativeapp.utils import needs_confirmation
|
|
44
|
-
from snowflake.cli._plugins.workspace.action_context import ActionContext
|
|
45
|
-
from snowflake.cli.api.cli_global_context import get_cli_context
|
|
46
|
-
from snowflake.cli.api.console.abc import AbstractConsole
|
|
47
|
-
from snowflake.cli.api.entities.common import EntityBase, get_sql_executor
|
|
48
|
-
from snowflake.cli.api.entities.utils import (
|
|
49
|
-
drop_generic_object,
|
|
50
|
-
execute_post_deploy_hooks,
|
|
51
|
-
generic_sql_error_handler,
|
|
52
|
-
print_messages,
|
|
53
|
-
)
|
|
54
|
-
from snowflake.cli.api.errno import (
|
|
55
|
-
APPLICATION_NO_LONGER_AVAILABLE,
|
|
56
|
-
CANNOT_UPGRADE_FROM_LOOSE_FILES_TO_VERSION,
|
|
57
|
-
CANNOT_UPGRADE_FROM_VERSION_TO_LOOSE_FILES,
|
|
58
|
-
NOT_SUPPORTED_ON_DEV_MODE_APPLICATIONS,
|
|
59
|
-
ONLY_SUPPORTED_ON_DEV_MODE_APPLICATIONS,
|
|
60
|
-
)
|
|
61
|
-
from snowflake.cli.api.metrics import CLICounterField
|
|
62
|
-
from snowflake.cli.api.project.schemas.entities.common import (
|
|
63
|
-
EntityModelBase,
|
|
64
|
-
Identifier,
|
|
65
|
-
PostDeployHook,
|
|
66
|
-
TargetField,
|
|
67
|
-
)
|
|
68
|
-
from snowflake.cli.api.project.schemas.updatable_model import DiscriminatorField
|
|
69
|
-
from snowflake.cli.api.project.util import (
|
|
70
|
-
append_test_resource_suffix,
|
|
71
|
-
extract_schema,
|
|
72
|
-
identifier_for_url,
|
|
73
|
-
unquote_identifier,
|
|
74
|
-
)
|
|
75
|
-
from snowflake.connector import DictCursor, ProgrammingError
|
|
76
|
-
|
|
77
|
-
# Reasons why an `alter application ... upgrade` might fail
|
|
78
|
-
UPGRADE_RESTRICTION_CODES = {
|
|
79
|
-
CANNOT_UPGRADE_FROM_LOOSE_FILES_TO_VERSION,
|
|
80
|
-
CANNOT_UPGRADE_FROM_VERSION_TO_LOOSE_FILES,
|
|
81
|
-
ONLY_SUPPORTED_ON_DEV_MODE_APPLICATIONS,
|
|
82
|
-
NOT_SUPPORTED_ON_DEV_MODE_APPLICATIONS,
|
|
83
|
-
APPLICATION_NO_LONGER_AVAILABLE,
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
ApplicationOwnedObject = TypedDict("ApplicationOwnedObject", {"name": str, "type": str})
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
class ApplicationEntityModel(EntityModelBase):
|
|
90
|
-
type: Literal["application"] = DiscriminatorField() # noqa A003
|
|
91
|
-
from_: TargetField[ApplicationPackageEntityModel] = Field(
|
|
92
|
-
alias="from",
|
|
93
|
-
title="An application package this entity should be created from",
|
|
94
|
-
)
|
|
95
|
-
debug: Optional[bool] = Field(
|
|
96
|
-
title="Whether to enable debug mode when using a named stage to create an application object",
|
|
97
|
-
default=None,
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
@field_validator("identifier")
|
|
101
|
-
@classmethod
|
|
102
|
-
def append_test_resource_suffix_to_identifier(
|
|
103
|
-
cls, input_value: Identifier | str
|
|
104
|
-
) -> Identifier | str:
|
|
105
|
-
identifier = (
|
|
106
|
-
input_value.name if isinstance(input_value, Identifier) else input_value
|
|
107
|
-
)
|
|
108
|
-
with_suffix = append_test_resource_suffix(identifier)
|
|
109
|
-
if isinstance(input_value, Identifier):
|
|
110
|
-
return input_value.model_copy(update=dict(name=with_suffix))
|
|
111
|
-
return with_suffix
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
class ApplicationEntity(EntityBase[ApplicationEntityModel]):
|
|
115
|
-
"""
|
|
116
|
-
A Native App application object, created from an application package.
|
|
117
|
-
"""
|
|
118
|
-
|
|
119
|
-
def action_deploy(
|
|
120
|
-
self,
|
|
121
|
-
ctx: ActionContext,
|
|
122
|
-
from_release_directive: bool,
|
|
123
|
-
prune: bool,
|
|
124
|
-
recursive: bool,
|
|
125
|
-
paths: List[Path],
|
|
126
|
-
validate: bool = ValidateOption,
|
|
127
|
-
stage_fqn: Optional[str] = None,
|
|
128
|
-
interactive: bool = InteractiveOption,
|
|
129
|
-
version: Optional[str] = None,
|
|
130
|
-
patch: Optional[int] = None,
|
|
131
|
-
force: Optional[bool] = ForceOption,
|
|
132
|
-
*args,
|
|
133
|
-
**kwargs,
|
|
134
|
-
):
|
|
135
|
-
model = self._entity_model
|
|
136
|
-
app_name = model.fqn.identifier
|
|
137
|
-
debug_mode = model.debug
|
|
138
|
-
if model.meta:
|
|
139
|
-
app_role = model.meta.role or ctx.default_role
|
|
140
|
-
app_warehouse = model.meta.warehouse or ctx.default_warehouse
|
|
141
|
-
post_deploy_hooks = model.meta.post_deploy
|
|
142
|
-
else:
|
|
143
|
-
app_role = ctx.default_role
|
|
144
|
-
app_warehouse = ctx.default_warehouse
|
|
145
|
-
post_deploy_hooks = None
|
|
146
|
-
|
|
147
|
-
package_entity: ApplicationPackageEntity = ctx.get_entity(model.from_.target)
|
|
148
|
-
package_model: ApplicationPackageEntityModel = (
|
|
149
|
-
package_entity._entity_model # noqa: SLF001
|
|
150
|
-
)
|
|
151
|
-
package_name = package_model.fqn.identifier
|
|
152
|
-
if package_model.meta and package_model.meta.role:
|
|
153
|
-
package_role = package_model.meta.role
|
|
154
|
-
else:
|
|
155
|
-
package_role = ctx.default_role
|
|
156
|
-
|
|
157
|
-
if not stage_fqn:
|
|
158
|
-
stage_fqn = f"{package_name}.{package_model.stage}"
|
|
159
|
-
stage_schema = extract_schema(stage_fqn)
|
|
160
|
-
|
|
161
|
-
is_interactive = False
|
|
162
|
-
if force:
|
|
163
|
-
policy = AllowAlwaysPolicy()
|
|
164
|
-
elif interactive:
|
|
165
|
-
is_interactive = True
|
|
166
|
-
policy = AskAlwaysPolicy()
|
|
167
|
-
else:
|
|
168
|
-
policy = DenyAlwaysPolicy()
|
|
169
|
-
|
|
170
|
-
def deploy_package():
|
|
171
|
-
package_entity.action_deploy(
|
|
172
|
-
ctx=ctx,
|
|
173
|
-
prune=True,
|
|
174
|
-
recursive=True,
|
|
175
|
-
paths=[],
|
|
176
|
-
validate=validate,
|
|
177
|
-
stage_fqn=stage_fqn,
|
|
178
|
-
interactive=interactive,
|
|
179
|
-
force=force,
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
self.deploy(
|
|
183
|
-
console=ctx.console,
|
|
184
|
-
project_root=ctx.project_root,
|
|
185
|
-
app_name=app_name,
|
|
186
|
-
app_role=app_role,
|
|
187
|
-
app_warehouse=app_warehouse,
|
|
188
|
-
package_name=package_name,
|
|
189
|
-
package_role=package_role,
|
|
190
|
-
stage_schema=stage_schema,
|
|
191
|
-
stage_fqn=stage_fqn,
|
|
192
|
-
debug_mode=debug_mode,
|
|
193
|
-
validate=validate,
|
|
194
|
-
from_release_directive=from_release_directive,
|
|
195
|
-
is_interactive=is_interactive,
|
|
196
|
-
policy=policy,
|
|
197
|
-
version=version,
|
|
198
|
-
patch=patch,
|
|
199
|
-
post_deploy_hooks=post_deploy_hooks,
|
|
200
|
-
deploy_package=deploy_package,
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
def action_drop(
|
|
204
|
-
self,
|
|
205
|
-
ctx: ActionContext,
|
|
206
|
-
interactive: bool,
|
|
207
|
-
force_drop: bool = False,
|
|
208
|
-
cascade: Optional[bool] = None,
|
|
209
|
-
*args,
|
|
210
|
-
**kwargs,
|
|
211
|
-
):
|
|
212
|
-
model = self._entity_model
|
|
213
|
-
app_name = model.fqn.identifier
|
|
214
|
-
if model.meta and model.meta.role:
|
|
215
|
-
app_role = model.meta.role
|
|
216
|
-
else:
|
|
217
|
-
app_role = ctx.default_role
|
|
218
|
-
self.drop(
|
|
219
|
-
console=ctx.console,
|
|
220
|
-
app_name=app_name,
|
|
221
|
-
app_role=app_role,
|
|
222
|
-
auto_yes=force_drop,
|
|
223
|
-
interactive=interactive,
|
|
224
|
-
cascade=cascade,
|
|
225
|
-
)
|
|
226
|
-
|
|
227
|
-
@classmethod
|
|
228
|
-
def drop(
|
|
229
|
-
cls,
|
|
230
|
-
console: AbstractConsole,
|
|
231
|
-
app_name: str,
|
|
232
|
-
app_role: str,
|
|
233
|
-
auto_yes: bool,
|
|
234
|
-
interactive: bool = False,
|
|
235
|
-
cascade: Optional[bool] = None,
|
|
236
|
-
):
|
|
237
|
-
"""
|
|
238
|
-
Attempts to drop the application object if all validations and user prompts allow so.
|
|
239
|
-
"""
|
|
240
|
-
|
|
241
|
-
needs_confirm = True
|
|
242
|
-
|
|
243
|
-
# 1. If existing application is not found, exit gracefully
|
|
244
|
-
show_obj_row = cls.get_existing_app_info(
|
|
245
|
-
app_name=app_name,
|
|
246
|
-
app_role=app_role,
|
|
247
|
-
)
|
|
248
|
-
if show_obj_row is None:
|
|
249
|
-
console.warning(
|
|
250
|
-
f"Role {app_role} does not own any application object with the name {app_name}, or the application object does not exist."
|
|
251
|
-
)
|
|
252
|
-
return
|
|
253
|
-
|
|
254
|
-
# 2. Check if created by the Snowflake CLI
|
|
255
|
-
row_comment = show_obj_row[COMMENT_COL]
|
|
256
|
-
if row_comment not in ALLOWED_SPECIAL_COMMENTS and needs_confirmation(
|
|
257
|
-
needs_confirm, auto_yes
|
|
258
|
-
):
|
|
259
|
-
should_drop_object = typer.confirm(
|
|
260
|
-
dedent(
|
|
261
|
-
f"""\
|
|
262
|
-
Application object {app_name} was not created by Snowflake CLI.
|
|
263
|
-
Application object details:
|
|
264
|
-
Name: {app_name}
|
|
265
|
-
Created on: {show_obj_row["created_on"]}
|
|
266
|
-
Source: {show_obj_row["source"]}
|
|
267
|
-
Owner: {show_obj_row[OWNER_COL]}
|
|
268
|
-
Comment: {show_obj_row[COMMENT_COL]}
|
|
269
|
-
Version: {show_obj_row["version"]}
|
|
270
|
-
Patch: {show_obj_row["patch"]}
|
|
271
|
-
Are you sure you want to drop it?
|
|
272
|
-
"""
|
|
273
|
-
)
|
|
274
|
-
)
|
|
275
|
-
if not should_drop_object:
|
|
276
|
-
console.message(f"Did not drop application object {app_name}.")
|
|
277
|
-
# The user desires to keep the app, therefore we can't proceed since it would
|
|
278
|
-
# leave behind an orphan app when we get to dropping the package
|
|
279
|
-
raise typer.Abort()
|
|
280
|
-
|
|
281
|
-
# 3. Check for application objects owned by the application
|
|
282
|
-
# This query will fail if the application package has already been dropped, so handle this case gracefully
|
|
283
|
-
has_objects_to_drop = False
|
|
284
|
-
message_prefix = ""
|
|
285
|
-
cascade_true_message = ""
|
|
286
|
-
cascade_false_message = ""
|
|
287
|
-
interactive_prompt = ""
|
|
288
|
-
non_interactive_abort = ""
|
|
289
|
-
try:
|
|
290
|
-
if application_objects := cls.get_objects_owned_by_application(
|
|
291
|
-
app_name=app_name,
|
|
292
|
-
app_role=app_role,
|
|
293
|
-
):
|
|
294
|
-
has_objects_to_drop = True
|
|
295
|
-
message_prefix = (
|
|
296
|
-
f"The following objects are owned by application {app_name}"
|
|
297
|
-
)
|
|
298
|
-
cascade_true_message = f"{message_prefix} and will be dropped:"
|
|
299
|
-
cascade_false_message = f"{message_prefix} and will NOT be dropped:"
|
|
300
|
-
interactive_prompt = "Would you like to drop these objects in addition to the application? [y/n/ABORT]"
|
|
301
|
-
non_interactive_abort = "Re-run teardown again with --cascade or --no-cascade to specify whether these objects should be dropped along with the application"
|
|
302
|
-
except ProgrammingError as e:
|
|
303
|
-
if e.errno != APPLICATION_NO_LONGER_AVAILABLE:
|
|
304
|
-
raise
|
|
305
|
-
application_objects = []
|
|
306
|
-
message_prefix = (
|
|
307
|
-
f"Could not determine which objects are owned by application {app_name}"
|
|
308
|
-
)
|
|
309
|
-
has_objects_to_drop = True # potentially, but we don't know what they are
|
|
310
|
-
cascade_true_message = (
|
|
311
|
-
f"{message_prefix}, an unknown number of objects will be dropped."
|
|
312
|
-
)
|
|
313
|
-
cascade_false_message = f"{message_prefix}, they will NOT be dropped."
|
|
314
|
-
interactive_prompt = f"Would you like to drop an unknown set of objects in addition to the application? [y/n/ABORT]"
|
|
315
|
-
non_interactive_abort = f"Re-run teardown again with --cascade or --no-cascade to specify whether any objects should be dropped along with the application."
|
|
316
|
-
|
|
317
|
-
if has_objects_to_drop:
|
|
318
|
-
if cascade is True:
|
|
319
|
-
# If the user explicitly passed the --cascade flag
|
|
320
|
-
console.message(cascade_true_message)
|
|
321
|
-
with console.indented():
|
|
322
|
-
for obj in application_objects:
|
|
323
|
-
console.message(cls.application_object_to_str(obj))
|
|
324
|
-
elif cascade is False:
|
|
325
|
-
# If the user explicitly passed the --no-cascade flag
|
|
326
|
-
console.message(cascade_false_message)
|
|
327
|
-
with console.indented():
|
|
328
|
-
for obj in application_objects:
|
|
329
|
-
console.message(cls.application_object_to_str(obj))
|
|
330
|
-
elif interactive:
|
|
331
|
-
# If the user didn't pass any cascade flag and the session is interactive
|
|
332
|
-
console.message(message_prefix)
|
|
333
|
-
with console.indented():
|
|
334
|
-
for obj in application_objects:
|
|
335
|
-
console.message(cls.application_object_to_str(obj))
|
|
336
|
-
user_response = typer.prompt(
|
|
337
|
-
interactive_prompt,
|
|
338
|
-
show_default=False,
|
|
339
|
-
default="ABORT",
|
|
340
|
-
).lower()
|
|
341
|
-
if user_response in ["y", "yes"]:
|
|
342
|
-
cascade = True
|
|
343
|
-
elif user_response in ["n", "no"]:
|
|
344
|
-
cascade = False
|
|
345
|
-
else:
|
|
346
|
-
raise typer.Abort()
|
|
347
|
-
else:
|
|
348
|
-
# Else abort since we don't know what to do and can't ask the user
|
|
349
|
-
console.message(message_prefix)
|
|
350
|
-
with console.indented():
|
|
351
|
-
for obj in application_objects:
|
|
352
|
-
console.message(cls.application_object_to_str(obj))
|
|
353
|
-
console.message(non_interactive_abort)
|
|
354
|
-
raise typer.Abort()
|
|
355
|
-
elif cascade is None:
|
|
356
|
-
# If there's nothing to drop, set cascade to an explicit False value
|
|
357
|
-
cascade = False
|
|
358
|
-
|
|
359
|
-
# 4. All validations have passed, drop object
|
|
360
|
-
drop_generic_object(
|
|
361
|
-
console=console,
|
|
362
|
-
object_type="application",
|
|
363
|
-
object_name=app_name,
|
|
364
|
-
role=app_role,
|
|
365
|
-
cascade=cascade,
|
|
366
|
-
)
|
|
367
|
-
return # The application object was successfully dropped, therefore exit gracefully
|
|
368
|
-
|
|
369
|
-
@staticmethod
|
|
370
|
-
def get_objects_owned_by_application(
|
|
371
|
-
app_name: str,
|
|
372
|
-
app_role: str,
|
|
373
|
-
) -> List[ApplicationOwnedObject]:
|
|
374
|
-
"""
|
|
375
|
-
Returns all application objects owned by this application.
|
|
376
|
-
"""
|
|
377
|
-
sql_executor = get_sql_executor()
|
|
378
|
-
with sql_executor.use_role(app_role):
|
|
379
|
-
results = sql_executor.execute_query(
|
|
380
|
-
f"show objects owned by application {app_name}"
|
|
381
|
-
).fetchall()
|
|
382
|
-
return [{"name": row[1], "type": row[2]} for row in results]
|
|
383
|
-
|
|
384
|
-
@classmethod
|
|
385
|
-
def application_objects_to_str(
|
|
386
|
-
cls, application_objects: list[ApplicationOwnedObject]
|
|
387
|
-
) -> str:
|
|
388
|
-
"""
|
|
389
|
-
Returns a list in an "(Object Type) Object Name" format. Database-level and schema-level object names are fully qualified:
|
|
390
|
-
(COMPUTE_POOL) POOL_NAME
|
|
391
|
-
(DATABASE) DB_NAME
|
|
392
|
-
(SCHEMA) DB_NAME.PUBLIC
|
|
393
|
-
...
|
|
394
|
-
"""
|
|
395
|
-
return "\n".join(
|
|
396
|
-
[cls.application_object_to_str(obj) for obj in application_objects]
|
|
397
|
-
)
|
|
398
|
-
|
|
399
|
-
@staticmethod
|
|
400
|
-
def application_object_to_str(obj: ApplicationOwnedObject) -> str:
|
|
401
|
-
return f"({obj['type']}) {obj['name']}"
|
|
402
|
-
|
|
403
|
-
@classmethod
|
|
404
|
-
def deploy(
|
|
405
|
-
cls,
|
|
406
|
-
console: AbstractConsole,
|
|
407
|
-
project_root: Path,
|
|
408
|
-
app_name: str,
|
|
409
|
-
app_role: str,
|
|
410
|
-
app_warehouse: str,
|
|
411
|
-
package_name: str,
|
|
412
|
-
package_role: str,
|
|
413
|
-
stage_schema: str,
|
|
414
|
-
stage_fqn: str,
|
|
415
|
-
debug_mode: bool,
|
|
416
|
-
validate: bool,
|
|
417
|
-
from_release_directive: bool,
|
|
418
|
-
is_interactive: bool,
|
|
419
|
-
policy: PolicyBase,
|
|
420
|
-
deploy_package: Callable,
|
|
421
|
-
version: Optional[str] = None,
|
|
422
|
-
patch: Optional[int] = None,
|
|
423
|
-
post_deploy_hooks: Optional[List[PostDeployHook]] = None,
|
|
424
|
-
drop_application_before_upgrade: Optional[Callable] = None,
|
|
425
|
-
):
|
|
426
|
-
"""
|
|
427
|
-
Create or upgrade the application object using the given strategy
|
|
428
|
-
(unversioned dev, versioned dev, or same-account release directive).
|
|
429
|
-
"""
|
|
430
|
-
|
|
431
|
-
# same-account release directive
|
|
432
|
-
if from_release_directive:
|
|
433
|
-
cls.create_or_upgrade_app(
|
|
434
|
-
console=console,
|
|
435
|
-
project_root=project_root,
|
|
436
|
-
package_name=package_name,
|
|
437
|
-
package_role=package_role,
|
|
438
|
-
app_name=app_name,
|
|
439
|
-
app_role=app_role,
|
|
440
|
-
app_warehouse=app_warehouse,
|
|
441
|
-
stage_schema=stage_schema,
|
|
442
|
-
stage_fqn=stage_fqn,
|
|
443
|
-
debug_mode=debug_mode,
|
|
444
|
-
policy=policy,
|
|
445
|
-
install_method=SameAccountInstallMethod.release_directive(),
|
|
446
|
-
is_interactive=is_interactive,
|
|
447
|
-
post_deploy_hooks=post_deploy_hooks,
|
|
448
|
-
drop_application_before_upgrade=drop_application_before_upgrade,
|
|
449
|
-
)
|
|
450
|
-
return
|
|
451
|
-
|
|
452
|
-
# versioned dev
|
|
453
|
-
if version:
|
|
454
|
-
try:
|
|
455
|
-
version_exists = ApplicationPackageEntity.get_existing_version_info(
|
|
456
|
-
version=version,
|
|
457
|
-
package_name=package_name,
|
|
458
|
-
package_role=package_role,
|
|
459
|
-
)
|
|
460
|
-
if not version_exists:
|
|
461
|
-
raise UsageError(
|
|
462
|
-
f"Application package {package_name} does not have any version {version} defined. Use 'snow app version create' to define a version in the application package first."
|
|
463
|
-
)
|
|
464
|
-
except ApplicationPackageDoesNotExistError as app_err:
|
|
465
|
-
raise UsageError(
|
|
466
|
-
f"Application package {package_name} does not exist. Use 'snow app version create' to first create an application package and then define a version in it."
|
|
467
|
-
)
|
|
468
|
-
|
|
469
|
-
cls.create_or_upgrade_app(
|
|
470
|
-
console=console,
|
|
471
|
-
project_root=project_root,
|
|
472
|
-
package_name=package_name,
|
|
473
|
-
package_role=package_role,
|
|
474
|
-
app_name=app_name,
|
|
475
|
-
app_role=app_role,
|
|
476
|
-
app_warehouse=app_warehouse,
|
|
477
|
-
stage_schema=stage_schema,
|
|
478
|
-
stage_fqn=stage_fqn,
|
|
479
|
-
debug_mode=debug_mode,
|
|
480
|
-
policy=policy,
|
|
481
|
-
install_method=SameAccountInstallMethod.versioned_dev(version, patch),
|
|
482
|
-
is_interactive=is_interactive,
|
|
483
|
-
post_deploy_hooks=post_deploy_hooks,
|
|
484
|
-
drop_application_before_upgrade=drop_application_before_upgrade,
|
|
485
|
-
)
|
|
486
|
-
return
|
|
487
|
-
|
|
488
|
-
# unversioned dev
|
|
489
|
-
deploy_package()
|
|
490
|
-
cls.create_or_upgrade_app(
|
|
491
|
-
console=console,
|
|
492
|
-
project_root=project_root,
|
|
493
|
-
package_name=package_name,
|
|
494
|
-
package_role=package_role,
|
|
495
|
-
app_name=app_name,
|
|
496
|
-
app_role=app_role,
|
|
497
|
-
app_warehouse=app_warehouse,
|
|
498
|
-
stage_schema=stage_schema,
|
|
499
|
-
stage_fqn=stage_fqn,
|
|
500
|
-
debug_mode=debug_mode,
|
|
501
|
-
policy=policy,
|
|
502
|
-
install_method=SameAccountInstallMethod.unversioned_dev(),
|
|
503
|
-
is_interactive=is_interactive,
|
|
504
|
-
post_deploy_hooks=post_deploy_hooks,
|
|
505
|
-
drop_application_before_upgrade=drop_application_before_upgrade,
|
|
506
|
-
)
|
|
507
|
-
|
|
508
|
-
@classmethod
|
|
509
|
-
def create_or_upgrade_app(
|
|
510
|
-
cls,
|
|
511
|
-
console: AbstractConsole,
|
|
512
|
-
project_root: Path,
|
|
513
|
-
package_name: str,
|
|
514
|
-
package_role: str,
|
|
515
|
-
app_name: str,
|
|
516
|
-
app_role: str,
|
|
517
|
-
app_warehouse: Optional[str],
|
|
518
|
-
stage_schema: Optional[str],
|
|
519
|
-
stage_fqn: str,
|
|
520
|
-
debug_mode: bool,
|
|
521
|
-
policy: PolicyBase,
|
|
522
|
-
install_method: SameAccountInstallMethod,
|
|
523
|
-
is_interactive: bool = False,
|
|
524
|
-
post_deploy_hooks: Optional[List[PostDeployHook]] = None,
|
|
525
|
-
drop_application_before_upgrade: Optional[Callable] = None,
|
|
526
|
-
):
|
|
527
|
-
sql_executor = get_sql_executor()
|
|
528
|
-
with sql_executor.use_role(app_role):
|
|
529
|
-
|
|
530
|
-
# 1. Need to use a warehouse to create an application object
|
|
531
|
-
with sql_executor.use_warehouse(app_warehouse):
|
|
532
|
-
|
|
533
|
-
# 2. Check for an existing application by the same name
|
|
534
|
-
show_app_row = cls.get_existing_app_info(
|
|
535
|
-
app_name=app_name,
|
|
536
|
-
app_role=app_role,
|
|
537
|
-
)
|
|
538
|
-
|
|
539
|
-
# 3. If existing application is found, perform a few validations and upgrade the application object.
|
|
540
|
-
if show_app_row:
|
|
541
|
-
|
|
542
|
-
install_method.ensure_app_usable(
|
|
543
|
-
app_name=app_name,
|
|
544
|
-
app_role=app_role,
|
|
545
|
-
show_app_row=show_app_row,
|
|
546
|
-
)
|
|
547
|
-
|
|
548
|
-
# If all the above checks are in order, proceed to upgrade
|
|
549
|
-
try:
|
|
550
|
-
console.step(
|
|
551
|
-
f"Upgrading existing application object {app_name}."
|
|
552
|
-
)
|
|
553
|
-
using_clause = install_method.using_clause(stage_fqn)
|
|
554
|
-
upgrade_cursor = sql_executor.execute_query(
|
|
555
|
-
f"alter application {app_name} upgrade {using_clause}",
|
|
556
|
-
)
|
|
557
|
-
print_messages(console, upgrade_cursor)
|
|
558
|
-
|
|
559
|
-
if install_method.is_dev_mode:
|
|
560
|
-
# if debug_mode is present (controlled), ensure it is up-to-date
|
|
561
|
-
if debug_mode is not None:
|
|
562
|
-
sql_executor.execute_query(
|
|
563
|
-
f"alter application {app_name} set debug_mode = {debug_mode}"
|
|
564
|
-
)
|
|
565
|
-
|
|
566
|
-
# hooks always executed after a create or upgrade
|
|
567
|
-
cls.execute_post_deploy_hooks(
|
|
568
|
-
console=console,
|
|
569
|
-
project_root=project_root,
|
|
570
|
-
post_deploy_hooks=post_deploy_hooks,
|
|
571
|
-
app_name=app_name,
|
|
572
|
-
app_warehouse=app_warehouse,
|
|
573
|
-
)
|
|
574
|
-
return
|
|
575
|
-
|
|
576
|
-
except ProgrammingError as err:
|
|
577
|
-
if err.errno not in UPGRADE_RESTRICTION_CODES:
|
|
578
|
-
generic_sql_error_handler(err=err)
|
|
579
|
-
else: # The existing application object was created from a different process.
|
|
580
|
-
console.warning(err.msg)
|
|
581
|
-
# TODO Drop the entity here instead of taking a callback once action_drop() is implemented
|
|
582
|
-
if drop_application_before_upgrade:
|
|
583
|
-
drop_application_before_upgrade()
|
|
584
|
-
else:
|
|
585
|
-
raise NotImplementedError
|
|
586
|
-
|
|
587
|
-
# 4. With no (more) existing application objects, create an application object using the release directives
|
|
588
|
-
console.step(f"Creating new application object {app_name} in account.")
|
|
589
|
-
|
|
590
|
-
if app_role != package_role:
|
|
591
|
-
with sql_executor.use_role(package_role):
|
|
592
|
-
sql_executor.execute_query(
|
|
593
|
-
f"grant install, develop on application package {package_name} to role {app_role}"
|
|
594
|
-
)
|
|
595
|
-
sql_executor.execute_query(
|
|
596
|
-
f"grant usage on schema {package_name}.{stage_schema} to role {app_role}"
|
|
597
|
-
)
|
|
598
|
-
sql_executor.execute_query(
|
|
599
|
-
f"grant read on stage {stage_fqn} to role {app_role}"
|
|
600
|
-
)
|
|
601
|
-
|
|
602
|
-
try:
|
|
603
|
-
# by default, applications are created in debug mode when possible;
|
|
604
|
-
# this can be overridden in the project definition
|
|
605
|
-
debug_mode_clause = ""
|
|
606
|
-
if install_method.is_dev_mode:
|
|
607
|
-
initial_debug_mode = (
|
|
608
|
-
debug_mode if debug_mode is not None else True
|
|
609
|
-
)
|
|
610
|
-
debug_mode_clause = f"debug_mode = {initial_debug_mode}"
|
|
611
|
-
|
|
612
|
-
using_clause = install_method.using_clause(stage_fqn)
|
|
613
|
-
create_cursor = sql_executor.execute_query(
|
|
614
|
-
dedent(
|
|
615
|
-
f"""\
|
|
616
|
-
create application {app_name}
|
|
617
|
-
from application package {package_name} {using_clause} {debug_mode_clause}
|
|
618
|
-
comment = {SPECIAL_COMMENT}
|
|
619
|
-
"""
|
|
620
|
-
),
|
|
621
|
-
)
|
|
622
|
-
print_messages(console, create_cursor)
|
|
623
|
-
|
|
624
|
-
# hooks always executed after a create or upgrade
|
|
625
|
-
cls.execute_post_deploy_hooks(
|
|
626
|
-
console=console,
|
|
627
|
-
project_root=project_root,
|
|
628
|
-
post_deploy_hooks=post_deploy_hooks,
|
|
629
|
-
app_name=app_name,
|
|
630
|
-
app_warehouse=app_warehouse,
|
|
631
|
-
)
|
|
632
|
-
|
|
633
|
-
except ProgrammingError as err:
|
|
634
|
-
generic_sql_error_handler(err)
|
|
635
|
-
|
|
636
|
-
@classmethod
|
|
637
|
-
def execute_post_deploy_hooks(
|
|
638
|
-
cls,
|
|
639
|
-
console: AbstractConsole,
|
|
640
|
-
project_root: Path,
|
|
641
|
-
post_deploy_hooks: Optional[List[PostDeployHook]],
|
|
642
|
-
app_name: str,
|
|
643
|
-
app_warehouse: Optional[str],
|
|
644
|
-
):
|
|
645
|
-
get_cli_context().metrics.set_counter_default(
|
|
646
|
-
CLICounterField.POST_DEPLOY_SCRIPTS, 0
|
|
647
|
-
)
|
|
648
|
-
|
|
649
|
-
if post_deploy_hooks:
|
|
650
|
-
with cls.use_application_warehouse(app_warehouse):
|
|
651
|
-
execute_post_deploy_hooks(
|
|
652
|
-
console=console,
|
|
653
|
-
project_root=project_root,
|
|
654
|
-
post_deploy_hooks=post_deploy_hooks,
|
|
655
|
-
deployed_object_type="application",
|
|
656
|
-
database_name=app_name,
|
|
657
|
-
)
|
|
658
|
-
|
|
659
|
-
@staticmethod
|
|
660
|
-
@contextmanager
|
|
661
|
-
def use_application_warehouse(
|
|
662
|
-
app_warehouse: Optional[str],
|
|
663
|
-
):
|
|
664
|
-
if app_warehouse:
|
|
665
|
-
with get_sql_executor().use_warehouse(app_warehouse):
|
|
666
|
-
yield
|
|
667
|
-
else:
|
|
668
|
-
raise ClickException(
|
|
669
|
-
dedent(
|
|
670
|
-
f"""\
|
|
671
|
-
Application warehouse cannot be empty.
|
|
672
|
-
Please provide a value for it in your connection information or your project definition file.
|
|
673
|
-
"""
|
|
674
|
-
)
|
|
675
|
-
)
|
|
676
|
-
|
|
677
|
-
@staticmethod
|
|
678
|
-
def get_existing_app_info(
|
|
679
|
-
app_name: str,
|
|
680
|
-
app_role: str,
|
|
681
|
-
) -> Optional[dict]:
|
|
682
|
-
"""
|
|
683
|
-
Check for an existing application object by the same name as in project definition, in account.
|
|
684
|
-
It executes a 'show applications like' query and returns the result as single row, if one exists.
|
|
685
|
-
"""
|
|
686
|
-
sql_executor = get_sql_executor()
|
|
687
|
-
with sql_executor.use_role(app_role):
|
|
688
|
-
return sql_executor.show_specific_object(
|
|
689
|
-
"applications", app_name, name_col=NAME_COL
|
|
690
|
-
)
|
|
691
|
-
|
|
692
|
-
@classmethod
|
|
693
|
-
def get_events(
|
|
694
|
-
cls,
|
|
695
|
-
app_name: str,
|
|
696
|
-
package_name: str,
|
|
697
|
-
since: str | datetime | None = None,
|
|
698
|
-
until: str | datetime | None = None,
|
|
699
|
-
record_types: list[str] | None = None,
|
|
700
|
-
scopes: list[str] | None = None,
|
|
701
|
-
consumer_org: str = "",
|
|
702
|
-
consumer_account: str = "",
|
|
703
|
-
consumer_app_hash: str = "",
|
|
704
|
-
first: int = -1,
|
|
705
|
-
last: int = -1,
|
|
706
|
-
):
|
|
707
|
-
|
|
708
|
-
record_types = record_types or []
|
|
709
|
-
scopes = scopes or []
|
|
710
|
-
|
|
711
|
-
if first >= 0 and last >= 0:
|
|
712
|
-
raise ValueError("first and last cannot be used together")
|
|
713
|
-
|
|
714
|
-
account_event_table = cls.get_account_event_table()
|
|
715
|
-
if not account_event_table:
|
|
716
|
-
raise NoEventTableForAccount()
|
|
717
|
-
|
|
718
|
-
# resource_attributes uses the unquoted/uppercase app and package name
|
|
719
|
-
app_name = unquote_identifier(app_name)
|
|
720
|
-
package_name = unquote_identifier(package_name)
|
|
721
|
-
org_name = unquote_identifier(consumer_org)
|
|
722
|
-
account_name = unquote_identifier(consumer_account)
|
|
723
|
-
|
|
724
|
-
# Filter on record attributes
|
|
725
|
-
if consumer_org and consumer_account:
|
|
726
|
-
# Look for events shared from a consumer account
|
|
727
|
-
app_clause = (
|
|
728
|
-
f"resource_attributes:\"snow.application.package.name\" = '{package_name}' "
|
|
729
|
-
f"and resource_attributes:\"snow.application.consumer.organization\" = '{org_name}' "
|
|
730
|
-
f"and resource_attributes:\"snow.application.consumer.name\" = '{account_name}'"
|
|
731
|
-
)
|
|
732
|
-
if consumer_app_hash:
|
|
733
|
-
# If the user has specified a hash of a specific app installation
|
|
734
|
-
# in the consumer account, filter events to that installation only
|
|
735
|
-
app_clause += f" and resource_attributes:\"snow.database.hash\" = '{consumer_app_hash.lower()}'"
|
|
736
|
-
else:
|
|
737
|
-
# Otherwise look for events from an app installed in the same account as the package
|
|
738
|
-
app_clause = f"resource_attributes:\"snow.database.name\" = '{app_name}'"
|
|
739
|
-
|
|
740
|
-
# Filter on event time
|
|
741
|
-
if isinstance(since, datetime):
|
|
742
|
-
since_clause = f"and timestamp >= '{since}'"
|
|
743
|
-
elif isinstance(since, str) and since:
|
|
744
|
-
since_clause = f"and timestamp >= sysdate() - interval '{since}'"
|
|
745
|
-
else:
|
|
746
|
-
since_clause = ""
|
|
747
|
-
if isinstance(until, datetime):
|
|
748
|
-
until_clause = f"and timestamp <= '{until}'"
|
|
749
|
-
elif isinstance(until, str) and until:
|
|
750
|
-
until_clause = f"and timestamp <= sysdate() - interval '{until}'"
|
|
751
|
-
else:
|
|
752
|
-
until_clause = ""
|
|
753
|
-
|
|
754
|
-
# Filter on event type (log, span, span_event)
|
|
755
|
-
type_in_values = ",".join(f"'{v}'" for v in record_types)
|
|
756
|
-
types_clause = (
|
|
757
|
-
f"and record_type in ({type_in_values})" if type_in_values else ""
|
|
758
|
-
)
|
|
759
|
-
|
|
760
|
-
# Filter on event scope (e.g. the logger name)
|
|
761
|
-
scope_in_values = ",".join(f"'{v}'" for v in scopes)
|
|
762
|
-
scopes_clause = (
|
|
763
|
-
f"and scope:name in ({scope_in_values})" if scope_in_values else ""
|
|
764
|
-
)
|
|
765
|
-
|
|
766
|
-
# Limit event count
|
|
767
|
-
first_clause = f"limit {first}" if first >= 0 else ""
|
|
768
|
-
last_clause = f"limit {last}" if last >= 0 else ""
|
|
769
|
-
|
|
770
|
-
query = dedent(
|
|
771
|
-
f"""\
|
|
772
|
-
select * from (
|
|
773
|
-
select timestamp, value::varchar value
|
|
774
|
-
from {account_event_table}
|
|
775
|
-
where ({app_clause})
|
|
776
|
-
{since_clause}
|
|
777
|
-
{until_clause}
|
|
778
|
-
{types_clause}
|
|
779
|
-
{scopes_clause}
|
|
780
|
-
order by timestamp desc
|
|
781
|
-
{last_clause}
|
|
782
|
-
) order by timestamp asc
|
|
783
|
-
{first_clause}
|
|
784
|
-
"""
|
|
785
|
-
)
|
|
786
|
-
sql_executor = get_sql_executor()
|
|
787
|
-
try:
|
|
788
|
-
return sql_executor.execute_query(query, cursor_class=DictCursor).fetchall()
|
|
789
|
-
except ProgrammingError as err:
|
|
790
|
-
generic_sql_error_handler(err)
|
|
791
|
-
|
|
792
|
-
@classmethod
|
|
793
|
-
def stream_events(
|
|
794
|
-
cls,
|
|
795
|
-
app_name: str,
|
|
796
|
-
package_name: str,
|
|
797
|
-
interval_seconds: int,
|
|
798
|
-
since: str | datetime | None = None,
|
|
799
|
-
record_types: list[str] | None = None,
|
|
800
|
-
scopes: list[str] | None = None,
|
|
801
|
-
consumer_org: str = "",
|
|
802
|
-
consumer_account: str = "",
|
|
803
|
-
consumer_app_hash: str = "",
|
|
804
|
-
last: int = -1,
|
|
805
|
-
) -> Generator[dict, None, None]:
|
|
806
|
-
try:
|
|
807
|
-
events = cls.get_events(
|
|
808
|
-
app_name=app_name,
|
|
809
|
-
package_name=package_name,
|
|
810
|
-
since=since,
|
|
811
|
-
record_types=record_types,
|
|
812
|
-
scopes=scopes,
|
|
813
|
-
consumer_org=consumer_org,
|
|
814
|
-
consumer_account=consumer_account,
|
|
815
|
-
consumer_app_hash=consumer_app_hash,
|
|
816
|
-
last=last,
|
|
817
|
-
)
|
|
818
|
-
yield from events # Yield the initial batch of events
|
|
819
|
-
last_event_time = events[-1]["TIMESTAMP"] if events else None
|
|
820
|
-
|
|
821
|
-
while True: # Then infinite poll for new events
|
|
822
|
-
time.sleep(interval_seconds)
|
|
823
|
-
previous_events = events
|
|
824
|
-
events = cls.get_events(
|
|
825
|
-
app_name=app_name,
|
|
826
|
-
package_name=package_name,
|
|
827
|
-
since=last_event_time,
|
|
828
|
-
record_types=record_types,
|
|
829
|
-
scopes=scopes,
|
|
830
|
-
consumer_org=consumer_org,
|
|
831
|
-
consumer_account=consumer_account,
|
|
832
|
-
consumer_app_hash=consumer_app_hash,
|
|
833
|
-
)
|
|
834
|
-
if not events:
|
|
835
|
-
continue
|
|
836
|
-
|
|
837
|
-
yield from _new_events_only(previous_events, events)
|
|
838
|
-
last_event_time = events[-1]["TIMESTAMP"]
|
|
839
|
-
except KeyboardInterrupt:
|
|
840
|
-
return
|
|
841
|
-
|
|
842
|
-
@staticmethod
|
|
843
|
-
def get_account_event_table():
|
|
844
|
-
query = "show parameters like 'event_table' in account"
|
|
845
|
-
sql_executor = get_sql_executor()
|
|
846
|
-
results = sql_executor.execute_query(query, cursor_class=DictCursor)
|
|
847
|
-
return next((r["value"] for r in results if r["key"] == "EVENT_TABLE"), "")
|
|
848
|
-
|
|
849
|
-
@classmethod
|
|
850
|
-
def get_snowsight_url(cls, app_name: str, app_warehouse: str | None) -> str:
|
|
851
|
-
"""Returns the URL that can be used to visit this app via Snowsight."""
|
|
852
|
-
name = identifier_for_url(app_name)
|
|
853
|
-
with cls.use_application_warehouse(app_warehouse):
|
|
854
|
-
sql_executor = get_sql_executor()
|
|
855
|
-
return make_snowsight_url(
|
|
856
|
-
sql_executor._conn, f"/#/apps/application/{name}" # noqa: SLF001
|
|
857
|
-
)
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
def _new_events_only(previous_events: list[dict], new_events: list[dict]) -> list[dict]:
|
|
861
|
-
# The timestamp that overlaps between both sets of events
|
|
862
|
-
overlap_time = new_events[0]["TIMESTAMP"]
|
|
863
|
-
|
|
864
|
-
# Remove all the events from the new result set
|
|
865
|
-
# if they were already printed. We iterate and remove
|
|
866
|
-
# instead of filtering in order to handle duplicates
|
|
867
|
-
# (i.e. if an event is present 3 times in new_events
|
|
868
|
-
# but only once in previous_events, it should still
|
|
869
|
-
# appear twice in new_events at the end
|
|
870
|
-
new_events = new_events.copy()
|
|
871
|
-
for event in reversed(previous_events):
|
|
872
|
-
if event["TIMESTAMP"] < overlap_time:
|
|
873
|
-
break
|
|
874
|
-
# No need to handle ValueError here since we know
|
|
875
|
-
# that events that pass the above if check will
|
|
876
|
-
# either be in both lists or in new_events only
|
|
877
|
-
new_events.remove(event)
|
|
878
|
-
return new_events
|