mirror of
https://github.com/clearml/clearml-server
synced 2025-06-26 23:15:47 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09ab2af34c | ||
|
|
8bb26a6b0b | ||
|
|
3f2304549d | ||
|
|
ad72a435f1 | ||
|
|
f34332344e | ||
|
|
d324b57dd7 | ||
|
|
2216bfe875 | ||
|
|
9beefa7473 | ||
|
|
8ebc334889 | ||
|
|
e662c850af | ||
|
|
1e5163e530 |
@@ -238,6 +238,7 @@ def reset_task(
|
||||
set__last_metrics={},
|
||||
set__metric_stats={},
|
||||
set__models__output=[],
|
||||
set__runtime={},
|
||||
unset__output__result=1,
|
||||
unset__output__error=1,
|
||||
unset__last_worker=1,
|
||||
|
||||
@@ -19,7 +19,7 @@ from pyparsing import (
|
||||
|
||||
from apiserver.utilities import json
|
||||
|
||||
EXTRA_CONFIG_PATHS = ("/opt/clearml/config",)
|
||||
EXTRA_CONFIG_PATHS = ("/opt/trains/config", "/opt/clearml/config")
|
||||
DEFAULT_PREFIXES = ("clearml", "trains")
|
||||
EXTRA_CONFIG_PATH_SEP = ":" if platform.system() != "Windows" else ";"
|
||||
|
||||
|
||||
@@ -176,6 +176,13 @@ class SafeMapField(MapField, DictValidationMixin):
|
||||
self.error("Empty keys are not allowed in a MapField")
|
||||
|
||||
|
||||
class NullableStringField(StringField):
|
||||
def validate(self, value):
|
||||
if value is None:
|
||||
return
|
||||
super(NullableStringField, self).validate(value)
|
||||
|
||||
|
||||
class SafeDictField(DictField, DictValidationMixin):
|
||||
def validate(self, value):
|
||||
self._safe_validate(value)
|
||||
|
||||
@@ -18,6 +18,7 @@ from apiserver.database.fields import (
|
||||
UnionField,
|
||||
SafeSortedListField,
|
||||
EmbeddedDocumentListField,
|
||||
NullableStringField,
|
||||
)
|
||||
from apiserver.database.model import AttributedDocument
|
||||
from apiserver.database.model.base import ProperDictMixin, GetMixin
|
||||
@@ -260,7 +261,7 @@ class Task(AttributedDocument):
|
||||
configuration = SafeMapField(field=EmbeddedDocumentField(ConfigurationItem))
|
||||
runtime = SafeDictField(default=dict)
|
||||
models: Models = EmbeddedDocumentField(Models, default=Models)
|
||||
container = SafeMapField(field=StringField(default=""))
|
||||
container = SafeMapField(field=NullableStringField())
|
||||
enqueue_status = StringField(
|
||||
choices=get_options(TaskStatus), exclude_by_default=True
|
||||
)
|
||||
|
||||
@@ -97,22 +97,6 @@ def _migrate_model_labels(db: Database):
|
||||
tasks.update_one({"_id": doc["_id"]}, {"$set": set_commands})
|
||||
|
||||
|
||||
def _migrate_project_description(db: Database):
|
||||
projects: Collection = db["project"]
|
||||
filter = {
|
||||
"$or": [
|
||||
{
|
||||
"$expr": {"$lt": [{"$strLenCP": "$description"}, 100]},
|
||||
"description": {"$regex": "^Auto-generated at ", "$options": "i"},
|
||||
},
|
||||
{"description": {"$regex": "^Auto-generated during move$", "$options": "i"}},
|
||||
{"description": {"$regex": "^Auto-generated while cloning$", "$options": "i"}},
|
||||
]
|
||||
}
|
||||
for doc in projects.find(filter=filter):
|
||||
projects.update_one({"_id": doc["_id"]}, {"$unset": {"description": 1}})
|
||||
|
||||
|
||||
def _migrate_project_names(db: Database):
|
||||
projects: Collection = db["project"]
|
||||
|
||||
@@ -141,5 +125,4 @@ def migrate_backend(db: Database):
|
||||
_migrate_docker_cmd(db)
|
||||
_migrate_model_labels(db)
|
||||
_migrate_project_names(db)
|
||||
_migrate_project_description(db)
|
||||
_drop_all_indices_from_collections(db, ["task*"])
|
||||
|
||||
22
apiserver/mongo/migrations/1_0_2.py
Normal file
22
apiserver/mongo/migrations/1_0_2.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from pymongo.collection import Collection
|
||||
from pymongo.database import Database
|
||||
|
||||
|
||||
def _migrate_project_description(db: Database):
|
||||
projects: Collection = db["project"]
|
||||
filter = {
|
||||
"$or": [
|
||||
{
|
||||
"$expr": {"$lt": [{"$strLenCP": "$description"}, 100]},
|
||||
"description": {"$regex": "^Auto-generated at ", "$options": "i"},
|
||||
},
|
||||
{"description": {"$regex": "^Auto-generated during move$", "$options": "i"}},
|
||||
{"description": {"$regex": "^Auto-generated while cloning$", "$options": "i"}},
|
||||
]
|
||||
}
|
||||
for doc in projects.find(filter=filter):
|
||||
projects.update_one({"_id": doc["_id"]}, {"$unset": {"description": 1}})
|
||||
|
||||
|
||||
def migrate_backend(db: Database):
|
||||
_migrate_project_description(db)
|
||||
@@ -12,7 +12,7 @@ funcsigs==1.0.2
|
||||
furl>=2.0.0
|
||||
gunicorn>=19.7.1
|
||||
humanfriendly==4.18
|
||||
jinja2==2.10.1
|
||||
jinja2==2.11.3
|
||||
jsonmodels>=2.3
|
||||
jsonschema>=2.6.0
|
||||
luqum>=0.10.0
|
||||
|
||||
@@ -93,3 +93,19 @@ supported_modes {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logout {
|
||||
authorize: false
|
||||
allow_roles = [ "*" ]
|
||||
"2.13" {
|
||||
description: """ Logout (including SSO, if used)) """
|
||||
request {
|
||||
type: object
|
||||
additionalProperties: false
|
||||
}
|
||||
response {
|
||||
type: object
|
||||
additionalProperties: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,14 +41,12 @@ def login(call: APICall, *_, **__):
|
||||
)
|
||||
|
||||
# Add authorization cookie
|
||||
call.result.cookies[
|
||||
config.get("apiserver.auth.session_auth_cookie_name")
|
||||
] = call.result.data_model.token
|
||||
call.result.set_auth_cookie(call.result.data_model.token)
|
||||
|
||||
|
||||
@endpoint("auth.logout", min_version="2.2")
|
||||
def logout(call: APICall, *_, **__):
|
||||
call.result.cookies[config.get("apiserver.auth.session_auth_cookie_name")] = None
|
||||
call.result.set_auth_cookie(None)
|
||||
|
||||
|
||||
@endpoint(
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from jsonmodels.fields import BoolField
|
||||
|
||||
from apiserver.apimodels.login import (
|
||||
GetSupportedModesRequest,
|
||||
GetSupportedModesResponse,
|
||||
@@ -35,3 +33,8 @@ def supported_modes(call: APICall, _, __: GetSupportedModesRequest):
|
||||
),
|
||||
authenticated=call.auth is not None,
|
||||
)
|
||||
|
||||
|
||||
@endpoint("login.logout", min_version="2.13")
|
||||
def logout(call: APICall, _, __):
|
||||
call.result.set_auth_cookie(None)
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "1.0.0"
|
||||
__version__ = "1.0.2"
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
auth {
|
||||
# Fixed users login credentials
|
||||
# No other user will be able to login
|
||||
# Note: password may be bcrypt-hashed (generate using `python3 -c 'import bcrypt,base64; print(base64.b64encode(bcrypt.hashpw("password".encode(), bcrypt.gensalt())))'`)
|
||||
fixed_users {
|
||||
enabled: true
|
||||
pass_hashed: false
|
||||
users: [
|
||||
{
|
||||
username: "jane"
|
||||
|
||||
@@ -84,7 +84,7 @@ class BasicConfig:
|
||||
if not path.is_dir() and str(path) != DEFAULT_EXTRA_CONFIG_PATH
|
||||
]
|
||||
if invalid:
|
||||
print(f"WARNING: Invalid paths in {self.extra_config_path_env_key} env var: {' '.join(invalid)}")
|
||||
print(f"WARNING: Invalid paths in {self.extra_config_path_env_key} env var: {' '.join(map(str,invalid))}")
|
||||
return [path for path in paths if path.is_dir()]
|
||||
|
||||
def _load(self, verbose=True):
|
||||
|
||||
@@ -5,10 +5,11 @@ from argparse import ArgumentParser
|
||||
from pathlib import Path
|
||||
|
||||
from boltons.iterutils import first
|
||||
from flask import Flask, request, send_from_directory, safe_join, abort, Response
|
||||
from flask._compat import fspath
|
||||
from flask import Flask, request, send_from_directory, abort, Response
|
||||
from flask_compress import Compress
|
||||
from flask_cors import CORS
|
||||
from werkzeug.exceptions import NotFound
|
||||
from werkzeug.security import safe_join
|
||||
|
||||
from config import config
|
||||
|
||||
@@ -34,7 +35,10 @@ def upload():
|
||||
if not filename:
|
||||
continue
|
||||
file_path = filename.lstrip(os.sep)
|
||||
target = Path(safe_join(app.config["UPLOAD_FOLDER"], file_path))
|
||||
safe_path = safe_join(app.config["UPLOAD_FOLDER"], file_path)
|
||||
if safe_path is None:
|
||||
raise NotFound()
|
||||
target = Path(safe_path)
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
file.save(str(target))
|
||||
results.append(file_path)
|
||||
@@ -61,8 +65,8 @@ def download(path):
|
||||
def delete(path):
|
||||
real_path = Path(
|
||||
safe_join(
|
||||
fspath(app.config["UPLOAD_FOLDER"]),
|
||||
fspath(path)
|
||||
os.fspath(app.config["UPLOAD_FOLDER"]),
|
||||
os.fspath(path)
|
||||
)
|
||||
)
|
||||
if not real_path.exists() or not real_path.is_file():
|
||||
|
||||
Reference in New Issue
Block a user