fred-oss 0.54.0__tar.gz → 0.56.0__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.
- fred_oss-0.56.0/MANIFEST.in +3 -0
- {fred_oss-0.54.0/src/main/fred_oss.egg-info → fred_oss-0.56.0}/PKG-INFO +1 -1
- {fred_oss-0.54.0 → fred_oss-0.56.0}/setup.py +2 -0
- fred_oss-0.56.0/src/main/fred/cli/main.py +59 -0
- fred_oss-0.56.0/src/main/fred/dao/service/_minio/__init__.py +1 -0
- fred_oss-0.56.0/src/main/fred/dao/service/_minio/policy/builder.py +16 -0
- fred_oss-0.56.0/src/main/fred/dao/service/_minio/policy/catalog.py +28 -0
- fred_oss-0.56.0/src/main/fred/dao/service/_minio/policy/loader.py +27 -0
- fred_oss-0.56.0/src/main/fred/dao/service/_minio/policy/templates/public_ro.json +24 -0
- fred_oss-0.56.0/src/main/fred/dao/service/_minio/policy/templates/public_rw.json +31 -0
- fred_oss-0.56.0/src/main/fred/dao/service/_minio/pool.py +55 -0
- fred_oss-0.54.0/src/main/fred/dao/service/_minio.py → fred_oss-0.56.0/src/main/fred/dao/service/_minio/service.py +13 -52
- fred_oss-0.56.0/src/main/fred/integrations/databricks/runtimes/16.4LTS.json +10 -0
- fred_oss-0.56.0/src/main/fred/rest/__init__.py +17 -0
- fred_oss-0.56.0/src/main/fred/rest/auth.py +23 -0
- fred_oss-0.56.0/src/main/fred/rest/config.py +32 -0
- fred_oss-0.56.0/src/main/fred/rest/router/catalog/default/__init__.py +10 -0
- fred_oss-0.56.0/src/main/fred/rest/router/catalog/default/_base.py +29 -0
- fred_oss-0.56.0/src/main/fred/rest/router/catalog/default/_example.py +31 -0
- fred_oss-0.56.0/src/main/fred/rest/router/catalog/default/catalog.py +20 -0
- fred_oss-0.56.0/src/main/fred/rest/router/catalog/interface.py +11 -0
- fred_oss-0.56.0/src/main/fred/rest/router/config.py +40 -0
- fred_oss-0.56.0/src/main/fred/rest/router/endpoint.py +94 -0
- fred_oss-0.56.0/src/main/fred/rest/router/interface.py +90 -0
- fred_oss-0.56.0/src/main/fred/rest/server.py +104 -0
- fred_oss-0.56.0/src/main/fred/rest/settings.py +46 -0
- fred_oss-0.56.0/src/main/fred/version +1 -0
- fred_oss-0.56.0/src/main/fred/worker/runner/plugins/__init__.py +0 -0
- fred_oss-0.56.0/src/main/fred/worker/runner/rest/__init__.py +0 -0
- fred_oss-0.56.0/src/main/fred/worker/runner/rest/routers/__init__.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0/src/main/fred_oss.egg-info}/PKG-INFO +1 -1
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred_oss.egg-info/SOURCES.txt +25 -1
- fred_oss-0.54.0/MANIFEST.in +0 -2
- fred_oss-0.54.0/src/main/fred/cli/main.py +0 -31
- fred_oss-0.54.0/src/main/fred/version +0 -1
- {fred_oss-0.54.0 → fred_oss-0.56.0}/NOTICE.txt +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/README.md +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/requirements.txt +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/setup.cfg +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/cli/__init__.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/cli/__main__.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/cli/interface.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/dao/__init__.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/dao/comp/__init__.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/dao/comp/_keyval.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/dao/comp/_pubsub.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/dao/comp/_queue.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/dao/comp/catalog.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/dao/comp/interface.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/dao/service/__init__.py +0 -0
- {fred_oss-0.54.0/src/main/fred/future/callback → fred_oss-0.56.0/src/main/fred/dao/service/_minio/policy}/__init__.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/dao/service/_redis.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/dao/service/_stdlib.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/dao/service/catalog.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/dao/service/interface.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/dao/service/utils.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/future/__init__.py +0 -0
- {fred_oss-0.54.0/src/main/fred/integrations/databricks/runtimes → fred_oss-0.56.0/src/main/fred/future/callback}/__init__.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/future/callback/_function.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/future/callback/catalog.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/future/callback/interface.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/future/impl.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/future/result.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/future/settings.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/future/utils.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/integrations/databricks/__init__.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/integrations/databricks/cli_ext.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/integrations/databricks/runtime.py +0 -0
- {fred_oss-0.54.0/src/main/fred/integrations/databricks/wrappers → fred_oss-0.56.0/src/main/fred/integrations/databricks/runtimes}/__init__.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/integrations/databricks/runtimes/scanner.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/integrations/databricks/runtimes/sync.py +0 -0
- {fred_oss-0.54.0/src/main/fred/utils → fred_oss-0.56.0/src/main/fred/integrations/databricks/wrappers}/__init__.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/integrations/databricks/wrappers/dbutils.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/integrations/runpod/__init__.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/integrations/runpod/cli_ext.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/integrations/runpod/helper.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/maturity.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/monad/__init__.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/monad/_either.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/monad/catalog.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/monad/interface.py +0 -0
- {fred_oss-0.54.0/src/main/fred/utils/imout → fred_oss-0.56.0/src/main/fred/rest/router}/__init__.py +0 -0
- {fred_oss-0.54.0/src/main/fred/worker/runner/model → fred_oss-0.56.0/src/main/fred/rest/router/catalog}/__init__.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/settings.py +0 -0
- {fred_oss-0.54.0/src/main/fred/worker/runner/plugins → fred_oss-0.56.0/src/main/fred/utils}/__init__.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/utils/dateops.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/utils/imops.py +0 -0
- {fred_oss-0.54.0/src/main/fred/worker/runner/rest → fred_oss-0.56.0/src/main/fred/utils/imout}/__init__.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/utils/imout/_filesystem.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/utils/imout/_minio.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/utils/imout/_string.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/utils/imout/catalog.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/utils/imout/interface.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/utils/mlops/__init__.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/utils/mlops/auto.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/utils/runtime.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/version.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/__init__.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/interface.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/__init__.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/backend.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/client.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/handler.py +0 -0
- {fred_oss-0.54.0/src/main/fred/worker/runner/rest/routers → fred_oss-0.56.0/src/main/fred/worker/runner/model}/__init__.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/model/_handler.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/model/_item.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/model/_request.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/model/_runner_spec.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/model/catalog.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/model/interface.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/plugins/_local.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/plugins/_runpod.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/plugins/catalog.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/plugins/interface.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/rest/auth.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/rest/cli_ext.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/rest/routers/_runner.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/rest/routers/catalog.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/rest/routers/interface.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/rest/server.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/rest/settings.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/settings.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/signal.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/status.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/runner/utils.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred/worker/settings.py +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred_oss.egg-info/dependency_links.txt +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred_oss.egg-info/entry_points.txt +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred_oss.egg-info/requires.txt +0 -0
- {fred_oss-0.54.0 → fred_oss-0.56.0}/src/main/fred_oss.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from fred.version import version
|
|
4
|
+
from fred.settings import logger_manager
|
|
5
|
+
from fred.cli.interface import AbstractCLI
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
logger = logger_manager.get_logger(name=__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CLIExtensionGroups:
|
|
12
|
+
"""CLI Extensions providing access to various integrations by following a lazy loading pattern."""
|
|
13
|
+
|
|
14
|
+
@property
|
|
15
|
+
def databricks(self):
|
|
16
|
+
from fred.integrations.databricks.cli_ext import DatabricksExt
|
|
17
|
+
return DatabricksExt()
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def runpod(self):
|
|
21
|
+
from fred.integrations.runpod.cli_ext import RunPodExt
|
|
22
|
+
return RunPodExt()
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def runner_server(self):
|
|
26
|
+
from fred.worker.runner.rest.cli_ext import RunnerServerExt
|
|
27
|
+
return RunnerServerExt()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class CLI(AbstractCLI, CLIExtensionGroups):
|
|
31
|
+
|
|
32
|
+
def version(self) -> str:
|
|
33
|
+
return version.value
|
|
34
|
+
|
|
35
|
+
def serve(
|
|
36
|
+
self,
|
|
37
|
+
classname: Optional[str] = None,
|
|
38
|
+
classpath: Optional[str] = None,
|
|
39
|
+
include_routers: Optional[list[str]] = None,
|
|
40
|
+
exclude_routers: Optional[list[str]] = None,
|
|
41
|
+
fastapi_configs: Optional[dict] = None,
|
|
42
|
+
server_configs: Optional[dict] = None,
|
|
43
|
+
):
|
|
44
|
+
from fred.rest.server import FredServer
|
|
45
|
+
|
|
46
|
+
include_routers = include_routers or []
|
|
47
|
+
exclude_routers = exclude_routers or []
|
|
48
|
+
fastapi_configs = fastapi_configs or {}
|
|
49
|
+
server_configs = server_configs or {}
|
|
50
|
+
|
|
51
|
+
logger.info("Starting the Fred-REST Server...")
|
|
52
|
+
server = FredServer.auto(
|
|
53
|
+
include_routers=include_routers,
|
|
54
|
+
exclude_routers=exclude_routers,
|
|
55
|
+
router_classname=classname,
|
|
56
|
+
router_classpath=classpath,
|
|
57
|
+
**fastapi_configs,
|
|
58
|
+
)
|
|
59
|
+
server.start(**server_configs)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .service import MinioService
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class MinioPolicyBuilderActionBucket(enum.StrEnum):
|
|
5
|
+
ALL = "s3:*"
|
|
6
|
+
LIST = "s3:ListBucket"
|
|
7
|
+
LOCATION = "s3:GetBucketLocation"
|
|
8
|
+
DELETE = "s3:DeleteBucket"
|
|
9
|
+
CREATE = "s3:CreateBucket"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MinioPolicyBuilderActionObject(enum.StrEnum):
|
|
13
|
+
GET = "s3:GetObject"
|
|
14
|
+
PUT = "s3:PutObject"
|
|
15
|
+
DELETE = "s3:DeleteObject"
|
|
16
|
+
ALL = "s3:*"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from fred.dao.service._minio.policy.loader import MinioPolicyLoader
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class MinioPolicyCatalog(enum.Enum):
|
|
8
|
+
BUCKET_PUBLIC_RO = MinioPolicyLoader(
|
|
9
|
+
title="[Bucket Policy] Public Read-Only",
|
|
10
|
+
filename="public_ro.json",
|
|
11
|
+
requires=[
|
|
12
|
+
"bucket_name",
|
|
13
|
+
],
|
|
14
|
+
)
|
|
15
|
+
BUCKET_PUBLIC_RW = MinioPolicyLoader(
|
|
16
|
+
title="[Bucket Policy] Public Read-Write",
|
|
17
|
+
filename="public_rw.json",
|
|
18
|
+
requires=[
|
|
19
|
+
"bucket_name",
|
|
20
|
+
],
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
def load(self, path: Optional[str] = None, **params) -> dict:
|
|
24
|
+
return self.value.load(path=path, **params)
|
|
25
|
+
|
|
26
|
+
def content(self, path: Optional[str] = None, **params) -> str:
|
|
27
|
+
import json
|
|
28
|
+
return json.dumps(self.load(path=path, **params))
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Optional
|
|
5
|
+
def load_policy(filename: str, path: Optional[str] = None, **kwargs) -> dict:
|
|
6
|
+
path = path or os.path.join(os.path.dirname(__file__), "templates")
|
|
7
|
+
filepath = os.path.join(path, filename)
|
|
8
|
+
# Read the policy file as a string
|
|
9
|
+
with open(filepath, "r", encoding="utf-8") as file:
|
|
10
|
+
policy_str = file.read()
|
|
11
|
+
# Replace placeholders in the policy with actual values from kwargs
|
|
12
|
+
for param_key, param_val in kwargs.items():
|
|
13
|
+
param_ref = "${{param_key}}".replace("param_key", param_key)
|
|
14
|
+
policy_str = policy_str.replace(param_ref, param_val)
|
|
15
|
+
return json.loads(policy_str)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True, slots=True)
|
|
19
|
+
class MinioPolicyLoader:
|
|
20
|
+
title: str
|
|
21
|
+
filename: str
|
|
22
|
+
requires: list[str] = field(default_factory=list)
|
|
23
|
+
|
|
24
|
+
def load(self, path: Optional[str] = None, **kwargs) -> dict:
|
|
25
|
+
if (missing := set(self.requires) - set(kwargs.keys())):
|
|
26
|
+
raise ValueError(f"Missing required parameters for policy '{self.filename}': {missing}")
|
|
27
|
+
return load_policy(filename=self.filename, path=path, **kwargs)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Version": "2012-10-17",
|
|
3
|
+
"Statement": [
|
|
4
|
+
{
|
|
5
|
+
"Effect": "Allow",
|
|
6
|
+
"Principal": {
|
|
7
|
+
"AWS": "*"
|
|
8
|
+
},
|
|
9
|
+
"Action": [
|
|
10
|
+
"s3:GetBucketLocation",
|
|
11
|
+
"s3:ListBucket"
|
|
12
|
+
],
|
|
13
|
+
"Resource": "arn:aws:s3:::${{bucket_name}}"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"Effect": "Allow",
|
|
17
|
+
"Principal": {
|
|
18
|
+
"AWS": "*"
|
|
19
|
+
},
|
|
20
|
+
"Action": "s3:GetObject",
|
|
21
|
+
"Resource": "arn:aws:s3:::${{bucket_name}}/*"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Version": "2012-10-17",
|
|
3
|
+
"Statement": [
|
|
4
|
+
{
|
|
5
|
+
"Effect": "Allow",
|
|
6
|
+
"Principal": {
|
|
7
|
+
"AWS": "*"
|
|
8
|
+
},
|
|
9
|
+
"Action": [
|
|
10
|
+
"s3:GetBucketLocation",
|
|
11
|
+
"s3:ListBucket",
|
|
12
|
+
"s3:ListBucketMultipartUploads"
|
|
13
|
+
],
|
|
14
|
+
"Resource": "arn:aws:s3:::${{bucket_name}}"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"Effect": "Allow",
|
|
18
|
+
"Principal": {
|
|
19
|
+
"AWS": "*"
|
|
20
|
+
},
|
|
21
|
+
"Action": [
|
|
22
|
+
"s3:GetObject",
|
|
23
|
+
"s3:PutObject",
|
|
24
|
+
"s3:DeleteObject",
|
|
25
|
+
"s3:ListMultipartUploadParts",
|
|
26
|
+
"s3:AbortMultipartUpload"
|
|
27
|
+
],
|
|
28
|
+
"Resource": "arn:aws:s3:::${{bucket_name}}/*"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from urllib3 import PoolManager, Retry
|
|
2
|
+
from urllib3.util import Timeout
|
|
3
|
+
|
|
4
|
+
from fred.settings import get_environ_variable, logger_manager
|
|
5
|
+
from fred.dao.service.interface import ServiceConnectionPoolInterface
|
|
6
|
+
|
|
7
|
+
logger = logger_manager.get_logger(name=__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MinioConnectionPool(ServiceConnectionPoolInterface[PoolManager]):
|
|
11
|
+
|
|
12
|
+
@classmethod
|
|
13
|
+
def _create_pool(cls, disable_cert: bool = False, **kwargs) -> PoolManager:
|
|
14
|
+
"""Create a urllib3 PoolManager with the given configurations.
|
|
15
|
+
|
|
16
|
+
TODO: Consider using the inverse of 'require_cert' as the default to ensure we do have cert-check automatically.
|
|
17
|
+
For now, we keep it as is to avoid breaking changes.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
require_cert (bool): Whether to require SSL certificate verification.
|
|
21
|
+
**kwargs: Additional keyword arguments to pass to the PoolManager constructor.
|
|
22
|
+
Returns:
|
|
23
|
+
PoolManager: A configured PoolManager instance.
|
|
24
|
+
"""
|
|
25
|
+
num_pools = kwargs.pop("num_pools", 10)
|
|
26
|
+
maxsize = kwargs.pop("maxsize", 10)
|
|
27
|
+
# Default timeout of 5 minutes
|
|
28
|
+
timeout_seconds = kwargs.pop("timeout", 300)
|
|
29
|
+
timeout = Timeout(
|
|
30
|
+
connect=timeout_seconds,
|
|
31
|
+
read=timeout_seconds,
|
|
32
|
+
)
|
|
33
|
+
# Default retries of 5 with exponential backoff
|
|
34
|
+
retry = Retry(
|
|
35
|
+
total=kwargs.pop("retries", 5),
|
|
36
|
+
backoff_factor=kwargs.pop("backoff_factor", 0.25),
|
|
37
|
+
status_forcelist=[500, 502, 503, 504],
|
|
38
|
+
)
|
|
39
|
+
# Configure certificate requirements for SSL connections
|
|
40
|
+
cert_reqs = "CERT_NONE"
|
|
41
|
+
ca_certs = None
|
|
42
|
+
if not disable_cert:
|
|
43
|
+
import certifi
|
|
44
|
+
cert_reqs = "CERT_REQUIRED"
|
|
45
|
+
ca_certs = get_environ_variable("SSL_CERT_FILE") or certifi.where()
|
|
46
|
+
# Finally, create and return the PoolManager instance
|
|
47
|
+
return PoolManager(
|
|
48
|
+
num_pools=num_pools,
|
|
49
|
+
maxsize=maxsize,
|
|
50
|
+
timeout=timeout,
|
|
51
|
+
retries=retry,
|
|
52
|
+
cert_reqs=cert_reqs,
|
|
53
|
+
ca_certs=ca_certs,
|
|
54
|
+
**kwargs
|
|
55
|
+
)
|
|
@@ -1,62 +1,14 @@
|
|
|
1
1
|
from minio import Minio
|
|
2
|
-
from urllib3 import PoolManager, Retry
|
|
3
|
-
from urllib3.util import Timeout
|
|
4
2
|
|
|
5
|
-
from fred.settings import
|
|
3
|
+
from fred.settings import logger_manager
|
|
4
|
+
from fred.dao.service.interface import ServiceInterface
|
|
6
5
|
from fred.dao.service.utils import get_minio_from_payload
|
|
7
|
-
from fred.dao.service.
|
|
6
|
+
from fred.dao.service._minio.pool import MinioConnectionPool
|
|
7
|
+
from fred.dao.service._minio.policy.catalog import MinioPolicyCatalog
|
|
8
8
|
|
|
9
9
|
logger = logger_manager.get_logger(name=__name__)
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class MinioConnectionPool(ServiceConnectionPoolInterface[PoolManager]):
|
|
13
|
-
|
|
14
|
-
@classmethod
|
|
15
|
-
def _create_pool(cls, disable_cert: bool = False, **kwargs) -> PoolManager:
|
|
16
|
-
"""Create a urllib3 PoolManager with the given configurations.
|
|
17
|
-
|
|
18
|
-
TODO: Consider using the inverse of 'require_cert' as the default to ensure we do have cert-check automatically.
|
|
19
|
-
For now, we keep it as is to avoid breaking changes.
|
|
20
|
-
|
|
21
|
-
Args:
|
|
22
|
-
require_cert (bool): Whether to require SSL certificate verification.
|
|
23
|
-
**kwargs: Additional keyword arguments to pass to the PoolManager constructor.
|
|
24
|
-
Returns:
|
|
25
|
-
PoolManager: A configured PoolManager instance.
|
|
26
|
-
"""
|
|
27
|
-
num_pools = kwargs.pop("num_pools", 10)
|
|
28
|
-
maxsize = kwargs.pop("maxsize", 10)
|
|
29
|
-
# Default timeout of 5 minutes
|
|
30
|
-
timeout_seconds = kwargs.pop("timeout", 300)
|
|
31
|
-
timeout = Timeout(
|
|
32
|
-
connect=timeout_seconds,
|
|
33
|
-
read=timeout_seconds,
|
|
34
|
-
)
|
|
35
|
-
# Default retries of 5 with exponential backoff
|
|
36
|
-
retry = Retry(
|
|
37
|
-
total=kwargs.pop("retries", 5),
|
|
38
|
-
backoff_factor=kwargs.pop("backoff_factor", 0.25),
|
|
39
|
-
status_forcelist=[500, 502, 503, 504],
|
|
40
|
-
)
|
|
41
|
-
# Configure certificate requirements for SSL connections
|
|
42
|
-
cert_reqs = "CERT_NONE"
|
|
43
|
-
ca_certs = None
|
|
44
|
-
if not disable_cert:
|
|
45
|
-
import certifi
|
|
46
|
-
cert_reqs = "CERT_REQUIRED"
|
|
47
|
-
ca_certs = get_environ_variable("SSL_CERT_FILE") or certifi.where()
|
|
48
|
-
# Finally, create and return the PoolManager instance
|
|
49
|
-
return PoolManager(
|
|
50
|
-
num_pools=num_pools,
|
|
51
|
-
maxsize=maxsize,
|
|
52
|
-
timeout=timeout,
|
|
53
|
-
retries=retry,
|
|
54
|
-
cert_reqs=cert_reqs,
|
|
55
|
-
ca_certs=ca_certs,
|
|
56
|
-
**kwargs
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
|
|
60
12
|
class MinioService(ServiceInterface[Minio]):
|
|
61
13
|
instance: Minio
|
|
62
14
|
metadata: dict = {}
|
|
@@ -120,3 +72,12 @@ class MinioService(ServiceInterface[Minio]):
|
|
|
120
72
|
except S3Error:
|
|
121
73
|
logger.debug(f"Object {object_name} in bucket {bucket_name} does not exist.")
|
|
122
74
|
return False
|
|
75
|
+
|
|
76
|
+
def make_bucket_public(self, bucket_name: str, readonly: bool = False):
|
|
77
|
+
"""Make a bucket public with either read-only or read-write access."""
|
|
78
|
+
policy = MinioPolicyCatalog.BUCKET_PUBLIC_RO \
|
|
79
|
+
if readonly else MinioPolicyCatalog.BUCKET_PUBLIC_RW
|
|
80
|
+
self.client.set_bucket_policy(
|
|
81
|
+
bucket_name=bucket_name,
|
|
82
|
+
policy=policy.content(bucket_name=bucket_name)
|
|
83
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
[Fred-REST] Fred's Simple Extensible REST-API Server
|
|
3
|
+
========================================================
|
|
4
|
+
This module provides a simple and extensible REST-API server implementation using FastAPI.
|
|
5
|
+
It includes configurations for authentication, router management, and server startup.
|
|
6
|
+
"""
|
|
7
|
+
from fred.maturity import Maturity, MaturityLevel
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
module_maturity = Maturity(
|
|
11
|
+
level=MaturityLevel.ALPHA,
|
|
12
|
+
reference=__name__,
|
|
13
|
+
message=(
|
|
14
|
+
"Fred-REST implementation is in early development "
|
|
15
|
+
"and therefore currently with incomplete and unstable features."
|
|
16
|
+
)
|
|
17
|
+
)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from fastapi import Security, HTTPException, status
|
|
2
|
+
from fastapi.security import APIKeyHeader
|
|
3
|
+
|
|
4
|
+
from fred.rest.settings import FRD_RESTAPI_TOKEN
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
async def verify_key(
|
|
8
|
+
api_key_header: str = Security(
|
|
9
|
+
APIKeyHeader(
|
|
10
|
+
name="X-API-Key",
|
|
11
|
+
auto_error=False
|
|
12
|
+
)
|
|
13
|
+
),
|
|
14
|
+
):
|
|
15
|
+
"""
|
|
16
|
+
Verify the provided API key against the expected token.
|
|
17
|
+
Raises an HTTPException if the key is invalid or missing.
|
|
18
|
+
"""
|
|
19
|
+
if api_key_header != FRD_RESTAPI_TOKEN:
|
|
20
|
+
raise HTTPException(
|
|
21
|
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
22
|
+
detail="Invalid or missing API Key",
|
|
23
|
+
)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from fred.rest.router.catalog.interface import RouterCatalogInterface
|
|
5
|
+
from fred.rest.settings import (
|
|
6
|
+
FRD_RESTAPI_ROUTERCATALOG_CLASSPATH,
|
|
7
|
+
FRD_RESTAPI_ROUTERCATALOG_CLASSNAME,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True, slots=True)
|
|
11
|
+
class ServerRouterCatalogConfig:
|
|
12
|
+
classname: str
|
|
13
|
+
classpath: str
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def auto(cls, classname: Optional[str] = None, classpath: Optional[str] = None) -> "ServerRouterCatalogConfig":
|
|
17
|
+
return cls(
|
|
18
|
+
classname=classname or FRD_RESTAPI_ROUTERCATALOG_CLASSNAME,
|
|
19
|
+
classpath=classpath or FRD_RESTAPI_ROUTERCATALOG_CLASSPATH,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def catalog(self) -> type[RouterCatalogInterface]:
|
|
24
|
+
import importlib
|
|
25
|
+
|
|
26
|
+
module = importlib.import_module(self.classpath)
|
|
27
|
+
catalog_class = getattr(module, self.classname, None)
|
|
28
|
+
if catalog_class is None:
|
|
29
|
+
raise ImportError(f"Could not find class '{self.classname}' in module '{self.classpath}'")
|
|
30
|
+
if not issubclass(catalog_class, RouterCatalogInterface):
|
|
31
|
+
raise TypeError(f"Class '{self.classname}' is not a subclass of RouterCatalogInterface")
|
|
32
|
+
return catalog_class
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from fred.rest.router.catalog.default.catalog import RouterCatalog
|
|
2
|
+
from fred.settings import logger_manager
|
|
3
|
+
|
|
4
|
+
logger = logger_manager.get_logger(name=__name__)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
logger.warning(
|
|
8
|
+
"You are using the default router catalog intended for demonstration purposes only. "
|
|
9
|
+
"For production use, please implement a custom router catalog."
|
|
10
|
+
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from fred.rest.router.interface import RouterInterfaceMixin
|
|
2
|
+
from fred.rest.router.endpoint import RouterEndpointAnnotation
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class RouterBaseMixin(RouterInterfaceMixin):
|
|
6
|
+
|
|
7
|
+
@RouterEndpointAnnotation.set(
|
|
8
|
+
path="/",
|
|
9
|
+
methods=["GET"],
|
|
10
|
+
summary="Base endpoint",
|
|
11
|
+
description="A simple base endpoint to check service availability."
|
|
12
|
+
)
|
|
13
|
+
def base(self, include_telemetry: bool = False, **kwargs) -> dict:
|
|
14
|
+
if include_telemetry:
|
|
15
|
+
from fred.utils.runtime import RuntimeProfilingSnapshot
|
|
16
|
+
kwargs["telemetry"] = RuntimeProfilingSnapshot.auto().to_dict()
|
|
17
|
+
return {
|
|
18
|
+
"ok": True,
|
|
19
|
+
**kwargs
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@RouterEndpointAnnotation.set(
|
|
23
|
+
path="/passthrough",
|
|
24
|
+
methods=["POST"],
|
|
25
|
+
summary="Passthrough POST endpoint",
|
|
26
|
+
description="An endpoint that returns all received parameters.",
|
|
27
|
+
)
|
|
28
|
+
def passthrough(self, **kwargs) -> dict:
|
|
29
|
+
return kwargs
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from fred.rest.router.interface import RouterInterfaceMixin
|
|
5
|
+
from fred.rest.router.endpoint import RouterEndpointAnnotation
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RouterExampleMixin(RouterInterfaceMixin):
|
|
9
|
+
|
|
10
|
+
@RouterEndpointAnnotation.set(
|
|
11
|
+
path="/ping",
|
|
12
|
+
methods=["GET"],
|
|
13
|
+
summary="Ping endpoint",
|
|
14
|
+
description="A simple ping endpoint to check service availability.",
|
|
15
|
+
)
|
|
16
|
+
def ping(self, pong: Optional[str] = None, **kwargs) -> dict:
|
|
17
|
+
from fred.utils.dateops import datetime_utcnow
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
"ping_time": datetime_utcnow().isoformat(),
|
|
21
|
+
"ping_response": pong or "pong",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@RouterEndpointAnnotation.set(
|
|
25
|
+
path="/passthrough",
|
|
26
|
+
methods=["GET"],
|
|
27
|
+
summary="Passthrough GET endpoint",
|
|
28
|
+
description="An endpoint that returns all received parameters.",
|
|
29
|
+
)
|
|
30
|
+
def passthrough(self, **kwargs) -> dict:
|
|
31
|
+
return kwargs
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
|
|
3
|
+
from fred.rest.router.config import RouterConfig
|
|
4
|
+
from fred.rest.router.catalog.default._base import RouterBaseMixin
|
|
5
|
+
from fred.rest.router.catalog.default._example import RouterExampleMixin
|
|
6
|
+
|
|
7
|
+
from fred.rest.router.catalog.interface import RouterCatalogInterface
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RouterCatalog(RouterCatalogInterface, enum.Enum):
|
|
11
|
+
BASE = RouterConfig.auto(prefix="")(apply=RouterBaseMixin)
|
|
12
|
+
EXAMPLE = RouterConfig.auto(prefix="/example")(apply=RouterExampleMixin)
|
|
13
|
+
|
|
14
|
+
def get_kwargs(self) -> dict:
|
|
15
|
+
match self:
|
|
16
|
+
case RouterCatalog.EXAMPLE:
|
|
17
|
+
# Disable the backend for the example router
|
|
18
|
+
return {"disregard_backend": True}
|
|
19
|
+
case _:
|
|
20
|
+
return {}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from fred.rest.router.interface import RouterInterface
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class RouterCatalogInterface:
|
|
5
|
+
|
|
6
|
+
def auto(self, **kwargs) -> RouterInterface:
|
|
7
|
+
return self.value.reference.auto(**kwargs)
|
|
8
|
+
|
|
9
|
+
def get_kwargs(self) -> dict:
|
|
10
|
+
# This method can be overridden to provide specific kwargs for router initialization
|
|
11
|
+
return {}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from fred.rest.router.interface import RouterInterface, RouterInterfaceMixin
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(frozen=True, slots=True)
|
|
7
|
+
class RouterConfig:
|
|
8
|
+
prefix: str
|
|
9
|
+
other: dict
|
|
10
|
+
|
|
11
|
+
@classmethod
|
|
12
|
+
def auto(cls, prefix: str = "", **kwargs) -> "RouterConfig":
|
|
13
|
+
return cls(
|
|
14
|
+
prefix=prefix,
|
|
15
|
+
other=kwargs,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
def get_applied_configs(self, router_class: type[RouterInterface]) -> "AppliedRouterConfig":
|
|
19
|
+
return AppliedRouterConfig(
|
|
20
|
+
reference=router_class,
|
|
21
|
+
config=self,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
def __call__(self, apply: type[RouterInterfaceMixin]) -> "AppliedRouterConfig":
|
|
25
|
+
router_classname = f"Router{apply.__name__}"
|
|
26
|
+
return self.get_applied_configs(
|
|
27
|
+
router_class=type(router_classname, (RouterInterface, apply, ), {})
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def get_configs(self) -> dict:
|
|
31
|
+
return {
|
|
32
|
+
"prefix": self.prefix,
|
|
33
|
+
**self.other,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True, slots=True)
|
|
38
|
+
class AppliedRouterConfig:
|
|
39
|
+
reference: type[RouterInterface]
|
|
40
|
+
config: RouterConfig
|