rhdl 0.1.0.post1729010231__tar.gz → 0.1.0.post1729158829__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.

Files changed (28) hide show
  1. {rhdl-0.1.0.post1729010231/rhdl.egg-info → rhdl-0.1.0.post1729158829}/PKG-INFO +1 -1
  2. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829/rhdl.egg-info}/PKG-INFO +1 -1
  3. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/rhdlcli/api.py +39 -51
  4. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/rhdlcli/cli.py +10 -7
  5. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/rhdlcli/downloader.py +10 -11
  6. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/rhdlcli/main.py +3 -3
  7. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/rhdlcli/options.py +2 -2
  8. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/rhdlcli/validator.py +2 -2
  9. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/tests/test_cli.py +2 -2
  10. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/tests/test_options.py +2 -2
  11. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/tests/test_validator.py +2 -2
  12. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/LICENSE +0 -0
  13. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/MANIFEST.in +0 -0
  14. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/requirements.txt +0 -0
  15. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/rhdl.egg-info/SOURCES.txt +0 -0
  16. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/rhdl.egg-info/dependency_links.txt +0 -0
  17. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/rhdl.egg-info/entry_points.txt +0 -0
  18. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/rhdl.egg-info/requires.txt +0 -0
  19. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/rhdl.egg-info/top_level.txt +0 -0
  20. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/rhdlcli/__init__.py +0 -0
  21. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/rhdlcli/files.py +0 -0
  22. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/rhdlcli/fs.py +0 -0
  23. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/rhdlcli/stats.py +0 -0
  24. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/rhdlcli/version.py +0 -0
  25. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/setup.cfg +0 -0
  26. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/setup.py +0 -0
  27. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/tests/test_files.py +0 -0
  28. {rhdl-0.1.0.post1729010231 → rhdl-0.1.0.post1729158829}/tests/test_stats.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rhdl
3
- Version: 0.1.0.post1729010231
3
+ Version: 0.1.0.post1729158829
4
4
  Summary: RHDL CLI module
5
5
  Home-page: https://gitlab.cee.redhat.com/rhdl/rhdl
6
6
  Author: Distributed CI team
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rhdl
3
- Version: 0.1.0.post1729010231
3
+ Version: 0.1.0.post1729158829
4
4
  Summary: RHDL CLI module
5
5
  Home-page: https://gitlab.cee.redhat.com/rhdl/rhdl
6
6
  Author: Distributed CI team
@@ -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 threading import local
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
- def build_hmac_context(component_id, base_url, access_key, secret_key):
21
- class HmacContext(object):
22
- """
23
- S3Context builds a request Session() object configured to download
24
- files from S3 through a redirection from RHDL API.
25
- """
26
-
27
- def __init__(self, base_url, access_key, secret_key):
28
- self.threadlocal = local()
29
- self.session_auth = HmacAuthBase(
30
- access_key, secret_key, service="api", region="us-east-1"
31
- )
32
- self.base_url = base_url
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(context):
79
+ def get_files_list(session):
92
80
  print("Download file list, it may take a few seconds")
93
- r = context.get("rhdl_files_list.json")
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(context, download_folder, file, i, nb_files):
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 = context.get(relative_file_path)
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(context, download_folder, 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
- (context, download_folder, file, i, nb_files)
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
- # download the latest RHEL-9.4 compose in the current folder
10
- rhdl download RHEL-9.4
9
+ # Login to RHDL
10
+ rhdl login
11
11
 
12
- # download in /tmp/repo folder
13
- rhdl download RHEL-9.4 --destination /tmp/repo
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 COMPOSE_ID [OPTIONS]",
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
- "compose_id", metavar="COMPOSE_ID", nargs="?", help="Compose ID"
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
- build_hmac_context,
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
- context = build_hmac_context(
34
- component_id=options["compose_id"],
35
- base_url=options["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(context)
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, configure
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"] == "configure":
39
- configure(options)
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 configure(options):
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 configure` or set env variables.")
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", "configure"]
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 test_parse_arguments_compose_id_argument():
26
- assert parse_arguments(["download", "RHEL-9.4"])["compose_id"] == "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
- "compose_id": "RHEL-9.4",
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(["configure"])
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 test_arguments_are_valid_with_configure_command():
38
- assert arguments_are_valid(parse_arguments(["configure"]))
37
+ def test_arguments_are_valid_with_login_command():
38
+ assert arguments_are_valid(parse_arguments(["login"]))