tundra-cli 0.0.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.
tundra/__init__.py ADDED
@@ -0,0 +1,8 @@
1
+ # Managed by bumpversion
2
+ __version__ = "0.15.5"
3
+
4
+ from tundra.error import SpecLoadingError
5
+
6
+ __all__ = [
7
+ "SpecLoadingError",
8
+ ]
tundra/cli/__init__.py ADDED
@@ -0,0 +1,10 @@
1
+ from .cli import cli # isort:skip
2
+
3
+ from tundra.cli import permissions
4
+
5
+
6
+ def main():
7
+ cli()
8
+
9
+
10
+ __all__ = ["permissions"]
tundra/cli/__main__.py ADDED
@@ -0,0 +1,3 @@
1
+ from . import main
2
+
3
+ main()
tundra/cli/cli.py ADDED
@@ -0,0 +1,25 @@
1
+ import logging
2
+
3
+ import click
4
+
5
+ import tundra
6
+ from tundra.logger import GLOBAL_LOGGER as logger
7
+
8
+
9
+ @click.group(invoke_without_command=True, no_args_is_help=True)
10
+ @click.option(
11
+ "-v", "--verbose", help="Increases log level with count, e.g -vv", count=True
12
+ )
13
+ @click.version_option(
14
+ version=tundra.__version__, prog_name="tundra",
15
+ message="%(prog)s %(version)s - Snowflake permissions with Iceberg table support"
16
+ )
17
+ @click.pass_context
18
+ def cli(ctx, verbose):
19
+ logger.setLevel(logging.WARNING)
20
+ if verbose == 1:
21
+ logger.setLevel(logging.INFO)
22
+ if verbose >= 2:
23
+ logger.setLevel(logging.DEBUG)
24
+
25
+ ctx.ensure_object(dict)
@@ -0,0 +1,206 @@
1
+ import sys
2
+
3
+ import click
4
+
5
+ from tundra import SpecLoadingError
6
+ from tundra.snowflake_connector import SnowflakeConnector
7
+ from tundra.snowflake_spec_loader import SnowflakeSpecLoader
8
+
9
+ from . import cli
10
+
11
+
12
+ def print_command(command, diff, dry=False):
13
+ """Prints the queries to the command line with prefixes"""
14
+ diff_prefix = ""
15
+ if command["already_granted"]:
16
+ if diff:
17
+ diff_prefix = " "
18
+ else:
19
+ pass
20
+ else:
21
+ if diff:
22
+ diff_prefix = "+ "
23
+
24
+ if command.get("run_status"):
25
+ foreground_color = "green"
26
+ run_prefix = "[SUCCESS] "
27
+ elif command.get("run_status") is None and dry:
28
+ foreground_color = "cyan"
29
+ run_prefix = "[PENDING] "
30
+ elif command.get("run_status") is None:
31
+ foreground_color = "cyan"
32
+ run_prefix = "[SKIPPED] "
33
+ else:
34
+ foreground_color = "red"
35
+ run_prefix = "[ERROR] "
36
+
37
+ click.secho(f"{diff_prefix}{run_prefix}{command['sql']};", fg=foreground_color)
38
+
39
+
40
+ @cli.command() # type: ignore
41
+ @click.argument("spec")
42
+ @click.option("--dry", help="Do not actually run, just check.", is_flag=True)
43
+ @click.option(
44
+ "--diff", help="Show full diff, both new and existing permissions.", is_flag=True
45
+ )
46
+ @click.option(
47
+ "--role",
48
+ multiple=True,
49
+ default=[],
50
+ help="Run grants for specific roles. Usage: --role testrole --role testrole2.",
51
+ )
52
+ @click.option(
53
+ "--user",
54
+ multiple=True,
55
+ default=[],
56
+ help="Run grants for specific users. Usage: --user testuser --user testuser2.",
57
+ )
58
+ @click.option(
59
+ "--ignore-memberships",
60
+ help="Do not handle role membership grants/revokes",
61
+ is_flag=True,
62
+ )
63
+ @click.pass_context
64
+ def run(ctx, spec, dry, diff, role, user, ignore_memberships, print_skipped=False):
65
+ """
66
+ Grant the permissions provided in the provided specification file for specific users and roles.
67
+ This fork includes support for Iceberg tables, external volumes, and catalog integrations.
68
+ """
69
+ if role and user:
70
+ run_list = ["roles", "users"]
71
+ elif role:
72
+ run_list = ["roles"]
73
+ elif user:
74
+ run_list = ["users"]
75
+ else:
76
+ run_list = ["roles", "users"]
77
+ if ctx.parent.params.get("verbose", 0) >= 1:
78
+ print_skipped = True
79
+ tundra_grants(
80
+ spec=spec,
81
+ dry=dry,
82
+ diff=diff,
83
+ roles=role,
84
+ users=user,
85
+ run_list=run_list,
86
+ ignore_memberships=ignore_memberships,
87
+ print_skipped=print_skipped,
88
+ )
89
+
90
+
91
+ @click.command()
92
+ @click.argument("spec")
93
+ @click.option(
94
+ "--role",
95
+ multiple=True,
96
+ default=[],
97
+ help="Run grants for specific roles. Usage: --role testrole --role testrole2.",
98
+ )
99
+ @click.option(
100
+ "--user",
101
+ multiple=True,
102
+ default=[],
103
+ help="Run grants for specific users. Usage: --user testuser --user testuser2.",
104
+ )
105
+ @click.option(
106
+ "--ignore-memberships",
107
+ help="Do not handle role membership grants/revokes",
108
+ is_flag=True,
109
+ )
110
+ @click.option(
111
+ "--run-list",
112
+ multiple=True,
113
+ default=["roles", "users"],
114
+ help="Run grants for specific users. Usage: --user testuser --user testuser2.",
115
+ )
116
+ def spec_test(spec, role, user, run_list, ignore_memberships):
117
+ """
118
+ Load SnowFlake spec based on the roles.yml provided. CLI use only for confirming specifications are valid.
119
+ This fork includes validation for Iceberg tables, external volumes, and catalog integrations.
120
+ """
121
+ load_specs(
122
+ spec,
123
+ role=role,
124
+ user=user,
125
+ run_list=run_list,
126
+ ignore_memberships=ignore_memberships,
127
+ do_spec_test=True,
128
+ )
129
+
130
+
131
+ def load_specs(spec, role, user, run_list, ignore_memberships, do_spec_test):
132
+ """
133
+ Load specs separately.
134
+ """
135
+ try:
136
+ click.secho("Confirming spec loads successfully")
137
+ spec_loader = SnowflakeSpecLoader(
138
+ spec,
139
+ roles=role,
140
+ users=user,
141
+ run_list=run_list,
142
+ ignore_memberships=ignore_memberships,
143
+ spec_test=do_spec_test,
144
+ )
145
+ click.secho("Snowflake specs successfully loaded", fg="green")
146
+ except SpecLoadingError as exc:
147
+ for line in str(exc).splitlines():
148
+ click.secho(line, fg="red")
149
+ sys.exit(1)
150
+
151
+ return spec_loader
152
+
153
+
154
+ def tundra_grants(
155
+ spec, dry, diff, roles, users, run_list, ignore_memberships, print_skipped
156
+ ):
157
+ """Grant the permissions provided in the provided specification file."""
158
+ spec_loader = load_specs(
159
+ spec,
160
+ role=roles,
161
+ user=users,
162
+ run_list=run_list,
163
+ ignore_memberships=ignore_memberships,
164
+ do_spec_test=False,
165
+ )
166
+
167
+ sql_grant_queries = spec_loader.generate_permission_queries(
168
+ roles=roles,
169
+ users=users,
170
+ run_list=run_list,
171
+ ignore_memberships=ignore_memberships,
172
+ )
173
+
174
+ click.secho()
175
+ if diff:
176
+ click.secho(
177
+ "SQL Commands generated for given spec file (Full diff with both new and already granted commands):"
178
+ )
179
+ else:
180
+ click.secho("SQL Commands generated for given spec file:")
181
+ click.secho()
182
+
183
+ conn = SnowflakeConnector()
184
+ for query in sql_grant_queries:
185
+ if not dry:
186
+ status = None
187
+ if not query.get("already_granted"):
188
+ try:
189
+ conn.run_query(query.get("sql", ""))
190
+ status = True
191
+ except Exception:
192
+ status = False
193
+
194
+ ran_query = query
195
+ ran_query["run_status"] = status
196
+ print_command(ran_query, diff)
197
+ # If already granted, print command
198
+ elif print_skipped:
199
+ print_command(query, diff)
200
+ # If dry, print commands
201
+ else:
202
+ if not query.get("already_granted") or print_skipped:
203
+ print_command(query, diff, dry=True)
204
+
205
+
206
+ cli.add_command(spec_test) # type: ignore