dataflow-core 2.1.14rc1__py3-none-any.whl → 2.1.15__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 +3 -9
- authenticator/dataflowhubauthenticator.py +29 -44
- dataflow/dataflow.py +45 -69
- dataflow/environment.py +20 -11
- dataflow/models/__init__.py +5 -3
- dataflow/models/app_types.py +8 -3
- dataflow/models/connection.py +5 -4
- dataflow/models/dataflow_zone.py +2 -3
- dataflow/models/environment.py +64 -21
- dataflow/models/git_ssh.py +2 -1
- dataflow/models/org_associations.py +38 -0
- dataflow/models/organization.py +78 -0
- dataflow/models/pinned_projects.py +2 -2
- dataflow/models/project_details.py +9 -6
- dataflow/models/recent_project_studio.py +1 -1
- dataflow/models/role.py +12 -6
- dataflow/models/role_server.py +2 -5
- dataflow/models/role_zone.py +8 -3
- dataflow/models/server_config.py +9 -5
- dataflow/models/team.py +10 -4
- dataflow/models/user.py +49 -12
- dataflow/models/user_team.py +1 -4
- dataflow/models/variables.py +6 -4
- dataflow/secrets_manager/factory.py +14 -8
- dataflow/secrets_manager/providers/gcp_manager.py +332 -0
- dataflow/secrets_manager/service.py +11 -9
- dataflow/utils/get_current_user.py +51 -28
- {dataflow_core-2.1.14rc1.dist-info → dataflow_core-2.1.15.dist-info}/METADATA +4 -1
- dataflow_core-2.1.15.dist-info/RECORD +63 -0
- {dataflow_core-2.1.14rc1.dist-info → dataflow_core-2.1.15.dist-info}/top_level.txt +1 -0
- dfmigration/__init__.py +0 -0
- dfmigration/env.py +45 -0
- dfmigration/versions/001_initial_baseline_migration.py +20 -0
- dfmigration/versions/__init__.py +0 -0
- dataflow/models/user_environment.py +0 -16
- dataflow_core-2.1.14rc1.dist-info/RECORD +0 -57
- {dataflow_core-2.1.14rc1.dist-info → dataflow_core-2.1.15.dist-info}/WHEEL +0 -0
- {dataflow_core-2.1.14rc1.dist-info → dataflow_core-2.1.15.dist-info}/entry_points.txt +0 -0
dataflow/models/environment.py
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
from sqlalchemy import
|
|
2
|
-
|
|
1
|
+
from sqlalchemy import (
|
|
2
|
+
Column, Integer, String, Boolean, Text,
|
|
3
|
+
ForeignKey, DateTime, UniqueConstraint, CheckConstraint
|
|
4
|
+
)
|
|
5
|
+
from sqlalchemy.orm import relationship, Session
|
|
3
6
|
from sqlalchemy.sql import func
|
|
4
7
|
from datetime import datetime, timezone
|
|
5
8
|
from dataflow.db import Base
|
|
@@ -11,14 +14,14 @@ class EnvironmentAttributes(Base):
|
|
|
11
14
|
"""
|
|
12
15
|
__abstract__ = True
|
|
13
16
|
|
|
14
|
-
name = Column(String)
|
|
17
|
+
name = Column(String, nullable=False)
|
|
15
18
|
url = Column(String)
|
|
16
|
-
enabled = Column(Boolean, default=True)
|
|
17
|
-
version = Column(String, default=0)
|
|
18
|
-
is_latest = Column(Boolean, default=True)
|
|
19
|
+
enabled = Column(Boolean, default=True, server_default='true')
|
|
20
|
+
version = Column(String, default=0, server_default='0')
|
|
21
|
+
is_latest = Column(Boolean, default=True, server_default='true')
|
|
19
22
|
base_env_id = Column(Integer, default=None)
|
|
20
23
|
short_name = Column(String(5))
|
|
21
|
-
status = Column(String, default="Saved")
|
|
24
|
+
status = Column(String, default="Saved", server_default="Saved")
|
|
22
25
|
icon = Column(String)
|
|
23
26
|
py_version = Column(String)
|
|
24
27
|
r_version = Column(String)
|
|
@@ -27,16 +30,15 @@ class EnvironmentAttributes(Base):
|
|
|
27
30
|
r_requirements = Column(Text)
|
|
28
31
|
created_date = Column(DateTime, server_default=func.now())
|
|
29
32
|
created_by = Column(String)
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
org_id = Column(Integer, ForeignKey('ORGANIZATION.id'))
|
|
32
34
|
|
|
33
35
|
class Environment(EnvironmentAttributes):
|
|
34
36
|
__tablename__ = 'ENVIRONMENT'
|
|
35
|
-
|
|
37
|
+
__table_args__ = (UniqueConstraint('short_name', 'org_id', name='_env_short_name_org_uc'),)
|
|
36
38
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
37
|
-
short_name = Column(String(5), unique=True)
|
|
38
39
|
|
|
39
|
-
#
|
|
40
|
+
# Relationships
|
|
41
|
+
organization = relationship("Organization", back_populates="environments")
|
|
40
42
|
archived_versions = relationship("ArchivedEnvironment", back_populates="original_environment")
|
|
41
43
|
|
|
42
44
|
class ArchivedEnvironment(EnvironmentAttributes):
|
|
@@ -44,23 +46,22 @@ class ArchivedEnvironment(EnvironmentAttributes):
|
|
|
44
46
|
|
|
45
47
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
46
48
|
original_env_id = Column(Integer, ForeignKey('ENVIRONMENT.id', ondelete='CASCADE'))
|
|
47
|
-
is_latest = Column(Boolean, default=False)
|
|
48
49
|
|
|
49
50
|
# Relationship with Environment
|
|
50
51
|
original_environment = relationship("Environment", back_populates="archived_versions")
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
54
53
|
class JobLogs(Base):
|
|
55
54
|
__tablename__ = "JOB_LOG"
|
|
55
|
+
__table_args__ = (UniqueConstraint('log_file_name', 'org_id', name='_job_log_file_org_uc'),)
|
|
56
56
|
|
|
57
57
|
id = Column(Integer, primary_key=True, index=True)
|
|
58
|
-
created_at = Column(DateTime, default=datetime.now)
|
|
58
|
+
created_at = Column(DateTime, default=datetime.now, server_default=func.now())
|
|
59
59
|
completed_at = Column(DateTime, nullable=True)
|
|
60
|
-
log_file_name = Column(String,
|
|
60
|
+
log_file_name = Column(String, nullable=False)
|
|
61
61
|
log_file_location = Column(String, nullable=False)
|
|
62
62
|
status = Column(String)
|
|
63
63
|
created_by = Column(String)
|
|
64
|
+
org_id = Column(Integer, ForeignKey('ORGANIZATION.id', ondelete='CASCADE'))
|
|
64
65
|
|
|
65
66
|
|
|
66
67
|
class LocalEnvironment(Base):
|
|
@@ -69,14 +70,56 @@ class LocalEnvironment(Base):
|
|
|
69
70
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
70
71
|
name = Column(String, nullable=False, index=True)
|
|
71
72
|
user_name = Column(String, ForeignKey('USER.user_name', ondelete='CASCADE'), nullable=False, index=True)
|
|
73
|
+
org_id = Column(Integer, ForeignKey('ORGANIZATION.id', ondelete='CASCADE'), nullable=False, index=True)
|
|
72
74
|
py_version = Column(String)
|
|
73
75
|
pip_libraries = Column(Text)
|
|
74
76
|
conda_libraries = Column(Text)
|
|
75
|
-
status = Column(String, default="Created")
|
|
76
|
-
cloned_from = Column(String,
|
|
77
|
+
status = Column(String, default="Created", server_default="Created")
|
|
78
|
+
cloned_from = Column(String, nullable=True)
|
|
77
79
|
updated_at = Column(DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc))
|
|
78
|
-
need_refresh = Column(Boolean, default=False)
|
|
80
|
+
need_refresh = Column(Boolean, default=False, server_default='false')
|
|
79
81
|
|
|
80
82
|
class EnvType(str, Enum):
|
|
81
83
|
dataflow = "dataflow"
|
|
82
|
-
local = "local"
|
|
84
|
+
local = "local"
|
|
85
|
+
|
|
86
|
+
class PipSource(Base):
|
|
87
|
+
__tablename__ = "PIP_SOURCE"
|
|
88
|
+
|
|
89
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
90
|
+
|
|
91
|
+
org_id = Column(Integer, ForeignKey("ORGANIZATION.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
92
|
+
user_name = Column(String, ForeignKey("USER.user_name", ondelete="CASCADE"), nullable=True, index=True)
|
|
93
|
+
|
|
94
|
+
name = Column(String, nullable=False)
|
|
95
|
+
url = Column(String, nullable=False)
|
|
96
|
+
is_index = Column(Boolean, default=False, nullable=False, server_default='false')
|
|
97
|
+
|
|
98
|
+
created_at = Column(DateTime, default=datetime.now(timezone.utc), nullable=False)
|
|
99
|
+
updated_at = Column(DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc), nullable=False)
|
|
100
|
+
|
|
101
|
+
__table_args__ = (
|
|
102
|
+
UniqueConstraint("org_id", "name", "user_name", name="uq_pip_source_per_user_org"),
|
|
103
|
+
CheckConstraint("NOT (is_index = TRUE AND user_name IS NOT NULL)", name="check_no_user_index_url"),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
@classmethod
|
|
107
|
+
def get_org_sources(cls, session: Session, org_id: int):
|
|
108
|
+
"""
|
|
109
|
+
Returns all sources for the given org (org-level).
|
|
110
|
+
"""
|
|
111
|
+
return session.query(cls).filter(
|
|
112
|
+
cls.org_id == org_id,
|
|
113
|
+
cls.user_name == None
|
|
114
|
+
).all()
|
|
115
|
+
|
|
116
|
+
@classmethod
|
|
117
|
+
def get_user_sources(cls, session: Session, org_id: int, user_name: str):
|
|
118
|
+
"""
|
|
119
|
+
Returns merged sources for a user in an org (org-level + user-level personal sources).
|
|
120
|
+
"""
|
|
121
|
+
return session.query(cls).filter(
|
|
122
|
+
cls.org_id == org_id,
|
|
123
|
+
((cls.user_name == None) | (cls.user_name == user_name))
|
|
124
|
+
).all()
|
|
125
|
+
|
dataflow/models/git_ssh.py
CHANGED
|
@@ -8,11 +8,12 @@ class GitSSH(Base):
|
|
|
8
8
|
|
|
9
9
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
|
10
10
|
user_name = Column(String, ForeignKey('USER.user_name', ondelete="CASCADE"), nullable=False)
|
|
11
|
+
org_id = Column(Integer, ForeignKey("ORGANIZATION.id"), index=True, nullable=False)
|
|
11
12
|
description = Column(String)
|
|
12
13
|
key_name = Column(String, nullable=False)
|
|
13
14
|
created_date = Column(DateTime, server_default=func.now(), nullable=False)
|
|
14
15
|
last_used_date = Column(DateTime)
|
|
15
16
|
|
|
16
17
|
__table_args__ = (
|
|
17
|
-
UniqueConstraint(user_name, key_name, name='
|
|
18
|
+
UniqueConstraint(user_name, key_name, org_id, name='user_name_key_name_org_id_unique'),
|
|
18
19
|
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from sqlalchemy import Column , Integer, String, Boolean, ForeignKey, UniqueConstraint, Enum
|
|
2
|
+
from sqlalchemy.orm import relationship
|
|
3
|
+
from dataflow.db import Base
|
|
4
|
+
import enum
|
|
5
|
+
from dataflow.models.environment import EnvType
|
|
6
|
+
|
|
7
|
+
class OrganizationUser(Base):
|
|
8
|
+
"""
|
|
9
|
+
Association Table between USER, ROLE, and ORGANIZATION
|
|
10
|
+
"""
|
|
11
|
+
__tablename__ = "ORGANIZATION_USER"
|
|
12
|
+
__table_args__ = (UniqueConstraint('org_id', 'user_id', name='uq_org_user'),)
|
|
13
|
+
|
|
14
|
+
org_id = Column(Integer, ForeignKey('ORGANIZATION.id', ondelete="CASCADE"), primary_key=True, nullable=False)
|
|
15
|
+
user_id = Column(Integer, ForeignKey('USER.user_id', ondelete="CASCADE"), primary_key=True, nullable=False)
|
|
16
|
+
role_id = Column(Integer, ForeignKey('ROLE.id', ondelete="SET NULL"), nullable=False)
|
|
17
|
+
active_env_short_name = Column(String, nullable=True)
|
|
18
|
+
active_env_type = Column(Enum(EnvType), nullable=True)
|
|
19
|
+
active_server_id = Column(Integer, ForeignKey('CUSTOM_SERVER.id', ondelete="SET NULL"))
|
|
20
|
+
show_server_page = Column(Boolean, default = True, server_default='true')
|
|
21
|
+
monthly_allocation = Column(Integer, nullable=True, default=0, server_default='0')
|
|
22
|
+
|
|
23
|
+
# Relationships
|
|
24
|
+
user = relationship("User", back_populates="org_user_assocs")
|
|
25
|
+
role = relationship("Role", back_populates="org_user_assocs")
|
|
26
|
+
organization = relationship("Organization", back_populates="org_user_assocs")
|
|
27
|
+
|
|
28
|
+
class OrganizationServer(Base):
|
|
29
|
+
__tablename__ = "ORGANIZATION_SERVER"
|
|
30
|
+
|
|
31
|
+
org_id = Column(Integer, ForeignKey('ORGANIZATION.id', ondelete="CASCADE"), primary_key=True, nullable=False)
|
|
32
|
+
server_id = Column(Integer, ForeignKey('SERVER_CONFIG.id', ondelete="CASCADE"), primary_key=True, nullable=False)
|
|
33
|
+
|
|
34
|
+
class OrganizationAppType(Base):
|
|
35
|
+
__tablename__ = "ORGANIZATION_APP_TYPE"
|
|
36
|
+
|
|
37
|
+
org_id = Column(Integer, ForeignKey('ORGANIZATION.id', ondelete="CASCADE"), primary_key=True, nullable=False)
|
|
38
|
+
app_type_id = Column(Integer, ForeignKey('APP_TYPE.id', ondelete="CASCADE"), primary_key=True, nullable=False)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from sqlalchemy import (
|
|
2
|
+
Column, Integer, String, Enum, DateTime, ForeignKey, Index, text
|
|
3
|
+
)
|
|
4
|
+
import uuid, enum
|
|
5
|
+
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
|
6
|
+
from sqlalchemy.sql import func
|
|
7
|
+
from sqlalchemy.orm import relationship
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from dataflow.db import Base
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Organization(Base):
|
|
13
|
+
"""
|
|
14
|
+
Organization model for the database.
|
|
15
|
+
"""
|
|
16
|
+
__tablename__ = "ORGANIZATION"
|
|
17
|
+
|
|
18
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
19
|
+
uid = Column(UUID(as_uuid=True), default=uuid.uuid4, nullable=False, unique=True, server_default=text("gen_random_uuid()"))
|
|
20
|
+
name = Column(String(255), nullable=False, unique=True)
|
|
21
|
+
invite_code = Column(String(64), nullable=False, unique=True)
|
|
22
|
+
email_domain = Column(String(255), nullable=False, unique=True)
|
|
23
|
+
spark_enabled_zones = Column(JSONB, default=func.json([]), server_default=text("'[]'::jsonb")) # List of zone IDs where Spark is enabled
|
|
24
|
+
created_at = Column(DateTime, default=datetime.utcnow, server_default=func.now())
|
|
25
|
+
|
|
26
|
+
# Association object link
|
|
27
|
+
org_user_assocs = relationship("OrganizationUser", back_populates="organization", cascade="all, delete-orphan")
|
|
28
|
+
custom_servers = relationship("CustomServerConfig")
|
|
29
|
+
onboarding_requests = relationship("UserOnboarding", back_populates="organization", cascade="all, delete-orphan")
|
|
30
|
+
servers = relationship("ServerConfig", secondary="ORGANIZATION_SERVER", back_populates="organizations")
|
|
31
|
+
apps = relationship("AppType", secondary="ORGANIZATION_APP_TYPE", back_populates="organizations")
|
|
32
|
+
roles = relationship("Role", cascade="all, delete-orphan")
|
|
33
|
+
environments = relationship("Environment", back_populates="organization")
|
|
34
|
+
|
|
35
|
+
class OnboardingStatus(enum.Enum):
|
|
36
|
+
pending = 'pending'
|
|
37
|
+
rejected = 'rejected'
|
|
38
|
+
accepted = 'accepted'
|
|
39
|
+
|
|
40
|
+
class OrganizationOnboarding(Base):
|
|
41
|
+
__tablename__ = 'ORGANIZATION_ONBOARDING'
|
|
42
|
+
# This prevents an org from having more than one active ('pending' or 'accepted') application
|
|
43
|
+
# while allowing multiple 'rejected' entries.
|
|
44
|
+
__table_args__ = (
|
|
45
|
+
Index(
|
|
46
|
+
'idx_pending_org_application',
|
|
47
|
+
'name',
|
|
48
|
+
unique=True,
|
|
49
|
+
postgresql_where=Column('status').in_([
|
|
50
|
+
OnboardingStatus.pending.value,
|
|
51
|
+
OnboardingStatus.accepted.value
|
|
52
|
+
])
|
|
53
|
+
),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
57
|
+
|
|
58
|
+
name = Column(String(255), nullable=False)
|
|
59
|
+
age = Column(Integer, nullable=True)
|
|
60
|
+
domain = Column(String(255), nullable=True)
|
|
61
|
+
no_of_employees = Column(String(50), nullable=True)
|
|
62
|
+
address = Column(String(500), nullable=True)
|
|
63
|
+
|
|
64
|
+
admin_first_name = Column(String(100), nullable=False)
|
|
65
|
+
admin_last_name = Column(String(100), nullable=True)
|
|
66
|
+
admin_designation = Column(String(100), nullable=False)
|
|
67
|
+
admin_email = Column(String(255), nullable=False, unique=True)
|
|
68
|
+
admin_username = Column(String(100), nullable=False, unique=True)
|
|
69
|
+
admin_password = Column(String(255), nullable=False)
|
|
70
|
+
|
|
71
|
+
discovery_source = Column(String(255), nullable=True)
|
|
72
|
+
additional_info = Column(String(1000), nullable=True)
|
|
73
|
+
size_of_data = Column(String(100), nullable=True)
|
|
74
|
+
|
|
75
|
+
user_id = Column(Integer, ForeignKey('USER.user_id'), nullable=False)
|
|
76
|
+
status = Column(Enum(OnboardingStatus), default=OnboardingStatus.pending, nullable=False)
|
|
77
|
+
|
|
78
|
+
user = relationship("User", back_populates="organization_onboarding")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from sqlalchemy import Column, Integer, ForeignKey, DateTime, UniqueConstraint
|
|
1
|
+
from sqlalchemy import Column, Integer, ForeignKey, DateTime, UniqueConstraint, func
|
|
2
2
|
from dataflow.db import Base
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
|
|
@@ -8,7 +8,7 @@ class PinnedProject(Base):
|
|
|
8
8
|
id = Column(Integer, primary_key=True)
|
|
9
9
|
user_id = Column(Integer, ForeignKey('USER.user_id', ondelete="CASCADE"), index=True)
|
|
10
10
|
project_id = Column(Integer, ForeignKey('PROJECT_DETAIL.project_id', ondelete="CASCADE"), index=True)
|
|
11
|
-
pinned_at = Column(DateTime, default=datetime.utcnow)
|
|
11
|
+
pinned_at = Column(DateTime, default=datetime.utcnow, server_default=func.now())
|
|
12
12
|
|
|
13
13
|
__table_args__ = (
|
|
14
14
|
UniqueConstraint("user_id", "project_id", name="uix_user_project"),
|
|
@@ -1,23 +1,26 @@
|
|
|
1
|
-
from sqlalchemy import Column, String, Enum, DateTime, Integer, func, ForeignKey
|
|
1
|
+
from sqlalchemy import Column, String, Enum, DateTime, Integer, func, ForeignKey, UniqueConstraint
|
|
2
2
|
from sqlalchemy.orm import relationship
|
|
3
3
|
from dataflow.db import Base
|
|
4
4
|
|
|
5
5
|
class ProjectDetails(Base):
|
|
6
6
|
__tablename__ = "PROJECT_DETAIL"
|
|
7
|
+
__table_args__ = (UniqueConstraint('org_id', 'slug', name='uq_project_org_slug'),)
|
|
7
8
|
|
|
8
9
|
project_id = Column(Integer, primary_key=True, autoincrement=True)
|
|
9
10
|
project_name = Column(String, nullable=False)
|
|
10
11
|
git_url = Column(String)
|
|
11
12
|
git_branch = Column(String, nullable=True)
|
|
12
13
|
git_folder = Column(String, nullable=True)
|
|
13
|
-
type = Column(String, ForeignKey('
|
|
14
|
-
slug = Column(String, nullable=False
|
|
14
|
+
type = Column(String, ForeignKey('APP_TYPE.name'), nullable=False)
|
|
15
|
+
slug = Column(String, nullable=False)
|
|
15
16
|
runtime = Column(String, nullable=False)
|
|
16
|
-
py_env = Column(
|
|
17
|
+
py_env = Column(Integer, nullable=True)
|
|
17
18
|
launch_url = Column(String, nullable=True)
|
|
18
|
-
status = Column(Enum("pending", "created" ,"deployed", "stopped", "failed", name="deployment_status"), default="created")
|
|
19
|
+
status = Column(Enum("pending", "created" ,"deployed", "stopped", "failed", name="deployment_status"), default="created", server_default="created")
|
|
19
20
|
last_deployed = Column(DateTime, nullable=True)
|
|
20
21
|
created_at = Column(DateTime, nullable=False, server_default=func.now())
|
|
21
22
|
created_by = Column(String, nullable=False)
|
|
23
|
+
org_id = Column(Integer, ForeignKey('ORGANIZATION.id', ondelete='CASCADE'), nullable=False)
|
|
24
|
+
airflow_config_file = Column(String, nullable=True)
|
|
22
25
|
|
|
23
|
-
app_type = relationship("AppType")
|
|
26
|
+
app_type = relationship("AppType")
|
|
@@ -12,7 +12,7 @@ class RecentProjectStudio(Base):
|
|
|
12
12
|
project_name = Column(String, nullable=False)
|
|
13
13
|
project_path = Column(String, nullable=False)
|
|
14
14
|
last_opened_date = Column(DateTime, server_default=func.now(), nullable=False)
|
|
15
|
-
remember = Column(Boolean, default=False)
|
|
15
|
+
remember = Column(Boolean, default=False, server_default='false')
|
|
16
16
|
|
|
17
17
|
__table_args__ = (
|
|
18
18
|
UniqueConstraint(user_name, project_path, app_name, name='user_name_project_path_app_name_unique'),
|
dataflow/models/role.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""models.py"""
|
|
2
|
-
from sqlalchemy import Column, Integer, String, Enum
|
|
2
|
+
from sqlalchemy import Column, Integer, String, Enum, ForeignKey, UniqueConstraint
|
|
3
3
|
from sqlalchemy.orm import relationship
|
|
4
4
|
from dataflow.db import Base
|
|
5
5
|
import enum
|
|
@@ -11,19 +11,25 @@ class BaseRoleField(enum.Enum):
|
|
|
11
11
|
|
|
12
12
|
class Role(Base):
|
|
13
13
|
"""
|
|
14
|
-
Table
|
|
14
|
+
Table ROLE
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
__tablename__='ROLE'
|
|
18
|
+
__table_args__ = (
|
|
19
|
+
UniqueConstraint('name', 'org_id', name='uq_role_name_org'),
|
|
20
|
+
)
|
|
18
21
|
|
|
19
22
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True, nullable=False)
|
|
20
|
-
name = Column(String,
|
|
23
|
+
name = Column(String, nullable=False)
|
|
24
|
+
org_id = Column(Integer, ForeignKey('ORGANIZATION.id'))
|
|
21
25
|
description = Column(String, nullable=True)
|
|
22
|
-
base_role = Column(Enum(BaseRoleField), nullable=False, default=BaseRoleField.user)
|
|
26
|
+
base_role = Column(Enum(BaseRoleField), nullable=False, default=BaseRoleField.user, server_default=BaseRoleField.user.value)
|
|
23
27
|
|
|
24
|
-
|
|
25
|
-
role_server_assocs = relationship("RoleServer", back_populates="role")
|
|
28
|
+
# Relationships
|
|
26
29
|
role_zone_assocs = relationship("RoleZone", back_populates="role")
|
|
30
|
+
org_user_assocs = relationship("OrganizationUser", back_populates="role", cascade="all, delete-orphan")
|
|
31
|
+
organization = relationship("Organization", back_populates="roles")
|
|
32
|
+
servers = relationship("CustomServerConfig", secondary="ROLE_SERVER", back_populates="roles")
|
|
27
33
|
|
|
28
34
|
def __repr__(self):
|
|
29
35
|
return f"<Role(id={self.id}, name='{self.name}', base_role='{self.base_role}')>"
|
dataflow/models/role_server.py
CHANGED
|
@@ -5,10 +5,7 @@ from dataflow.db import Base
|
|
|
5
5
|
|
|
6
6
|
class RoleServer(Base):
|
|
7
7
|
__tablename__ = 'ROLE_SERVER'
|
|
8
|
-
__table_args__ = (UniqueConstraint('role_id', '
|
|
8
|
+
__table_args__ = (UniqueConstraint('role_id', 'custom_server_id', name='_role_server_uc'),)
|
|
9
9
|
|
|
10
10
|
role_id = Column(Integer, ForeignKey('ROLE.id', ondelete="CASCADE"), nullable=False, primary_key=True)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
role = relationship("Role", back_populates="role_server_assocs")
|
|
14
|
-
server = relationship("CustomServerConfig", back_populates="role_server_assocs")
|
|
11
|
+
custom_server_id = Column(Integer, ForeignKey('CUSTOM_SERVER.id', ondelete="CASCADE"), nullable=False, primary_key=True)
|
dataflow/models/role_zone.py
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
from typing import Dict, List, Optional
|
|
2
|
-
from sqlalchemy import Column, Integer, ForeignKey, UniqueConstraint, Boolean
|
|
2
|
+
from sqlalchemy import Column, Integer, ForeignKey, UniqueConstraint, Boolean, Index
|
|
3
3
|
from sqlalchemy.orm import relationship
|
|
4
4
|
from dataflow.db import Base
|
|
5
5
|
|
|
6
6
|
class RoleZone(Base):
|
|
7
7
|
__tablename__ = 'ROLE_ZONE'
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
role_id = Column(Integer, ForeignKey('ROLE.id', ondelete="CASCADE"), primary_key=True)
|
|
10
10
|
zone_id = Column(Integer, ForeignKey('DATAFLOW_ZONE.id', ondelete="CASCADE"), primary_key=True)
|
|
11
|
-
is_default = Column(Boolean, default=False, nullable=False)
|
|
11
|
+
is_default = Column(Boolean, default=False, nullable=False, server_default='false')
|
|
12
|
+
|
|
13
|
+
__table_args__ = (
|
|
14
|
+
Index('idx_role_runtime_default', 'role_id', unique=True,
|
|
15
|
+
postgresql_where=is_default.is_(True)),
|
|
16
|
+
)
|
|
12
17
|
|
|
13
18
|
role = relationship("Role", back_populates="role_zone_assocs")
|
|
14
19
|
zone = relationship("DataflowZone", back_populates="role_zone_assocs")
|
dataflow/models/server_config.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from sqlalchemy import Column, Integer, String, Boolean, Text, ForeignKey
|
|
1
|
+
from sqlalchemy import Column, Integer, String, Boolean, Text, ForeignKey, text
|
|
2
2
|
from sqlalchemy.dialects.postgresql import JSONB
|
|
3
3
|
from sqlalchemy.sql import func
|
|
4
4
|
from sqlalchemy.orm import relationship
|
|
@@ -14,20 +14,24 @@ class ServerConfig(Base):
|
|
|
14
14
|
ram = Column(String, nullable=False)
|
|
15
15
|
cpu = Column(String, nullable=False)
|
|
16
16
|
gpu = Column(String)
|
|
17
|
-
default = Column(Boolean, default=False)
|
|
18
|
-
tags = Column(JSONB, default=func.json([]))
|
|
17
|
+
default = Column(Boolean, default=False, server_default='false')
|
|
18
|
+
tags = Column(JSONB, default=func.json([]), server_default=text("'[]'::jsonb"))
|
|
19
19
|
description = Column(Text, nullable=True)
|
|
20
|
-
kubespawner_override = Column(JSONB, default=func.json({}))
|
|
20
|
+
kubespawner_override = Column(JSONB, default=func.json({}), server_default=text("'{}'::jsonb"))
|
|
21
21
|
|
|
22
|
+
# Relationships
|
|
23
|
+
organizations = relationship("Organization", secondary="ORGANIZATION_SERVER", back_populates="servers")
|
|
22
24
|
|
|
23
25
|
class CustomServerConfig(Base):
|
|
24
26
|
__tablename__ = "CUSTOM_SERVER"
|
|
25
27
|
|
|
26
28
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
27
29
|
base_server_id = Column(Integer, ForeignKey(ServerConfig.id, ondelete="CASCADE"), nullable=False)
|
|
30
|
+
org_id = Column(Integer, ForeignKey('ORGANIZATION.id', ondelete="CASCADE"), nullable=False)
|
|
28
31
|
display_name = Column(String, nullable=False, unique=True, index=True)
|
|
29
32
|
description = Column(Text, nullable=True)
|
|
30
33
|
|
|
31
34
|
# Relationship to the server_config table
|
|
32
35
|
server_config = relationship(ServerConfig)
|
|
33
|
-
|
|
36
|
+
organization = relationship("Organization", back_populates="custom_servers")
|
|
37
|
+
roles = relationship("Role", secondary="ROLE_SERVER", back_populates="servers")
|
dataflow/models/team.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""models.py"""
|
|
2
|
-
from sqlalchemy import Column, Integer, String
|
|
2
|
+
from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint
|
|
3
3
|
from sqlalchemy.orm import relationship
|
|
4
4
|
from dataflow.db import Base
|
|
5
5
|
|
|
@@ -9,9 +9,15 @@ class Team(Base):
|
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
__tablename__='TEAM'
|
|
12
|
+
__table_args__ = (
|
|
13
|
+
UniqueConstraint('team_name', 'org_id', name='uc_team_name_org_id'),
|
|
14
|
+
)
|
|
12
15
|
|
|
13
16
|
team_id = Column(Integer, primary_key=True, index=True, autoincrement=True, nullable=False)
|
|
14
|
-
team_name = Column(String,
|
|
17
|
+
team_name = Column(String, nullable=False)
|
|
18
|
+
org_id = Column(Integer, ForeignKey('ORGANIZATION.id', ondelete="CASCADE"), nullable=False)
|
|
15
19
|
description = Column(String, nullable=True)
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
|
|
21
|
+
# relationships
|
|
22
|
+
users = relationship("User", secondary="USER_TEAM", back_populates="teams")
|
|
23
|
+
organization = relationship("Organization")
|
dataflow/models/user.py
CHANGED
|
@@ -1,31 +1,68 @@
|
|
|
1
1
|
"""models.py"""
|
|
2
|
-
from sqlalchemy import Column, Integer, String, Boolean, LargeBinary, ForeignKey
|
|
2
|
+
from sqlalchemy import Column, Integer, String, Boolean, LargeBinary, Enum, ForeignKey, DateTime, func
|
|
3
|
+
from sqlalchemy.dialects.postgresql import ENUM
|
|
4
|
+
from sqlalchemy import Index
|
|
3
5
|
from sqlalchemy.orm import relationship
|
|
4
6
|
from dataflow.db import Base
|
|
7
|
+
import enum
|
|
5
8
|
|
|
6
9
|
class User(Base):
|
|
7
10
|
"""
|
|
8
11
|
Table USER
|
|
9
12
|
"""
|
|
10
|
-
|
|
11
|
-
__tablename__='USER'
|
|
13
|
+
__tablename__ = 'USER'
|
|
12
14
|
|
|
13
15
|
user_id = Column(Integer, primary_key=True, index=True, autoincrement=True, nullable=False)
|
|
14
16
|
user_name = Column(String, unique=True, nullable=False)
|
|
15
17
|
first_name = Column(String)
|
|
16
18
|
last_name = Column(String)
|
|
17
19
|
email = Column(String, unique=True)
|
|
18
|
-
role_id = Column(Integer, ForeignKey('ROLE.id'), nullable=False)
|
|
19
20
|
image = Column(LargeBinary)
|
|
20
21
|
image_url = Column(String, nullable=True)
|
|
21
|
-
active = Column(Boolean, nullable=False, default=True)
|
|
22
|
+
active = Column(Boolean, nullable=False, default=True, server_default='true')
|
|
22
23
|
password = Column(String, nullable=False)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
active_org_id = Column(Integer, ForeignKey('ORGANIZATION.id'))
|
|
25
|
+
|
|
26
|
+
# Relationships
|
|
27
|
+
org_user_assocs = relationship("OrganizationUser", back_populates="user", cascade="all, delete-orphan")
|
|
28
|
+
teams = relationship("Team", secondary="USER_TEAM", back_populates="users")
|
|
29
|
+
onboarding_requests = relationship("UserOnboarding", back_populates="user", cascade="all, delete-orphan")
|
|
30
|
+
organization_onboarding = relationship("OrganizationOnboarding", back_populates="user", cascade="all, delete-orphan")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class OnboardingStatus(enum.Enum):
|
|
34
|
+
pending = 'pending'
|
|
35
|
+
rejected = 'rejected'
|
|
36
|
+
accepted = 'accepted'
|
|
37
|
+
|
|
38
|
+
class UserOnboarding(Base):
|
|
39
|
+
"""
|
|
40
|
+
SQLAlchemy model for the "USER_ONBOARDING" table.
|
|
41
|
+
This table stores user applications to organizations.
|
|
42
|
+
"""
|
|
43
|
+
__tablename__ = "USER_ONBOARDING"
|
|
44
|
+
# This prevents a user from having more than one active ('pending' or 'accepted') application
|
|
45
|
+
# for a given organization, while allowing multiple 'rejected' entries.
|
|
46
|
+
__table_args__ = (
|
|
47
|
+
Index(
|
|
48
|
+
'idx_pending_user_org_application',
|
|
49
|
+
'user_id',
|
|
50
|
+
'org_id',
|
|
51
|
+
unique=True,
|
|
52
|
+
postgresql_where=Column('status').in_([
|
|
53
|
+
OnboardingStatus.pending.value,
|
|
54
|
+
OnboardingStatus.accepted.value
|
|
55
|
+
])
|
|
56
|
+
),
|
|
57
|
+
)
|
|
28
58
|
|
|
29
|
-
|
|
59
|
+
id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
|
|
60
|
+
user_id = Column(Integer, ForeignKey("USER.user_id", ondelete="CASCADE"), nullable=False)
|
|
61
|
+
org_id = Column(Integer, ForeignKey("ORGANIZATION.id", ondelete="CASCADE"), nullable=False)
|
|
62
|
+
status = Column(Enum(OnboardingStatus, name='onboarding_status'), nullable=False, default=OnboardingStatus.pending.value, server_default='pending')
|
|
63
|
+
created_at = Column(DateTime, default=func.now(), nullable=False, server_default=func.now())
|
|
64
|
+
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), nullable=False, server_default=func.now())
|
|
30
65
|
|
|
31
|
-
|
|
66
|
+
# Relationships
|
|
67
|
+
user = relationship("User", back_populates="onboarding_requests")
|
|
68
|
+
organization = relationship("Organization", back_populates="onboarding_requests")
|
dataflow/models/user_team.py
CHANGED
|
@@ -8,7 +8,4 @@ class UserTeam(Base):
|
|
|
8
8
|
__table_args__ = (UniqueConstraint('user_id', 'team_id', name='_user_team_uc'),)
|
|
9
9
|
|
|
10
10
|
user_id = Column(Integer, ForeignKey('USER.user_id', ondelete="CASCADE"), nullable=False, primary_key=True)
|
|
11
|
-
team_id = Column(Integer, ForeignKey('TEAM.team_id', ondelete="CASCADE"), nullable=False, primary_key=True)
|
|
12
|
-
|
|
13
|
-
user = relationship("User", back_populates="user_team_assocs")
|
|
14
|
-
team = relationship("Team", back_populates="user_team_assocs")
|
|
11
|
+
team_id = Column(Integer, ForeignKey('TEAM.team_id', ondelete="CASCADE"), nullable=False, primary_key=True)
|
dataflow/models/variables.py
CHANGED
|
@@ -14,7 +14,8 @@ class Variable(Base):
|
|
|
14
14
|
__tablename__ = 'VARIABLE'
|
|
15
15
|
|
|
16
16
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True, nullable=False)
|
|
17
|
-
key = Column(String, nullable=False)
|
|
17
|
+
key = Column(String, index=True, nullable=False)
|
|
18
|
+
org_id = Column(Integer, ForeignKey("ORGANIZATION.id"), index=True, nullable=False)
|
|
18
19
|
value = Column(Text, nullable=False)
|
|
19
20
|
type = Column(String, nullable=False)
|
|
20
21
|
description = Column(Text, nullable=True)
|
|
@@ -24,11 +25,12 @@ class Variable(Base):
|
|
|
24
25
|
created_at = Column(DateTime, server_default=func.now())
|
|
25
26
|
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
|
26
27
|
created_by = Column(String, ForeignKey('USER.user_name'), nullable=True)
|
|
27
|
-
is_active = Column(Boolean, default=True, nullable=False)
|
|
28
|
+
is_active = Column(Boolean, default=True, nullable=False, server_default='true')
|
|
28
29
|
datatype = Column(Enum(DataType, name="data_type"), nullable=False)
|
|
29
|
-
set_as_env = Column(Boolean, default=False, nullable=False)
|
|
30
|
+
set_as_env = Column(Boolean, default=False, nullable=False, server_default='false')
|
|
31
|
+
|
|
30
32
|
|
|
31
33
|
__table_args__ = (
|
|
32
34
|
CheckConstraint(type.in_(['variable', 'secret']), name='check_variable_type'),
|
|
33
|
-
UniqueConstraint('key', 'runtime', 'slug', 'created_by', name='unique_key'),
|
|
35
|
+
UniqueConstraint('key', 'org_id', 'runtime', 'slug', 'created_by', name='unique_key'),
|
|
34
36
|
)
|
|
@@ -3,6 +3,7 @@ import os
|
|
|
3
3
|
from .interface import SecretManager
|
|
4
4
|
from .providers.aws_manager import AWSSecretsManager
|
|
5
5
|
from .providers.azure_manager import AzureKeyVault
|
|
6
|
+
from .providers.gcp_manager import GCPSecretsManager
|
|
6
7
|
from ..configuration import ConfigurationManager
|
|
7
8
|
|
|
8
9
|
# A custom exception for clear error messages
|
|
@@ -17,10 +18,6 @@ def get_secret_manager() -> SecretManager:
|
|
|
17
18
|
to determine which cloud provider's secret manager to instantiate.
|
|
18
19
|
"""
|
|
19
20
|
try:
|
|
20
|
-
# dataflow_config = None
|
|
21
|
-
# if os.getenv('HOSTNAME'):
|
|
22
|
-
# dataflow_config = ConfigurationManager('/dataflow/app/auth_config/dataflow_auth.cfg')
|
|
23
|
-
# else:
|
|
24
21
|
dataflow_config = ConfigurationManager('/dataflow/app/config/dataflow.cfg')
|
|
25
22
|
except Exception as e:
|
|
26
23
|
raise SecretProviderError(
|
|
@@ -49,11 +46,20 @@ def get_secret_manager() -> SecretManager:
|
|
|
49
46
|
)
|
|
50
47
|
return AzureKeyVault(vault_url=vault_url)
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
elif provider == "gcp":
|
|
50
|
+
project_id = dataflow_config.get_config_value('cloudProvider', 'gcp_project_id')
|
|
51
|
+
region = dataflow_config.get_config_value('cloudProvider', 'gcp_region')
|
|
52
|
+
if not project_id:
|
|
53
|
+
raise SecretProviderError(
|
|
54
|
+
"GCP_PROJECT_ID must be set when using the GCP provider."
|
|
55
|
+
)
|
|
56
|
+
if not region:
|
|
57
|
+
raise SecretProviderError(
|
|
58
|
+
"GCP_REGION must be set when using the GCP provider."
|
|
59
|
+
)
|
|
60
|
+
return GCPSecretsManager(project_id=project_id, region=region)
|
|
55
61
|
|
|
56
62
|
else:
|
|
57
63
|
raise SecretProviderError(
|
|
58
|
-
f"Unsupported secret provider: '{provider}'. Supported providers are: aws, azure
|
|
64
|
+
f"Unsupported secret provider: '{provider}'. Supported providers are: aws, azure and gcp"
|
|
59
65
|
)
|