snowflake-cli-labs 3.0.0rc2__py3-none-any.whl → 3.0.0rc4__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.
- snowflake/cli/__about__.py +1 -1
- snowflake/cli/_app/commands_registration/builtin_plugins.py +2 -0
- snowflake/cli/_app/secret.py +9 -0
- snowflake/cli/_app/snow_connector.py +39 -27
- snowflake/cli/_app/telemetry.py +28 -0
- snowflake/cli/_plugins/connection/commands.py +9 -4
- snowflake/cli/_plugins/git/manager.py +53 -7
- snowflake/cli/_plugins/helpers/commands.py +61 -0
- snowflake/cli/{api/project/schemas/snowpark/__init__.py → _plugins/helpers/plugin_spec.py} +17 -0
- snowflake/cli/_plugins/nativeapp/artifacts.py +10 -9
- snowflake/cli/_plugins/nativeapp/bundle_context.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/artifact_processor.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/compiler.py +6 -1
- snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/extension_function_utils.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/models.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +5 -1
- snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +4 -1
- snowflake/cli/_plugins/nativeapp/commands.py +87 -96
- snowflake/cli/_plugins/nativeapp/entities/__init__.py +0 -0
- snowflake/cli/{api/entities/application_entity.py → _plugins/nativeapp/entities/application.py} +264 -83
- snowflake/cli/_plugins/nativeapp/entities/application_package.py +1392 -0
- snowflake/cli/_plugins/nativeapp/exceptions.py +0 -9
- snowflake/cli/_plugins/nativeapp/manager.py +69 -185
- snowflake/cli/_plugins/nativeapp/policy.py +3 -0
- snowflake/cli/_plugins/nativeapp/project_model.py +2 -2
- snowflake/cli/_plugins/nativeapp/run_processor.py +17 -20
- snowflake/cli/_plugins/nativeapp/same_account_install_method.py +0 -4
- snowflake/cli/_plugins/nativeapp/teardown_processor.py +4 -6
- snowflake/cli/_plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +122 -88
- snowflake/cli/_plugins/nativeapp/version/commands.py +7 -39
- snowflake/cli/_plugins/nativeapp/version/version_processor.py +46 -312
- snowflake/cli/_plugins/object/manager.py +36 -15
- snowflake/cli/_plugins/snowpark/commands.py +4 -4
- snowflake/cli/_plugins/snowpark/common.py +4 -4
- snowflake/cli/{api/entities → _plugins/snowpark}/snowpark_entity.py +2 -2
- snowflake/cli/{api/project/schemas/entities/snowpark_entity.py → _plugins/snowpark/snowpark_entity_model.py} +3 -6
- snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +1 -1
- snowflake/cli/_plugins/stage/manager.py +9 -4
- snowflake/cli/_plugins/streamlit/commands.py +15 -3
- snowflake/cli/_plugins/streamlit/manager.py +12 -4
- snowflake/cli/{api/entities → _plugins/streamlit}/streamlit_entity.py +2 -2
- snowflake/cli/{api/project/schemas/entities → _plugins/streamlit}/streamlit_entity_model.py +5 -12
- snowflake/cli/_plugins/workspace/commands.py +116 -36
- snowflake/cli/_plugins/workspace/plugin_spec.py +1 -1
- snowflake/cli/api/cli_global_context.py +7 -0
- snowflake/cli/api/commands/decorators.py +14 -0
- snowflake/cli/api/commands/flags.py +18 -0
- snowflake/cli/api/commands/snow_typer.py +1 -1
- snowflake/cli/api/config.py +25 -6
- snowflake/cli/api/connections.py +3 -1
- snowflake/cli/api/entities/common.py +4 -0
- snowflake/cli/api/entities/utils.py +3 -14
- snowflake/cli/api/errno.py +1 -0
- snowflake/cli/api/identifiers.py +4 -3
- snowflake/cli/api/metrics.py +92 -0
- snowflake/cli/api/project/definition_conversion.py +61 -18
- snowflake/cli/api/project/schemas/entities/common.py +17 -4
- snowflake/cli/api/project/schemas/entities/entities.py +11 -10
- snowflake/cli/api/project/schemas/project_definition.py +5 -7
- snowflake/cli/api/project/schemas/v1/__init__.py +0 -0
- snowflake/cli/api/project/schemas/{identifier_model.py → v1/identifier_model.py} +0 -7
- snowflake/cli/api/project/schemas/v1/native_app/__init__.py +0 -0
- snowflake/cli/api/project/schemas/{native_app → v1/native_app}/native_app.py +4 -4
- snowflake/cli/api/project/schemas/v1/snowpark/__init__.py +0 -0
- snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/callable.py +2 -2
- snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/snowpark.py +2 -2
- snowflake/cli/api/project/schemas/v1/streamlit/__init__.py +0 -0
- snowflake/cli/api/project/schemas/{streamlit → v1/streamlit}/streamlit.py +2 -1
- snowflake/cli/api/rendering/sql_templates.py +6 -0
- snowflake/cli/api/rest_api.py +11 -5
- snowflake/cli/api/sql_execution.py +6 -15
- snowflake/cli/api/utils/definition_rendering.py +24 -4
- {snowflake_cli_labs-3.0.0rc2.dist-info → snowflake_cli_labs-3.0.0rc4.dist-info}/METADATA +9 -7
- {snowflake_cli_labs-3.0.0rc2.dist-info → snowflake_cli_labs-3.0.0rc4.dist-info}/RECORD +83 -79
- snowflake/cli/_plugins/nativeapp/init.py +0 -345
- snowflake/cli/api/entities/application_package_entity.py +0 -658
- snowflake/cli/api/project/schemas/entities/application_entity_model.py +0 -56
- snowflake/cli/api/project/schemas/entities/application_package_entity_model.py +0 -94
- snowflake/cli/api/project/schemas/streamlit/__init__.py +0 -13
- /snowflake/cli/{api/project/schemas/native_app → _plugins/helpers}/__init__.py +0 -0
- /snowflake/cli/api/project/schemas/{native_app → v1/native_app}/application.py +0 -0
- /snowflake/cli/api/project/schemas/{native_app → v1/native_app}/package.py +0 -0
- /snowflake/cli/api/project/schemas/{native_app → v1/native_app}/path_mapping.py +0 -0
- /snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/argument.py +0 -0
- {snowflake_cli_labs-3.0.0rc2.dist-info → snowflake_cli_labs-3.0.0rc4.dist-info}/WHEEL +0 -0
- {snowflake_cli_labs-3.0.0rc2.dist-info → snowflake_cli_labs-3.0.0rc4.dist-info}/entry_points.txt +0 -0
- {snowflake_cli_labs-3.0.0rc2.dist-info → snowflake_cli_labs-3.0.0rc4.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,658 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from contextlib import contextmanager
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from textwrap import dedent
|
|
5
|
-
from typing import Callable, List, Optional
|
|
6
|
-
|
|
7
|
-
import typer
|
|
8
|
-
from click import ClickException
|
|
9
|
-
from snowflake.cli._plugins.nativeapp.artifacts import build_bundle
|
|
10
|
-
from snowflake.cli._plugins.nativeapp.bundle_context import BundleContext
|
|
11
|
-
from snowflake.cli._plugins.nativeapp.codegen.compiler import NativeAppCompiler
|
|
12
|
-
from snowflake.cli._plugins.nativeapp.constants import (
|
|
13
|
-
ALLOWED_SPECIAL_COMMENTS,
|
|
14
|
-
COMMENT_COL,
|
|
15
|
-
EXTERNAL_DISTRIBUTION,
|
|
16
|
-
INTERNAL_DISTRIBUTION,
|
|
17
|
-
NAME_COL,
|
|
18
|
-
OWNER_COL,
|
|
19
|
-
SPECIAL_COMMENT,
|
|
20
|
-
)
|
|
21
|
-
from snowflake.cli._plugins.nativeapp.exceptions import (
|
|
22
|
-
ApplicationPackageAlreadyExistsError,
|
|
23
|
-
ApplicationPackageDoesNotExistError,
|
|
24
|
-
CouldNotDropApplicationPackageWithVersions,
|
|
25
|
-
SetupScriptFailedValidation,
|
|
26
|
-
)
|
|
27
|
-
from snowflake.cli._plugins.nativeapp.utils import (
|
|
28
|
-
needs_confirmation,
|
|
29
|
-
)
|
|
30
|
-
from snowflake.cli._plugins.stage.diff import DiffResult
|
|
31
|
-
from snowflake.cli._plugins.stage.manager import StageManager
|
|
32
|
-
from snowflake.cli._plugins.workspace.action_context import ActionContext
|
|
33
|
-
from snowflake.cli.api.console.abc import AbstractConsole
|
|
34
|
-
from snowflake.cli.api.entities.common import EntityBase, get_sql_executor
|
|
35
|
-
from snowflake.cli.api.entities.utils import (
|
|
36
|
-
drop_generic_object,
|
|
37
|
-
ensure_correct_owner,
|
|
38
|
-
execute_post_deploy_hooks,
|
|
39
|
-
generic_sql_error_handler,
|
|
40
|
-
render_script_templates,
|
|
41
|
-
sync_deploy_root_with_stage,
|
|
42
|
-
validation_item_to_str,
|
|
43
|
-
)
|
|
44
|
-
from snowflake.cli.api.errno import (
|
|
45
|
-
DOES_NOT_EXIST_OR_NOT_AUTHORIZED,
|
|
46
|
-
)
|
|
47
|
-
from snowflake.cli.api.exceptions import SnowflakeSQLExecutionError
|
|
48
|
-
from snowflake.cli.api.project.schemas.entities.application_package_entity_model import (
|
|
49
|
-
ApplicationPackageEntityModel,
|
|
50
|
-
)
|
|
51
|
-
from snowflake.cli.api.project.schemas.entities.common import PostDeployHook
|
|
52
|
-
from snowflake.cli.api.project.schemas.native_app.path_mapping import PathMapping
|
|
53
|
-
from snowflake.cli.api.project.util import extract_schema
|
|
54
|
-
from snowflake.cli.api.rendering.jinja import (
|
|
55
|
-
get_basic_jinja_env,
|
|
56
|
-
)
|
|
57
|
-
from snowflake.connector import ProgrammingError
|
|
58
|
-
from snowflake.connector.cursor import DictCursor
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
|
|
62
|
-
"""
|
|
63
|
-
A Native App application package.
|
|
64
|
-
"""
|
|
65
|
-
|
|
66
|
-
def action_bundle(self, ctx: ActionContext, *args, **kwargs):
|
|
67
|
-
model = self._entity_model
|
|
68
|
-
return self.bundle(
|
|
69
|
-
project_root=ctx.project_root,
|
|
70
|
-
deploy_root=Path(model.deploy_root),
|
|
71
|
-
bundle_root=Path(model.bundle_root),
|
|
72
|
-
generated_root=Path(model.generated_root),
|
|
73
|
-
package_name=model.identifier,
|
|
74
|
-
artifacts=model.artifacts,
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
def action_deploy(
|
|
78
|
-
self,
|
|
79
|
-
ctx: ActionContext,
|
|
80
|
-
prune: bool,
|
|
81
|
-
recursive: bool,
|
|
82
|
-
paths: List[Path],
|
|
83
|
-
validate: bool,
|
|
84
|
-
stage_fqn: Optional[str] = None,
|
|
85
|
-
*args,
|
|
86
|
-
**kwargs,
|
|
87
|
-
):
|
|
88
|
-
model = self._entity_model
|
|
89
|
-
package_name = model.fqn.identifier
|
|
90
|
-
return self.deploy(
|
|
91
|
-
console=ctx.console,
|
|
92
|
-
project_root=ctx.project_root,
|
|
93
|
-
deploy_root=Path(model.deploy_root),
|
|
94
|
-
bundle_root=Path(model.bundle_root),
|
|
95
|
-
generated_root=Path(model.generated_root),
|
|
96
|
-
artifacts=model.artifacts,
|
|
97
|
-
package_name=package_name,
|
|
98
|
-
package_role=(model.meta and model.meta.role) or ctx.default_role,
|
|
99
|
-
package_distribution=model.distribution,
|
|
100
|
-
prune=prune,
|
|
101
|
-
recursive=recursive,
|
|
102
|
-
paths=paths,
|
|
103
|
-
print_diff=True,
|
|
104
|
-
validate=validate,
|
|
105
|
-
stage_fqn=stage_fqn or f"{package_name}.{model.stage}",
|
|
106
|
-
package_warehouse=(
|
|
107
|
-
(model.meta and model.meta.warehouse) or ctx.default_warehouse
|
|
108
|
-
),
|
|
109
|
-
post_deploy_hooks=model.meta and model.meta.post_deploy,
|
|
110
|
-
package_scripts=[], # Package scripts are not supported in PDFv2
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
def action_drop(self, ctx: ActionContext, force_drop: bool, *args, **kwargs):
|
|
114
|
-
model = self._entity_model
|
|
115
|
-
package_name = model.fqn.identifier
|
|
116
|
-
if model.meta and model.meta.role:
|
|
117
|
-
package_role = model.meta.role
|
|
118
|
-
else:
|
|
119
|
-
package_role = ctx.default_role
|
|
120
|
-
|
|
121
|
-
self.drop(
|
|
122
|
-
console=ctx.console,
|
|
123
|
-
package_name=package_name,
|
|
124
|
-
package_role=package_role,
|
|
125
|
-
force_drop=force_drop,
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
def action_validate(self, ctx: ActionContext, *args, **kwargs):
|
|
129
|
-
model = self._entity_model
|
|
130
|
-
package_name = model.fqn.identifier
|
|
131
|
-
stage_fqn = f"{package_name}.{model.stage}"
|
|
132
|
-
if model.meta and model.meta.role:
|
|
133
|
-
package_role = model.meta.role
|
|
134
|
-
else:
|
|
135
|
-
package_role = ctx.default_role
|
|
136
|
-
|
|
137
|
-
def deploy_to_scratch_stage_fn():
|
|
138
|
-
self.action_deploy(
|
|
139
|
-
ctx=ctx,
|
|
140
|
-
prune=True,
|
|
141
|
-
recursive=True,
|
|
142
|
-
paths=[],
|
|
143
|
-
validate=False,
|
|
144
|
-
stage_fqn=model.scratch_stage,
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
self.validate_setup_script(
|
|
148
|
-
console=ctx.console,
|
|
149
|
-
package_name=package_name,
|
|
150
|
-
package_role=package_role,
|
|
151
|
-
stage_fqn=stage_fqn,
|
|
152
|
-
use_scratch_stage=True,
|
|
153
|
-
scratch_stage_fqn=model.scratch_stage,
|
|
154
|
-
deploy_to_scratch_stage_fn=deploy_to_scratch_stage_fn,
|
|
155
|
-
)
|
|
156
|
-
ctx.console.message("Setup script is valid")
|
|
157
|
-
|
|
158
|
-
@staticmethod
|
|
159
|
-
def bundle(
|
|
160
|
-
project_root: Path,
|
|
161
|
-
deploy_root: Path,
|
|
162
|
-
bundle_root: Path,
|
|
163
|
-
generated_root: Path,
|
|
164
|
-
artifacts: list[PathMapping],
|
|
165
|
-
package_name: str,
|
|
166
|
-
):
|
|
167
|
-
bundle_map = build_bundle(project_root, deploy_root, artifacts)
|
|
168
|
-
bundle_context = BundleContext(
|
|
169
|
-
package_name=package_name,
|
|
170
|
-
artifacts=artifacts,
|
|
171
|
-
project_root=project_root,
|
|
172
|
-
bundle_root=bundle_root,
|
|
173
|
-
deploy_root=deploy_root,
|
|
174
|
-
generated_root=generated_root,
|
|
175
|
-
)
|
|
176
|
-
compiler = NativeAppCompiler(bundle_context)
|
|
177
|
-
compiler.compile_artifacts()
|
|
178
|
-
return bundle_map
|
|
179
|
-
|
|
180
|
-
@classmethod
|
|
181
|
-
def deploy(
|
|
182
|
-
cls,
|
|
183
|
-
console: AbstractConsole,
|
|
184
|
-
project_root: Path,
|
|
185
|
-
deploy_root: Path,
|
|
186
|
-
bundle_root: Path,
|
|
187
|
-
generated_root: Path,
|
|
188
|
-
artifacts: list[PathMapping],
|
|
189
|
-
package_name: str,
|
|
190
|
-
package_role: str,
|
|
191
|
-
package_distribution: str,
|
|
192
|
-
package_warehouse: str | None,
|
|
193
|
-
prune: bool,
|
|
194
|
-
recursive: bool,
|
|
195
|
-
paths: List[Path],
|
|
196
|
-
print_diff: bool,
|
|
197
|
-
validate: bool,
|
|
198
|
-
stage_fqn: str,
|
|
199
|
-
post_deploy_hooks: list[PostDeployHook] | None,
|
|
200
|
-
package_scripts: List[str],
|
|
201
|
-
) -> DiffResult:
|
|
202
|
-
# 1. Create a bundle
|
|
203
|
-
bundle_map = cls.bundle(
|
|
204
|
-
project_root=project_root,
|
|
205
|
-
deploy_root=deploy_root,
|
|
206
|
-
bundle_root=bundle_root,
|
|
207
|
-
generated_root=generated_root,
|
|
208
|
-
artifacts=artifacts,
|
|
209
|
-
package_name=package_name,
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
# 2. Create an empty application package, if none exists
|
|
213
|
-
cls.create_app_package(
|
|
214
|
-
console=console,
|
|
215
|
-
package_name=package_name,
|
|
216
|
-
package_role=package_role,
|
|
217
|
-
package_distribution=package_distribution,
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
with get_sql_executor().use_role(package_role):
|
|
221
|
-
if package_scripts:
|
|
222
|
-
cls.apply_package_scripts(
|
|
223
|
-
console=console,
|
|
224
|
-
package_scripts=package_scripts,
|
|
225
|
-
package_warehouse=package_warehouse,
|
|
226
|
-
project_root=project_root,
|
|
227
|
-
package_role=package_role,
|
|
228
|
-
package_name=package_name,
|
|
229
|
-
)
|
|
230
|
-
|
|
231
|
-
# 3. Upload files from deploy root local folder to the above stage
|
|
232
|
-
stage_schema = extract_schema(stage_fqn)
|
|
233
|
-
diff = sync_deploy_root_with_stage(
|
|
234
|
-
console=console,
|
|
235
|
-
deploy_root=deploy_root,
|
|
236
|
-
package_name=package_name,
|
|
237
|
-
stage_schema=stage_schema,
|
|
238
|
-
bundle_map=bundle_map,
|
|
239
|
-
role=package_role,
|
|
240
|
-
prune=prune,
|
|
241
|
-
recursive=recursive,
|
|
242
|
-
stage_fqn=stage_fqn,
|
|
243
|
-
local_paths_to_sync=paths,
|
|
244
|
-
print_diff=print_diff,
|
|
245
|
-
)
|
|
246
|
-
|
|
247
|
-
if post_deploy_hooks:
|
|
248
|
-
cls.execute_post_deploy_hooks(
|
|
249
|
-
console=console,
|
|
250
|
-
project_root=project_root,
|
|
251
|
-
post_deploy_hooks=post_deploy_hooks,
|
|
252
|
-
package_name=package_name,
|
|
253
|
-
package_warehouse=package_warehouse,
|
|
254
|
-
)
|
|
255
|
-
|
|
256
|
-
if validate:
|
|
257
|
-
cls.validate_setup_script(
|
|
258
|
-
console=console,
|
|
259
|
-
package_name=package_name,
|
|
260
|
-
package_role=package_role,
|
|
261
|
-
stage_fqn=stage_fqn,
|
|
262
|
-
use_scratch_stage=False,
|
|
263
|
-
scratch_stage_fqn="",
|
|
264
|
-
deploy_to_scratch_stage_fn=lambda *args: None,
|
|
265
|
-
)
|
|
266
|
-
|
|
267
|
-
return diff
|
|
268
|
-
|
|
269
|
-
@staticmethod
|
|
270
|
-
def get_existing_app_pkg_info(
|
|
271
|
-
package_name: str,
|
|
272
|
-
package_role: str,
|
|
273
|
-
) -> Optional[dict]:
|
|
274
|
-
"""
|
|
275
|
-
Check for an existing application package by the same name as in project definition, in account.
|
|
276
|
-
It executes a 'show application packages like' query and returns the result as single row, if one exists.
|
|
277
|
-
"""
|
|
278
|
-
sql_executor = get_sql_executor()
|
|
279
|
-
with sql_executor.use_role(package_role):
|
|
280
|
-
return sql_executor.show_specific_object(
|
|
281
|
-
"application packages", package_name, name_col=NAME_COL
|
|
282
|
-
)
|
|
283
|
-
|
|
284
|
-
@staticmethod
|
|
285
|
-
def get_app_pkg_distribution_in_snowflake(
|
|
286
|
-
package_name: str,
|
|
287
|
-
package_role: str,
|
|
288
|
-
) -> str:
|
|
289
|
-
"""
|
|
290
|
-
Returns the 'distribution' attribute of a 'describe application package' SQL query, in lowercase.
|
|
291
|
-
"""
|
|
292
|
-
sql_executor = get_sql_executor()
|
|
293
|
-
with sql_executor.use_role(package_role):
|
|
294
|
-
try:
|
|
295
|
-
desc_cursor = sql_executor.execute_query(
|
|
296
|
-
f"describe application package {package_name}"
|
|
297
|
-
)
|
|
298
|
-
except ProgrammingError as err:
|
|
299
|
-
generic_sql_error_handler(err)
|
|
300
|
-
|
|
301
|
-
if desc_cursor.rowcount is None or desc_cursor.rowcount == 0:
|
|
302
|
-
raise SnowflakeSQLExecutionError()
|
|
303
|
-
else:
|
|
304
|
-
for row in desc_cursor:
|
|
305
|
-
if row[0].lower() == "distribution":
|
|
306
|
-
return row[1].lower()
|
|
307
|
-
raise ProgrammingError(
|
|
308
|
-
msg=dedent(
|
|
309
|
-
f"""\
|
|
310
|
-
Could not find the 'distribution' attribute for application package {package_name} in the output of SQL query:
|
|
311
|
-
'describe application package {package_name}'
|
|
312
|
-
"""
|
|
313
|
-
)
|
|
314
|
-
)
|
|
315
|
-
|
|
316
|
-
@classmethod
|
|
317
|
-
def verify_project_distribution(
|
|
318
|
-
cls,
|
|
319
|
-
console: AbstractConsole,
|
|
320
|
-
package_name: str,
|
|
321
|
-
package_role: str,
|
|
322
|
-
package_distribution: str,
|
|
323
|
-
expected_distribution: Optional[str] = None,
|
|
324
|
-
) -> bool:
|
|
325
|
-
"""
|
|
326
|
-
Returns true if the 'distribution' attribute of an existing application package in snowflake
|
|
327
|
-
is the same as the the attribute specified in project definition file.
|
|
328
|
-
"""
|
|
329
|
-
actual_distribution = (
|
|
330
|
-
expected_distribution
|
|
331
|
-
if expected_distribution
|
|
332
|
-
else cls.get_app_pkg_distribution_in_snowflake(
|
|
333
|
-
package_name=package_name,
|
|
334
|
-
package_role=package_role,
|
|
335
|
-
)
|
|
336
|
-
)
|
|
337
|
-
project_def_distribution = package_distribution.lower()
|
|
338
|
-
if actual_distribution != project_def_distribution:
|
|
339
|
-
console.warning(
|
|
340
|
-
dedent(
|
|
341
|
-
f"""\
|
|
342
|
-
Application package {package_name} in your Snowflake account has distribution property {actual_distribution},
|
|
343
|
-
which does not match the value specified in project definition file: {project_def_distribution}.
|
|
344
|
-
"""
|
|
345
|
-
)
|
|
346
|
-
)
|
|
347
|
-
return False
|
|
348
|
-
return True
|
|
349
|
-
|
|
350
|
-
@staticmethod
|
|
351
|
-
@contextmanager
|
|
352
|
-
def use_package_warehouse(
|
|
353
|
-
package_warehouse: Optional[str],
|
|
354
|
-
):
|
|
355
|
-
if package_warehouse:
|
|
356
|
-
with get_sql_executor().use_warehouse(package_warehouse):
|
|
357
|
-
yield
|
|
358
|
-
else:
|
|
359
|
-
raise ClickException(
|
|
360
|
-
dedent(
|
|
361
|
-
f"""\
|
|
362
|
-
Application package warehouse cannot be empty.
|
|
363
|
-
Please provide a value for it in your connection information or your project definition file.
|
|
364
|
-
"""
|
|
365
|
-
)
|
|
366
|
-
)
|
|
367
|
-
|
|
368
|
-
@classmethod
|
|
369
|
-
def apply_package_scripts(
|
|
370
|
-
cls,
|
|
371
|
-
console: AbstractConsole,
|
|
372
|
-
package_scripts: List[str],
|
|
373
|
-
package_warehouse: Optional[str],
|
|
374
|
-
project_root: Path,
|
|
375
|
-
package_role: str,
|
|
376
|
-
package_name: str,
|
|
377
|
-
) -> None:
|
|
378
|
-
"""
|
|
379
|
-
Assuming the application package exists and we are using the correct role,
|
|
380
|
-
applies all package scripts in-order to the application package.
|
|
381
|
-
"""
|
|
382
|
-
|
|
383
|
-
if package_scripts:
|
|
384
|
-
console.warning(
|
|
385
|
-
"WARNING: native_app.package.scripts is deprecated. Please migrate to using native_app.package.post_deploy."
|
|
386
|
-
)
|
|
387
|
-
|
|
388
|
-
queued_queries = render_script_templates(
|
|
389
|
-
project_root,
|
|
390
|
-
dict(package_name=package_name),
|
|
391
|
-
package_scripts,
|
|
392
|
-
get_basic_jinja_env(),
|
|
393
|
-
)
|
|
394
|
-
|
|
395
|
-
# once we're sure all the templates expanded correctly, execute all of them
|
|
396
|
-
with cls.use_package_warehouse(
|
|
397
|
-
package_warehouse=package_warehouse,
|
|
398
|
-
):
|
|
399
|
-
try:
|
|
400
|
-
for i, queries in enumerate(queued_queries):
|
|
401
|
-
console.step(f"Applying package script: {package_scripts[i]}")
|
|
402
|
-
get_sql_executor().execute_queries(queries)
|
|
403
|
-
except ProgrammingError as err:
|
|
404
|
-
generic_sql_error_handler(
|
|
405
|
-
err, role=package_role, warehouse=package_warehouse
|
|
406
|
-
)
|
|
407
|
-
|
|
408
|
-
@classmethod
|
|
409
|
-
def create_app_package(
|
|
410
|
-
cls,
|
|
411
|
-
console: AbstractConsole,
|
|
412
|
-
package_name: str,
|
|
413
|
-
package_role: str,
|
|
414
|
-
package_distribution: str,
|
|
415
|
-
) -> None:
|
|
416
|
-
"""
|
|
417
|
-
Creates the application package with our up-to-date stage if none exists.
|
|
418
|
-
"""
|
|
419
|
-
|
|
420
|
-
# 1. Check for existing existing application package
|
|
421
|
-
show_obj_row = cls.get_existing_app_pkg_info(
|
|
422
|
-
package_name=package_name,
|
|
423
|
-
package_role=package_role,
|
|
424
|
-
)
|
|
425
|
-
|
|
426
|
-
if show_obj_row:
|
|
427
|
-
# 1. Check for the right owner role
|
|
428
|
-
ensure_correct_owner(
|
|
429
|
-
row=show_obj_row, role=package_role, obj_name=package_name
|
|
430
|
-
)
|
|
431
|
-
|
|
432
|
-
# 2. Check distribution of the existing application package
|
|
433
|
-
actual_distribution = cls.get_app_pkg_distribution_in_snowflake(
|
|
434
|
-
package_name=package_name,
|
|
435
|
-
package_role=package_role,
|
|
436
|
-
)
|
|
437
|
-
if not cls.verify_project_distribution(
|
|
438
|
-
console=console,
|
|
439
|
-
package_name=package_name,
|
|
440
|
-
package_role=package_role,
|
|
441
|
-
package_distribution=package_distribution,
|
|
442
|
-
expected_distribution=actual_distribution,
|
|
443
|
-
):
|
|
444
|
-
console.warning(
|
|
445
|
-
f"Continuing to execute `snow app run` on application package {package_name} with distribution '{actual_distribution}'."
|
|
446
|
-
)
|
|
447
|
-
|
|
448
|
-
# 3. If actual_distribution is external, skip comment check
|
|
449
|
-
if actual_distribution == INTERNAL_DISTRIBUTION:
|
|
450
|
-
row_comment = show_obj_row[COMMENT_COL]
|
|
451
|
-
|
|
452
|
-
if row_comment not in ALLOWED_SPECIAL_COMMENTS:
|
|
453
|
-
raise ApplicationPackageAlreadyExistsError(package_name)
|
|
454
|
-
|
|
455
|
-
return
|
|
456
|
-
|
|
457
|
-
# If no application package pre-exists, create an application package, with the specified distribution in the project definition file.
|
|
458
|
-
sql_executor = get_sql_executor()
|
|
459
|
-
with sql_executor.use_role(package_role):
|
|
460
|
-
console.step(f"Creating new application package {package_name} in account.")
|
|
461
|
-
sql_executor.execute_query(
|
|
462
|
-
dedent(
|
|
463
|
-
f"""\
|
|
464
|
-
create application package {package_name}
|
|
465
|
-
comment = {SPECIAL_COMMENT}
|
|
466
|
-
distribution = {package_distribution}
|
|
467
|
-
"""
|
|
468
|
-
)
|
|
469
|
-
)
|
|
470
|
-
|
|
471
|
-
@classmethod
|
|
472
|
-
def execute_post_deploy_hooks(
|
|
473
|
-
cls,
|
|
474
|
-
console: AbstractConsole,
|
|
475
|
-
project_root: Path,
|
|
476
|
-
post_deploy_hooks: Optional[List[PostDeployHook]],
|
|
477
|
-
package_name: str,
|
|
478
|
-
package_warehouse: Optional[str],
|
|
479
|
-
):
|
|
480
|
-
with cls.use_package_warehouse(package_warehouse):
|
|
481
|
-
execute_post_deploy_hooks(
|
|
482
|
-
console=console,
|
|
483
|
-
project_root=project_root,
|
|
484
|
-
post_deploy_hooks=post_deploy_hooks,
|
|
485
|
-
deployed_object_type="application package",
|
|
486
|
-
database_name=package_name,
|
|
487
|
-
)
|
|
488
|
-
|
|
489
|
-
@classmethod
|
|
490
|
-
def validate_setup_script(
|
|
491
|
-
cls,
|
|
492
|
-
console: AbstractConsole,
|
|
493
|
-
package_name: str,
|
|
494
|
-
package_role: str,
|
|
495
|
-
stage_fqn: str,
|
|
496
|
-
use_scratch_stage: bool,
|
|
497
|
-
scratch_stage_fqn: str,
|
|
498
|
-
deploy_to_scratch_stage_fn: Callable,
|
|
499
|
-
):
|
|
500
|
-
"""Validates Native App setup script SQL."""
|
|
501
|
-
with console.phase(f"Validating Snowflake Native App setup script."):
|
|
502
|
-
validation_result = cls.get_validation_result(
|
|
503
|
-
console=console,
|
|
504
|
-
package_name=package_name,
|
|
505
|
-
package_role=package_role,
|
|
506
|
-
stage_fqn=stage_fqn,
|
|
507
|
-
use_scratch_stage=use_scratch_stage,
|
|
508
|
-
scratch_stage_fqn=scratch_stage_fqn,
|
|
509
|
-
deploy_to_scratch_stage_fn=deploy_to_scratch_stage_fn,
|
|
510
|
-
)
|
|
511
|
-
|
|
512
|
-
# First print warnings, regardless of the outcome of validation
|
|
513
|
-
for warning in validation_result.get("warnings", []):
|
|
514
|
-
console.warning(validation_item_to_str(warning))
|
|
515
|
-
|
|
516
|
-
# Then print errors
|
|
517
|
-
for error in validation_result.get("errors", []):
|
|
518
|
-
# Print them as warnings for now since we're going to be
|
|
519
|
-
# revamping CLI output soon
|
|
520
|
-
console.warning(validation_item_to_str(error))
|
|
521
|
-
|
|
522
|
-
# Then raise an exception if validation failed
|
|
523
|
-
if validation_result["status"] == "FAIL":
|
|
524
|
-
raise SetupScriptFailedValidation()
|
|
525
|
-
|
|
526
|
-
@staticmethod
|
|
527
|
-
def get_validation_result(
|
|
528
|
-
console: AbstractConsole,
|
|
529
|
-
package_name: str,
|
|
530
|
-
package_role: str,
|
|
531
|
-
stage_fqn: str,
|
|
532
|
-
use_scratch_stage: bool,
|
|
533
|
-
scratch_stage_fqn: str,
|
|
534
|
-
deploy_to_scratch_stage_fn: Callable,
|
|
535
|
-
):
|
|
536
|
-
"""Call system$validate_native_app_setup() to validate deployed Native App setup script."""
|
|
537
|
-
if use_scratch_stage:
|
|
538
|
-
stage_fqn = scratch_stage_fqn
|
|
539
|
-
deploy_to_scratch_stage_fn()
|
|
540
|
-
prefixed_stage_fqn = StageManager.get_standard_stage_prefix(stage_fqn)
|
|
541
|
-
sql_executor = get_sql_executor()
|
|
542
|
-
try:
|
|
543
|
-
cursor = sql_executor.execute_query(
|
|
544
|
-
f"call system$validate_native_app_setup('{prefixed_stage_fqn}')"
|
|
545
|
-
)
|
|
546
|
-
except ProgrammingError as err:
|
|
547
|
-
if err.errno == DOES_NOT_EXIST_OR_NOT_AUTHORIZED:
|
|
548
|
-
raise ApplicationPackageDoesNotExistError(package_name)
|
|
549
|
-
generic_sql_error_handler(err)
|
|
550
|
-
else:
|
|
551
|
-
if not cursor.rowcount:
|
|
552
|
-
raise SnowflakeSQLExecutionError()
|
|
553
|
-
return json.loads(cursor.fetchone()[0])
|
|
554
|
-
finally:
|
|
555
|
-
if use_scratch_stage:
|
|
556
|
-
console.step(f"Dropping stage {scratch_stage_fqn}.")
|
|
557
|
-
with sql_executor.use_role(package_role):
|
|
558
|
-
sql_executor.execute_query(
|
|
559
|
-
f"drop stage if exists {scratch_stage_fqn}"
|
|
560
|
-
)
|
|
561
|
-
|
|
562
|
-
@classmethod
|
|
563
|
-
def drop(
|
|
564
|
-
cls,
|
|
565
|
-
console: AbstractConsole,
|
|
566
|
-
package_name: str,
|
|
567
|
-
package_role: str,
|
|
568
|
-
force_drop: bool,
|
|
569
|
-
):
|
|
570
|
-
sql_executor = get_sql_executor()
|
|
571
|
-
needs_confirm = True
|
|
572
|
-
|
|
573
|
-
# 1. If existing application package is not found, exit gracefully
|
|
574
|
-
show_obj_row = cls.get_existing_app_pkg_info(
|
|
575
|
-
package_name=package_name,
|
|
576
|
-
package_role=package_role,
|
|
577
|
-
)
|
|
578
|
-
if show_obj_row is None:
|
|
579
|
-
console.warning(
|
|
580
|
-
f"Role {package_role} does not own any application package with the name {package_name}, or the application package does not exist."
|
|
581
|
-
)
|
|
582
|
-
return
|
|
583
|
-
|
|
584
|
-
# 2. Check for the right owner
|
|
585
|
-
ensure_correct_owner(row=show_obj_row, role=package_role, obj_name=package_name)
|
|
586
|
-
|
|
587
|
-
with sql_executor.use_role(package_role):
|
|
588
|
-
# 3. Check for versions in the application package
|
|
589
|
-
show_versions_query = f"show versions in application package {package_name}"
|
|
590
|
-
show_versions_cursor = sql_executor.execute_query(
|
|
591
|
-
show_versions_query, cursor_class=DictCursor
|
|
592
|
-
)
|
|
593
|
-
if show_versions_cursor.rowcount is None:
|
|
594
|
-
raise SnowflakeSQLExecutionError(show_versions_query)
|
|
595
|
-
|
|
596
|
-
if show_versions_cursor.rowcount > 0:
|
|
597
|
-
# allow dropping a package with versions when --force is set
|
|
598
|
-
if not force_drop:
|
|
599
|
-
raise CouldNotDropApplicationPackageWithVersions(
|
|
600
|
-
"Drop versions first, or use --force to override."
|
|
601
|
-
)
|
|
602
|
-
|
|
603
|
-
# 4. Check distribution of the existing application package
|
|
604
|
-
actual_distribution = cls.get_app_pkg_distribution_in_snowflake(
|
|
605
|
-
package_name=package_name,
|
|
606
|
-
package_role=package_role,
|
|
607
|
-
)
|
|
608
|
-
if not cls.verify_project_distribution(
|
|
609
|
-
console=console,
|
|
610
|
-
package_name=package_name,
|
|
611
|
-
package_role=package_role,
|
|
612
|
-
package_distribution=actual_distribution,
|
|
613
|
-
):
|
|
614
|
-
console.warning(
|
|
615
|
-
f"Dropping application package {package_name} with distribution '{actual_distribution}'."
|
|
616
|
-
)
|
|
617
|
-
|
|
618
|
-
# 5. If distribution is internal, check if created by the Snowflake CLI
|
|
619
|
-
row_comment = show_obj_row[COMMENT_COL]
|
|
620
|
-
if actual_distribution == INTERNAL_DISTRIBUTION:
|
|
621
|
-
if row_comment in ALLOWED_SPECIAL_COMMENTS:
|
|
622
|
-
needs_confirm = False
|
|
623
|
-
else:
|
|
624
|
-
if needs_confirmation(needs_confirm, force_drop):
|
|
625
|
-
console.warning(
|
|
626
|
-
f"Application package {package_name} was not created by Snowflake CLI."
|
|
627
|
-
)
|
|
628
|
-
else:
|
|
629
|
-
if needs_confirmation(needs_confirm, force_drop):
|
|
630
|
-
console.warning(
|
|
631
|
-
f"Application package {package_name} in your Snowflake account has distribution property '{EXTERNAL_DISTRIBUTION}' and could be associated with one or more of your listings on Snowflake Marketplace."
|
|
632
|
-
)
|
|
633
|
-
|
|
634
|
-
if needs_confirmation(needs_confirm, force_drop):
|
|
635
|
-
should_drop_object = typer.confirm(
|
|
636
|
-
dedent(
|
|
637
|
-
f"""\
|
|
638
|
-
Application package details:
|
|
639
|
-
Name: {package_name}
|
|
640
|
-
Created on: {show_obj_row["created_on"]}
|
|
641
|
-
Distribution: {actual_distribution}
|
|
642
|
-
Owner: {show_obj_row[OWNER_COL]}
|
|
643
|
-
Comment: {show_obj_row[COMMENT_COL]}
|
|
644
|
-
Are you sure you want to drop it?
|
|
645
|
-
"""
|
|
646
|
-
)
|
|
647
|
-
)
|
|
648
|
-
if not should_drop_object:
|
|
649
|
-
console.message(f"Did not drop application package {package_name}.")
|
|
650
|
-
return # The user desires to keep the application package, therefore exit gracefully
|
|
651
|
-
|
|
652
|
-
# All validations have passed, drop object
|
|
653
|
-
drop_generic_object(
|
|
654
|
-
console=console,
|
|
655
|
-
object_type="application package",
|
|
656
|
-
object_name=package_name,
|
|
657
|
-
role=package_role,
|
|
658
|
-
)
|