rhdl 0.1.0.post1729010231__tar.gz → 0.1.0.post1729151260__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.
Potentially problematic release.
This version of rhdl might be problematic. Click here for more details.
- {rhdl-0.1.0.post1729010231/rhdl.egg-info → rhdl-0.1.0.post1729151260}/PKG-INFO +1 -1
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260/rhdl.egg-info}/PKG-INFO +1 -1
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/rhdlcli/api.py +39 -51
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/rhdlcli/cli.py +10 -7
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/rhdlcli/downloader.py +10 -11
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/rhdlcli/main.py +3 -3
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/rhdlcli/options.py +2 -2
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/rhdlcli/validator.py +2 -2
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/tests/test_cli.py +2 -2
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/tests/test_options.py +2 -2
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/tests/test_validator.py +2 -2
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/LICENSE +0 -0
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/MANIFEST.in +0 -0
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/requirements.txt +0 -0
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/rhdl.egg-info/SOURCES.txt +0 -0
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/rhdl.egg-info/dependency_links.txt +0 -0
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/rhdl.egg-info/entry_points.txt +0 -0
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/rhdl.egg-info/requires.txt +0 -0
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/rhdl.egg-info/top_level.txt +0 -0
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/rhdlcli/__init__.py +0 -0
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/rhdlcli/files.py +0 -0
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/rhdlcli/fs.py +0 -0
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/rhdlcli/stats.py +0 -0
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/rhdlcli/version.py +0 -0
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/setup.cfg +0 -0
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/setup.py +0 -0
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/tests/test_files.py +0 -0
- {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729151260}/tests/test_stats.py +0 -0
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
import os
|
|
4
4
|
import requests
|
|
5
|
+
import sys
|
|
5
6
|
import time
|
|
6
7
|
|
|
7
8
|
from concurrent.futures import ThreadPoolExecutor
|
|
8
9
|
from functools import wraps
|
|
9
|
-
from
|
|
10
|
-
|
|
10
|
+
from urllib.parse import urljoin
|
|
11
11
|
from rhdlcli.fs import create_parent_dir
|
|
12
12
|
from rhdllib.auth import HmacAuthBase
|
|
13
13
|
|
|
@@ -17,49 +17,19 @@ TEN_SECONDS = 10
|
|
|
17
17
|
REQUESTS_TIMEOUT = (FIVE_SECONDS, TEN_SECONDS)
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
@property
|
|
35
|
-
def session(self):
|
|
36
|
-
"""
|
|
37
|
-
Each thread must have its own `requests.Session()` instance.
|
|
38
|
-
`session` is a property looking for `session` object in a
|
|
39
|
-
thread-local context.
|
|
40
|
-
"""
|
|
41
|
-
if not hasattr(self.threadlocal, "session"):
|
|
42
|
-
session = requests.Session()
|
|
43
|
-
session.auth = self.session_auth
|
|
44
|
-
session.stream = True
|
|
45
|
-
self.threadlocal.session = session
|
|
46
|
-
return self.threadlocal.session
|
|
47
|
-
|
|
48
|
-
def get(self, relpath):
|
|
49
|
-
return self.session.get(
|
|
50
|
-
"%s/%s" % (self.base_url, relpath.lstrip("/")), timeout=REQUESTS_TIMEOUT
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
def head(self, relpath):
|
|
54
|
-
# allow_redirects must be set to True to get the final HTTP status
|
|
55
|
-
return self.session.head(
|
|
56
|
-
"%s/%s" % (self.base_url, relpath.lstrip("/")),
|
|
57
|
-
allow_redirects=True,
|
|
58
|
-
timeout=REQUESTS_TIMEOUT,
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
base_url = "%s/api/v1/components/%s/files" % (base_url, component_id)
|
|
62
|
-
return HmacContext(base_url, access_key, secret_key)
|
|
20
|
+
class HmacSession(requests.Session):
|
|
21
|
+
def __init__(self, base_url, access_key, secret_key):
|
|
22
|
+
self.base_url = base_url
|
|
23
|
+
self.access_key = access_key
|
|
24
|
+
self.secret_key = secret_key
|
|
25
|
+
super(HmacSession, self).__init__()
|
|
26
|
+
|
|
27
|
+
def request(self, method, url, *args, **kwargs):
|
|
28
|
+
url = urljoin(self.base_url, url)
|
|
29
|
+
auth = HmacAuthBase(
|
|
30
|
+
self.access_key, self.secret_key, service="api", region="us-east-1"
|
|
31
|
+
)
|
|
32
|
+
return super(HmacSession, self).request(method, url, auth=auth, *args, **kwargs)
|
|
63
33
|
|
|
64
34
|
|
|
65
35
|
def retry(tries=3, delay=2, multiplier=2):
|
|
@@ -87,16 +57,34 @@ def retry(tries=3, delay=2, multiplier=2):
|
|
|
87
57
|
return decorated_retry
|
|
88
58
|
|
|
89
59
|
|
|
60
|
+
def get_component(options):
|
|
61
|
+
session = HmacSession(
|
|
62
|
+
base_url=options["base_url"],
|
|
63
|
+
access_key=options["access_key"],
|
|
64
|
+
secret_key=options["secret_key"],
|
|
65
|
+
)
|
|
66
|
+
compose = options["compose"]
|
|
67
|
+
r = session.get(
|
|
68
|
+
f"/api/v1/components?compose_id_startswith={compose}&state=active&sort=-released_at"
|
|
69
|
+
)
|
|
70
|
+
r.raise_for_status()
|
|
71
|
+
components = r.json()["components"]
|
|
72
|
+
if len(components) <= 0:
|
|
73
|
+
print(f"No component found with compose id starting with {compose}")
|
|
74
|
+
sys.exit(1)
|
|
75
|
+
return components[0]
|
|
76
|
+
|
|
77
|
+
|
|
90
78
|
@retry()
|
|
91
|
-
def get_files_list(
|
|
79
|
+
def get_files_list(session):
|
|
92
80
|
print("Download file list, it may take a few seconds")
|
|
93
|
-
r =
|
|
81
|
+
r = session.get("rhdl_files_list.json")
|
|
94
82
|
r.raise_for_status()
|
|
95
83
|
return r.json()
|
|
96
84
|
|
|
97
85
|
|
|
98
86
|
@retry()
|
|
99
|
-
def download_file(
|
|
87
|
+
def download_file(session, download_folder, file, i, nb_files):
|
|
100
88
|
start_time = time.monotonic()
|
|
101
89
|
relative_file_path = os.path.join(file["path"], file["name"])
|
|
102
90
|
destination = os.path.join(download_folder, relative_file_path)
|
|
@@ -105,7 +93,7 @@ def download_file(context, download_folder, file, i, nb_files):
|
|
|
105
93
|
return
|
|
106
94
|
print(f"({i + 1}/{nb_files}): < Getting {destination}")
|
|
107
95
|
create_parent_dir(destination)
|
|
108
|
-
r =
|
|
96
|
+
r = session.get(relative_file_path)
|
|
109
97
|
r.raise_for_status()
|
|
110
98
|
with open(destination, "wb") as f:
|
|
111
99
|
for chunk in r.iter_content(chunk_size=1024 * 1024):
|
|
@@ -115,14 +103,14 @@ def download_file(context, download_folder, file, i, nb_files):
|
|
|
115
103
|
return file
|
|
116
104
|
|
|
117
105
|
|
|
118
|
-
def download_files(
|
|
106
|
+
def download_files(session, download_folder, files):
|
|
119
107
|
nb_files = len(files)
|
|
120
108
|
with ThreadPoolExecutor(max_workers=10) as executor:
|
|
121
109
|
for file in executor.map(
|
|
122
110
|
download_file,
|
|
123
111
|
*zip(
|
|
124
112
|
*[
|
|
125
|
-
(
|
|
113
|
+
(session, download_folder, file, i, nb_files)
|
|
126
114
|
for i, file in enumerate(files)
|
|
127
115
|
]
|
|
128
116
|
),
|
|
@@ -6,11 +6,14 @@ from rhdlcli.version import __version__
|
|
|
6
6
|
|
|
7
7
|
EXAMPLES = """
|
|
8
8
|
examples:
|
|
9
|
-
#
|
|
10
|
-
rhdl
|
|
9
|
+
# Login to RHDL
|
|
10
|
+
rhdl login
|
|
11
11
|
|
|
12
|
-
# download in
|
|
13
|
-
rhdl download RHEL-
|
|
12
|
+
# download lastest RHEL-10 compose in the current folder
|
|
13
|
+
rhdl download RHEL-10
|
|
14
|
+
|
|
15
|
+
# download latest RHEL-10 in /tmp/repo folder
|
|
16
|
+
rhdl download RHEL-10 --destination /tmp/repo
|
|
14
17
|
"""
|
|
15
18
|
|
|
16
19
|
COPYRIGHT = """
|
|
@@ -50,7 +53,7 @@ class IncludeExcludeAction(argparse.Action):
|
|
|
50
53
|
|
|
51
54
|
def parse_arguments(arguments):
|
|
52
55
|
parser = argparse.ArgumentParser(
|
|
53
|
-
usage="rhdl COMMAND
|
|
56
|
+
usage="rhdl COMMAND [OPTIONS]",
|
|
54
57
|
description="Download the latest RHEL compose easily.",
|
|
55
58
|
epilog=EXAMPLES + COPYRIGHT,
|
|
56
59
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
@@ -59,10 +62,10 @@ def parse_arguments(arguments):
|
|
|
59
62
|
"command",
|
|
60
63
|
metavar="COMMAND",
|
|
61
64
|
nargs="?",
|
|
62
|
-
help="Available commands: download",
|
|
65
|
+
help="Available commands: download, login",
|
|
63
66
|
)
|
|
64
67
|
parser.add_argument(
|
|
65
|
-
"
|
|
68
|
+
"compose", metavar="COMPOSE", nargs="?", help="Compose ID or NAME"
|
|
66
69
|
)
|
|
67
70
|
parser.add_argument(
|
|
68
71
|
"-d",
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
import os
|
|
4
|
-
|
|
4
|
+
from urllib.parse import urljoin
|
|
5
5
|
from rhdlcli.api import (
|
|
6
|
+
get_component,
|
|
6
7
|
get_files_list,
|
|
7
8
|
download_files,
|
|
8
|
-
|
|
9
|
+
HmacSession,
|
|
9
10
|
)
|
|
10
11
|
from rhdlcli.stats import check_download_folder_has_enough_space
|
|
11
12
|
from rhdlcli.files import get_files_to_remove, filter_files
|
|
@@ -30,21 +31,19 @@ def clean_download_folder(download_folder, files):
|
|
|
30
31
|
|
|
31
32
|
|
|
32
33
|
def download_component(options):
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
base_url=
|
|
34
|
+
component = get_component(options)
|
|
35
|
+
session = HmacSession(
|
|
36
|
+
base_url=urljoin(
|
|
37
|
+
options["base_url"], f"/api/v1/components/{component['compose_id']}/files/"
|
|
38
|
+
),
|
|
36
39
|
access_key=options["access_key"],
|
|
37
40
|
secret_key=options["secret_key"],
|
|
38
41
|
)
|
|
39
|
-
files_list = get_files_list(
|
|
40
|
-
|
|
42
|
+
files_list = get_files_list(session)
|
|
41
43
|
files = files_list["files"]
|
|
42
44
|
files = filter_files(files, options["include_and_exclude"])
|
|
43
|
-
|
|
44
45
|
download_folder = options["destination"]
|
|
45
46
|
clean_download_folder(download_folder, files)
|
|
46
47
|
check_download_folder_has_enough_space(download_folder, files)
|
|
47
|
-
|
|
48
|
-
download_files(context, download_folder, files)
|
|
49
|
-
|
|
48
|
+
download_files(session, download_folder, files)
|
|
50
49
|
recreate_symlinks(download_folder, files_list["symlinks"])
|
|
@@ -6,7 +6,7 @@ import sys
|
|
|
6
6
|
|
|
7
7
|
from rhdlcli.cli import parse_arguments
|
|
8
8
|
from rhdlcli.downloader import download_component
|
|
9
|
-
from rhdlcli.options import build_options,
|
|
9
|
+
from rhdlcli.options import build_options, login
|
|
10
10
|
from rhdlcli.validator import (
|
|
11
11
|
exit_if_arguments_invalid,
|
|
12
12
|
exit_if_credentials_invalid,
|
|
@@ -35,8 +35,8 @@ def main():
|
|
|
35
35
|
cwd = os.path.dirname(sys.argv[0])
|
|
36
36
|
env_variables = dict(os.environ)
|
|
37
37
|
options = build_options(cwd, arguments, env_variables)
|
|
38
|
-
if options["command"] == "
|
|
39
|
-
|
|
38
|
+
if options["command"] == "login":
|
|
39
|
+
login(options)
|
|
40
40
|
return
|
|
41
41
|
exit_if_credentials_invalid(options)
|
|
42
42
|
download_component(options)
|
|
@@ -39,14 +39,14 @@ def build_options(cwd, arguments, env_variables):
|
|
|
39
39
|
return options
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
def
|
|
42
|
+
def login(options):
|
|
43
43
|
app_config_path = options["app_config_path"]
|
|
44
44
|
if not os.path.exists(app_config_path):
|
|
45
45
|
os.makedirs(app_config_path)
|
|
46
46
|
|
|
47
47
|
access_key = input("RHDL Access Key [None]: ")
|
|
48
48
|
secret_key = input("RHDL Secret Key [None]:")
|
|
49
|
-
default_base_url = "https://api.distributed-ci.io"
|
|
49
|
+
default_base_url = "https://api.rhdl.distributed-ci.io"
|
|
50
50
|
base_url = input(f"RHDL server host [{default_base_url}]:") or default_base_url
|
|
51
51
|
credentials = {
|
|
52
52
|
"base_url": base_url,
|
|
@@ -10,13 +10,13 @@ def credentials_are_defined(options):
|
|
|
10
10
|
|
|
11
11
|
def exit_if_credentials_invalid(options):
|
|
12
12
|
if not credentials_are_defined(options):
|
|
13
|
-
print("Credentials are invalid. Run `rhdl
|
|
13
|
+
print("Credentials are invalid. Run `rhdl login` or set env variables.")
|
|
14
14
|
sys.exit(1)
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def arguments_are_valid(arguments):
|
|
18
18
|
command = arguments["command"]
|
|
19
|
-
AVAILABLE_COMMANDS = ["download", "
|
|
19
|
+
AVAILABLE_COMMANDS = ["download", "login"]
|
|
20
20
|
if arguments["command"] not in AVAILABLE_COMMANDS:
|
|
21
21
|
print(
|
|
22
22
|
f"Invalid command {command}. Available commands are: {','.join(AVAILABLE_COMMANDS)}"
|
|
@@ -22,8 +22,8 @@ def test_parse_arguments_command_argument():
|
|
|
22
22
|
assert parse_arguments(["download", "RHEL-9.4"])["command"] == "download"
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
def
|
|
26
|
-
assert parse_arguments(["download", "RHEL-9.4"])["
|
|
25
|
+
def test_parse_arguments_compose_argument():
|
|
26
|
+
assert parse_arguments(["download", "RHEL-9.4"])["compose"] == "RHEL-9.4"
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
def test_parse_arguments_destination_argument():
|
|
@@ -14,7 +14,7 @@ def test_build_options():
|
|
|
14
14
|
options = build_options(cwd, arguments, env_variables)
|
|
15
15
|
assert options == {
|
|
16
16
|
"command": "download",
|
|
17
|
-
"
|
|
17
|
+
"compose": "RHEL-9.4",
|
|
18
18
|
"destination": "/tmp/repo",
|
|
19
19
|
"app_config_path": ANY,
|
|
20
20
|
"base_url": "",
|
|
@@ -45,7 +45,7 @@ def test_build_options_transform_relative_folder_into_absolute_folder():
|
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
def test_build_options_read_XDG_CONFIG_HOME_env_variable_for_app_config_path():
|
|
48
|
-
arguments = parse_arguments(["
|
|
48
|
+
arguments = parse_arguments(["login"])
|
|
49
49
|
cwd = "/tmp"
|
|
50
50
|
env_variables = {"RHDL_BASE_URL": "", "RHDL_ACCESS_KEY": "", "RHDL_SECRET_KEY": ""}
|
|
51
51
|
assert build_options(cwd, arguments, env_variables)["app_config_path"].endswith(
|
|
@@ -34,5 +34,5 @@ def test_credentials_are_defined():
|
|
|
34
34
|
)
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
def
|
|
38
|
-
assert arguments_are_valid(parse_arguments(["
|
|
37
|
+
def test_arguments_are_valid_with_login_command():
|
|
38
|
+
assert arguments_are_valid(parse_arguments(["login"]))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|