dataflow-core 2.0.10__py3-none-any.whl → 2.1.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.
- authenticator/dataflowairflowauthenticator.py +51 -76
- authenticator/dataflowhubauthenticator.py +13 -19
- authenticator/dataflowsupersetauthenticator.py +33 -15
- dataflow/{models/database.py → database_manager.py} +4 -3
- dataflow/dataflow.py +129 -19
- dataflow/db.py +51 -0
- dataflow/environment.py +1 -2
- dataflow/models/__init__.py +22 -0
- dataflow/models/app_types.py +10 -0
- dataflow/models/blacklist_library.py +26 -0
- dataflow/models/connection.py +25 -0
- dataflow/models/environment.py +5 -5
- dataflow/models/environment_status.py +17 -0
- dataflow/models/git_ssh.py +18 -0
- dataflow/models/pinned_projects.py +15 -0
- dataflow/models/project_details.py +23 -0
- dataflow/models/recent_project_studio.py +19 -0
- dataflow/models/recent_projects.py +9 -0
- dataflow/models/role.py +19 -0
- dataflow/models/role_server.py +14 -0
- dataflow/models/runtime.py +11 -0
- dataflow/models/server_config.py +33 -0
- dataflow/models/session.py +17 -0
- dataflow/models/team.py +17 -0
- dataflow/models/user.py +29 -0
- dataflow/models/user_environment.py +16 -0
- dataflow/models/user_team.py +14 -0
- dataflow/models/variables.py +27 -0
- dataflow/utils/aws_secrets_manager.py +16 -20
- dataflow/utils/get_current_user.py +35 -0
- dataflow/utils/json_handler.py +33 -0
- dataflow/utils/logger.py +41 -0
- {dataflow_core-2.0.10.dist-info → dataflow_core-2.1.0.dist-info}/METADATA +1 -1
- dataflow_core-2.1.0.dist-info/RECORD +43 -0
- authenticator/package/__init__.py +0 -0
- authenticator/package/configuration.py +0 -27
- authenticator/package/models/__init__.py +0 -0
- authenticator/package/models/database.py +0 -32
- authenticator/package/models/session.py +0 -20
- authenticator/package/models/user.py +0 -22
- dataflow_core-2.0.10.dist-info/RECORD +0 -26
- {dataflow_core-2.0.10.dist-info → dataflow_core-2.1.0.dist-info}/WHEEL +0 -0
- {dataflow_core-2.0.10.dist-info → dataflow_core-2.1.0.dist-info}/entry_points.txt +0 -0
- {dataflow_core-2.0.10.dist-info → dataflow_core-2.1.0.dist-info}/top_level.txt +0 -0
|
@@ -1,83 +1,58 @@
|
|
|
1
|
-
from
|
|
2
|
-
from
|
|
3
|
-
|
|
4
|
-
from
|
|
1
|
+
from flask import redirect, request
|
|
2
|
+
from flask_appbuilder.security.views import AuthDBView
|
|
3
|
+
from flask_appbuilder.security.views import expose
|
|
4
|
+
from flask_login import login_user
|
|
5
5
|
from airflow.www.security import FabAirflowSecurityManagerOverride
|
|
6
|
-
from
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
6
|
+
from dataflow.dataflow import Dataflow
|
|
7
|
+
import logging
|
|
8
|
+
|
|
9
|
+
logging.basicConfig(
|
|
10
|
+
level=logging.INFO,
|
|
11
|
+
format='%(asctime)s - %(levelname)s - %(message)s',
|
|
12
|
+
datefmt='%Y-%m-%d %H:%M:%S'
|
|
13
|
+
)
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
dataflow = Dataflow()
|
|
17
|
+
|
|
18
|
+
class DataflowAuthDBView(AuthDBView):
|
|
19
|
+
@expose('/login/', methods=['GET', 'POST'])
|
|
20
|
+
def login(self):
|
|
21
|
+
"""
|
|
22
|
+
Override the default login method to handle custom authentication
|
|
23
|
+
"""
|
|
23
24
|
try:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
for part in parts:
|
|
29
|
-
if part.startswith('dataflow_session='):
|
|
30
|
-
user_session_id = part
|
|
31
|
-
break
|
|
32
|
-
|
|
33
|
-
if user_session_id is None:
|
|
34
|
-
raise Exception("No session id found")
|
|
25
|
+
session_id = request.cookies.get('dataflow_session')
|
|
26
|
+
if not session_id:
|
|
27
|
+
logger.info("No session cookie found, falling back to standard login.")
|
|
28
|
+
return super().login()
|
|
35
29
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
username=user_data["user_name"],
|
|
50
|
-
first_name=user_data.get("first_name", ""),
|
|
51
|
-
last_name=user_data.get("last_name", ""),
|
|
52
|
-
email=user_data.get("email", ""),
|
|
53
|
-
role=user_role
|
|
30
|
+
user_details = dataflow.auth(session_id)
|
|
31
|
+
logger.info(f"User details retrieved for: {user_details['user_name']}")
|
|
32
|
+
user = self.appbuilder.sm.find_user(username=user_details['user_name'])
|
|
33
|
+
if user:
|
|
34
|
+
logger.info(f"User found: {user}")
|
|
35
|
+
login_user(user, remember=False)
|
|
36
|
+
else:
|
|
37
|
+
user = self.appbuilder.sm.add_user(
|
|
38
|
+
username=user_details['user_name'],
|
|
39
|
+
first_name=user_details.get("first_name", ""),
|
|
40
|
+
last_name=user_details.get("last_name", ""),
|
|
41
|
+
email=user_details.get("email", ""),
|
|
42
|
+
role=self.appbuilder.sm.find_role(user_details.get("base_role", "user").title())
|
|
54
43
|
)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
44
|
+
logger.info(f"New user created: {user}")
|
|
45
|
+
if user:
|
|
46
|
+
login_user(user, remember=False)
|
|
47
|
+
|
|
48
|
+
return redirect(self.appbuilder.get_url_for_index)
|
|
58
49
|
|
|
59
50
|
except Exception as e:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def find_user(self, username=None):
|
|
63
|
-
"""Find user by username or email."""
|
|
64
|
-
return self.airflow_db.query(self.user_model).filter_by(username=username).one_or_none()
|
|
51
|
+
logger.error(f"Login failed: {e}")
|
|
52
|
+
return super().login()
|
|
65
53
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
"""Create a user."""
|
|
72
|
-
user = self.user_model()
|
|
73
|
-
user.first_name = first_name
|
|
74
|
-
user.last_name = last_name
|
|
75
|
-
user.username = username
|
|
76
|
-
user.email = email
|
|
77
|
-
user.active = True
|
|
78
|
-
user.roles = role if isinstance(role, list) else [role]
|
|
79
|
-
user.password = password
|
|
80
|
-
self.airflow_db.add(user)
|
|
81
|
-
self.airflow_db.commit()
|
|
82
|
-
return user
|
|
83
|
-
|
|
54
|
+
class DataflowAirflowAuthenticator(FabAirflowSecurityManagerOverride):
|
|
55
|
+
authdbview = DataflowAuthDBView
|
|
56
|
+
|
|
57
|
+
def __init__(self, appbuilder):
|
|
58
|
+
super().__init__(appbuilder)
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
from .
|
|
2
|
-
from .
|
|
3
|
-
from .package.models import (
|
|
1
|
+
from dataflow.db import get_db
|
|
2
|
+
from dataflow.models import (
|
|
4
3
|
user as m_user,
|
|
5
4
|
session as m_session
|
|
6
5
|
)
|
|
@@ -11,15 +10,7 @@ from jupyterhub.auth import Authenticator
|
|
|
11
10
|
class DataflowHubAuthenticator(Authenticator):
|
|
12
11
|
def __init__(self, **kwargs):
|
|
13
12
|
super().__init__(**kwargs)
|
|
14
|
-
|
|
15
|
-
self.dataflow_config = ConfigurationManager('/dataflow/app/config/dataflow.cfg')
|
|
16
|
-
self.database_url = self.dataflow_config.get_config_value('database', 'database_url')
|
|
17
|
-
|
|
18
|
-
self.db_instance = DatabaseManager(self.database_url)
|
|
19
|
-
self.db = next(self.db_instance.get_session())
|
|
20
|
-
|
|
21
|
-
m_user.Base.metadata.create_all(bind=self.db_instance.get_engine())
|
|
22
|
-
m_session.Base.metadata.create_all(bind=self.db_instance.get_engine())
|
|
13
|
+
self.db = next(get_db())
|
|
23
14
|
|
|
24
15
|
def generate_session_id(self):
|
|
25
16
|
return str(uuid.uuid4())
|
|
@@ -39,8 +30,8 @@ class DataflowHubAuthenticator(Authenticator):
|
|
|
39
30
|
|
|
40
31
|
# Check if the user already has an existing session
|
|
41
32
|
existing_session = (
|
|
42
|
-
self.db.query(m_session.
|
|
43
|
-
.filter(m_session.
|
|
33
|
+
self.db.query(m_session.Session)
|
|
34
|
+
.filter(m_session.Session.user_id == user.user_id)
|
|
44
35
|
.first()
|
|
45
36
|
)
|
|
46
37
|
|
|
@@ -50,16 +41,16 @@ class DataflowHubAuthenticator(Authenticator):
|
|
|
50
41
|
else:
|
|
51
42
|
# Generate a new session_id
|
|
52
43
|
session_id = self.generate_session_id()
|
|
53
|
-
query = self.db.query(m_session.
|
|
54
|
-
isSession = query.filter(m_session.
|
|
44
|
+
query = self.db.query(m_session.Session)
|
|
45
|
+
isSession = query.filter(m_session.Session.session_id == session_id).first()
|
|
55
46
|
|
|
56
47
|
# If session_id(uuid string) already exists in the database, generate a new one
|
|
57
48
|
while isSession is not None:
|
|
58
49
|
session_id = self.generate_session_id()
|
|
59
|
-
isSession = query.filter(m_session.
|
|
50
|
+
isSession = query.filter(m_session.Session.session_id == session_id).first()
|
|
60
51
|
|
|
61
52
|
# add session_id to the database
|
|
62
|
-
db_item = m_session.
|
|
53
|
+
db_item = m_session.Session(user_id=user.user_id, session_id=session_id)
|
|
63
54
|
self.db.add(db_item)
|
|
64
55
|
self.db.commit()
|
|
65
56
|
self.db.refresh(db_item)
|
|
@@ -86,4 +77,7 @@ class DataflowHubAuthenticator(Authenticator):
|
|
|
86
77
|
return user_dict
|
|
87
78
|
|
|
88
79
|
except Exception as e:
|
|
89
|
-
return None
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
finally:
|
|
83
|
+
self.db.close()
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
from flask import redirect, request
|
|
1
|
+
from flask import redirect, request, jsonify
|
|
2
2
|
from flask_appbuilder.security.views import AuthDBView
|
|
3
|
-
from superset.security import SupersetSecurityManager
|
|
4
3
|
from flask_appbuilder.security.views import expose
|
|
5
4
|
from flask_login import login_user
|
|
6
|
-
from
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
from flask_jwt_extended import (
|
|
6
|
+
create_access_token
|
|
7
|
+
)
|
|
8
|
+
from flask_appbuilder.const import (
|
|
9
|
+
API_SECURITY_ACCESS_TOKEN_KEY
|
|
10
|
+
)
|
|
11
|
+
from superset.security import SupersetSecurityManager
|
|
12
12
|
from dataflow.dataflow import Dataflow
|
|
13
13
|
|
|
14
|
-
class
|
|
14
|
+
class DataflowAuthDBView(AuthDBView):
|
|
15
15
|
def __init__(self):
|
|
16
16
|
self.dataflow = Dataflow()
|
|
17
17
|
|
|
18
18
|
@expose('/login/', methods=['GET'])
|
|
19
|
-
def
|
|
19
|
+
def login_redirect(self):
|
|
20
20
|
try:
|
|
21
21
|
session_id = request.cookies.get('dataflow_session')
|
|
22
22
|
|
|
@@ -30,8 +30,7 @@ class CustomAuthDBView(AuthDBView):
|
|
|
30
30
|
first_name=user_details.get("first_name", ""),
|
|
31
31
|
last_name=user_details.get("last_name", ""),
|
|
32
32
|
email=user_details.get("email", ""),
|
|
33
|
-
role=self.appbuilder.sm.find_role('Admin')
|
|
34
|
-
password=""
|
|
33
|
+
role=self.appbuilder.sm.find_role('Admin')
|
|
35
34
|
)
|
|
36
35
|
if user:
|
|
37
36
|
login_user(user, remember=False)
|
|
@@ -41,8 +40,27 @@ class CustomAuthDBView(AuthDBView):
|
|
|
41
40
|
except Exception as e:
|
|
42
41
|
return super().login()
|
|
43
42
|
|
|
43
|
+
@expose("/login", methods=["POST"])
|
|
44
|
+
def login_token(self):
|
|
45
|
+
login_payload = request.get_json()
|
|
46
|
+
user = self.appbuilder.sm.find_user(username=login_payload["user_name"])
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
if not user:
|
|
49
|
+
user = self.appbuilder.sm.add_user(
|
|
50
|
+
username=login_payload["user_name"],
|
|
51
|
+
first_name=login_payload.get("first_name", ""),
|
|
52
|
+
last_name=login_payload.get("last_name", ""),
|
|
53
|
+
email=login_payload.get("email", ""),
|
|
54
|
+
role=self.appbuilder.sm.find_role('Admin')
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
resp = dict()
|
|
58
|
+
resp[API_SECURITY_ACCESS_TOKEN_KEY] = create_access_token(
|
|
59
|
+
identity=str(user.id), fresh=True
|
|
60
|
+
)
|
|
61
|
+
return jsonify(resp)
|
|
62
|
+
|
|
63
|
+
class DataflowSecurityManager(SupersetSecurityManager):
|
|
64
|
+
authdbview = DataflowAuthDBView
|
|
47
65
|
def __init__(self, appbuilder):
|
|
48
|
-
super(
|
|
66
|
+
super(DataflowSecurityManager, self).__init__(appbuilder)
|
|
@@ -7,6 +7,7 @@ from sqlalchemy.orm import sessionmaker
|
|
|
7
7
|
class DatabaseManager:
|
|
8
8
|
def __init__(self, db_url):
|
|
9
9
|
self.db_url = db_url
|
|
10
|
+
self.engine = self.get_engine()
|
|
10
11
|
|
|
11
12
|
def get_engine(self):
|
|
12
13
|
try:
|
|
@@ -17,8 +18,8 @@ class DatabaseManager:
|
|
|
17
18
|
|
|
18
19
|
def get_session(self):
|
|
19
20
|
try:
|
|
20
|
-
engine = self.
|
|
21
|
-
session = sessionmaker(autocommit=False, autoflush=False, bind=engine
|
|
21
|
+
engine = self.engine
|
|
22
|
+
session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
|
22
23
|
db = session()
|
|
23
24
|
try:
|
|
24
25
|
yield db
|
|
@@ -29,4 +30,4 @@ class DatabaseManager:
|
|
|
29
30
|
raise e
|
|
30
31
|
|
|
31
32
|
def get_base(self):
|
|
32
|
-
return declarative_base()
|
|
33
|
+
return declarative_base()
|
dataflow/dataflow.py
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import os, requests
|
|
2
|
-
from .
|
|
3
|
-
from .models.environment import JobLogs
|
|
4
|
-
from sqlalchemy.inspection import inspect
|
|
1
|
+
import os, requests
|
|
2
|
+
from .database_manager import DatabaseManager
|
|
5
3
|
from .utils.aws_secrets_manager import SecretsManagerClient
|
|
6
|
-
import json
|
|
7
|
-
from
|
|
4
|
+
import json
|
|
5
|
+
from .configuration import ConfigurationManager
|
|
8
6
|
|
|
9
7
|
|
|
10
8
|
class Dataflow:
|
|
@@ -12,7 +10,15 @@ class Dataflow:
|
|
|
12
10
|
self.secrets_manager = SecretsManagerClient()
|
|
13
11
|
|
|
14
12
|
def auth(self, session_id: str):
|
|
15
|
-
"""
|
|
13
|
+
"""
|
|
14
|
+
Retrieve and return user information using their session ID.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
session_id (str): User's session ID from cookies
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
dict: User information including username, name, email, and role
|
|
21
|
+
"""
|
|
16
22
|
try:
|
|
17
23
|
dataflow_config = ConfigurationManager('/dataflow/app/auth_config/dataflow_auth.cfg')
|
|
18
24
|
auth_api = dataflow_config.get_config_value('auth', 'ui_auth_api')
|
|
@@ -30,7 +36,7 @@ class Dataflow:
|
|
|
30
36
|
"first_name": user_data["first_name"],
|
|
31
37
|
"last_name": user_data["last_name"] if user_data.get("last_name") else "",
|
|
32
38
|
"email": user_data["email"],
|
|
33
|
-
"role": user_data["
|
|
39
|
+
"role": user_data["base_role"]
|
|
34
40
|
}
|
|
35
41
|
return user_dict
|
|
36
42
|
|
|
@@ -38,26 +44,124 @@ class Dataflow:
|
|
|
38
44
|
return e
|
|
39
45
|
|
|
40
46
|
def variable(self, variable_name: str):
|
|
41
|
-
"""
|
|
47
|
+
"""
|
|
48
|
+
Retrieve a Dataflow variable.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
variable_name (str): Name of the variable to retrieve
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
str or None: Variable value if found, None otherwise
|
|
55
|
+
"""
|
|
42
56
|
try:
|
|
43
|
-
host_name = os.environ
|
|
44
|
-
user_name = host_name.replace("jupyter-","")
|
|
57
|
+
host_name = os.environ.get("HOSTNAME", "")
|
|
58
|
+
user_name = host_name.replace("jupyter-", "") if host_name.startswith("jupyter-") else host_name
|
|
59
|
+
runtime = os.environ.get("RUNTIME")
|
|
60
|
+
slug = os.environ.get("SLUG")
|
|
61
|
+
|
|
62
|
+
dataflow_config = ConfigurationManager('/dataflow/app/auth_config/dataflow_auth.cfg')
|
|
63
|
+
variable_api = dataflow_config.get_config_value("auth", "db_get_variables")
|
|
64
|
+
if not variable_api:
|
|
65
|
+
print("[Dataflow.variable] Variable Unreachable")
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
if runtime:
|
|
69
|
+
query_params = {
|
|
70
|
+
"variable_key": variable_name,
|
|
71
|
+
"runtime": runtime,
|
|
72
|
+
"slug": slug
|
|
73
|
+
}
|
|
74
|
+
response = requests.get(variable_api, params=query_params)
|
|
75
|
+
if response.status_code == 200:
|
|
76
|
+
response_text = response.text.strip().strip('"')
|
|
77
|
+
return response_text
|
|
78
|
+
|
|
79
|
+
query_params["slug"] = "global"
|
|
80
|
+
response = requests.get(variable_api, params=query_params)
|
|
81
|
+
if response.status_code == 200:
|
|
82
|
+
response_text = response.text.strip().strip('"')
|
|
83
|
+
return response_text
|
|
84
|
+
else:
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
query_params = {
|
|
88
|
+
"variable_key": variable_name,
|
|
89
|
+
"runtime": None,
|
|
90
|
+
"slug": None,
|
|
91
|
+
"created_by": user_name
|
|
92
|
+
}
|
|
93
|
+
response = requests.get(variable_api, params=query_params)
|
|
94
|
+
if response.status_code == 200:
|
|
95
|
+
response_text = response.text.strip().strip('"')
|
|
96
|
+
return response_text
|
|
97
|
+
else:
|
|
98
|
+
return None
|
|
99
|
+
except Exception as e:
|
|
100
|
+
print(f"[Dataflow.variable] Exception occurred: {e}")
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
def secret(self, secret_name: str):
|
|
104
|
+
"""
|
|
105
|
+
Retrieve a Dataflow secret value.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
secret_name (str): Name of the secret to retrieve
|
|
45
109
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
110
|
+
Returns:
|
|
111
|
+
str or None: Secret value if found, None otherwise
|
|
112
|
+
"""
|
|
113
|
+
try:
|
|
114
|
+
host_name = os.environ.get("HOSTNAME", "")
|
|
115
|
+
user_name = host_name.replace("jupyter-", "") if host_name.startswith("jupyter-") else host_name
|
|
116
|
+
runtime = os.environ.get("RUNTIME")
|
|
117
|
+
slug = os.environ.get("SLUG")
|
|
118
|
+
|
|
119
|
+
dataflow_config = ConfigurationManager('/dataflow/app/auth_config/dataflow_auth.cfg')
|
|
120
|
+
secret_api = dataflow_config.get_config_value("auth", "db_get_secrets")
|
|
121
|
+
if not secret_api:
|
|
122
|
+
print("[Dataflow.secret] Secret API Unreachable")
|
|
123
|
+
return None
|
|
124
|
+
|
|
125
|
+
query_params = {
|
|
126
|
+
"secret_key": secret_name,
|
|
127
|
+
"created_by": user_name
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if runtime:
|
|
131
|
+
query_params["runtime"] = runtime
|
|
132
|
+
if slug:
|
|
133
|
+
query_params["slug"] = slug
|
|
134
|
+
|
|
135
|
+
response = requests.get(secret_api, params=query_params)
|
|
49
136
|
|
|
137
|
+
if response.status_code == 200:
|
|
138
|
+
response_text = response.text.strip().strip('"')
|
|
139
|
+
return response_text
|
|
140
|
+
else:
|
|
141
|
+
return None
|
|
50
142
|
except Exception as e:
|
|
143
|
+
print(f"[Dataflow.secret] Exception occurred: {e}")
|
|
51
144
|
return None
|
|
145
|
+
|
|
146
|
+
def connection(self, conn_id: str, mode="session"):
|
|
147
|
+
"""
|
|
148
|
+
Connects with a Dataflow connection.
|
|
52
149
|
|
|
53
|
-
|
|
54
|
-
|
|
150
|
+
Args:
|
|
151
|
+
conn_id (str): Connection identifier
|
|
152
|
+
mode (str): Return type - "session" (default) or "engine" or "url"
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Session or Engine: SQLAlchemy session or engine based on mode
|
|
156
|
+
"""
|
|
55
157
|
try:
|
|
56
158
|
host_name = os.environ["HOSTNAME"]
|
|
57
159
|
user_name=host_name.replace("jupyter-","")
|
|
160
|
+
runtime = os.environ.get("RUNTIME")
|
|
161
|
+
slug = os.environ.get("SLUG")
|
|
58
162
|
|
|
59
163
|
vault_path = "connections"
|
|
60
|
-
secret = self.secrets_manager.get_secret_by_key(vault_path, user_name, conn_id)
|
|
164
|
+
secret = self.secrets_manager.get_secret_by_key(vault_path, user_name, conn_id, runtime, slug)
|
|
61
165
|
|
|
62
166
|
conn_type = secret['conn_type'].lower()
|
|
63
167
|
username = secret['login']
|
|
@@ -71,7 +175,7 @@ class Dataflow:
|
|
|
71
175
|
|
|
72
176
|
connection_string = f"{conn_type}://{user_info}{host}:{port}{db_info}"
|
|
73
177
|
|
|
74
|
-
extra = secret.get('extra', '')
|
|
178
|
+
extra = secret.get('extra', '')
|
|
75
179
|
if extra:
|
|
76
180
|
try:
|
|
77
181
|
extra_params = json.loads(extra)
|
|
@@ -82,8 +186,14 @@ class Dataflow:
|
|
|
82
186
|
# If 'extra' is not valid JSON, skip adding extra parameters
|
|
83
187
|
pass
|
|
84
188
|
|
|
189
|
+
if mode == "url":
|
|
190
|
+
return connection_string
|
|
191
|
+
|
|
85
192
|
connection_instance = DatabaseManager(connection_string)
|
|
86
|
-
|
|
193
|
+
if mode == "engine":
|
|
194
|
+
return connection_instance.get_engine()
|
|
195
|
+
elif mode == "session":
|
|
196
|
+
return next(connection_instance.get_session())
|
|
87
197
|
|
|
88
198
|
except Exception as e:
|
|
89
199
|
return None
|
dataflow/db.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from .database_manager import DatabaseManager
|
|
2
|
+
from .configuration import ConfigurationManager
|
|
3
|
+
from sqlalchemy.orm import declarative_base
|
|
4
|
+
from sqlalchemy.exc import SQLAlchemyError
|
|
5
|
+
from .utils.logger import CustomLogger
|
|
6
|
+
|
|
7
|
+
logger = CustomLogger().get_logger(__name__)
|
|
8
|
+
|
|
9
|
+
dataflow_config = ConfigurationManager('/dataflow/app/config/dataflow.cfg')
|
|
10
|
+
db_url = dataflow_config.get_config_value('database', 'database_url')
|
|
11
|
+
local_db_url = dataflow_config.get_config_value('database', 'single_user_db_url')
|
|
12
|
+
|
|
13
|
+
db_manager = DatabaseManager(db_url)
|
|
14
|
+
local_db_manager = None
|
|
15
|
+
|
|
16
|
+
Base = declarative_base()
|
|
17
|
+
Local_Base = declarative_base()
|
|
18
|
+
|
|
19
|
+
def create_tables(local_db=False):
|
|
20
|
+
"""
|
|
21
|
+
Create all tables in the database.
|
|
22
|
+
This is called at the start of the application.
|
|
23
|
+
"""
|
|
24
|
+
try:
|
|
25
|
+
if local_db:
|
|
26
|
+
global local_db_manager
|
|
27
|
+
if local_db_manager is None:
|
|
28
|
+
local_db_manager = DatabaseManager(local_db_url)
|
|
29
|
+
Local_Base.metadata.create_all(bind=local_db_manager.get_engine())
|
|
30
|
+
else:
|
|
31
|
+
Base.metadata.create_all(bind=db_manager.get_engine())
|
|
32
|
+
logger.info("Database tables created successfully")
|
|
33
|
+
except SQLAlchemyError as e:
|
|
34
|
+
error_message = f"Failed to create tables: {str(e)}"
|
|
35
|
+
logger.error(error_message)
|
|
36
|
+
raise e
|
|
37
|
+
|
|
38
|
+
def get_local_db():
|
|
39
|
+
"""
|
|
40
|
+
Get a local database session.
|
|
41
|
+
|
|
42
|
+
Yields:
|
|
43
|
+
Session: Local database session
|
|
44
|
+
"""
|
|
45
|
+
global local_db_manager
|
|
46
|
+
if local_db_manager is None:
|
|
47
|
+
local_db_manager = DatabaseManager(local_db_url)
|
|
48
|
+
yield from local_db_manager.get_session()
|
|
49
|
+
|
|
50
|
+
def get_db():
|
|
51
|
+
yield from db_manager.get_session()
|
dataflow/environment.py
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import os, shutil, subprocess, datetime
|
|
2
|
-
from .models.database import DatabaseManager
|
|
3
2
|
from .models.environment import JobLogs, Environment
|
|
4
3
|
import json, asyncio, pkg_resources
|
|
5
4
|
from sqlalchemy.orm import Session
|
|
6
|
-
from
|
|
5
|
+
from .configuration import ConfigurationManager
|
|
7
6
|
|
|
8
7
|
class EnvironmentManager:
|
|
9
8
|
def __init__(self):
|
dataflow/models/__init__.py
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# init for loading models in the application
|
|
2
|
+
|
|
3
|
+
from .role import Role
|
|
4
|
+
from .user import User
|
|
5
|
+
from .team import Team
|
|
6
|
+
from .environment import Environment
|
|
7
|
+
from .project_details import ProjectDetails
|
|
8
|
+
from .recent_projects import RecentProjects
|
|
9
|
+
from .pinned_projects import PinnedProject
|
|
10
|
+
from .app_types import AppType
|
|
11
|
+
from .blacklist_library import BlacklistedLibrary
|
|
12
|
+
from .environment_status import EnvironmentStatus
|
|
13
|
+
from .session import Session
|
|
14
|
+
from .server_config import ServerConfig
|
|
15
|
+
from .runtime import RuntimeZone
|
|
16
|
+
from .environment_status import EnvironmentStatus
|
|
17
|
+
from .user_team import UserTeam
|
|
18
|
+
from .role_server import RoleServer
|
|
19
|
+
from .variables import Variable
|
|
20
|
+
from .recent_project_studio import RecentProjectStudio
|
|
21
|
+
from .connection import Connection
|
|
22
|
+
from .git_ssh import GitSSH
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from sqlalchemy import Column, Integer, String, Boolean
|
|
2
|
+
from dataflow.db import Base
|
|
3
|
+
|
|
4
|
+
class AppType(Base):
|
|
5
|
+
__tablename__ = "RUNTIME_APP_TYPE"
|
|
6
|
+
|
|
7
|
+
id = Column(Integer, primary_key=True, autoincrement=True, unique=True)
|
|
8
|
+
name = Column(String, unique=True, nullable=True)
|
|
9
|
+
display_name = Column(String, nullable=False)
|
|
10
|
+
code_based = Column(Boolean, nullable=False)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# models/blacklist_library.py
|
|
2
|
+
from sqlalchemy import Column, Integer, String, UniqueConstraint
|
|
3
|
+
from dataflow.db import Base
|
|
4
|
+
|
|
5
|
+
class BlacklistedLibrary(Base):
|
|
6
|
+
"""
|
|
7
|
+
BlacklistedLibrary model represents a table for storing blacklisted libraries with their versions.
|
|
8
|
+
|
|
9
|
+
Attributes:
|
|
10
|
+
id (int): Primary key of the table, auto-incremented.
|
|
11
|
+
library_name (str): The name of the blacklisted library.
|
|
12
|
+
version (str): The version of the blacklisted library.
|
|
13
|
+
|
|
14
|
+
Unique constraint to ensure the combination of library_name and version is unique.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
__tablename__ = "BLACKLISTED_LIBRARY"
|
|
18
|
+
|
|
19
|
+
id = Column(Integer, primary_key=True, index=True, doc="Primary key for the library.")
|
|
20
|
+
library_name = Column(String, index=True, doc="The name of the blacklisted library.")
|
|
21
|
+
version = Column(String, doc="The version of the blacklisted library.")
|
|
22
|
+
|
|
23
|
+
__table_args__ = (
|
|
24
|
+
UniqueConstraint('library_name', 'version', name='uq_library_version'),
|
|
25
|
+
)
|
|
26
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from sqlalchemy import Column, String, Integer, Boolean, DateTime, UniqueConstraint
|
|
2
|
+
from sqlalchemy.sql import func
|
|
3
|
+
from dataflow.db import Base
|
|
4
|
+
|
|
5
|
+
class Connection(Base):
|
|
6
|
+
"""
|
|
7
|
+
Database model for storing non-sensitive connection metadata
|
|
8
|
+
"""
|
|
9
|
+
__tablename__ = "CONNECTION"
|
|
10
|
+
|
|
11
|
+
id = Column(Integer, primary_key=True, index=True)
|
|
12
|
+
conn_id = Column(String, index=True, nullable=False)
|
|
13
|
+
description = Column(String, nullable=True)
|
|
14
|
+
conn_type = Column(String, nullable=False)
|
|
15
|
+
runtime = Column(String, nullable=True)
|
|
16
|
+
slug = Column(String, nullable=True)
|
|
17
|
+
status = Column(Boolean, default=False)
|
|
18
|
+
created_by = Column(String, nullable=True)
|
|
19
|
+
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
20
|
+
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
|
21
|
+
is_active = Column(Boolean, default=True)
|
|
22
|
+
|
|
23
|
+
__table_args__ = (
|
|
24
|
+
UniqueConstraint('conn_id', 'runtime', 'slug', 'is_active', 'created_by', name='uq_active_conn_with_runtime_slug'),
|
|
25
|
+
)
|