dataflow-core 2.1.18rc2__tar.gz → 2.1.18rc3__tar.gz
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.
Potentially problematic release.
This version of dataflow-core might be problematic. Click here for more details.
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/PKG-INFO +1 -1
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/authenticator/dataflowairflowauthenticator.py +10 -2
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/authenticator/dataflowhubauthenticator.py +124 -3
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/authenticator/dataflowsupersetauthenticator.py +11 -6
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/configuration.py +7 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/database_manager.py +23 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/db.py +4 -3
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/environment.py +20 -18
- dataflow_core-2.1.18rc3/dataflow/models/app_types.py +31 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/blacklist_library.py +3 -3
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/connection.py +19 -2
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/dataflow_zone.py +15 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/environment.py +125 -4
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/environment_status.py +6 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/git_ssh.py +16 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/org_associations.py +35 -2
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/organization.py +55 -10
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/pinned_projects.py +13 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/pod_activity.py +14 -1
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/pod_session_history.py +14 -1
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/project_details.py +26 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/recent_project_studio.py +16 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/recent_projects.py +7 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/role.py +19 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/role_server.py +8 -1
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/role_zone.py +17 -2
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/server_config.py +36 -0
- dataflow_core-2.1.18rc3/dataflow/models/session.py +22 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/team.py +11 -2
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/user.py +40 -7
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/user_team.py +11 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/variables.py +26 -2
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/schemas/connection.py +47 -5
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/schemas/git_ssh.py +39 -5
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/schemas/secret.py +32 -4
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/secrets_manager/factory.py +6 -2
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/secrets_manager/interface.py +3 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/secrets_manager/providers/aws_manager.py +55 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/secrets_manager/providers/azure_manager.py +55 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/utils/get_current_user.py +16 -2
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow_core.egg-info/PKG-INFO +1 -1
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dfmigration/env.py +10 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/setup.py +1 -1
- dataflow_core-2.1.18rc2/dataflow/models/app_types.py +0 -15
- dataflow_core-2.1.18rc2/dataflow/models/session.py +0 -17
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/README.md +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/authenticator/__init__.py +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/__init__.py +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/dataflow.py +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/models/__init__.py +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/schemas/__init__.py +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/scripts/clone_environment.sh +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/scripts/create_environment.sh +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/scripts/update_environment.sh +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/secrets_manager/__init__.py +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/secrets_manager/providers/__init__.py +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/secrets_manager/providers/gcp_manager.py +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/secrets_manager/service.py +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/secrets_manager/utils.py +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/utils/__init__.py +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/utils/exceptions.py +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow/utils/logger.py +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow_core.egg-info/SOURCES.txt +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow_core.egg-info/dependency_links.txt +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow_core.egg-info/entry_points.txt +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow_core.egg-info/requires.txt +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dataflow_core.egg-info/top_level.txt +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dfmigration/__init__.py +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dfmigration/versions/001_initial_baseline_migration.py +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/dfmigration/versions/__init__.py +0 -0
- {dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/setup.cfg +0 -0
{dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/authenticator/dataflowairflowauthenticator.py
RENAMED
|
@@ -18,9 +18,14 @@ dataflow = Dataflow()
|
|
|
18
18
|
class DataflowAuthDBView(AuthDBView):
|
|
19
19
|
@expose('/login/', methods=['GET', 'POST'])
|
|
20
20
|
def login(self):
|
|
21
|
+
|
|
22
|
+
"""This method checks for a 'dataflow_session' cookie, retrieves user details from Dataflow,
|
|
23
|
+
and logs in or creates the user in Airflow accordingly.
|
|
24
|
+
If the cookie is not present, it falls back to the standard login process.
|
|
25
|
+
|
|
26
|
+
Overrides the default login method to integrate with Dataflow authentication.
|
|
21
27
|
"""
|
|
22
|
-
|
|
23
|
-
"""
|
|
28
|
+
|
|
24
29
|
try:
|
|
25
30
|
session_id = request.cookies.get('dataflow_session')
|
|
26
31
|
if not session_id:
|
|
@@ -52,6 +57,9 @@ class DataflowAuthDBView(AuthDBView):
|
|
|
52
57
|
return super().login()
|
|
53
58
|
|
|
54
59
|
class DataflowAirflowAuthenticator(FabAirflowSecurityManagerOverride):
|
|
60
|
+
|
|
61
|
+
"""Custom Security Manager to integrate Airflow authentication with Dataflow."""
|
|
62
|
+
|
|
55
63
|
authdbview = DataflowAuthDBView
|
|
56
64
|
|
|
57
65
|
def __init__(self, appbuilder):
|
{dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/authenticator/dataflowhubauthenticator.py
RENAMED
|
@@ -10,6 +10,13 @@ from dataflow.models import user as m_user, session as m_session
|
|
|
10
10
|
from sqlalchemy import or_
|
|
11
11
|
|
|
12
12
|
class DataflowBaseAuthenticator(Authenticator):
|
|
13
|
+
|
|
14
|
+
"""Base Authenticator to handle Dataflow authentication and session management.
|
|
15
|
+
Provides methods to authenticate users via Dataflow credentials, manage sessions.
|
|
16
|
+
|
|
17
|
+
Overrides JupyterHub's Authenticator class.
|
|
18
|
+
"""
|
|
19
|
+
|
|
13
20
|
enable_dataflow_auth = Bool(True, config=True, help="Enable username/password authentication")
|
|
14
21
|
|
|
15
22
|
def __init__(self, **kwargs):
|
|
@@ -24,9 +31,19 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
24
31
|
raise
|
|
25
32
|
|
|
26
33
|
def generate_session_id(self):
|
|
34
|
+
|
|
35
|
+
"""Generate and return a unique session ID using UUID4."""
|
|
36
|
+
|
|
27
37
|
return str(uuid.uuid4())
|
|
28
38
|
|
|
29
39
|
def set_session_cookie(self, handler, session_id):
|
|
40
|
+
|
|
41
|
+
"""Set the dataflow_session cookie in the user's browser.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
handler: The request handler to set the cookie on.
|
|
45
|
+
session_id: The session ID to set in the cookie."""
|
|
46
|
+
|
|
30
47
|
expires = datetime.now(ZoneInfo("UTC")) + timedelta(days=60)
|
|
31
48
|
host = handler.request.host
|
|
32
49
|
domain = '.'.join(host.split('.')[-2:]) if len(host.split('.')) >= 2 else host
|
|
@@ -43,6 +60,16 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
43
60
|
self.log.info(f"Set session cookie: dataflow_session={session_id} for host={host}")
|
|
44
61
|
|
|
45
62
|
def get_or_create_session(self, user_id):
|
|
63
|
+
|
|
64
|
+
"""Retrieve existing session ID for user or create a new one.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
user_id: The ID of the user to get or create a session for.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
session_id (str): The existing or newly created session ID.
|
|
71
|
+
"""
|
|
72
|
+
|
|
46
73
|
session_id = self.generate_session_id()
|
|
47
74
|
while self.db.query(m_session.Session).filter(
|
|
48
75
|
m_session.Session.session_id == session_id
|
|
@@ -57,6 +84,16 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
57
84
|
return session_id
|
|
58
85
|
|
|
59
86
|
def check_blocked_users(self, username, authenticated):
|
|
87
|
+
|
|
88
|
+
"""Check if the authenticated user is blocked based on allowed_users list.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
username (str): The username of the authenticated user.
|
|
92
|
+
authenticated (dict|None): The authentication data returned from authenticate method.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
username (str|None): The username if not blocked, else None."""
|
|
96
|
+
|
|
60
97
|
self.log.info(f"Checking blocked users for {username}: authenticated={authenticated}, allowed_users={self.allowed_users}")
|
|
61
98
|
|
|
62
99
|
if not authenticated:
|
|
@@ -70,20 +107,46 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
70
107
|
return super().check_blocked_users(username, authenticated)
|
|
71
108
|
|
|
72
109
|
def extract_username_from_email(self, email):
|
|
73
|
-
|
|
110
|
+
|
|
111
|
+
"""Extract username from email by removing domain
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
email (str): User's email address
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
username (str): Extracted username after removing domain
|
|
118
|
+
"""
|
|
119
|
+
|
|
74
120
|
if '@' in email:
|
|
75
121
|
return email.split('@')[0]
|
|
76
122
|
return email
|
|
77
123
|
|
|
78
124
|
def generate_secure_password(self):
|
|
79
|
-
|
|
125
|
+
|
|
126
|
+
"""Generate secure random password hash
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
password_hash (str): Securely hashed password
|
|
130
|
+
"""
|
|
131
|
+
|
|
80
132
|
salt = secrets.token_hex(16)
|
|
81
133
|
random_uuid = str(uuid.uuid4())
|
|
82
134
|
hash_obj = hashlib.sha256((random_uuid + salt).encode())
|
|
83
135
|
return hash_obj.hexdigest()
|
|
84
136
|
|
|
85
137
|
def create_new_user(self, email, first_name=None, last_name=None):
|
|
86
|
-
|
|
138
|
+
|
|
139
|
+
"""Create a new user with Applicant role
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
email (str): User's email address
|
|
143
|
+
first_name (str): User's first name
|
|
144
|
+
last_name (str): User's last name
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
new_user (m_user.User|None): Created user object or None if creation failed
|
|
148
|
+
"""
|
|
149
|
+
|
|
87
150
|
try:
|
|
88
151
|
username = self.extract_username_from_email(email)
|
|
89
152
|
username = re.sub(r'[^a-z0-9]', '', username.lower())
|
|
@@ -130,6 +193,17 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
130
193
|
return None
|
|
131
194
|
|
|
132
195
|
async def authenticate_dataflow(self, handler, data):
|
|
196
|
+
|
|
197
|
+
"""Authenticate user using Dataflow username/password.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
handler: The request handler.
|
|
201
|
+
data: The authentication data containing username and password.
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
dict|None: Authentication result with username and session_id if successful, else None.
|
|
205
|
+
"""
|
|
206
|
+
|
|
133
207
|
if not (self.enable_dataflow_auth and isinstance(data, dict) and data.get("username") and data.get("password")):
|
|
134
208
|
return None
|
|
135
209
|
user_name_or_email = data["username"]
|
|
@@ -162,6 +236,18 @@ class DataflowBaseAuthenticator(Authenticator):
|
|
|
162
236
|
self.db.close()
|
|
163
237
|
|
|
164
238
|
class DataflowGoogleAuthenticator(DataflowBaseAuthenticator, GoogleOAuthenticator):
|
|
239
|
+
|
|
240
|
+
"""Authenticator to handle Google OAuth authentication with Dataflow integration.
|
|
241
|
+
|
|
242
|
+
Overrides
|
|
243
|
+
- DataflowBaseAuthenticator
|
|
244
|
+
- GoogleOAuthenticator
|
|
245
|
+
|
|
246
|
+
Requires Google OAuth credentials.
|
|
247
|
+
- google_client_id
|
|
248
|
+
- google_client_secret
|
|
249
|
+
"""
|
|
250
|
+
|
|
165
251
|
dataflow_oauth_type = Unicode(
|
|
166
252
|
default_value="google",
|
|
167
253
|
config=True,
|
|
@@ -180,6 +266,17 @@ class DataflowGoogleAuthenticator(DataflowBaseAuthenticator, GoogleOAuthenticato
|
|
|
180
266
|
f"enable_dataflow_auth={self.enable_dataflow_auth}")
|
|
181
267
|
|
|
182
268
|
async def authenticate(self, handler, data):
|
|
269
|
+
|
|
270
|
+
"""Authenticate user using Google OAuth with Dataflow integration.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
handler: The request handler.
|
|
274
|
+
data: The authentication data.
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
dict|None: Authentication result with username and session_id if successful, else None.
|
|
278
|
+
"""
|
|
279
|
+
|
|
183
280
|
self.log.info(f"Authenticate called with data: {data}, request_uri: {handler.request.uri}")
|
|
184
281
|
result = await self.authenticate_dataflow(handler, data)
|
|
185
282
|
if result:
|
|
@@ -228,6 +325,19 @@ class DataflowGoogleAuthenticator(DataflowBaseAuthenticator, GoogleOAuthenticato
|
|
|
228
325
|
self.db.close()
|
|
229
326
|
|
|
230
327
|
class DataflowAzureAuthenticator(DataflowBaseAuthenticator, AzureAdOAuthenticator):
|
|
328
|
+
|
|
329
|
+
"""Authenticator to handle Azure AD OAuth authentication with Dataflow integration.
|
|
330
|
+
|
|
331
|
+
Overrides
|
|
332
|
+
- DataflowBaseAuthenticator
|
|
333
|
+
- AzureAdOAuthenticator
|
|
334
|
+
|
|
335
|
+
Requires Azure AD OAuth credentials.
|
|
336
|
+
- azure_client_id
|
|
337
|
+
- azure_client_secret
|
|
338
|
+
- azure_tenant_id
|
|
339
|
+
"""
|
|
340
|
+
|
|
231
341
|
azure_client_id = Unicode(config=True, help="Azure AD OAuth client ID")
|
|
232
342
|
azure_client_secret = Unicode(config=True, help="Azure AD OAuth client secret")
|
|
233
343
|
azure_tenant_id = Unicode(config=True, help="Azure AD tenant ID")
|
|
@@ -249,6 +359,17 @@ class DataflowAzureAuthenticator(DataflowBaseAuthenticator, AzureAdOAuthenticato
|
|
|
249
359
|
f"enable_dataflow_auth={self.enable_dataflow_auth}")
|
|
250
360
|
|
|
251
361
|
async def authenticate(self, handler, data):
|
|
362
|
+
|
|
363
|
+
"""Authenticate user using Azure AD OAuth with Dataflow integration.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
handler: The request handler.
|
|
367
|
+
data: The authentication data.
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
dict|None: Authentication result with username and session_id if successful, else None.
|
|
371
|
+
"""
|
|
372
|
+
|
|
252
373
|
result = await self.authenticate_dataflow(handler, data)
|
|
253
374
|
if result:
|
|
254
375
|
return result
|
{dataflow_core-2.1.18rc2 → dataflow_core-2.1.18rc3}/authenticator/dataflowsupersetauthenticator.py
RENAMED
|
@@ -12,6 +12,7 @@ from superset.security import SupersetSecurityManager
|
|
|
12
12
|
from dataflow.dataflow import Dataflow
|
|
13
13
|
|
|
14
14
|
class DataflowAuthDBView(AuthDBView):
|
|
15
|
+
|
|
15
16
|
def __init__(self):
|
|
16
17
|
self.dataflow = Dataflow()
|
|
17
18
|
|
|
@@ -33,14 +34,15 @@ class DataflowAuthDBView(AuthDBView):
|
|
|
33
34
|
@expose('/login/', methods=['GET', "POST"])
|
|
34
35
|
def login(self):
|
|
35
36
|
"""
|
|
36
|
-
|
|
37
|
+
This method handles authentication for superset in Dataflow.
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
-
|
|
39
|
+
Methods:
|
|
40
|
+
- GET:
|
|
41
|
+
Used for browser-based login. Authenticates using session cookie and redirects to home.
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
-
|
|
43
|
-
|
|
43
|
+
- POST:
|
|
44
|
+
Used for API-based login. Returns JWT access token for programmatic access.
|
|
45
|
+
Returns JSON response with access token
|
|
44
46
|
"""
|
|
45
47
|
if request.method == "GET":
|
|
46
48
|
session_id = request.cookies.get('dataflow_session')
|
|
@@ -71,6 +73,9 @@ class DataflowAuthDBView(AuthDBView):
|
|
|
71
73
|
return jsonify(resp)
|
|
72
74
|
|
|
73
75
|
class DataflowSecurityManager(SupersetSecurityManager):
|
|
76
|
+
|
|
77
|
+
"""Custom Security Manager integrating Dataflow authentication with superset."""
|
|
78
|
+
|
|
74
79
|
authdbview = DataflowAuthDBView
|
|
75
80
|
def __init__(self, appbuilder):
|
|
76
81
|
super(DataflowSecurityManager, self).__init__(appbuilder)
|
|
@@ -20,6 +20,13 @@ class ConfigurationManager:
|
|
|
20
20
|
def get_config_value(self, section, option):
|
|
21
21
|
"""
|
|
22
22
|
Get configuration value
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
section (str): The section in the config file.
|
|
26
|
+
option (str): The option within the section.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
str | None: The configuration value or None if not found.
|
|
23
30
|
"""
|
|
24
31
|
try:
|
|
25
32
|
return self.config.get(section, option)
|
|
@@ -5,11 +5,22 @@ from sqlalchemy import create_engine
|
|
|
5
5
|
from sqlalchemy.orm import sessionmaker
|
|
6
6
|
|
|
7
7
|
class DatabaseManager:
|
|
8
|
+
|
|
9
|
+
"""Manages database connections and sessions."""
|
|
10
|
+
|
|
8
11
|
def __init__(self, db_url):
|
|
9
12
|
self.db_url = db_url
|
|
10
13
|
self.engine = self.get_engine()
|
|
11
14
|
|
|
12
15
|
def get_engine(self):
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
Create a new SQLAlchemy engine instance.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Engine: The SQLAlchemy engine instance.
|
|
22
|
+
"""
|
|
23
|
+
|
|
13
24
|
try:
|
|
14
25
|
engine = create_engine(self.db_url)
|
|
15
26
|
return engine
|
|
@@ -17,6 +28,12 @@ class DatabaseManager:
|
|
|
17
28
|
raise e
|
|
18
29
|
|
|
19
30
|
def get_session(self):
|
|
31
|
+
"""
|
|
32
|
+
Create a new SQLAlchemy session.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Session: The SQLAlchemy session instance.
|
|
36
|
+
"""
|
|
20
37
|
try:
|
|
21
38
|
engine = self.engine
|
|
22
39
|
session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
|
@@ -30,4 +47,10 @@ class DatabaseManager:
|
|
|
30
47
|
raise e
|
|
31
48
|
|
|
32
49
|
def get_base(self):
|
|
50
|
+
"""
|
|
51
|
+
Get the declarative base class for the ORM models.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Base: The declarative base class.
|
|
55
|
+
"""
|
|
33
56
|
return declarative_base()
|
|
@@ -23,9 +23,10 @@ Base = declarative_base()
|
|
|
23
23
|
Local_Base = declarative_base()
|
|
24
24
|
|
|
25
25
|
def create_tables(local_db=False):
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
"""Create all tables in the database.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
local_db (bool): Flag indicating whether to create tables in the local database.
|
|
29
30
|
"""
|
|
30
31
|
try:
|
|
31
32
|
if local_db:
|
|
@@ -19,8 +19,7 @@ class EnvironmentManager:
|
|
|
19
19
|
self.logger = CustomLogger().get_logger(__name__)
|
|
20
20
|
|
|
21
21
|
async def create_env(self, env_name, py_version, pip_libraries, conda_libraries, status, env_version='1', user_name=None, db:Session=None):
|
|
22
|
-
"""
|
|
23
|
-
Creates a conda environment with specified Python version and packages.
|
|
22
|
+
"""Creates a conda environment with specified Python version and packages.
|
|
24
23
|
|
|
25
24
|
Args:
|
|
26
25
|
env_name (str): Name of the environment
|
|
@@ -84,8 +83,7 @@ class EnvironmentManager:
|
|
|
84
83
|
raise ValueError("Invalid status. Use 'draft' or 'published'.")
|
|
85
84
|
|
|
86
85
|
async def clone_env(self, source_path, env_name, pip_libraries, conda_libraries, user_name, db=None, local_clone=False):
|
|
87
|
-
"""
|
|
88
|
-
Clones an existing conda environment.
|
|
86
|
+
"""Clones an existing conda environment.
|
|
89
87
|
|
|
90
88
|
Args:
|
|
91
89
|
source_path (str): Path to source environment
|
|
@@ -141,8 +139,7 @@ class EnvironmentManager:
|
|
|
141
139
|
return clone_status
|
|
142
140
|
|
|
143
141
|
async def revert_env(self, env_name, curr_version, revert_version, new_version, user_name, db: Session):
|
|
144
|
-
"""
|
|
145
|
-
Reverts an environment to a previous version.
|
|
142
|
+
"""Reverts an environment to a previous version.
|
|
146
143
|
|
|
147
144
|
Args:
|
|
148
145
|
env_name (str): Name of the environment
|
|
@@ -195,8 +192,7 @@ class EnvironmentManager:
|
|
|
195
192
|
log_file_location=None,
|
|
196
193
|
py_version=None
|
|
197
194
|
):
|
|
198
|
-
"""
|
|
199
|
-
Executes environment operations (create or clone).
|
|
195
|
+
"""Executes environment operations (create or clone).
|
|
200
196
|
|
|
201
197
|
Args:
|
|
202
198
|
env_name (str): Name of the environment
|
|
@@ -325,8 +321,7 @@ class EnvironmentManager:
|
|
|
325
321
|
return "failed"
|
|
326
322
|
|
|
327
323
|
def _setup_logging(self, env_name: str, env_version: str, user_name: str, db: Session):
|
|
328
|
-
"""
|
|
329
|
-
Sets up logging for environment operations.
|
|
324
|
+
"""Sets up logging for environment operations.
|
|
330
325
|
|
|
331
326
|
Args:
|
|
332
327
|
env_name (str): Name of the environment
|
|
@@ -352,8 +347,7 @@ class EnvironmentManager:
|
|
|
352
347
|
return log_file_location
|
|
353
348
|
|
|
354
349
|
async def _update_job_status(self, log_file_name: str, build_status: str, log_file_location: str, db: Session):
|
|
355
|
-
"""
|
|
356
|
-
Updates job status with retry logic.
|
|
350
|
+
"""Updates job status with retry logic.
|
|
357
351
|
|
|
358
352
|
Args:
|
|
359
353
|
db (Session): Database session
|
|
@@ -386,8 +380,7 @@ class EnvironmentManager:
|
|
|
386
380
|
self.logger.error(f"Failed to update job log after multiple attempts: {e}")
|
|
387
381
|
|
|
388
382
|
def create_job_entry(self, user_name: str, db: Session, log_file_name: str, log_file_location: str):
|
|
389
|
-
"""
|
|
390
|
-
Creates or updates a job entry for environment tracking.
|
|
383
|
+
"""Creates or updates a job entry for environment tracking.
|
|
391
384
|
|
|
392
385
|
Args:
|
|
393
386
|
user_name (str): The user who initiated the job
|
|
@@ -428,8 +421,7 @@ class EnvironmentManager:
|
|
|
428
421
|
return job
|
|
429
422
|
|
|
430
423
|
def update_job_log(self, db, log_file_name, final_build_status):
|
|
431
|
-
"""
|
|
432
|
-
Updates the JobLogs table with completion time and status.
|
|
424
|
+
"""Updates the JobLogs table with completion time and status.
|
|
433
425
|
|
|
434
426
|
Args:
|
|
435
427
|
db (Session): Database session
|
|
@@ -460,8 +452,15 @@ class EnvironmentManager:
|
|
|
460
452
|
return datetime.datetime.now().strftime("%b %d %I:%M:%S %p")
|
|
461
453
|
|
|
462
454
|
def update_environment_db(self, env_short_name, version, pip_libraries, conda_libraries, status, db: Session):
|
|
463
|
-
"""
|
|
464
|
-
|
|
455
|
+
"""Updates the environment table with the new version and libraries.
|
|
456
|
+
|
|
457
|
+
Args:
|
|
458
|
+
env_short_name (str): Short name of the environment
|
|
459
|
+
version (str): Version of the environment
|
|
460
|
+
pip_libraries (list): List of pip libraries
|
|
461
|
+
conda_libraries (list): List of conda libraries
|
|
462
|
+
status (str): Build status ('success' or 'failed')
|
|
463
|
+
db (Session): Database session
|
|
465
464
|
"""
|
|
466
465
|
try:
|
|
467
466
|
if isinstance(pip_libraries, list):
|
|
@@ -561,6 +560,9 @@ class EnvironmentManager:
|
|
|
561
560
|
conda_channels (list): List of conda channels
|
|
562
561
|
conda_packages (list): List of conda packages to install
|
|
563
562
|
pip_packages (list): List of pip packages to install
|
|
563
|
+
|
|
564
|
+
Returns:
|
|
565
|
+
str: Path to the created YAML file
|
|
564
566
|
"""
|
|
565
567
|
try:
|
|
566
568
|
# Create the environment specification
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from sqlalchemy import Column, Integer, String, Boolean
|
|
2
|
+
from sqlalchemy.orm import relationship
|
|
3
|
+
from dataflow.db import Base
|
|
4
|
+
|
|
5
|
+
class AppType(Base):
|
|
6
|
+
|
|
7
|
+
"""TABLE 'APP_TYPE'.
|
|
8
|
+
|
|
9
|
+
Attributes:
|
|
10
|
+
id (int): Primary key for the app type.
|
|
11
|
+
name (str): Unique name identifier for the app type.
|
|
12
|
+
display_name (str): Human-readable name for the app type.
|
|
13
|
+
code_based (bool): Indicates if the app type is code-based.
|
|
14
|
+
studio (bool): Indicates if the app type is associated with Dataflow Studio.
|
|
15
|
+
runtime (bool): Indicates if the app type is a runtime application.
|
|
16
|
+
organizations (list): Relationship to organizations using this app type.
|
|
17
|
+
|
|
18
|
+
Relationships:
|
|
19
|
+
organizations: Many-to-many relationship with Organization model via ORGANIZATION_APP_TYPE association table.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
__tablename__ = "APP_TYPE"
|
|
23
|
+
|
|
24
|
+
id = Column(Integer, primary_key=True, autoincrement=True, unique=True)
|
|
25
|
+
name = Column(String, unique=True, nullable=False)
|
|
26
|
+
display_name = Column(String, nullable=False)
|
|
27
|
+
code_based = Column(Boolean, nullable=False)
|
|
28
|
+
studio = Column(Boolean, nullable=False, default=False, server_default='false')
|
|
29
|
+
runtime = Column(Boolean, nullable=False, default=False, server_default='false')
|
|
30
|
+
|
|
31
|
+
organizations = relationship("Organization", secondary="ORGANIZATION_APP_TYPE", back_populates="apps")
|
|
@@ -3,15 +3,15 @@ from sqlalchemy import Column, Integer, String, UniqueConstraint
|
|
|
3
3
|
from dataflow.db import Base
|
|
4
4
|
|
|
5
5
|
class BlacklistedLibrary(Base):
|
|
6
|
-
"""
|
|
7
|
-
BlacklistedLibrary model represents a table for storing blacklisted libraries with their versions.
|
|
6
|
+
"""TABLE 'BLACKLISTED_LIBRARY'
|
|
8
7
|
|
|
9
8
|
Attributes:
|
|
10
9
|
id (int): Primary key of the table, auto-incremented.
|
|
11
10
|
library_name (str): The name of the blacklisted library.
|
|
12
11
|
version (str): The version of the blacklisted library.
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
Constraints:
|
|
14
|
+
Unique constraint to ensure unique combination of library_name and version.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
__tablename__ = "BLACKLISTED_LIBRARY"
|
|
@@ -3,9 +3,26 @@ from sqlalchemy.sql import func
|
|
|
3
3
|
from dataflow.db import Base
|
|
4
4
|
|
|
5
5
|
class Connection(Base):
|
|
6
|
+
"""TABLE 'CONNECTION'
|
|
7
|
+
|
|
8
|
+
Attributes:
|
|
9
|
+
id (int): Primary key for the connection.
|
|
10
|
+
conn_id (str): Identifier for the connection.
|
|
11
|
+
org_id (int): Foreign key referencing the organization.
|
|
12
|
+
description (str): Description of the connection.
|
|
13
|
+
conn_type (str): Type of the connection.
|
|
14
|
+
runtime (str): Runtime environment for the connection.
|
|
15
|
+
slug (str): Slug identifier for the connection.
|
|
16
|
+
status (bool): Status of the connection (active/inactive).
|
|
17
|
+
created_by (str): User who created the connection.
|
|
18
|
+
created_at (datetime): Timestamp of when the connection was created.
|
|
19
|
+
updated_at (datetime): Timestamp of the last update to the connection.
|
|
20
|
+
is_active (bool): Indicates if the connection is active.
|
|
21
|
+
|
|
22
|
+
Constraints:
|
|
23
|
+
UniqueConstraint: Ensures unique active connections based on conn_id, org_id, runtime, slug, is_active, and created_by.
|
|
6
24
|
"""
|
|
7
|
-
|
|
8
|
-
"""
|
|
25
|
+
|
|
9
26
|
__tablename__ = "CONNECTION"
|
|
10
27
|
|
|
11
28
|
id = Column(Integer, primary_key=True, index=True)
|
|
@@ -3,6 +3,21 @@ from sqlalchemy.orm import relationship
|
|
|
3
3
|
from dataflow.db import Base
|
|
4
4
|
|
|
5
5
|
class DataflowZone(Base):
|
|
6
|
+
|
|
7
|
+
"""TABLE 'DATAFLOW_ZONE'.
|
|
8
|
+
|
|
9
|
+
Attributes:
|
|
10
|
+
id (int): Primary key for the dataflow zone.
|
|
11
|
+
slug (str): Unique slug identifier for the zone.
|
|
12
|
+
display_name (str): Human-readable name for the zone.
|
|
13
|
+
is_runtime (bool): Indicates if the zone is a runtime zone.
|
|
14
|
+
subdomain (str): Subdomain associated with the zone.
|
|
15
|
+
display_order (int): Order for displaying the zone in lists.
|
|
16
|
+
|
|
17
|
+
Relationships:
|
|
18
|
+
role_zone_assocs: One-to-many relationship with RoleZone model.
|
|
19
|
+
"""
|
|
20
|
+
|
|
6
21
|
__tablename__ = "DATAFLOW_ZONE"
|
|
7
22
|
|
|
8
23
|
id = Column(Integer, primary_key=True, autoincrement=True)
|