seqslab-cli 3.2.6__tar.gz → 3.2.7__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.
- {seqslab-cli-3.2.6/python/seqslab_cli.egg-info → seqslab-cli-3.2.7}/PKG-INFO +1 -1
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/__init__.py +1 -1
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/cli.py +2 -2
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/drs/api/base.py +17 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/drs/commands.py +28 -0
- seqslab-cli-3.2.7/python/seqslab/scr/__init__.py +15 -0
- seqslab-cli-3.2.7/python/seqslab/scr/commands.py +209 -0
- seqslab-cli-3.2.7/python/seqslab/scr/internal/common.py +46 -0
- seqslab-cli-3.2.7/python/seqslab/scr/resource/base.py +124 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/settings.py +4 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/trs/commands.py +19 -9
- {seqslab-cli-3.2.6/python/seqslab/wes → seqslab-cli-3.2.7/python/seqslab/trs}/resource/azure.py +2 -2
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/trs/resource/base.py +9 -28
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/trs/resource/common.py +2 -2
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/trs/template/base.py +2 -2
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/workspace/commands.py +0 -33
- seqslab-cli-3.2.7/python/seqslab/workspace/internal/__init__.py +0 -0
- seqslab-cli-3.2.7/python/seqslab/workspace/resource/__init__.py +0 -0
- seqslab-cli-3.2.7/python/seqslab/workspace/resource/azure.py +25 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/workspace/resource/base.py +1 -15
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7/python/seqslab_cli.egg-info}/PKG-INFO +1 -1
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab_cli.egg-info/SOURCES.txt +7 -1
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab_cli.egg-info/requires.txt +8 -8
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/requirements.txt +8 -8
- seqslab-cli-3.2.6/python/seqslab/wes/utils.py +0 -102
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/LICENSE +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/MANIFEST.in +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/README.md +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/auth/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/auth/azuread.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/auth/commands.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/context.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/drs/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/drs/api/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/drs/api/azure.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/drs/api/common.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/drs/api/template.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/drs/internal/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/drs/internal/aiocopy.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/drs/internal/common.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/drs/internal/utils.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/drs/storage/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/drs/storage/azure.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/drs/storage/base.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/drs/utils/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/drs/utils/atgxmetadata.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/drs/utils/biomimetype.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/drs/utils/progressbar.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/exceptions.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/organization/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/organization/commands.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/organization/resource/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/organization/resource/base.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/plugin.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/role/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/role/commands.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/role/internal/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/role/internal/common.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/role/resource/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/role/resource/azure.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/role/resource/base.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/runsheet/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/runsheet/runsheet.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/sample_sheet/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/sample_sheet/_version.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/sample_sheet/util.py +0 -0
- {seqslab-cli-3.2.6/python/seqslab/trs → seqslab-cli-3.2.7/python/seqslab/scr}/internal/__init__.py +0 -0
- {seqslab-cli-3.2.6/python/seqslab/trs/register → seqslab-cli-3.2.7/python/seqslab/scr/resource}/__init__.py +0 -0
- {seqslab-cli-3.2.6/python/seqslab/workspace → seqslab-cli-3.2.7/python/seqslab/scr}/resource/azure.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/session_logger.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/statusbar.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/trs/__init__.py +0 -0
- {seqslab-cli-3.2.6/python/seqslab/trs/resource → seqslab-cli-3.2.7/python/seqslab/trs/internal}/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/trs/internal/utils.py +0 -0
- {seqslab-cli-3.2.6/python/seqslab/trs/template → seqslab-cli-3.2.7/python/seqslab/trs/register}/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/trs/register/azure.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/trs/register/base.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/trs/register/common.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/trs/register/template.py +0 -0
- {seqslab-cli-3.2.6/python/seqslab/user/internal → seqslab-cli-3.2.7/python/seqslab/trs/resource}/__init__.py +0 -0
- {seqslab-cli-3.2.6/python/seqslab/user/resource → seqslab-cli-3.2.7/python/seqslab/trs/template}/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/trs/template/template.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/usage_logger.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/user/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/user/commands.py +0 -0
- {seqslab-cli-3.2.6/python/seqslab/wes → seqslab-cli-3.2.7/python/seqslab/user}/internal/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/user/internal/common.py +0 -0
- {seqslab-cli-3.2.6/python/seqslab/wes → seqslab-cli-3.2.7/python/seqslab/user}/resource/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/user/resource/azure.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/user/resource/base.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/wes/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/wes/commands.py +0 -0
- {seqslab-cli-3.2.6/python/seqslab/wes/template → seqslab-cli-3.2.7/python/seqslab/wes/internal}/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/wes/internal/common.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/wes/internal/parameters.py +0 -0
- {seqslab-cli-3.2.6/python/seqslab/workspace/internal → seqslab-cli-3.2.7/python/seqslab/wes/resource}/__init__.py +0 -0
- {seqslab-cli-3.2.6/python/seqslab/trs → seqslab-cli-3.2.7/python/seqslab/wes}/resource/azure.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/wes/resource/base.py +0 -0
- {seqslab-cli-3.2.6/python/seqslab/workspace/resource → seqslab-cli-3.2.7/python/seqslab/wes/template}/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/wes/template/base.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/wes/template/template.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/workspace/__init__.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab/workspace/internal/common.py +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab_cli.egg-info/dependency_links.txt +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab_cli.egg-info/entry_points.txt +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab_cli.egg-info/top_level.txt +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/python/seqslab_cli.egg-info/zip-safe +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/setup.cfg +0 -0
- {seqslab-cli-3.2.6 → seqslab-cli-3.2.7}/setup.py +0 -0
|
@@ -21,7 +21,7 @@ import signal
|
|
|
21
21
|
|
|
22
22
|
from nubia import Nubia, Options
|
|
23
23
|
|
|
24
|
-
from seqslab import auth, drs, wes, trs, workspace, user, role, organization
|
|
24
|
+
from seqslab import auth, drs, wes, trs, workspace, user, role, organization, scr
|
|
25
25
|
from seqslab.plugin import SQLBPlugin
|
|
26
26
|
|
|
27
27
|
|
|
@@ -42,7 +42,7 @@ def main():
|
|
|
42
42
|
plugin = SQLBPlugin()
|
|
43
43
|
shell = Nubia(
|
|
44
44
|
name="seqslab-cli",
|
|
45
|
-
command_pkgs=[auth, drs, wes, trs, workspace, user, role, organization],
|
|
45
|
+
command_pkgs=[auth, drs, wes, trs, workspace, user, role, organization, scr],
|
|
46
46
|
plugin=plugin,
|
|
47
47
|
options=Options(
|
|
48
48
|
persistent_history=False,
|
|
@@ -63,6 +63,7 @@ class DRSregister:
|
|
|
63
63
|
DRS_RESOURCES_URL = f"https://{API_HOSTNAME}/ga4gh/drs/{__version__}/service-info/workspaces/{{" \
|
|
64
64
|
f"name}}/resources/?backend={{backend}} "
|
|
65
65
|
DRS_UPLOAD_URL = f"https://{API_HOSTNAME}/ga4gh/drs/{__version__}/objects/upload/?backend={{backend}}"
|
|
66
|
+
DRS_CONTENTS_ACCESS_URL = f"https://{API_HOSTNAME}/ga4gh/drs/{__version__}/contents/{{object_id}}/access/"
|
|
66
67
|
|
|
67
68
|
@lru_cache(maxsize=16)
|
|
68
69
|
def root_path(self, workspace_name: str) -> str:
|
|
@@ -346,3 +347,19 @@ class DRSregister:
|
|
|
346
347
|
checksums=obj.get('checksums')
|
|
347
348
|
)._asdict()
|
|
348
349
|
return results
|
|
350
|
+
|
|
351
|
+
def create_download_link(self, drs_id: str):
|
|
352
|
+
"""
|
|
353
|
+
api: ga4gh/drs/v1/contents/${drs_id}/access/
|
|
354
|
+
:response: download url for the given drs_id
|
|
355
|
+
"""
|
|
356
|
+
try:
|
|
357
|
+
token = BaseAuth.get_token().get("tokens").get("access")
|
|
358
|
+
except KeyError:
|
|
359
|
+
raise KeyError(f"No tokens, Please sign in first!")
|
|
360
|
+
|
|
361
|
+
with requests.post(url=self.DRS_CONTENTS_ACCESS_URL.format(object_id=drs_id),
|
|
362
|
+
headers={"Authorization": f"Bearer {token}"}) as response:
|
|
363
|
+
if response.status_code not in [requests.codes.created]:
|
|
364
|
+
raise requests.HTTPError(f"{response.status_code}: {repr(response.text)}")
|
|
365
|
+
return response.json()
|
|
@@ -347,6 +347,34 @@ class BaseDatahub:
|
|
|
347
347
|
__log(result, output)
|
|
348
348
|
return 0
|
|
349
349
|
|
|
350
|
+
@command
|
|
351
|
+
@argument("workspace",
|
|
352
|
+
type=str,
|
|
353
|
+
positional=False,
|
|
354
|
+
description="Specify the workspace based on the signed in account (required).", )
|
|
355
|
+
@argument("id",
|
|
356
|
+
type=str,
|
|
357
|
+
positional=False,
|
|
358
|
+
description="A DRS object ID (required if there is no self URI).")
|
|
359
|
+
def share_link(self, workspace: str, id: str) -> int:
|
|
360
|
+
"""
|
|
361
|
+
Generate a share link enabling external users to download the DRS object as a zip archive through web
|
|
362
|
+
browsers. The share link expires either after the first successful download or after a period of three months.
|
|
363
|
+
"""
|
|
364
|
+
|
|
365
|
+
if not workspace:
|
|
366
|
+
logging.error("Invalid workspace name")
|
|
367
|
+
cprint("Invalid workspace name", "red")
|
|
368
|
+
return errno.EINVAL
|
|
369
|
+
|
|
370
|
+
api_backend = drs_register().load_register(workspace)
|
|
371
|
+
r = api_backend.create_download_link(drs_id=id)
|
|
372
|
+
|
|
373
|
+
if isinstance(r, int):
|
|
374
|
+
return r
|
|
375
|
+
cprint(r.get('url'), 'yellow')
|
|
376
|
+
return 0
|
|
377
|
+
|
|
350
378
|
@staticmethod
|
|
351
379
|
@async_exception_handler
|
|
352
380
|
async def _download(drs_id: str, dst: str, **kwargs):
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import environ
|
|
2
|
+
|
|
3
|
+
env = environ.Env()
|
|
4
|
+
environ.Env.read_env("/etc/seqslab/cli_apps.env")
|
|
5
|
+
|
|
6
|
+
name = "scr"
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
__version__ = "v3"
|
|
13
|
+
|
|
14
|
+
PRIVATE_NAME = env.str("PRIVATE_NAME", "api")
|
|
15
|
+
API_HOSTNAME = f"{PRIVATE_NAME}.seqslab.net"
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import csv
|
|
3
|
+
import errno
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
import re
|
|
7
|
+
from io import StringIO
|
|
8
|
+
from typing import List
|
|
9
|
+
|
|
10
|
+
from nubia import argument, command, context
|
|
11
|
+
from tabulate import tabulate
|
|
12
|
+
from termcolor import cprint
|
|
13
|
+
|
|
14
|
+
from seqslab.exceptions import exception_handler
|
|
15
|
+
from .internal.common import get_factory
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BaseSCR:
|
|
19
|
+
|
|
20
|
+
@command(aliases=[])
|
|
21
|
+
@argument("reload",
|
|
22
|
+
type=bool,
|
|
23
|
+
positional=False,
|
|
24
|
+
description="Specify whether to force reload system cache for SCR (optional, default = False).")
|
|
25
|
+
def list(self, reload: bool = False) -> int:
|
|
26
|
+
"""
|
|
27
|
+
List all SCR records.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
backend = get_factory().load_resource()
|
|
31
|
+
result = backend.list_scr(reload)
|
|
32
|
+
|
|
33
|
+
if isinstance(result, int):
|
|
34
|
+
return result
|
|
35
|
+
self.__log(result)
|
|
36
|
+
return 0
|
|
37
|
+
|
|
38
|
+
@command(aliases=[])
|
|
39
|
+
@argument("login_server",
|
|
40
|
+
type=str,
|
|
41
|
+
positional=False,
|
|
42
|
+
description="Specify a container registry login endpoint, e.g. docker.io (required).")
|
|
43
|
+
@argument("username",
|
|
44
|
+
type=str,
|
|
45
|
+
positional=False,
|
|
46
|
+
description="Specify the account for this container registry (required).")
|
|
47
|
+
@argument("password",
|
|
48
|
+
type=str,
|
|
49
|
+
positional=False,
|
|
50
|
+
description="Specify the password for this container registry (required).")
|
|
51
|
+
def register(self, login_server: str, username: str, password: str) -> int:
|
|
52
|
+
"""
|
|
53
|
+
Register a container registry to SCR.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
backend = get_factory().load_resource()
|
|
57
|
+
kwargs = {"login_server": login_server, "username": username, "password": password}
|
|
58
|
+
result = backend.register_scr(**kwargs)
|
|
59
|
+
|
|
60
|
+
if isinstance(result, int):
|
|
61
|
+
return result
|
|
62
|
+
self.__log(result)
|
|
63
|
+
return 0
|
|
64
|
+
|
|
65
|
+
@command(aliases=[])
|
|
66
|
+
@argument("id",
|
|
67
|
+
type=str,
|
|
68
|
+
positional=False,
|
|
69
|
+
description="Specify the ID of the SCR (required).")
|
|
70
|
+
@argument("reload",
|
|
71
|
+
type=bool,
|
|
72
|
+
positional=False,
|
|
73
|
+
description="Specify whether to force reload system cache for SCR (optional, default = False).")
|
|
74
|
+
def get(self, id: str, reload: bool = False) -> int:
|
|
75
|
+
"""
|
|
76
|
+
Get an SCR record.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
backend = get_factory().load_resource()
|
|
80
|
+
kwargs = {"id": id, "reload": reload}
|
|
81
|
+
result = backend.get_scr(**kwargs)
|
|
82
|
+
if isinstance(result, int):
|
|
83
|
+
return result
|
|
84
|
+
|
|
85
|
+
auth = base64.b64decode(result.get('authorization')[6:]).decode('utf-8').split(':')
|
|
86
|
+
result['username'] = auth[0]
|
|
87
|
+
result['password'] = auth[1]
|
|
88
|
+
|
|
89
|
+
self.__log(result)
|
|
90
|
+
return 0
|
|
91
|
+
|
|
92
|
+
@command(aliases=[])
|
|
93
|
+
@argument("id",
|
|
94
|
+
type=str,
|
|
95
|
+
positional=False,
|
|
96
|
+
description="Specify the ID of an SCR (required).")
|
|
97
|
+
@argument("username",
|
|
98
|
+
type=str,
|
|
99
|
+
positional=False,
|
|
100
|
+
description="Specify the account for this container registry (optional).")
|
|
101
|
+
@argument("password",
|
|
102
|
+
type=str,
|
|
103
|
+
positional=False,
|
|
104
|
+
description="Specify the password for this container registry (optional).")
|
|
105
|
+
def update(self, id: str, username: str = '', password: str = '') -> int:
|
|
106
|
+
"""
|
|
107
|
+
Update an SCR record.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
backend = get_factory().load_resource()
|
|
111
|
+
kwargs = {}
|
|
112
|
+
if username:
|
|
113
|
+
kwargs['username'] = username
|
|
114
|
+
if password:
|
|
115
|
+
kwargs["password"] = password
|
|
116
|
+
if not kwargs:
|
|
117
|
+
cprint('Nothing to be done due to username and password are not given')
|
|
118
|
+
return 0
|
|
119
|
+
|
|
120
|
+
result = backend.update_scr(id, **kwargs)
|
|
121
|
+
if isinstance(result, int):
|
|
122
|
+
return result
|
|
123
|
+
self.__log(result)
|
|
124
|
+
return 0
|
|
125
|
+
|
|
126
|
+
@command(aliases=[])
|
|
127
|
+
@argument("id",
|
|
128
|
+
type=str,
|
|
129
|
+
positional=False,
|
|
130
|
+
description="Specify the ID of the SCR (required).")
|
|
131
|
+
def deregister(self, id: str) -> int:
|
|
132
|
+
"""
|
|
133
|
+
Deregister an SCR record.
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
backend = get_factory().load_resource()
|
|
137
|
+
kwargs = {}
|
|
138
|
+
result = backend.deregister_scr(id, **kwargs)
|
|
139
|
+
|
|
140
|
+
if isinstance(result, int):
|
|
141
|
+
return result
|
|
142
|
+
self.__log(result)
|
|
143
|
+
return 0
|
|
144
|
+
|
|
145
|
+
@command(aliases=[])
|
|
146
|
+
@argument("id",
|
|
147
|
+
type=str,
|
|
148
|
+
positional=False,
|
|
149
|
+
description="Specify the ID of the SCR (required).")
|
|
150
|
+
@argument("repository",
|
|
151
|
+
type=str,
|
|
152
|
+
positional=False,
|
|
153
|
+
description="Specify the repository name (required).")
|
|
154
|
+
@argument("reload",
|
|
155
|
+
type=bool,
|
|
156
|
+
positional=False,
|
|
157
|
+
description="Specify whether to force reload system cache for SCR (optional, default = False).")
|
|
158
|
+
def repository(self, id: str, repository: str, reload: bool = False) -> int:
|
|
159
|
+
"""
|
|
160
|
+
Get repository.
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
backend = get_factory().load_resource()
|
|
164
|
+
kwargs = {"registry_id": id, "repository_name": repository, "reload": reload}
|
|
165
|
+
result = backend.get_repository(**kwargs)
|
|
166
|
+
if isinstance(result, int):
|
|
167
|
+
return result
|
|
168
|
+
|
|
169
|
+
self.__log(result)
|
|
170
|
+
return 0
|
|
171
|
+
|
|
172
|
+
def __log(self, results):
|
|
173
|
+
self._stdout(results=results, output='json')
|
|
174
|
+
msg = f"List all workspaces."
|
|
175
|
+
logging.info(msg)
|
|
176
|
+
|
|
177
|
+
@staticmethod
|
|
178
|
+
def _stdout(results: List[dict], output: str) -> int:
|
|
179
|
+
"""
|
|
180
|
+
stdout:: support different format [json, tsv, table]
|
|
181
|
+
"""
|
|
182
|
+
if output == "tsv":
|
|
183
|
+
s = StringIO()
|
|
184
|
+
writer = csv.DictWriter(s, fieldnames=list(results[0].keys()))
|
|
185
|
+
writer.writeheader()
|
|
186
|
+
for result in results:
|
|
187
|
+
writer.writerow(result)
|
|
188
|
+
s.seek(0)
|
|
189
|
+
content = s.read().replace(',', '\t')
|
|
190
|
+
cprint(content)
|
|
191
|
+
elif output == 'table':
|
|
192
|
+
table_header = list(results[0].keys())
|
|
193
|
+
table_datas = [result.values() for result in results]
|
|
194
|
+
cprint(tabulate(
|
|
195
|
+
tabular_data=table_datas,
|
|
196
|
+
headers=table_header,
|
|
197
|
+
tablefmt='pipe'
|
|
198
|
+
))
|
|
199
|
+
else:
|
|
200
|
+
cprint(json.dumps(results, indent=4))
|
|
201
|
+
return 0
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@command
|
|
205
|
+
class scr(BaseSCR):
|
|
206
|
+
""" SeqsLab Container Registry SCR commands"""
|
|
207
|
+
|
|
208
|
+
def __init__(self):
|
|
209
|
+
pass
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from typing import TypeVar
|
|
3
|
+
|
|
4
|
+
from nubia import context
|
|
5
|
+
from seqslab.wes.resource.azure import AzureResource
|
|
6
|
+
from seqslab.settings import SCR_RESOURCE_BACKEND
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
Copyright (C) 2022, Atgenomix Incorporated.
|
|
11
|
+
|
|
12
|
+
All Rights Reserved.
|
|
13
|
+
|
|
14
|
+
This program is an unpublished copyrighted work which is proprietary to
|
|
15
|
+
Atgenomix Incorporated and contains confidential information that is not to
|
|
16
|
+
be reproduced or disclosed to any other person or entity without prior
|
|
17
|
+
written consent from Atgenomix, Inc. in each and every instance.
|
|
18
|
+
|
|
19
|
+
Unauthorized reproduction of this program as well as unauthorized
|
|
20
|
+
preparation of derivative works based upon the program or distribution of
|
|
21
|
+
copies by sale, rental, lease or lending are violations of federal copyright
|
|
22
|
+
laws and state trade secret laws, punishable by civil and criminal penalties.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
Resource = TypeVar("Resource", bound=AzureResource)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Factory:
|
|
29
|
+
"""Storage backend factory"""
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def load_resource() -> Resource:
|
|
33
|
+
backend = context.get_context().args.backend
|
|
34
|
+
name = SCR_RESOURCE_BACKEND.get(backend)
|
|
35
|
+
mod, mem = name.rsplit(".", 1)
|
|
36
|
+
__import__(mod)
|
|
37
|
+
module = sys.modules[mod]
|
|
38
|
+
backend_class = getattr(module, mem)
|
|
39
|
+
return backend_class()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
_factory = Factory()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_factory():
|
|
46
|
+
return _factory
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import logging
|
|
3
|
+
from tenacity import retry, wait_fixed, stop_after_attempt
|
|
4
|
+
from seqslab.auth.commands import BaseAuth
|
|
5
|
+
from seqslab import trs, drs, wes, workspace, scr
|
|
6
|
+
from functools import lru_cache
|
|
7
|
+
from nubia import context
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BaseResource:
|
|
11
|
+
logger = logging.getLogger()
|
|
12
|
+
TRS_BASE_URL = f"https://{trs.API_HOSTNAME}/trs/{trs.__version__}"
|
|
13
|
+
TRS_CR_URL = f"{TRS_BASE_URL}/container-registry/"
|
|
14
|
+
TRS_CR_REPO_URL = f"{TRS_CR_URL}{{registry_id}}/repository/{{repository_name}}"
|
|
15
|
+
@staticmethod
|
|
16
|
+
@retry(stop=stop_after_attempt(3), wait=wait_fixed(2), reraise=True)
|
|
17
|
+
def request_wrapper(
|
|
18
|
+
callback,
|
|
19
|
+
url,
|
|
20
|
+
headers,
|
|
21
|
+
status,
|
|
22
|
+
data=None,
|
|
23
|
+
stream=False,
|
|
24
|
+
) -> requests.Response:
|
|
25
|
+
with callback(url, headers=headers, data=data, stream=stream) as r:
|
|
26
|
+
if r.status_code in status:
|
|
27
|
+
return r
|
|
28
|
+
r.raise_for_status()
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
@retry(stop=stop_after_attempt(3), wait=wait_fixed(2), reraise=True)
|
|
32
|
+
@lru_cache(maxsize=16)
|
|
33
|
+
def list_scr(reload: bool):
|
|
34
|
+
ctx = context.get_context()
|
|
35
|
+
backend = ctx.args.backend
|
|
36
|
+
token = BaseAuth.get_token().get("tokens").get("access")
|
|
37
|
+
url = BaseResource.TRS_CR_URL.format(backend=backend) + f'?backend={backend}'
|
|
38
|
+
if reload:
|
|
39
|
+
url += f'&force=true'
|
|
40
|
+
r = BaseResource.request_wrapper(
|
|
41
|
+
callback=requests.get,
|
|
42
|
+
url=BaseResource.TRS_CR_URL.format(backend=backend),
|
|
43
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
44
|
+
status=[requests.codes.ok])
|
|
45
|
+
return r.json()
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
@retry(stop=stop_after_attempt(3), wait=wait_fixed(2), reraise=True)
|
|
49
|
+
def register_scr(**kwargs):
|
|
50
|
+
ctx = context.get_context()
|
|
51
|
+
backend = ctx.args.backend
|
|
52
|
+
token = BaseAuth.get_token().get("tokens").get("access")
|
|
53
|
+
r = BaseResource.request_wrapper(
|
|
54
|
+
callback=requests.post,
|
|
55
|
+
url=BaseResource.TRS_CR_URL.format(backend=backend) + f'?backend={backend}',
|
|
56
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
57
|
+
status=[requests.codes.created],
|
|
58
|
+
data=kwargs)
|
|
59
|
+
return r.json()
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
@retry(stop=stop_after_attempt(3), wait=wait_fixed(2), reraise=True)
|
|
63
|
+
@lru_cache(maxsize=16)
|
|
64
|
+
def get_scr(**kwargs):
|
|
65
|
+
ctx = context.get_context()
|
|
66
|
+
backend = ctx.args.backend
|
|
67
|
+
token = BaseAuth.get_token().get("tokens").get("access")
|
|
68
|
+
url = BaseResource.TRS_CR_URL.format(backend=backend) + f"{kwargs.get('id')}/"
|
|
69
|
+
if kwargs.get('reload'):
|
|
70
|
+
url += '?force=true'
|
|
71
|
+
|
|
72
|
+
r = BaseResource.request_wrapper(
|
|
73
|
+
callback=requests.get,
|
|
74
|
+
url=url,
|
|
75
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
76
|
+
status=[requests.codes.ok])
|
|
77
|
+
return r.json()
|
|
78
|
+
|
|
79
|
+
@staticmethod
|
|
80
|
+
@retry(stop=stop_after_attempt(3), wait=wait_fixed(2), reraise=True)
|
|
81
|
+
def update_scr(scr_id: str, **kwargs):
|
|
82
|
+
ctx = context.get_context()
|
|
83
|
+
backend = ctx.args.backend
|
|
84
|
+
token = BaseAuth.get_token().get("tokens").get("access")
|
|
85
|
+
r = BaseResource.request_wrapper(
|
|
86
|
+
callback=requests.patch,
|
|
87
|
+
url=BaseResource.TRS_CR_URL.format(backend=backend) + f'{scr_id}/',
|
|
88
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
89
|
+
data=kwargs,
|
|
90
|
+
status=[requests.codes.ok])
|
|
91
|
+
return r.json()
|
|
92
|
+
|
|
93
|
+
@staticmethod
|
|
94
|
+
@retry(stop=stop_after_attempt(3), wait=wait_fixed(2), reraise=True)
|
|
95
|
+
def deregister_scr(scr_id: str, **kwargs):
|
|
96
|
+
ctx = context.get_context()
|
|
97
|
+
backend = ctx.args.backend
|
|
98
|
+
token = BaseAuth.get_token().get("tokens").get("access")
|
|
99
|
+
BaseResource.request_wrapper(
|
|
100
|
+
callback=requests.delete,
|
|
101
|
+
url=BaseResource.TRS_CR_URL.format(backend=backend) + f'{scr_id}/',
|
|
102
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
103
|
+
status=[requests.codes.no_content])
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
@retry(stop=stop_after_attempt(3), wait=wait_fixed(2), reraise=True)
|
|
107
|
+
@lru_cache(maxsize=16)
|
|
108
|
+
def get_repository(**kwargs):
|
|
109
|
+
ctx = context.get_context()
|
|
110
|
+
backend = ctx.args.backend
|
|
111
|
+
token = BaseAuth.get_token().get("tokens").get("access")
|
|
112
|
+
url = BaseResource.TRS_CR_REPO_URL.format(
|
|
113
|
+
registry_id=kwargs.get('registry_id'),
|
|
114
|
+
repository_name=kwargs.get('repository_name'),
|
|
115
|
+
)
|
|
116
|
+
if kwargs.get('reload'):
|
|
117
|
+
url += '?force=true'
|
|
118
|
+
|
|
119
|
+
r = BaseResource.request_wrapper(
|
|
120
|
+
callback=requests.get,
|
|
121
|
+
url=url,
|
|
122
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
123
|
+
status=[requests.codes.ok])
|
|
124
|
+
return r.json()
|
|
@@ -271,21 +271,31 @@ class BaseTools:
|
|
|
271
271
|
return 0
|
|
272
272
|
|
|
273
273
|
@command
|
|
274
|
-
@argument("
|
|
274
|
+
@argument("scr_id",
|
|
275
275
|
type=str,
|
|
276
|
-
description="Specify
|
|
277
|
-
|
|
276
|
+
description="Specify SCR ID (required).")
|
|
277
|
+
@argument("repositories",
|
|
278
|
+
type=List[str],
|
|
279
|
+
positional=False,
|
|
280
|
+
description="Specify the repository names you want to query (required).")
|
|
281
|
+
@argument("reload",
|
|
282
|
+
type=bool,
|
|
283
|
+
positional=False,
|
|
284
|
+
description="Specify whether to force reload system cache for SCR (optional, default = False).")
|
|
285
|
+
def images(self, scr_id: str, repositories: List[str] = [], reload: bool = False) -> int:
|
|
278
286
|
"""
|
|
279
|
-
List
|
|
287
|
+
List image tags and details of given repositories in an SCR.
|
|
280
288
|
"""
|
|
281
|
-
if not
|
|
282
|
-
cprint("Enter
|
|
289
|
+
if not repositories:
|
|
290
|
+
cprint("Enter at least one repository", "red")
|
|
283
291
|
return errno.EIO
|
|
284
292
|
try:
|
|
285
|
-
resource = trs_resource().load_resource(
|
|
286
|
-
ret = resource.container_registry(
|
|
293
|
+
resource = trs_resource().load_resource()
|
|
294
|
+
ret = resource.container_registry(scr_id, repositories, reload)
|
|
287
295
|
images_info = TrsImagesTemplate().create(ret)
|
|
288
|
-
cprint(f'
|
|
296
|
+
cprint(f'SCR ID: {scr_id}\n'
|
|
297
|
+
f'Repository list: {repositories}\n'
|
|
298
|
+
f'image list:\n----------------------------------------------', 'yellow')
|
|
289
299
|
for img in images_info:
|
|
290
300
|
cprint(json.dumps(img), 'yellow')
|
|
291
301
|
return 0
|
|
@@ -4,7 +4,7 @@ from abc import ABC
|
|
|
4
4
|
from functools import lru_cache
|
|
5
5
|
from types import TracebackType
|
|
6
6
|
from typing import (
|
|
7
|
-
Union, Any, Optional, NoReturn, Type, NamedTuple, OrderedDict
|
|
7
|
+
Union, Any, Optional, NoReturn, Type, NamedTuple, OrderedDict, List
|
|
8
8
|
)
|
|
9
9
|
|
|
10
10
|
import aiohttp
|
|
@@ -40,11 +40,7 @@ class BaseResource(ABC):
|
|
|
40
40
|
logger = logging.getLogger()
|
|
41
41
|
|
|
42
42
|
TOKENS_KEY = "tokens"
|
|
43
|
-
|
|
44
|
-
TRS_RESOURCES_URL = f"https://{API_HOSTNAME}/trs/{__version__}/service-info/workspaces/{{" \
|
|
45
|
-
f"name}}/resources/?backend={{backend}}"
|
|
46
|
-
TRS_CONTAINER_REGISTRY_URL = f"https://{API_HOSTNAME}/trs/{__version__}/service-info/workspaces/{{" \
|
|
47
|
-
f"name}}/container-registries/?backend={{backend}}"
|
|
43
|
+
TRS_CONTAINER_REGISTRY_URL = f"https://{API_HOSTNAME}/trs/{__version__}/container-registry/{{scr_id}}"
|
|
48
44
|
|
|
49
45
|
class Response(NamedTuple):
|
|
50
46
|
status: int
|
|
@@ -52,11 +48,7 @@ class BaseResource(ABC):
|
|
|
52
48
|
body: Any = None
|
|
53
49
|
attrs: OrderedDict = {}
|
|
54
50
|
|
|
55
|
-
def __init__(self
|
|
56
|
-
"""
|
|
57
|
-
:param workspace: resource group in Azure, or project in GCS
|
|
58
|
-
"""
|
|
59
|
-
self._workspace = workspace
|
|
51
|
+
def __init__(self):
|
|
60
52
|
self.session = aiohttp.ClientSession(raise_for_status=True)
|
|
61
53
|
|
|
62
54
|
def __enter__(self) -> "BaseResource":
|
|
@@ -98,26 +90,15 @@ class BaseResource(ABC):
|
|
|
98
90
|
|
|
99
91
|
@staticmethod
|
|
100
92
|
@retry(stop=stop_after_attempt(3), wait=wait_fixed(5), reraise=True)
|
|
101
|
-
|
|
102
|
-
def workspace(name) -> dict:
|
|
103
|
-
ctx = context.get_context()
|
|
104
|
-
backend = ctx.args.backend
|
|
105
|
-
token = BaseAuth.get_token().get("tokens").get("access")
|
|
106
|
-
url = BaseResource.TRS_RESOURCES_URL.format(name=name, backend=backend)
|
|
107
|
-
with requests.get(url,
|
|
108
|
-
headers={"Authorization": f"Bearer {token}"}) as response:
|
|
109
|
-
if response.status_code not in [requests.codes.ok]:
|
|
110
|
-
raise requests.HTTPError()
|
|
111
|
-
return response.json()
|
|
112
|
-
|
|
113
|
-
@staticmethod
|
|
114
|
-
@retry(stop=stop_after_attempt(3), wait=wait_fixed(5), reraise=True)
|
|
115
|
-
@lru_cache(maxsize=16)
|
|
116
|
-
def container_registry(name) -> dict:
|
|
93
|
+
def container_registry(scr_id: str, repositories: List[str], reload: bool) -> dict:
|
|
117
94
|
ctx = context.get_context()
|
|
118
95
|
backend = ctx.args.backend
|
|
119
96
|
token = BaseAuth.get_token().get("tokens").get("access")
|
|
120
|
-
|
|
97
|
+
qp = f'?force={reload}'
|
|
98
|
+
for repo in repositories:
|
|
99
|
+
qp += f'&repositories={repo}'
|
|
100
|
+
print(BaseResource.TRS_CONTAINER_REGISTRY_URL.format(scr_id=scr_id) + f'{qp}')
|
|
101
|
+
with requests.get(BaseResource.TRS_CONTAINER_REGISTRY_URL.format(scr_id=scr_id) + f'{qp}',
|
|
121
102
|
headers={"Authorization": f"Bearer {token}"}) as response:
|
|
122
103
|
if response.status_code not in [requests.codes.ok]:
|
|
123
104
|
raise requests.HTTPError('{"detail":"Workspace must be in SeqsLab supported Azure resource group.",'
|
|
@@ -29,14 +29,14 @@ class Factory:
|
|
|
29
29
|
"""Storage backend factory"""
|
|
30
30
|
|
|
31
31
|
@staticmethod
|
|
32
|
-
def load_resource(
|
|
32
|
+
def load_resource() -> Resource:
|
|
33
33
|
backend = context.get_context().args.backend
|
|
34
34
|
name = TRS_RESOURCE_BACKEND.get(backend)
|
|
35
35
|
mod, mem = name.rsplit(".", 1)
|
|
36
36
|
__import__(mod)
|
|
37
37
|
module = sys.modules[mod]
|
|
38
38
|
backend_class = getattr(module, mem)
|
|
39
|
-
return backend_class(
|
|
39
|
+
return backend_class()
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
_factory = Factory()
|
|
@@ -180,10 +180,10 @@ class TrsImagesTemplate:
|
|
|
180
180
|
@staticmethod
|
|
181
181
|
def create(container_registry_info: dict) -> dict:
|
|
182
182
|
image_list = []
|
|
183
|
-
cr = container_registry_info.get('login_server')
|
|
183
|
+
cr = container_registry_info.get('login_server', None)
|
|
184
184
|
for repo in container_registry_info.get('repositories', []):
|
|
185
185
|
name = repo.get('name')
|
|
186
|
-
for tag in repo.get('tags'):
|
|
186
|
+
for tag in repo.get('tags', []):
|
|
187
187
|
pass
|
|
188
188
|
image_list.append({
|
|
189
189
|
"image_type": tag.get('type'),
|