|
62 | 62 | init_db_data_parser.add_argument( |
63 | 63 | "--resource", |
64 | 64 | type=str, |
65 | | - help=("Either `default` or path to the JSON file of the first resource."), |
66 | | - required=True, |
| 65 | + help="Either `default` or path to the JSON file of the first resource.", |
| 66 | + required=False, |
67 | 67 | ) |
68 | 68 | init_db_data_parser.add_argument( |
69 | 69 | "--profile", |
70 | 70 | type=str, |
71 | | - help=("Either `default` or path to the JSON file of the first profile."), |
72 | | - required=True, |
| 71 | + help="Either `default` or path to the JSON file of the first profile.", |
| 72 | + required=False, |
| 73 | +) |
| 74 | +init_db_data_parser.add_argument( |
| 75 | + "--admin-email", |
| 76 | + type=str, |
| 77 | + help="Email of the first admin user to create.", |
| 78 | + required=False, |
| 79 | +) |
| 80 | +init_db_data_parser.add_argument( |
| 81 | + "--admin-pwd", |
| 82 | + type=str, |
| 83 | + help="Password of the first admin user to create.", |
| 84 | + required=False, |
73 | 85 | ) |
74 | 86 |
|
75 | 87 | # fractalctl update-db-data |
@@ -124,119 +136,126 @@ def set_db(): |
124 | 136 |
|
125 | 137 |
|
126 | 138 | def init_db_data( |
127 | | - *, |
128 | | - resource: str, |
129 | | - profile: str, |
| 139 | + resource: str | None = None, |
| 140 | + profile: str | None = None, |
| 141 | + admin_email: str | None = None, |
| 142 | + admin_password: str | None = None, |
130 | 143 | ) -> None: |
131 | 144 | from fractal_server.app.security import _create_first_user |
132 | 145 | from fractal_server.app.security import _create_first_group |
133 | | - from fractal_server.syringe import Inject |
134 | | - from fractal_server.config import get_init_data_settings |
135 | 146 | from fractal_server.app.db import get_sync_db |
136 | 147 | from sqlalchemy import select, func |
137 | 148 | from fractal_server.app.models.security import UserOAuth |
138 | 149 | from fractal_server.app.models import Resource, Profile |
139 | 150 | from fractal_server.app.schemas.v2.resource import cast_serialize_resource |
140 | 151 | from fractal_server.app.schemas.v2.profile import cast_serialize_profile |
141 | 152 |
|
142 | | - init_data_settings = Inject(get_init_data_settings) |
143 | | - |
144 | 153 | # Create default group and user |
145 | 154 | print() |
146 | 155 | _create_first_group() |
147 | 156 | print() |
148 | | - asyncio.run( |
149 | | - _create_first_user( |
150 | | - email=init_data_settings.FRACTAL_DEFAULT_ADMIN_EMAIL, |
151 | | - password=( |
152 | | - init_data_settings.FRACTAL_DEFAULT_ADMIN_PASSWORD.get_secret_value() # noqa E501 |
153 | | - ), |
154 | | - is_superuser=True, |
155 | | - is_verified=True, |
156 | | - ) |
157 | | - ) |
158 | | - print() |
159 | 157 |
|
160 | | - # Create resource and profile |
161 | | - with next(get_sync_db()) as db: |
162 | | - # Preliminary check |
163 | | - num_resources = db.execute(select(func.count(Resource.id))).scalar() |
164 | | - if num_resources != 0: |
165 | | - print(f"There exist already {num_resources=} resources. Exit.") |
166 | | - sys.exit(1) |
167 | | - |
168 | | - # Get resource/profile data |
169 | | - if resource == "default": |
170 | | - _python_version = ( |
171 | | - f"{sys.version_info.major}.{sys.version_info.minor}" |
172 | | - ) |
173 | | - resource_data = { |
174 | | - "name": "Local resource", |
175 | | - "type": ResourceType.LOCAL, |
176 | | - "jobs_local_dir": (Path.cwd() / "data-jobs").as_posix(), |
177 | | - "tasks_local_dir": (Path.cwd() / "data-tasks").as_posix(), |
178 | | - "tasks_python_config": { |
179 | | - "default_version": _python_version, |
180 | | - "versions": { |
181 | | - _python_version: sys.executable, |
182 | | - }, |
183 | | - }, |
184 | | - "jobs_poll_interval": 0, |
185 | | - "jobs_runner_config": {}, |
186 | | - "tasks_pixi_config": {}, |
187 | | - } |
188 | | - print("Prepared default resource data.") |
189 | | - else: |
190 | | - with open(resource) as f: |
191 | | - resource_data = json.load(f) |
192 | | - print(f"Read resource data from {resource}.") |
193 | | - if profile == "default": |
194 | | - profile_data = { |
195 | | - "resource_type": "local", |
196 | | - "name": "Local profile", |
197 | | - } |
198 | | - print("Prepared default profile data.") |
199 | | - else: |
200 | | - with open(profile) as f: |
201 | | - profile_data = json.load(f) |
202 | | - print(f"Read profile data from {profile}.") |
203 | | - |
204 | | - # Validate resource/profile data |
205 | | - try: |
206 | | - resource_data = cast_serialize_resource(resource_data) |
207 | | - except ValidationError as e: |
208 | | - sys.exit( |
209 | | - f"ERROR: Invalid resource data.\nOriginal error:\n{str(e)}" |
210 | | - ) |
211 | | - try: |
212 | | - profile_data = cast_serialize_profile(profile_data) |
213 | | - except ValidationError as e: |
214 | | - sys.exit( |
215 | | - f"ERROR: Invalid profile data.\nOriginal error:\n{str(e)}" |
| 158 | + # Create admin user if requested |
| 159 | + if (admin_email is None) != (admin_password is None): |
| 160 | + print("You must provide both --admin-email and --admin-pwd. Exit.") |
| 161 | + sys.exit(1) |
| 162 | + if admin_password and admin_email: |
| 163 | + asyncio.run( |
| 164 | + _create_first_user( |
| 165 | + email=admin_email, |
| 166 | + password=admin_password, |
| 167 | + is_superuser=True, |
| 168 | + is_verified=True, |
216 | 169 | ) |
217 | | - |
218 | | - # Create resource/profile db objects |
219 | | - resource_obj = Resource(**resource_data) |
220 | | - db.add(resource_obj) |
221 | | - db.commit() |
222 | | - db.refresh(resource_obj) |
223 | | - profile_data["resource_id"] = resource_obj.id |
224 | | - profile_obj = Profile(**profile_data) |
225 | | - db.add(profile_obj) |
226 | | - db.commit() |
227 | | - db.refresh(profile_obj) |
228 | | - |
229 | | - # Associate profile to users |
230 | | - res = db.execute(select(UserOAuth)) |
231 | | - users = res.unique().scalars().all() |
232 | | - for user in users: |
233 | | - print(f"Now set profile_id={profile_obj.id} for {user.email}.") |
234 | | - user.profile_id = profile_obj.id |
235 | | - db.add(user) |
236 | | - db.commit() |
237 | | - db.expunge_all() |
| 170 | + ) |
238 | 171 | print() |
239 | 172 |
|
| 173 | + # Create resource and profile if requested |
| 174 | + if (resource is None) != (profile is None): |
| 175 | + print("You must provide both --resource and --profile. Exit.") |
| 176 | + sys.exit(1) |
| 177 | + if resource and profile: |
| 178 | + with next(get_sync_db()) as db: |
| 179 | + # Preliminary check |
| 180 | + num_resources = db.execute( |
| 181 | + select(func.count(Resource.id)) |
| 182 | + ).scalar() |
| 183 | + if num_resources != 0: |
| 184 | + print(f"There exist already {num_resources=} resources. Exit.") |
| 185 | + sys.exit(1) |
| 186 | + |
| 187 | + # Get resource/profile data |
| 188 | + if resource == "default": |
| 189 | + _python_version = ( |
| 190 | + f"{sys.version_info.major}.{sys.version_info.minor}" |
| 191 | + ) |
| 192 | + resource_data = { |
| 193 | + "name": "Local resource", |
| 194 | + "type": ResourceType.LOCAL, |
| 195 | + "jobs_local_dir": (Path.cwd() / "data-jobs").as_posix(), |
| 196 | + "tasks_local_dir": (Path.cwd() / "data-tasks").as_posix(), |
| 197 | + "tasks_python_config": { |
| 198 | + "default_version": _python_version, |
| 199 | + "versions": { |
| 200 | + _python_version: sys.executable, |
| 201 | + }, |
| 202 | + }, |
| 203 | + "jobs_poll_interval": 0, |
| 204 | + "jobs_runner_config": {}, |
| 205 | + "tasks_pixi_config": {}, |
| 206 | + } |
| 207 | + print("Prepared default resource data.") |
| 208 | + else: |
| 209 | + with open(resource) as f: |
| 210 | + resource_data = json.load(f) |
| 211 | + print(f"Read resource data from {resource}.") |
| 212 | + if profile == "default": |
| 213 | + profile_data = { |
| 214 | + "resource_type": "local", |
| 215 | + "name": "Local profile", |
| 216 | + } |
| 217 | + print("Prepared default profile data.") |
| 218 | + else: |
| 219 | + with open(profile) as f: |
| 220 | + profile_data = json.load(f) |
| 221 | + print(f"Read profile data from {profile}.") |
| 222 | + |
| 223 | + # Validate resource/profile data |
| 224 | + try: |
| 225 | + resource_data = cast_serialize_resource(resource_data) |
| 226 | + except ValidationError as e: |
| 227 | + sys.exit( |
| 228 | + f"ERROR: Invalid resource data.\nOriginal error:\n{str(e)}" |
| 229 | + ) |
| 230 | + try: |
| 231 | + profile_data = cast_serialize_profile(profile_data) |
| 232 | + except ValidationError as e: |
| 233 | + sys.exit( |
| 234 | + f"ERROR: Invalid profile data.\nOriginal error:\n{str(e)}" |
| 235 | + ) |
| 236 | + |
| 237 | + # Create resource/profile db objects |
| 238 | + resource_obj = Resource(**resource_data) |
| 239 | + db.add(resource_obj) |
| 240 | + db.commit() |
| 241 | + db.refresh(resource_obj) |
| 242 | + profile_data["resource_id"] = resource_obj.id |
| 243 | + profile_obj = Profile(**profile_data) |
| 244 | + db.add(profile_obj) |
| 245 | + db.commit() |
| 246 | + db.refresh(profile_obj) |
| 247 | + |
| 248 | + # Associate profile to users |
| 249 | + res = db.execute(select(UserOAuth)) |
| 250 | + users = res.unique().scalars().all() |
| 251 | + for user in users: |
| 252 | + print(f"Now set profile_id={profile_obj.id} for {user.email}.") |
| 253 | + user.profile_id = profile_obj.id |
| 254 | + db.add(user) |
| 255 | + db.commit() |
| 256 | + db.expunge_all() |
| 257 | + print() |
| 258 | + |
240 | 259 |
|
241 | 260 | def update_db_data(): |
242 | 261 | """ |
@@ -328,6 +347,8 @@ def run(): |
328 | 347 | init_db_data( |
329 | 348 | resource=args.resource, |
330 | 349 | profile=args.profile, |
| 350 | + admin_email=args.admin_email, |
| 351 | + admin_password=args.admin_pwd, |
331 | 352 | ) |
332 | 353 | elif args.cmd == "update-db-data": |
333 | 354 | update_db_data() |
|
0 commit comments