snowflake-cli 3.10.1__py3-none-any.whl → 3.11.0__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/auth/__init__.py +13 -0
- snowflake/cli/_app/auth/errors.py +28 -0
- snowflake/cli/_app/auth/oidc_providers.py +393 -0
- snowflake/cli/_app/constants.py +10 -0
- snowflake/cli/_app/snow_connector.py +35 -0
- snowflake/cli/_plugins/auth/__init__.py +4 -2
- snowflake/cli/_plugins/auth/keypair/commands.py +2 -0
- snowflake/cli/_plugins/auth/oidc/__init__.py +13 -0
- snowflake/cli/_plugins/auth/oidc/commands.py +47 -0
- snowflake/cli/_plugins/auth/oidc/manager.py +66 -0
- snowflake/cli/_plugins/auth/oidc/plugin_spec.py +30 -0
- snowflake/cli/_plugins/connection/commands.py +37 -3
- snowflake/cli/_plugins/dbt/manager.py +1 -3
- snowflake/cli/_plugins/dcm/commands.py +79 -88
- snowflake/cli/_plugins/dcm/manager.py +17 -57
- snowflake/cli/_plugins/notebook/notebook_entity.py +2 -0
- snowflake/cli/_plugins/notebook/notebook_entity_model.py +8 -1
- snowflake/cli/_plugins/object/command_aliases.py +16 -1
- snowflake/cli/_plugins/object/commands.py +27 -1
- snowflake/cli/_plugins/object/manager.py +12 -1
- snowflake/cli/_plugins/snowpark/commands.py +8 -1
- snowflake/cli/api/commands/decorators.py +7 -0
- snowflake/cli/api/commands/flags.py +26 -0
- snowflake/cli/api/config.py +24 -0
- snowflake/cli/api/connections.py +1 -0
- snowflake/cli/api/utils/dict_utils.py +42 -1
- {snowflake_cli-3.10.1.dist-info → snowflake_cli-3.11.0.dist-info}/METADATA +12 -38
- {snowflake_cli-3.10.1.dist-info → snowflake_cli-3.11.0.dist-info}/RECORD +32 -25
- {snowflake_cli-3.10.1.dist-info → snowflake_cli-3.11.0.dist-info}/WHEEL +0 -0
- {snowflake_cli-3.10.1.dist-info → snowflake_cli-3.11.0.dist-info}/entry_points.txt +0 -0
- {snowflake_cli-3.10.1.dist-info → snowflake_cli-3.11.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Copyright (c) 2024 Snowflake Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from snowflake.cli._plugins.auth import app
|
|
16
|
+
from snowflake.cli.api.plugins.command import (
|
|
17
|
+
SNOWCLI_ROOT_COMMAND_PATH,
|
|
18
|
+
CommandSpec,
|
|
19
|
+
CommandType,
|
|
20
|
+
plugin_hook_impl,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@plugin_hook_impl
|
|
25
|
+
def command_spec():
|
|
26
|
+
return CommandSpec(
|
|
27
|
+
parent_command_path=SNOWCLI_ROOT_COMMAND_PATH,
|
|
28
|
+
command_type=CommandType.COMMAND_GROUP,
|
|
29
|
+
typer_instance=app.create_instance(),
|
|
30
|
+
)
|
|
@@ -53,6 +53,7 @@ from snowflake.cli.api.commands.flags import (
|
|
|
53
53
|
TokenFilePathOption,
|
|
54
54
|
UserOption,
|
|
55
55
|
WarehouseOption,
|
|
56
|
+
WorkloadIdentityProviderOption,
|
|
56
57
|
)
|
|
57
58
|
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
|
|
58
59
|
from snowflake.cli.api.config import (
|
|
@@ -62,7 +63,9 @@ from snowflake.cli.api.config import (
|
|
|
62
63
|
get_all_connections,
|
|
63
64
|
get_connection_dict,
|
|
64
65
|
get_default_connection_name,
|
|
66
|
+
remove_connection_from_proper_file,
|
|
65
67
|
set_config_value,
|
|
68
|
+
unset_config_value,
|
|
66
69
|
)
|
|
67
70
|
from snowflake.cli.api.console import cli_console
|
|
68
71
|
from snowflake.cli.api.constants import ObjectType
|
|
@@ -220,6 +223,12 @@ def add(
|
|
|
220
223
|
*AuthenticatorOption.param_decls,
|
|
221
224
|
help="Chosen authenticator, if other than password-based",
|
|
222
225
|
),
|
|
226
|
+
workload_identity_provider: Optional[str] = typer.Option(
|
|
227
|
+
None,
|
|
228
|
+
"-W",
|
|
229
|
+
*WorkloadIdentityProviderOption.param_decls,
|
|
230
|
+
help="Workload identity provider type",
|
|
231
|
+
),
|
|
223
232
|
private_key_file: Optional[str] = typer.Option(
|
|
224
233
|
None,
|
|
225
234
|
"--private-key",
|
|
@@ -256,6 +265,7 @@ def add(
|
|
|
256
265
|
"port": port,
|
|
257
266
|
"region": region,
|
|
258
267
|
"authenticator": authenticator,
|
|
268
|
+
"workload_identity_provider": workload_identity_provider,
|
|
259
269
|
"private_key_file": private_key_file,
|
|
260
270
|
"token_file_path": token_file_path,
|
|
261
271
|
}
|
|
@@ -317,6 +327,30 @@ def add(
|
|
|
317
327
|
)
|
|
318
328
|
|
|
319
329
|
|
|
330
|
+
@app.command(requires_connection=False)
|
|
331
|
+
def remove(
|
|
332
|
+
connection_name: str = typer.Argument(
|
|
333
|
+
help="Name of the connection to remove.",
|
|
334
|
+
show_default=False,
|
|
335
|
+
),
|
|
336
|
+
**options,
|
|
337
|
+
):
|
|
338
|
+
"""Removes a connection from configuration file."""
|
|
339
|
+
if not connection_exists(connection_name):
|
|
340
|
+
raise UsageError(f"Connection {connection_name} does not exist.")
|
|
341
|
+
|
|
342
|
+
is_default = get_default_connection_name() == connection_name
|
|
343
|
+
if is_default:
|
|
344
|
+
unset_config_value(path=["default_connection_name"])
|
|
345
|
+
|
|
346
|
+
connections_file = remove_connection_from_proper_file(connection_name)
|
|
347
|
+
|
|
348
|
+
return MessageResult(
|
|
349
|
+
f"Removed connection {connection_name} from {connections_file}."
|
|
350
|
+
f"{' It was the default connection, so default connection is now unset.' if is_default else ''}"
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
|
|
320
354
|
@app.command(requires_connection=True)
|
|
321
355
|
def test(
|
|
322
356
|
**options,
|
|
@@ -355,9 +389,9 @@ def test(
|
|
|
355
389
|
"Host": conn.host,
|
|
356
390
|
"Account": conn.account,
|
|
357
391
|
"User": conn.user,
|
|
358
|
-
"Role": f
|
|
359
|
-
"Database": f
|
|
360
|
-
"Warehouse": f
|
|
392
|
+
"Role": f"{conn.role or 'not set'}",
|
|
393
|
+
"Database": f"{conn.database or 'not set'}",
|
|
394
|
+
"Warehouse": f"{conn.warehouse or 'not set'}",
|
|
361
395
|
}
|
|
362
396
|
|
|
363
397
|
if conn_ctx.enable_diag:
|
|
@@ -26,7 +26,6 @@ from snowflake.cli.api.console import cli_console
|
|
|
26
26
|
from snowflake.cli.api.constants import DEFAULT_SIZE_LIMIT_MB, ObjectType
|
|
27
27
|
from snowflake.cli.api.exceptions import CliError
|
|
28
28
|
from snowflake.cli.api.identifiers import FQN
|
|
29
|
-
from snowflake.cli.api.project.util import unquote_identifier
|
|
30
29
|
from snowflake.cli.api.secure_path import SecurePath
|
|
31
30
|
from snowflake.cli.api.sql_execution import SqlExecutionMixin
|
|
32
31
|
from snowflake.connector.cursor import SnowflakeCursor
|
|
@@ -67,8 +66,7 @@ class DBTManager(SqlExecutionMixin):
|
|
|
67
66
|
|
|
68
67
|
with cli_console.phase("Creating temporary stage"):
|
|
69
68
|
stage_manager = StageManager()
|
|
70
|
-
|
|
71
|
-
stage_fqn = FQN.from_string(f"DBT_{unquoted_name}_STAGE").using_context()
|
|
69
|
+
stage_fqn = FQN.from_string(f"dbt_{fqn.name}_stage").using_context()
|
|
72
70
|
stage_name = stage_manager.get_standard_stage_prefix(stage_fqn)
|
|
73
71
|
stage_manager.create(stage_fqn, temporary=True)
|
|
74
72
|
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
-
|
|
15
14
|
from typing import List, Optional
|
|
16
15
|
|
|
17
16
|
import typer
|
|
@@ -22,13 +21,13 @@ from snowflake.cli._plugins.dcm.manager import DCMProjectManager
|
|
|
22
21
|
from snowflake.cli._plugins.object.command_aliases import add_object_command_aliases
|
|
23
22
|
from snowflake.cli._plugins.object.commands import scope_option
|
|
24
23
|
from snowflake.cli._plugins.object.manager import ObjectManager
|
|
24
|
+
from snowflake.cli.api.artifacts.upload import sync_artifacts_with_stage
|
|
25
25
|
from snowflake.cli.api.cli_global_context import get_cli_context
|
|
26
26
|
from snowflake.cli.api.commands.decorators import with_project_definition
|
|
27
27
|
from snowflake.cli.api.commands.flags import (
|
|
28
28
|
IfExistsOption,
|
|
29
29
|
IfNotExistsOption,
|
|
30
30
|
OverrideableOption,
|
|
31
|
-
PruneOption,
|
|
32
31
|
entity_argument,
|
|
33
32
|
identifier_argument,
|
|
34
33
|
like_option,
|
|
@@ -46,6 +45,7 @@ from snowflake.cli.api.output.types import (
|
|
|
46
45
|
QueryJsonValueResult,
|
|
47
46
|
QueryResult,
|
|
48
47
|
)
|
|
48
|
+
from snowflake.cli.api.project.project_paths import ProjectPaths
|
|
49
49
|
|
|
50
50
|
app = SnowTyperFactory(
|
|
51
51
|
name="dcm",
|
|
@@ -54,12 +54,6 @@ app = SnowTyperFactory(
|
|
|
54
54
|
)
|
|
55
55
|
|
|
56
56
|
dcm_identifier = identifier_argument(sf_object="DCM Project", example="MY_PROJECT")
|
|
57
|
-
version_flag = typer.Option(
|
|
58
|
-
None,
|
|
59
|
-
"--version",
|
|
60
|
-
help="Version of the DCM Project to use. If not specified default version is used. For names containing '$', use single quotes to prevent shell expansion (e.g., 'VERSION$1').",
|
|
61
|
-
show_default=False,
|
|
62
|
-
)
|
|
63
57
|
variables_flag = variables_option(
|
|
64
58
|
'Variables for the execution context; for example: `-D "<key>=<value>"`.'
|
|
65
59
|
)
|
|
@@ -72,6 +66,41 @@ configuration_flag = typer.Option(
|
|
|
72
66
|
from_option = OverrideableOption(
|
|
73
67
|
None,
|
|
74
68
|
"--from",
|
|
69
|
+
mutually_exclusive=["prune"],
|
|
70
|
+
show_default=False,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
prune_option = OverrideableOption(
|
|
74
|
+
False,
|
|
75
|
+
"--prune",
|
|
76
|
+
help="Remove unused artifacts from the stage during sync. Mutually exclusive with --from.",
|
|
77
|
+
mutually_exclusive=["from_stage"],
|
|
78
|
+
show_default=False,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
alias_option = typer.Option(
|
|
82
|
+
None,
|
|
83
|
+
"--alias",
|
|
84
|
+
help="Alias for the deployment.",
|
|
85
|
+
show_default=False,
|
|
86
|
+
)
|
|
87
|
+
output_path_option = OverrideableOption(
|
|
88
|
+
None,
|
|
89
|
+
"--output-path",
|
|
90
|
+
show_default=False,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
terse_option = typer.Option(
|
|
94
|
+
False,
|
|
95
|
+
"--terse",
|
|
96
|
+
help="Returns only a subset of output columns.",
|
|
97
|
+
show_default=False,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
limit_option = typer.Option(
|
|
101
|
+
None,
|
|
102
|
+
"--limit",
|
|
103
|
+
help="Limits the maximum number of rows returned.",
|
|
75
104
|
show_default=False,
|
|
76
105
|
)
|
|
77
106
|
|
|
@@ -85,32 +114,33 @@ add_object_command_aliases(
|
|
|
85
114
|
),
|
|
86
115
|
scope_option=scope_option(help_example="`list --in database my_db`"),
|
|
87
116
|
ommit_commands=["create"],
|
|
117
|
+
terse_option=terse_option,
|
|
118
|
+
limit_option=limit_option,
|
|
88
119
|
)
|
|
89
120
|
|
|
90
121
|
|
|
91
122
|
@app.command(requires_connection=True)
|
|
92
123
|
def deploy(
|
|
93
124
|
identifier: FQN = dcm_identifier,
|
|
94
|
-
version: Optional[str] = version_flag,
|
|
95
125
|
from_stage: Optional[str] = from_option(
|
|
96
|
-
help="
|
|
126
|
+
help="Deploy DCM Project deployment from a given stage."
|
|
97
127
|
),
|
|
98
128
|
variables: Optional[List[str]] = variables_flag,
|
|
99
129
|
configuration: Optional[str] = configuration_flag,
|
|
130
|
+
alias: Optional[str] = alias_option,
|
|
131
|
+
prune: bool = prune_option(),
|
|
100
132
|
**options,
|
|
101
133
|
):
|
|
102
134
|
"""
|
|
103
135
|
Applies changes defined in DCM Project to Snowflake.
|
|
104
136
|
"""
|
|
105
|
-
if version and from_stage:
|
|
106
|
-
raise CliError("--version and --from are mutually exclusive.")
|
|
107
|
-
|
|
108
137
|
result = DCMProjectManager().execute(
|
|
109
138
|
project_name=identifier,
|
|
110
139
|
configuration=configuration,
|
|
111
|
-
|
|
112
|
-
from_stage=from_stage,
|
|
140
|
+
from_stage=from_stage if from_stage else _sync_local_files(prune=prune),
|
|
113
141
|
variables=variables,
|
|
142
|
+
alias=alias,
|
|
143
|
+
output_path=None,
|
|
114
144
|
)
|
|
115
145
|
return QueryJsonValueResult(result)
|
|
116
146
|
|
|
@@ -118,27 +148,27 @@ def deploy(
|
|
|
118
148
|
@app.command(requires_connection=True)
|
|
119
149
|
def plan(
|
|
120
150
|
identifier: FQN = dcm_identifier,
|
|
121
|
-
version: Optional[str] = version_flag,
|
|
122
151
|
from_stage: Optional[str] = from_option(
|
|
123
|
-
help="Plan DCM Project deployment from given stage
|
|
152
|
+
help="Plan DCM Project deployment from a given stage."
|
|
124
153
|
),
|
|
125
154
|
variables: Optional[List[str]] = variables_flag,
|
|
126
155
|
configuration: Optional[str] = configuration_flag,
|
|
156
|
+
prune: bool = prune_option(),
|
|
157
|
+
output_path: Optional[str] = output_path_option(
|
|
158
|
+
help="Stage path where the deployment plan output will be stored."
|
|
159
|
+
),
|
|
127
160
|
**options,
|
|
128
161
|
):
|
|
129
162
|
"""
|
|
130
163
|
Plans a DCM Project deployment (validates without executing).
|
|
131
164
|
"""
|
|
132
|
-
if version and from_stage:
|
|
133
|
-
raise CliError("--version and --from are mutually exclusive.")
|
|
134
|
-
|
|
135
165
|
result = DCMProjectManager().execute(
|
|
136
166
|
project_name=identifier,
|
|
137
167
|
configuration=configuration,
|
|
138
|
-
|
|
139
|
-
from_stage=from_stage,
|
|
168
|
+
from_stage=from_stage if from_stage else _sync_local_files(prune=prune),
|
|
140
169
|
dry_run=True,
|
|
141
170
|
variables=variables,
|
|
171
|
+
output_path=output_path,
|
|
142
172
|
)
|
|
143
173
|
return QueryJsonValueResult(result)
|
|
144
174
|
|
|
@@ -147,11 +177,6 @@ def plan(
|
|
|
147
177
|
@with_project_definition()
|
|
148
178
|
def create(
|
|
149
179
|
entity_id: str = entity_argument("dcm"),
|
|
150
|
-
no_version: bool = typer.Option(
|
|
151
|
-
False,
|
|
152
|
-
"--no-version",
|
|
153
|
-
help="Do not initialize DCM Project with a new version, only create the snowflake object.",
|
|
154
|
-
),
|
|
155
180
|
if_not_exists: bool = IfNotExistsOption(
|
|
156
181
|
help="Do nothing if the project already exists."
|
|
157
182
|
),
|
|
@@ -159,7 +184,6 @@ def create(
|
|
|
159
184
|
):
|
|
160
185
|
"""
|
|
161
186
|
Creates a DCM Project in Snowflake.
|
|
162
|
-
By default, the DCM Project is initialized with a new version created from local files.
|
|
163
187
|
"""
|
|
164
188
|
cli_context = get_cli_context()
|
|
165
189
|
project: DCMProjectEntityModel = get_entity_for_operation(
|
|
@@ -175,76 +199,23 @@ def create(
|
|
|
175
199
|
return MessageResult(message)
|
|
176
200
|
raise CliError(message)
|
|
177
201
|
|
|
178
|
-
if
|
|
179
|
-
object_type="stage", fqn=FQN.from_stage(project.stage)
|
|
180
|
-
):
|
|
202
|
+
if om.object_exists(object_type="stage", fqn=FQN.from_stage(project.stage)):
|
|
181
203
|
raise CliError(f"Stage '{project.stage}' already exists.")
|
|
182
204
|
|
|
183
205
|
dpm = DCMProjectManager()
|
|
184
206
|
with cli_console.phase(f"Creating DCM Project '{project.fqn}'"):
|
|
185
|
-
dpm.create(project=project
|
|
207
|
+
dpm.create(project=project)
|
|
186
208
|
|
|
187
|
-
|
|
188
|
-
return MessageResult(f"DCM Project '{project.fqn}' successfully created.")
|
|
189
|
-
return MessageResult(
|
|
190
|
-
f"DCM Project '{project.fqn}' successfully created and initial version is added."
|
|
191
|
-
)
|
|
209
|
+
return MessageResult(f"DCM Project '{project.fqn}' successfully created.")
|
|
192
210
|
|
|
193
211
|
|
|
194
212
|
@app.command(requires_connection=True)
|
|
195
|
-
|
|
196
|
-
def add_version(
|
|
197
|
-
entity_id: str = entity_argument("dcm"),
|
|
198
|
-
_from: Optional[str] = from_option(
|
|
199
|
-
help="Create a new version using given stage instead of uploading local files."
|
|
200
|
-
),
|
|
201
|
-
_alias: Optional[str] = typer.Option(
|
|
202
|
-
None, "--alias", help="Alias for the version.", show_default=False
|
|
203
|
-
),
|
|
204
|
-
comment: Optional[str] = typer.Option(
|
|
205
|
-
None, "--comment", help="Version comment.", show_default=False
|
|
206
|
-
),
|
|
207
|
-
prune: bool = PruneOption(default=True),
|
|
208
|
-
**options,
|
|
209
|
-
):
|
|
210
|
-
"""Uploads local files to Snowflake and cerates a new DCM Project version."""
|
|
211
|
-
if _from is not None and prune:
|
|
212
|
-
cli_console.warning(
|
|
213
|
-
"When `--from` option is used, `--prune` option will be ignored and files from stage will be used as they are."
|
|
214
|
-
)
|
|
215
|
-
prune = False
|
|
216
|
-
cli_context = get_cli_context()
|
|
217
|
-
project: DCMProjectEntityModel = get_entity_for_operation(
|
|
218
|
-
cli_context=cli_context,
|
|
219
|
-
entity_id=entity_id,
|
|
220
|
-
project_definition=cli_context.project_definition,
|
|
221
|
-
entity_type="dcm",
|
|
222
|
-
)
|
|
223
|
-
om = ObjectManager()
|
|
224
|
-
if not om.object_exists(object_type="dcm", fqn=project.fqn):
|
|
225
|
-
raise CliError(
|
|
226
|
-
f"DCM Project '{project.fqn}' does not exist. Use `dcm create` command first."
|
|
227
|
-
)
|
|
228
|
-
DCMProjectManager().add_version(
|
|
229
|
-
project=project,
|
|
230
|
-
prune=prune,
|
|
231
|
-
from_stage=_from,
|
|
232
|
-
alias=_alias,
|
|
233
|
-
comment=comment,
|
|
234
|
-
)
|
|
235
|
-
alias_str = "" if _alias is None else f"'{_alias}' "
|
|
236
|
-
return MessageResult(
|
|
237
|
-
f"New version {alias_str}added to DCM Project '{project.fqn}'."
|
|
238
|
-
)
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
@app.command(requires_connection=True)
|
|
242
|
-
def list_versions(
|
|
213
|
+
def list_deployments(
|
|
243
214
|
identifier: FQN = dcm_identifier,
|
|
244
215
|
**options,
|
|
245
216
|
):
|
|
246
217
|
"""
|
|
247
|
-
Lists
|
|
218
|
+
Lists deployments of given DCM Project.
|
|
248
219
|
"""
|
|
249
220
|
pm = DCMProjectManager()
|
|
250
221
|
results = pm.list_versions(project_name=identifier)
|
|
@@ -252,7 +223,7 @@ def list_versions(
|
|
|
252
223
|
|
|
253
224
|
|
|
254
225
|
@app.command(requires_connection=True)
|
|
255
|
-
def
|
|
226
|
+
def drop_deployment(
|
|
256
227
|
identifier: FQN = dcm_identifier,
|
|
257
228
|
version_name: str = typer.Argument(
|
|
258
229
|
help="Name or alias of the version to drop. For names containing '$', use single quotes to prevent shell expansion (e.g., 'VERSION$1').",
|
|
@@ -272,7 +243,7 @@ def drop_version(
|
|
|
272
243
|
)
|
|
273
244
|
|
|
274
245
|
dpm = DCMProjectManager()
|
|
275
|
-
dpm.
|
|
246
|
+
dpm.drop_deployment(
|
|
276
247
|
project_name=identifier,
|
|
277
248
|
version_name=version_name,
|
|
278
249
|
if_exists=if_exists,
|
|
@@ -280,3 +251,23 @@ def drop_version(
|
|
|
280
251
|
return MessageResult(
|
|
281
252
|
f"Version '{version_name}' dropped from DCM Project '{identifier}'."
|
|
282
253
|
)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _sync_local_files(prune: bool = False) -> str:
|
|
257
|
+
cli_context = get_cli_context()
|
|
258
|
+
project_entity = get_entity_for_operation(
|
|
259
|
+
cli_context=cli_context,
|
|
260
|
+
entity_id=None,
|
|
261
|
+
project_definition=cli_context.project_definition,
|
|
262
|
+
entity_type="dcm",
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
with cli_console.phase("Syncing local files to stage"):
|
|
266
|
+
sync_artifacts_with_stage(
|
|
267
|
+
project_paths=ProjectPaths(project_root=cli_context.project_root),
|
|
268
|
+
stage_root=project_entity.stage,
|
|
269
|
+
artifacts=project_entity.artifacts,
|
|
270
|
+
prune=prune,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
return project_entity.stage
|
|
@@ -11,37 +11,36 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
-
|
|
15
|
-
from typing import List
|
|
14
|
+
|
|
15
|
+
from typing import List
|
|
16
16
|
|
|
17
17
|
from snowflake.cli._plugins.dcm.dcm_project_entity_model import DCMProjectEntityModel
|
|
18
18
|
from snowflake.cli._plugins.stage.manager import StageManager
|
|
19
|
-
from snowflake.cli.api.artifacts.upload import sync_artifacts_with_stage
|
|
20
|
-
from snowflake.cli.api.cli_global_context import get_cli_context
|
|
21
19
|
from snowflake.cli.api.commands.utils import parse_key_value_variables
|
|
22
|
-
from snowflake.cli.api.console.console import cli_console
|
|
23
20
|
from snowflake.cli.api.identifiers import FQN
|
|
24
|
-
from snowflake.cli.api.project.project_paths import ProjectPaths
|
|
25
21
|
from snowflake.cli.api.sql_execution import SqlExecutionMixin
|
|
26
22
|
from snowflake.cli.api.stage_path import StagePath
|
|
27
|
-
from snowflake.connector.cursor import SnowflakeCursor
|
|
28
23
|
|
|
29
24
|
|
|
30
25
|
class DCMProjectManager(SqlExecutionMixin):
|
|
31
26
|
def execute(
|
|
32
27
|
self,
|
|
33
28
|
project_name: FQN,
|
|
29
|
+
from_stage: str,
|
|
34
30
|
configuration: str | None = None,
|
|
35
|
-
version: str | None = None,
|
|
36
|
-
from_stage: str | None = None,
|
|
37
31
|
variables: List[str] | None = None,
|
|
38
32
|
dry_run: bool = False,
|
|
33
|
+
alias: str | None = None,
|
|
34
|
+
output_path: str | None = None,
|
|
39
35
|
):
|
|
36
|
+
|
|
40
37
|
query = f"EXECUTE DCM PROJECT {project_name.sql_identifier}"
|
|
41
38
|
if dry_run:
|
|
42
39
|
query += " PLAN"
|
|
43
40
|
else:
|
|
44
41
|
query += " DEPLOY"
|
|
42
|
+
if alias:
|
|
43
|
+
query += f" AS {alias}"
|
|
45
44
|
if configuration or variables:
|
|
46
45
|
query += f" USING"
|
|
47
46
|
if configuration:
|
|
@@ -50,23 +49,16 @@ class DCMProjectManager(SqlExecutionMixin):
|
|
|
50
49
|
query += StageManager.parse_execute_variables(
|
|
51
50
|
parse_key_value_variables(variables)
|
|
52
51
|
).removeprefix(" using")
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
query += f"
|
|
52
|
+
stage_path = StagePath.from_stage_str(from_stage)
|
|
53
|
+
query += f" FROM {stage_path.absolute_path()}"
|
|
54
|
+
if output_path:
|
|
55
|
+
output_stage_path = StagePath.from_stage_str(output_path)
|
|
56
|
+
query += f" OUTPUT_PATH {output_stage_path.absolute_path()}"
|
|
58
57
|
return self.execute_query(query=query)
|
|
59
58
|
|
|
60
|
-
def
|
|
61
|
-
query =
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def create(
|
|
65
|
-
self, project: DCMProjectEntityModel, initialize_version_from_local_files: bool
|
|
66
|
-
) -> None:
|
|
67
|
-
self._create_object(project.fqn)
|
|
68
|
-
if initialize_version_from_local_files:
|
|
69
|
-
self.add_version(project=project)
|
|
59
|
+
def create(self, project: DCMProjectEntityModel) -> None:
|
|
60
|
+
query = f"CREATE DCM PROJECT {project.fqn.sql_identifier}"
|
|
61
|
+
self.execute_query(query)
|
|
70
62
|
|
|
71
63
|
def _create_version(
|
|
72
64
|
self,
|
|
@@ -84,43 +76,11 @@ class DCMProjectManager(SqlExecutionMixin):
|
|
|
84
76
|
query += f" COMMENT = '{comment}'"
|
|
85
77
|
return self.execute_query(query=query)
|
|
86
78
|
|
|
87
|
-
def add_version(
|
|
88
|
-
self,
|
|
89
|
-
project: DCMProjectEntityModel,
|
|
90
|
-
prune: bool = False,
|
|
91
|
-
from_stage: Optional[str] = None,
|
|
92
|
-
alias: Optional[str] = None,
|
|
93
|
-
comment: Optional[str] = None,
|
|
94
|
-
):
|
|
95
|
-
"""
|
|
96
|
-
Adds a version to DCM Project. If [from_stage] is not defined,
|
|
97
|
-
uploads local files to the stage defined in DCM Project definition.
|
|
98
|
-
"""
|
|
99
|
-
|
|
100
|
-
if not from_stage:
|
|
101
|
-
cli_context = get_cli_context()
|
|
102
|
-
from_stage = project.stage
|
|
103
|
-
with cli_console.phase("Uploading artifacts"):
|
|
104
|
-
sync_artifacts_with_stage(
|
|
105
|
-
project_paths=ProjectPaths(project_root=cli_context.project_root),
|
|
106
|
-
stage_root=from_stage,
|
|
107
|
-
artifacts=project.artifacts,
|
|
108
|
-
prune=prune,
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
with cli_console.phase(f"Creating DCM Project version from stage {from_stage}"):
|
|
112
|
-
return self._create_version(
|
|
113
|
-
project_name=project.fqn,
|
|
114
|
-
from_stage=from_stage, # type:ignore
|
|
115
|
-
alias=alias,
|
|
116
|
-
comment=comment,
|
|
117
|
-
)
|
|
118
|
-
|
|
119
79
|
def list_versions(self, project_name: FQN):
|
|
120
80
|
query = f"SHOW VERSIONS IN DCM PROJECT {project_name.identifier}"
|
|
121
81
|
return self.execute_query(query=query)
|
|
122
82
|
|
|
123
|
-
def
|
|
83
|
+
def drop_deployment(
|
|
124
84
|
self,
|
|
125
85
|
project_name: FQN,
|
|
126
86
|
version_name: str,
|
|
@@ -60,6 +60,8 @@ class NotebookEntity(EntityBase[NotebookEntityModel]):
|
|
|
60
60
|
query += f"\nCOMPUTE_POOL = '{self.model.compute_pool}'"
|
|
61
61
|
if self.model.runtime_name:
|
|
62
62
|
query += f"\nRUNTIME_NAME = '{self.model.runtime_name}'"
|
|
63
|
+
if self.model.runtime_environment_version and not self.model.compute_pool:
|
|
64
|
+
query += f"\nRUNTIME_ENVIRONMENT_VERSION = '{self.model.runtime_environment_version}'"
|
|
63
65
|
|
|
64
66
|
query += (
|
|
65
67
|
";\n// Cannot use IDENTIFIER(...)"
|
|
@@ -20,6 +20,9 @@ class NotebookEntityModel(EntityModelBaseWithArtifacts):
|
|
|
20
20
|
)
|
|
21
21
|
notebook_file: Path = Field(title="Notebook file")
|
|
22
22
|
query_warehouse: str = Field(title="Snowflake warehouse to execute the notebook")
|
|
23
|
+
runtime_environment_version: Optional[str] = Field(
|
|
24
|
+
title="Runtime environment version", default=None
|
|
25
|
+
)
|
|
23
26
|
compute_pool: Optional[str] = Field(
|
|
24
27
|
title="Compute pool to run the notebook in", default=None
|
|
25
28
|
)
|
|
@@ -37,6 +40,10 @@ class NotebookEntityModel(EntityModelBaseWithArtifacts):
|
|
|
37
40
|
def validate_container_setup(self):
|
|
38
41
|
if self.compute_pool and not self.runtime_name:
|
|
39
42
|
raise ValueError("compute_pool is specified without runtime_name")
|
|
40
|
-
if self.runtime_name and not self.compute_pool
|
|
43
|
+
if self.runtime_name and not self.compute_pool:
|
|
41
44
|
raise ValueError("runtime_name is specified without compute_pool")
|
|
45
|
+
if self.compute_pool and self.runtime_environment_version:
|
|
46
|
+
raise ValueError(
|
|
47
|
+
"runtime_environment_version is only applicable for notebooks using warehouse, not compute pool"
|
|
48
|
+
)
|
|
42
49
|
return self
|
|
@@ -22,8 +22,10 @@ from snowflake.cli._plugins.object.commands import (
|
|
|
22
22
|
ScopeOption,
|
|
23
23
|
describe,
|
|
24
24
|
drop,
|
|
25
|
+
limit_option_,
|
|
25
26
|
list_,
|
|
26
27
|
scope_option, # noqa: F401
|
|
28
|
+
terse_option_,
|
|
27
29
|
)
|
|
28
30
|
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
|
|
29
31
|
from snowflake.cli.api.constants import ObjectType
|
|
@@ -37,6 +39,8 @@ def add_object_command_aliases(
|
|
|
37
39
|
like_option: Optional[typer.Option],
|
|
38
40
|
scope_option: Optional[typer.Option],
|
|
39
41
|
ommit_commands: Optional[List[str]] = None,
|
|
42
|
+
terse_option: Optional[typer.Option] = None,
|
|
43
|
+
limit_option: Optional[typer.Option] = None,
|
|
40
44
|
):
|
|
41
45
|
if ommit_commands is None:
|
|
42
46
|
ommit_commands = list()
|
|
@@ -47,11 +51,18 @@ def add_object_command_aliases(
|
|
|
47
51
|
if not scope_option:
|
|
48
52
|
|
|
49
53
|
@app.command("list", requires_connection=True)
|
|
50
|
-
def list_cmd(
|
|
54
|
+
def list_cmd(
|
|
55
|
+
like: str = like_option, # type: ignore
|
|
56
|
+
terse: bool = terse_option if terse_option else terse_option_(), # type: ignore
|
|
57
|
+
limit: Optional[int] = limit_option if limit_option else limit_option_(), # type: ignore
|
|
58
|
+
**options,
|
|
59
|
+
):
|
|
51
60
|
return list_(
|
|
52
61
|
object_type=object_type.value.cli_name,
|
|
53
62
|
like=like,
|
|
54
63
|
scope=ScopeOption.default,
|
|
64
|
+
terse=terse,
|
|
65
|
+
limit=limit,
|
|
55
66
|
**options,
|
|
56
67
|
)
|
|
57
68
|
|
|
@@ -61,12 +72,16 @@ def add_object_command_aliases(
|
|
|
61
72
|
def list_cmd(
|
|
62
73
|
like: str = like_option, # type: ignore
|
|
63
74
|
scope: Tuple[str, str] = scope_option, # type: ignore
|
|
75
|
+
terse: bool = terse_option if terse_option else terse_option_(), # type: ignore
|
|
76
|
+
limit: Optional[int] = limit_option if limit_option else limit_option_(), # type: ignore
|
|
64
77
|
**options,
|
|
65
78
|
):
|
|
66
79
|
return list_(
|
|
67
80
|
object_type=object_type.value.cli_name,
|
|
68
81
|
like=like,
|
|
69
82
|
scope=scope,
|
|
83
|
+
terse=terse,
|
|
84
|
+
limit=limit,
|
|
70
85
|
**options,
|
|
71
86
|
)
|
|
72
87
|
|
|
@@ -94,6 +94,24 @@ def scope_option(help_example: str):
|
|
|
94
94
|
)
|
|
95
95
|
|
|
96
96
|
|
|
97
|
+
def terse_option_():
|
|
98
|
+
return typer.Option(
|
|
99
|
+
None,
|
|
100
|
+
"--terse",
|
|
101
|
+
help=f"Returns only a subset of available columns.",
|
|
102
|
+
hidden=True,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def limit_option_():
|
|
107
|
+
return typer.Option(
|
|
108
|
+
None,
|
|
109
|
+
"--limit",
|
|
110
|
+
help=f"Limits the maximum number of rows returned.",
|
|
111
|
+
hidden=True,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
97
115
|
ScopeOption = scope_option(
|
|
98
116
|
help_example="`list table --in database my_db`. Some object types have specialized scopes (e.g. list service --in compute-pool my_pool)"
|
|
99
117
|
)
|
|
@@ -110,11 +128,19 @@ def list_(
|
|
|
110
128
|
object_type: str = ObjectArgument,
|
|
111
129
|
like: str = LikeOption,
|
|
112
130
|
scope: Tuple[str, str] = ScopeOption,
|
|
131
|
+
terse: Optional[bool] = terse_option_(),
|
|
132
|
+
limit: Optional[int] = limit_option_(),
|
|
113
133
|
**options,
|
|
114
134
|
):
|
|
115
135
|
_scope_validate(object_type, scope)
|
|
116
136
|
return QueryResult(
|
|
117
|
-
ObjectManager().show(
|
|
137
|
+
ObjectManager().show(
|
|
138
|
+
object_type=object_type,
|
|
139
|
+
like=like,
|
|
140
|
+
scope=scope,
|
|
141
|
+
terse=terse,
|
|
142
|
+
limit=limit,
|
|
143
|
+
)
|
|
118
144
|
)
|
|
119
145
|
|
|
120
146
|
|