Skip to content

Commit

Permalink
Copy grants while deploying snowpark functions and procedures (#1185)
Browse files Browse the repository at this point in the history
* Add copy grants

* Added notes
  • Loading branch information
sfc-gh-jsikorski committed Jun 11, 2024
1 parent 1cde2d0 commit 0a1c7a9
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 0 deletions.
1 change: 1 addition & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
## Fixes and improvements
* Fixed error handling for malformatted `config.toml`
* Fixed ZIP packaging of Snowpark project dependencies containing implicit namespace packages like `snowflake`.
* Deploying function/procedure with `--replace` flag now copies all grants

# v2.4.0
## Backward incompatibility
Expand Down
1 change: 1 addition & 0 deletions src/snowflake/cli/plugins/snowpark/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ def create_query(

query = [
f"create or replace {self._object_type.value.sf_name} {identifier}",
f"copy grants",
f"returns {return_type}",
"language python",
f"runtime_version={runtime or DEFAULT_RUNTIME}",
Expand Down
4 changes: 4 additions & 0 deletions tests/snowpark/test_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def test_deploy_function(
dedent(
"""\
create or replace function MockDatabase.MockSchema.func1(a string default 'default value', b variant)
copy grants
returns string
language python
runtime_version=3.10
Expand Down Expand Up @@ -85,6 +86,7 @@ def test_deploy_function_with_external_access(
dedent(
"""\
create or replace function MockDatabase.MockSchema.func1(a string, b variant)
copy grants
returns string
language python
runtime_version=3.8
Expand Down Expand Up @@ -204,6 +206,7 @@ def test_deploy_function_needs_update_because_packages_changes(
dedent(
"""\
create or replace function MockDatabase.MockSchema.func1(a string default 'default value', b variant)
copy grants
returns string
language python
runtime_version=3.10
Expand Down Expand Up @@ -254,6 +257,7 @@ def test_deploy_function_needs_update_because_handler_changes(
dedent(
"""\
create or replace function MockDatabase.MockSchema.func1(a string default 'default value', b variant)
copy grants
returns string
language python
runtime_version=3.10
Expand Down
3 changes: 3 additions & 0 deletions tests/snowpark/test_procedure.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def test_deploy_procedure(
dedent(
"""\
create or replace procedure MockDatabase.MockSchema.procedureName(name string)
copy grants
returns string
language python
runtime_version=3.8
Expand All @@ -69,6 +70,7 @@ def test_deploy_procedure(
dedent(
"""\
create or replace procedure MockDatabase.MockSchema.test()
copy grants
returns string
language python
runtime_version=3.10
Expand Down Expand Up @@ -124,6 +126,7 @@ def test_deploy_procedure_with_external_access(
dedent(
"""\
create or replace procedure MockDatabase.MockSchema.procedureName(name string)
copy grants
returns string
language python
runtime_version=3.8
Expand Down
46 changes: 46 additions & 0 deletions tests_integration/test_snowpark.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,36 @@ def test_snowpark_flow(
returns="VARCHAR(16777216)",
)

# Grants are given correctly

_test_steps.set_grants_on_selected_object(
object_type="procedure",
object_name="hello_procedure(VARCHAR)",
privillege="USAGE",
role="test_role",
)

_test_steps.set_grants_on_selected_object(
object_type="function",
object_name="hello_function(VARCHAR)",
privillege="USAGE",
role="test_role",
)

_test_steps.assert_that_object_has_expected_grant(
object_type="procedure",
object_name="hello_procedure(VARCHAR)",
expected_privillege="USAGE",
expected_role="test_role",
)

_test_steps.assert_that_object_has_expected_grant(
object_type="function",
object_name="hello_function(VARCHAR)",
expected_privillege="USAGE",
expected_role="test_role",
)

# Created objects can be executed
_test_steps.snowpark_execute_should_return_expected_value(
object_type="procedure",
Expand Down Expand Up @@ -215,6 +245,22 @@ def test_snowpark_flow(
*expected_files, stage_name=STAGE_NAME
)

# Grants are preserved after updates

_test_steps.assert_that_object_has_expected_grant(
object_type="procedure",
object_name="hello_procedure(VARCHAR)",
expected_privillege="USAGE",
expected_role="test_role",
)

_test_steps.assert_that_object_has_expected_grant(
object_type="function",
object_name="hello_function(VARCHAR)",
expected_privillege="USAGE",
expected_role="test_role",
)

# Check if objects can be dropped
_test_steps.object_drop_should_finish_successfully(
object_type="procedure", identifier="hello_procedure(varchar)"
Expand Down
27 changes: 27 additions & 0 deletions tests_integration/testing_utils/snowpark_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,3 +351,30 @@ def get_actual_files_staged_in_db(self, stage_name: str):
stage_name=stage_name
)
]

def set_grants_on_selected_object(
self, object_type: str, object_name: str, privillege: str, role: str
):
self._setup.sql_test_helper.execute_single_sql(
f"GRANT {privillege} ON {object_type} {object_name} TO ROLE {role};"
)

def assert_that_object_has_expected_grant(
self,
object_type: str,
object_name: str,
expected_privillege: str,
expected_role: str,
):
result = self._setup.sql_test_helper.execute_single_sql(
f"SHOW GRANTS ON {object_type} {object_name};"
)
assert any(
[
(
grant.get("privilege") == expected_privillege.upper()
and grant.get("grantee_name") == expected_role.upper()
)
for grant in result
]
)

0 comments on commit 0a1c7a9

Please sign in to comment.