snowflake-cli-labs 3.0.0rc1__py3-none-any.whl → 3.0.0rc2__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/cli_app.py +10 -1
- snowflake/cli/_app/snow_connector.py +76 -29
- snowflake/cli/_app/telemetry.py +8 -4
- snowflake/cli/_app/version_check.py +74 -0
- snowflake/cli/_plugins/git/commands.py +55 -14
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +2 -5
- snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +49 -31
- snowflake/cli/_plugins/nativeapp/manager.py +46 -87
- snowflake/cli/_plugins/nativeapp/run_processor.py +56 -260
- snowflake/cli/_plugins/nativeapp/same_account_install_method.py +74 -0
- snowflake/cli/_plugins/nativeapp/teardown_processor.py +9 -152
- snowflake/cli/_plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +91 -17
- snowflake/cli/_plugins/snowpark/commands.py +1 -1
- snowflake/cli/_plugins/snowpark/models.py +2 -1
- snowflake/cli/_plugins/streamlit/commands.py +1 -1
- snowflake/cli/_plugins/streamlit/manager.py +9 -0
- snowflake/cli/_plugins/workspace/action_context.py +2 -1
- snowflake/cli/_plugins/workspace/commands.py +48 -16
- snowflake/cli/_plugins/workspace/manager.py +1 -0
- snowflake/cli/api/cli_global_context.py +136 -313
- snowflake/cli/api/commands/flags.py +76 -91
- snowflake/cli/api/commands/snow_typer.py +6 -4
- snowflake/cli/api/config.py +1 -1
- snowflake/cli/api/connections.py +214 -0
- snowflake/cli/api/console/abc.py +4 -2
- snowflake/cli/api/entities/application_entity.py +687 -2
- snowflake/cli/api/entities/application_package_entity.py +151 -46
- snowflake/cli/api/entities/common.py +1 -0
- snowflake/cli/api/entities/utils.py +41 -17
- snowflake/cli/api/identifiers.py +3 -0
- snowflake/cli/api/project/definition.py +11 -0
- snowflake/cli/api/project/definition_conversion.py +171 -13
- snowflake/cli/api/project/schemas/entities/common.py +0 -12
- snowflake/cli/api/project/schemas/identifier_model.py +2 -2
- snowflake/cli/api/project/schemas/project_definition.py +101 -39
- snowflake/cli/api/rendering/project_definition_templates.py +4 -0
- snowflake/cli/api/rendering/sql_templates.py +7 -0
- snowflake/cli/api/utils/definition_rendering.py +3 -1
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/METADATA +6 -6
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/RECORD +44 -42
- snowflake/cli/api/commands/typer_pre_execute.py +0 -26
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/WHEEL +0 -0
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/entry_points.txt +0 -0
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/licenses/LICENSE +0 -0
|
@@ -16,24 +16,15 @@ from __future__ import annotations
|
|
|
16
16
|
|
|
17
17
|
import time
|
|
18
18
|
from abc import ABC, abstractmethod
|
|
19
|
-
from contextlib import contextmanager
|
|
20
19
|
from datetime import datetime
|
|
21
20
|
from functools import cached_property
|
|
22
21
|
from pathlib import Path
|
|
23
22
|
from textwrap import dedent
|
|
24
|
-
from typing import Generator, List, Optional
|
|
23
|
+
from typing import Generator, List, Optional
|
|
25
24
|
|
|
26
|
-
from click import ClickException
|
|
27
25
|
from snowflake.cli._plugins.connection.util import make_snowsight_url
|
|
28
26
|
from snowflake.cli._plugins.nativeapp.artifacts import (
|
|
29
27
|
BundleMap,
|
|
30
|
-
build_bundle,
|
|
31
|
-
)
|
|
32
|
-
from snowflake.cli._plugins.nativeapp.codegen.compiler import (
|
|
33
|
-
NativeAppCompiler,
|
|
34
|
-
)
|
|
35
|
-
from snowflake.cli._plugins.nativeapp.constants import (
|
|
36
|
-
NAME_COL,
|
|
37
28
|
)
|
|
38
29
|
from snowflake.cli._plugins.nativeapp.exceptions import (
|
|
39
30
|
NoEventTableForAccount,
|
|
@@ -45,6 +36,10 @@ from snowflake.cli._plugins.stage.diff import (
|
|
|
45
36
|
DiffResult,
|
|
46
37
|
)
|
|
47
38
|
from snowflake.cli.api.console import cli_console as cc
|
|
39
|
+
from snowflake.cli.api.entities.application_entity import (
|
|
40
|
+
ApplicationEntity,
|
|
41
|
+
ApplicationOwnedObject,
|
|
42
|
+
)
|
|
48
43
|
from snowflake.cli.api.entities.application_package_entity import (
|
|
49
44
|
ApplicationPackageEntity,
|
|
50
45
|
)
|
|
@@ -63,8 +58,6 @@ from snowflake.cli.api.project.util import (
|
|
|
63
58
|
from snowflake.cli.api.sql_execution import SqlExecutionMixin
|
|
64
59
|
from snowflake.connector import DictCursor, ProgrammingError
|
|
65
60
|
|
|
66
|
-
ApplicationOwnedObject = TypedDict("ApplicationOwnedObject", {"name": str, "type": str})
|
|
67
|
-
|
|
68
61
|
|
|
69
62
|
class NativeAppCommandProcessor(ABC):
|
|
70
63
|
@abstractmethod
|
|
@@ -141,20 +134,8 @@ class NativeAppManager(SqlExecutionMixin):
|
|
|
141
134
|
def application_warehouse(self) -> Optional[str]:
|
|
142
135
|
return self.na_project.application_warehouse
|
|
143
136
|
|
|
144
|
-
@contextmanager
|
|
145
137
|
def use_application_warehouse(self):
|
|
146
|
-
|
|
147
|
-
with self.use_warehouse(self.application_warehouse):
|
|
148
|
-
yield
|
|
149
|
-
else:
|
|
150
|
-
raise ClickException(
|
|
151
|
-
dedent(
|
|
152
|
-
f"""\
|
|
153
|
-
Application warehouse cannot be empty.
|
|
154
|
-
Please provide a value for it in your connection information or your project definition file.
|
|
155
|
-
"""
|
|
156
|
-
)
|
|
157
|
-
)
|
|
138
|
+
return ApplicationEntity.use_application_warehouse(self.application_warehouse)
|
|
158
139
|
|
|
159
140
|
@property
|
|
160
141
|
def project_identifier(self) -> str:
|
|
@@ -219,10 +200,14 @@ class NativeAppManager(SqlExecutionMixin):
|
|
|
219
200
|
"""
|
|
220
201
|
Populates the local deploy root from artifact sources.
|
|
221
202
|
"""
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
203
|
+
return ApplicationPackageEntity.bundle(
|
|
204
|
+
project_root=self.project_root,
|
|
205
|
+
deploy_root=self.deploy_root,
|
|
206
|
+
bundle_root=self.bundle_root,
|
|
207
|
+
generated_root=self.generated_root,
|
|
208
|
+
package_name=self.package_name,
|
|
209
|
+
artifacts=self.artifacts,
|
|
210
|
+
)
|
|
226
211
|
|
|
227
212
|
def sync_deploy_root_with_stage(
|
|
228
213
|
self,
|
|
@@ -249,14 +234,10 @@ class NativeAppManager(SqlExecutionMixin):
|
|
|
249
234
|
)
|
|
250
235
|
|
|
251
236
|
def get_existing_app_info(self) -> Optional[dict]:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
with self.use_role(self.app_role):
|
|
257
|
-
return self.show_specific_object(
|
|
258
|
-
"applications", self.app_name, name_col=NAME_COL
|
|
259
|
-
)
|
|
237
|
+
return ApplicationEntity.get_existing_app_info(
|
|
238
|
+
app_name=self.app_name,
|
|
239
|
+
app_role=self.app_role,
|
|
240
|
+
)
|
|
260
241
|
|
|
261
242
|
def get_existing_app_pkg_info(self) -> Optional[dict]:
|
|
262
243
|
return ApplicationPackageEntity.get_existing_app_pkg_info(
|
|
@@ -264,32 +245,19 @@ class NativeAppManager(SqlExecutionMixin):
|
|
|
264
245
|
package_role=self.package_role,
|
|
265
246
|
)
|
|
266
247
|
|
|
267
|
-
def get_objects_owned_by_application(self)
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
results = self._execute_query(
|
|
273
|
-
f"show objects owned by application {self.app_name}"
|
|
274
|
-
).fetchall()
|
|
275
|
-
return [{"name": row[1], "type": row[2]} for row in results]
|
|
248
|
+
def get_objects_owned_by_application(self):
|
|
249
|
+
return ApplicationEntity.get_objects_owned_by_application(
|
|
250
|
+
app_name=self.app_name,
|
|
251
|
+
app_role=self.app_role,
|
|
252
|
+
)
|
|
276
253
|
|
|
277
254
|
def _application_objects_to_str(
|
|
278
255
|
self, application_objects: list[ApplicationOwnedObject]
|
|
279
256
|
) -> str:
|
|
280
|
-
|
|
281
|
-
Returns a list in an "(Object Type) Object Name" format. Database-level and schema-level object names are fully qualified:
|
|
282
|
-
(COMPUTE_POOL) POOL_NAME
|
|
283
|
-
(DATABASE) DB_NAME
|
|
284
|
-
(SCHEMA) DB_NAME.PUBLIC
|
|
285
|
-
...
|
|
286
|
-
"""
|
|
287
|
-
return "\n".join(
|
|
288
|
-
[self._application_object_to_str(obj) for obj in application_objects]
|
|
289
|
-
)
|
|
257
|
+
return ApplicationEntity.application_objects_to_str(application_objects)
|
|
290
258
|
|
|
291
|
-
def _application_object_to_str(self, obj: ApplicationOwnedObject)
|
|
292
|
-
return
|
|
259
|
+
def _application_object_to_str(self, obj: ApplicationOwnedObject):
|
|
260
|
+
return ApplicationEntity.application_object_to_str(obj)
|
|
293
261
|
|
|
294
262
|
def get_snowsight_url(self) -> str:
|
|
295
263
|
"""Returns the URL that can be used to visit this app via Snowsight."""
|
|
@@ -343,35 +311,26 @@ class NativeAppManager(SqlExecutionMixin):
|
|
|
343
311
|
validate: bool = True,
|
|
344
312
|
print_diff: bool = True,
|
|
345
313
|
) -> DiffResult:
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
self.
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
# 4. Execute post-deploy hooks
|
|
368
|
-
with self.use_package_warehouse():
|
|
369
|
-
self.execute_package_post_deploy_hooks()
|
|
370
|
-
|
|
371
|
-
if validate:
|
|
372
|
-
self.validate(use_scratch_stage=False)
|
|
373
|
-
|
|
374
|
-
return diff
|
|
314
|
+
return ApplicationPackageEntity.deploy(
|
|
315
|
+
console=cc,
|
|
316
|
+
project_root=self.project_root,
|
|
317
|
+
deploy_root=self.deploy_root,
|
|
318
|
+
bundle_root=self.bundle_root,
|
|
319
|
+
generated_root=self.generated_root,
|
|
320
|
+
artifacts=self.artifacts,
|
|
321
|
+
package_name=self.package_name,
|
|
322
|
+
package_role=self.package_role,
|
|
323
|
+
package_distribution=self.package_distribution,
|
|
324
|
+
prune=prune,
|
|
325
|
+
recursive=recursive,
|
|
326
|
+
paths=local_paths_to_sync,
|
|
327
|
+
print_diff=print_diff,
|
|
328
|
+
validate=validate,
|
|
329
|
+
stage_fqn=stage_fqn or self.stage_fqn,
|
|
330
|
+
package_warehouse=self.package_warehouse,
|
|
331
|
+
post_deploy_hooks=self.package_post_deploy_hooks,
|
|
332
|
+
package_scripts=self.package_scripts,
|
|
333
|
+
)
|
|
375
334
|
|
|
376
335
|
def deploy_to_scratch_stage_fn(self):
|
|
377
336
|
bundle_map = self.build_bundle()
|
|
@@ -15,135 +15,33 @@
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
from pathlib import Path
|
|
18
|
-
from textwrap import dedent
|
|
19
18
|
from typing import Optional
|
|
20
19
|
|
|
21
20
|
import typer
|
|
22
|
-
from click import UsageError
|
|
23
21
|
from snowflake.cli._plugins.nativeapp.artifacts import BundleMap
|
|
24
|
-
from snowflake.cli._plugins.nativeapp.constants import (
|
|
25
|
-
ALLOWED_SPECIAL_COMMENTS,
|
|
26
|
-
COMMENT_COL,
|
|
27
|
-
PATCH_COL,
|
|
28
|
-
SPECIAL_COMMENT,
|
|
29
|
-
VERSION_COL,
|
|
30
|
-
)
|
|
31
|
-
from snowflake.cli._plugins.nativeapp.exceptions import (
|
|
32
|
-
ApplicationCreatedExternallyError,
|
|
33
|
-
ApplicationPackageDoesNotExistError,
|
|
34
|
-
)
|
|
35
22
|
from snowflake.cli._plugins.nativeapp.manager import (
|
|
36
23
|
NativeAppCommandProcessor,
|
|
37
24
|
NativeAppManager,
|
|
38
25
|
)
|
|
39
26
|
from snowflake.cli._plugins.nativeapp.policy import PolicyBase
|
|
40
|
-
from snowflake.cli._plugins.nativeapp.
|
|
41
|
-
|
|
27
|
+
from snowflake.cli._plugins.nativeapp.same_account_install_method import (
|
|
28
|
+
SameAccountInstallMethod,
|
|
42
29
|
)
|
|
43
|
-
from snowflake.cli._plugins.stage.manager import StageManager
|
|
44
30
|
from snowflake.cli.api.console import cli_console as cc
|
|
31
|
+
from snowflake.cli.api.entities.application_entity import (
|
|
32
|
+
ApplicationEntity,
|
|
33
|
+
)
|
|
45
34
|
from snowflake.cli.api.entities.utils import (
|
|
46
|
-
ensure_correct_owner,
|
|
47
35
|
generic_sql_error_handler,
|
|
48
36
|
)
|
|
49
37
|
from snowflake.cli.api.errno import (
|
|
50
38
|
APPLICATION_NO_LONGER_AVAILABLE,
|
|
51
39
|
APPLICATION_OWNS_EXTERNAL_OBJECTS,
|
|
52
|
-
CANNOT_UPGRADE_FROM_LOOSE_FILES_TO_VERSION,
|
|
53
|
-
CANNOT_UPGRADE_FROM_VERSION_TO_LOOSE_FILES,
|
|
54
|
-
NOT_SUPPORTED_ON_DEV_MODE_APPLICATIONS,
|
|
55
|
-
ONLY_SUPPORTED_ON_DEV_MODE_APPLICATIONS,
|
|
56
40
|
)
|
|
57
41
|
from snowflake.cli.api.exceptions import SnowflakeSQLExecutionError
|
|
58
42
|
from snowflake.cli.api.project.schemas.native_app.native_app import NativeApp
|
|
59
|
-
from snowflake.cli.api.project.util import (
|
|
60
|
-
identifier_to_show_like_pattern,
|
|
61
|
-
unquote_identifier,
|
|
62
|
-
)
|
|
63
|
-
from snowflake.cli.api.utils.cursor import find_all_rows
|
|
64
43
|
from snowflake.connector import ProgrammingError
|
|
65
|
-
from snowflake.connector.cursor import
|
|
66
|
-
|
|
67
|
-
# Reasons why an `alter application ... upgrade` might fail
|
|
68
|
-
UPGRADE_RESTRICTION_CODES = {
|
|
69
|
-
CANNOT_UPGRADE_FROM_LOOSE_FILES_TO_VERSION,
|
|
70
|
-
CANNOT_UPGRADE_FROM_VERSION_TO_LOOSE_FILES,
|
|
71
|
-
ONLY_SUPPORTED_ON_DEV_MODE_APPLICATIONS,
|
|
72
|
-
NOT_SUPPORTED_ON_DEV_MODE_APPLICATIONS,
|
|
73
|
-
APPLICATION_NO_LONGER_AVAILABLE,
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def print_messages(create_or_upgrade_cursor: Optional[SnowflakeCursor]):
|
|
78
|
-
"""
|
|
79
|
-
Shows messages in the console returned by the CREATE or UPGRADE
|
|
80
|
-
APPLICATION command.
|
|
81
|
-
"""
|
|
82
|
-
if not create_or_upgrade_cursor:
|
|
83
|
-
return
|
|
84
|
-
|
|
85
|
-
messages = [row[0] for row in create_or_upgrade_cursor.fetchall()]
|
|
86
|
-
for message in messages:
|
|
87
|
-
cc.warning(message)
|
|
88
|
-
cc.message("")
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
class SameAccountInstallMethod:
|
|
92
|
-
_requires_created_by_cli: bool
|
|
93
|
-
_from_release_directive: bool
|
|
94
|
-
version: Optional[str]
|
|
95
|
-
patch: Optional[int]
|
|
96
|
-
|
|
97
|
-
def __init__(
|
|
98
|
-
self,
|
|
99
|
-
requires_created_by_cli: bool,
|
|
100
|
-
version: Optional[str] = None,
|
|
101
|
-
patch: Optional[int] = None,
|
|
102
|
-
from_release_directive: bool = False,
|
|
103
|
-
):
|
|
104
|
-
self._requires_created_by_cli = requires_created_by_cli
|
|
105
|
-
self.version = version
|
|
106
|
-
self.patch = patch
|
|
107
|
-
self._from_release_directive = from_release_directive
|
|
108
|
-
|
|
109
|
-
@classmethod
|
|
110
|
-
def unversioned_dev(cls):
|
|
111
|
-
"""aka. stage dev aka loose files"""
|
|
112
|
-
return cls(True)
|
|
113
|
-
|
|
114
|
-
@classmethod
|
|
115
|
-
def versioned_dev(cls, version: str, patch: Optional[int] = None):
|
|
116
|
-
return cls(False, version, patch)
|
|
117
|
-
|
|
118
|
-
@classmethod
|
|
119
|
-
def release_directive(cls):
|
|
120
|
-
return cls(False, from_release_directive=True)
|
|
121
|
-
|
|
122
|
-
@property
|
|
123
|
-
def is_dev_mode(self) -> bool:
|
|
124
|
-
return not self._from_release_directive
|
|
125
|
-
|
|
126
|
-
def using_clause(self, app: NativeAppProjectModel) -> str:
|
|
127
|
-
if self._from_release_directive:
|
|
128
|
-
return ""
|
|
129
|
-
|
|
130
|
-
if self.version:
|
|
131
|
-
patch_clause = f"patch {self.patch}" if self.patch else ""
|
|
132
|
-
return f"using version {self.version} {patch_clause}"
|
|
133
|
-
|
|
134
|
-
stage_name = StageManager.quote_stage_name(app.stage_fqn)
|
|
135
|
-
return f"using {stage_name}"
|
|
136
|
-
|
|
137
|
-
def ensure_app_usable(self, app: NativeAppProjectModel, show_app_row: dict):
|
|
138
|
-
"""Raise an exception if we cannot proceed with install given the pre-existing application object"""
|
|
139
|
-
|
|
140
|
-
if self._requires_created_by_cli:
|
|
141
|
-
if show_app_row[COMMENT_COL] not in ALLOWED_SPECIAL_COMMENTS:
|
|
142
|
-
# this application object was not created by this tooling
|
|
143
|
-
raise ApplicationCreatedExternallyError(app.app_name)
|
|
144
|
-
|
|
145
|
-
# expected owner
|
|
146
|
-
ensure_correct_owner(row=show_app_row, role=app.app_role, obj_name=app.app_name)
|
|
44
|
+
from snowflake.connector.cursor import SnowflakeCursor
|
|
147
45
|
|
|
148
46
|
|
|
149
47
|
class NativeAppRunProcessor(NativeAppManager, NativeAppCommandProcessor):
|
|
@@ -165,35 +63,11 @@ class NativeAppRunProcessor(NativeAppManager, NativeAppCommandProcessor):
|
|
|
165
63
|
return show_obj_cursor
|
|
166
64
|
|
|
167
65
|
def get_existing_version_info(self, version: str) -> Optional[dict]:
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
"""
|
|
174
|
-
with self.use_role(self.package_role):
|
|
175
|
-
try:
|
|
176
|
-
query = f"show versions like {identifier_to_show_like_pattern(version)} in application package {self.package_name}"
|
|
177
|
-
cursor = self._execute_query(query, cursor_class=DictCursor)
|
|
178
|
-
|
|
179
|
-
if cursor.rowcount is None:
|
|
180
|
-
raise SnowflakeSQLExecutionError(query)
|
|
181
|
-
|
|
182
|
-
matching_rows = find_all_rows(
|
|
183
|
-
cursor, lambda row: row[VERSION_COL] == unquote_identifier(version)
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
if not matching_rows:
|
|
187
|
-
return None
|
|
188
|
-
|
|
189
|
-
return max(matching_rows, key=lambda row: row[PATCH_COL])
|
|
190
|
-
|
|
191
|
-
except ProgrammingError as err:
|
|
192
|
-
if err.msg.__contains__("does not exist or not authorized"):
|
|
193
|
-
raise ApplicationPackageDoesNotExistError(self.package_name)
|
|
194
|
-
else:
|
|
195
|
-
generic_sql_error_handler(err=err, role=self.package_role)
|
|
196
|
-
return None
|
|
66
|
+
return ApplicationEntity.get_existing_version_info(
|
|
67
|
+
version=version,
|
|
68
|
+
package_name=self.package_name,
|
|
69
|
+
package_role=self.package_role,
|
|
70
|
+
)
|
|
197
71
|
|
|
198
72
|
def drop_application_before_upgrade(
|
|
199
73
|
self, policy: PolicyBase, is_interactive: bool, cascade: bool = False
|
|
@@ -249,90 +123,26 @@ class NativeAppRunProcessor(NativeAppManager, NativeAppCommandProcessor):
|
|
|
249
123
|
install_method: SameAccountInstallMethod,
|
|
250
124
|
is_interactive: bool = False,
|
|
251
125
|
):
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
f"alter application {self.app_name} upgrade {using_clause}",
|
|
273
|
-
)
|
|
274
|
-
print_messages(upgrade_cursor)
|
|
275
|
-
|
|
276
|
-
if install_method.is_dev_mode:
|
|
277
|
-
# if debug_mode is present (controlled), ensure it is up-to-date
|
|
278
|
-
if self.debug_mode is not None:
|
|
279
|
-
self._execute_query(
|
|
280
|
-
f"alter application {self.app_name} set debug_mode = {self.debug_mode}"
|
|
281
|
-
)
|
|
282
|
-
|
|
283
|
-
# hooks always executed after a create or upgrade
|
|
284
|
-
self.execute_app_post_deploy_hooks()
|
|
285
|
-
return
|
|
286
|
-
|
|
287
|
-
except ProgrammingError as err:
|
|
288
|
-
if err.errno not in UPGRADE_RESTRICTION_CODES:
|
|
289
|
-
generic_sql_error_handler(err=err)
|
|
290
|
-
else: # The existing application object was created from a different process.
|
|
291
|
-
cc.warning(err.msg)
|
|
292
|
-
self.drop_application_before_upgrade(policy, is_interactive)
|
|
293
|
-
|
|
294
|
-
# 4. With no (more) existing application objects, create an application object using the release directives
|
|
295
|
-
cc.step(f"Creating new application object {self.app_name} in account.")
|
|
296
|
-
|
|
297
|
-
if self.app_role != self.package_role:
|
|
298
|
-
with self.use_role(self.package_role):
|
|
299
|
-
self._execute_query(
|
|
300
|
-
f"grant install, develop on application package {self.package_name} to role {self.app_role}"
|
|
301
|
-
)
|
|
302
|
-
self._execute_query(
|
|
303
|
-
f"grant usage on schema {self.package_name}.{self.stage_schema} to role {self.app_role}"
|
|
304
|
-
)
|
|
305
|
-
self._execute_query(
|
|
306
|
-
f"grant read on stage {self.stage_fqn} to role {self.app_role}"
|
|
307
|
-
)
|
|
308
|
-
|
|
309
|
-
try:
|
|
310
|
-
# by default, applications are created in debug mode when possible;
|
|
311
|
-
# this can be overridden in the project definition
|
|
312
|
-
debug_mode_clause = ""
|
|
313
|
-
if install_method.is_dev_mode:
|
|
314
|
-
initial_debug_mode = (
|
|
315
|
-
self.debug_mode if self.debug_mode is not None else True
|
|
316
|
-
)
|
|
317
|
-
debug_mode_clause = f"debug_mode = {initial_debug_mode}"
|
|
318
|
-
|
|
319
|
-
using_clause = install_method.using_clause(self._na_project)
|
|
320
|
-
create_cursor = self._execute_query(
|
|
321
|
-
dedent(
|
|
322
|
-
f"""\
|
|
323
|
-
create application {self.app_name}
|
|
324
|
-
from application package {self.package_name} {using_clause} {debug_mode_clause}
|
|
325
|
-
comment = {SPECIAL_COMMENT}
|
|
326
|
-
"""
|
|
327
|
-
),
|
|
328
|
-
)
|
|
329
|
-
print_messages(create_cursor)
|
|
330
|
-
|
|
331
|
-
# hooks always executed after a create or upgrade
|
|
332
|
-
self.execute_app_post_deploy_hooks()
|
|
333
|
-
|
|
334
|
-
except ProgrammingError as err:
|
|
335
|
-
generic_sql_error_handler(err)
|
|
126
|
+
def drop_app():
|
|
127
|
+
self.drop_application_before_upgrade(policy, is_interactive)
|
|
128
|
+
|
|
129
|
+
return ApplicationEntity.create_or_upgrade_app(
|
|
130
|
+
console=cc,
|
|
131
|
+
project_root=self.project_root,
|
|
132
|
+
package_name=self.package_name,
|
|
133
|
+
package_role=self.package_role,
|
|
134
|
+
app_name=self.app_name,
|
|
135
|
+
app_role=self.app_role,
|
|
136
|
+
app_warehouse=self.application_warehouse,
|
|
137
|
+
stage_schema=self.stage_schema,
|
|
138
|
+
stage_fqn=self.stage_fqn,
|
|
139
|
+
debug_mode=self.debug_mode,
|
|
140
|
+
policy=policy,
|
|
141
|
+
install_method=install_method,
|
|
142
|
+
is_interactive=is_interactive,
|
|
143
|
+
post_deploy_hooks=self.app_post_deploy_hooks,
|
|
144
|
+
drop_application_before_upgrade=drop_app,
|
|
145
|
+
)
|
|
336
146
|
|
|
337
147
|
def process(
|
|
338
148
|
self,
|
|
@@ -346,46 +156,32 @@ class NativeAppRunProcessor(NativeAppManager, NativeAppCommandProcessor):
|
|
|
346
156
|
*args,
|
|
347
157
|
**kwargs,
|
|
348
158
|
):
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
"""
|
|
353
|
-
|
|
354
|
-
# same-account release directive
|
|
355
|
-
if from_release_directive:
|
|
356
|
-
self.create_or_upgrade_app(
|
|
357
|
-
policy=policy,
|
|
358
|
-
is_interactive=is_interactive,
|
|
359
|
-
install_method=SameAccountInstallMethod.release_directive(),
|
|
159
|
+
def deploy_package():
|
|
160
|
+
self.deploy(
|
|
161
|
+
bundle_map=bundle_map, prune=True, recursive=True, validate=validate
|
|
360
162
|
)
|
|
361
|
-
return
|
|
362
163
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
self.
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
is_interactive=is_interactive,
|
|
380
|
-
)
|
|
381
|
-
return
|
|
382
|
-
|
|
383
|
-
# unversioned dev
|
|
384
|
-
self.deploy(
|
|
385
|
-
bundle_map=bundle_map, prune=True, recursive=True, validate=validate
|
|
386
|
-
)
|
|
387
|
-
self.create_or_upgrade_app(
|
|
388
|
-
policy=policy,
|
|
164
|
+
def drop_app():
|
|
165
|
+
self.drop_application_before_upgrade(policy, is_interactive)
|
|
166
|
+
|
|
167
|
+
ApplicationEntity.deploy(
|
|
168
|
+
console=cc,
|
|
169
|
+
project_root=self.project_root,
|
|
170
|
+
app_name=self.app_name,
|
|
171
|
+
app_role=self.app_role,
|
|
172
|
+
app_warehouse=self.application_warehouse,
|
|
173
|
+
package_name=self.package_name,
|
|
174
|
+
package_role=self.package_role,
|
|
175
|
+
stage_schema=self.stage_schema,
|
|
176
|
+
stage_fqn=self.stage_fqn,
|
|
177
|
+
debug_mode=self.debug_mode,
|
|
178
|
+
validate=validate,
|
|
179
|
+
from_release_directive=from_release_directive,
|
|
389
180
|
is_interactive=is_interactive,
|
|
390
|
-
|
|
181
|
+
policy=policy,
|
|
182
|
+
version=version,
|
|
183
|
+
patch=patch,
|
|
184
|
+
post_deploy_hooks=self.app_post_deploy_hooks,
|
|
185
|
+
deploy_package=deploy_package,
|
|
186
|
+
drop_application_before_upgrade=drop_app,
|
|
391
187
|
)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from snowflake.cli._plugins.nativeapp.constants import (
|
|
4
|
+
ALLOWED_SPECIAL_COMMENTS,
|
|
5
|
+
COMMENT_COL,
|
|
6
|
+
)
|
|
7
|
+
from snowflake.cli._plugins.nativeapp.exceptions import (
|
|
8
|
+
ApplicationCreatedExternallyError,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
# from snowflake.cli._plugins.nativeapp.project_model import NativeAppProjectModel
|
|
12
|
+
from snowflake.cli._plugins.stage.manager import StageManager
|
|
13
|
+
from snowflake.cli.api.entities.utils import ensure_correct_owner
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SameAccountInstallMethod:
|
|
17
|
+
_requires_created_by_cli: bool
|
|
18
|
+
_from_release_directive: bool
|
|
19
|
+
version: Optional[str]
|
|
20
|
+
patch: Optional[int]
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
requires_created_by_cli: bool,
|
|
25
|
+
version: Optional[str] = None,
|
|
26
|
+
patch: Optional[int] = None,
|
|
27
|
+
from_release_directive: bool = False,
|
|
28
|
+
):
|
|
29
|
+
self._requires_created_by_cli = requires_created_by_cli
|
|
30
|
+
self.version = version
|
|
31
|
+
self.patch = patch
|
|
32
|
+
self._from_release_directive = from_release_directive
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def unversioned_dev(cls):
|
|
36
|
+
"""aka. stage dev aka loose files"""
|
|
37
|
+
return cls(True)
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def versioned_dev(cls, version: str, patch: Optional[int] = None):
|
|
41
|
+
return cls(False, version, patch)
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def release_directive(cls):
|
|
45
|
+
return cls(False, from_release_directive=True)
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def is_dev_mode(self) -> bool:
|
|
49
|
+
return not self._from_release_directive
|
|
50
|
+
|
|
51
|
+
def using_clause(
|
|
52
|
+
self,
|
|
53
|
+
stage_fqn: str,
|
|
54
|
+
) -> str:
|
|
55
|
+
if self._from_release_directive:
|
|
56
|
+
return ""
|
|
57
|
+
|
|
58
|
+
if self.version:
|
|
59
|
+
patch_clause = f"patch {self.patch}" if self.patch else ""
|
|
60
|
+
return f"using version {self.version} {patch_clause}"
|
|
61
|
+
|
|
62
|
+
stage_name = StageManager.quote_stage_name(stage_fqn)
|
|
63
|
+
return f"using {stage_name}"
|
|
64
|
+
|
|
65
|
+
def ensure_app_usable(self, app_name: str, app_role: str, show_app_row: dict):
|
|
66
|
+
"""Raise an exception if we cannot proceed with install given the pre-existing application object"""
|
|
67
|
+
|
|
68
|
+
if self._requires_created_by_cli:
|
|
69
|
+
if show_app_row[COMMENT_COL] not in ALLOWED_SPECIAL_COMMENTS:
|
|
70
|
+
# this application object was not created by this tooling
|
|
71
|
+
raise ApplicationCreatedExternallyError(app_name)
|
|
72
|
+
|
|
73
|
+
# expected owner
|
|
74
|
+
ensure_correct_owner(row=show_app_row, role=app_role, obj_name=app_name)
|