Skip to content

Commit 2b345c2

Browse files
jajreidyjlibrova
authored andcommitted
Fixes artifact location to be able to be created by args
1 parent edb8e9f commit 2b345c2

File tree

4 files changed

+405
-226
lines changed

4 files changed

+405
-226
lines changed

python_scripts/pulp_client.py

Lines changed: 80 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
# Local imports
2525
from pulp_utils import (
2626
DEFAULT_TIMEOUT, DEFAULT_TASK_TIMEOUT,
27-
create_session_with_retry, validate_file_path
27+
create_session_with_retry, validate_file_path,
28+
sanitize_error_message, read_file_with_base64_decode
2829
)
2930

3031
# Optional imports with fallback
@@ -142,8 +143,8 @@ def _retrieve_token(self) -> None:
142143
self._access_token = token["access_token"]
143144

144145
except requests.RequestException as e:
145-
logging.error("Failed to retrieve OAuth2 token: %s", e)
146-
logging.error("Traceback: %s", traceback.format_exc())
146+
logging.error("Failed to retrieve OAuth2 token: %s", sanitize_error_message(str(e)))
147+
logging.error("Traceback: %s", sanitize_error_message(traceback.format_exc()))
147148
raise
148149

149150
@property
@@ -265,8 +266,8 @@ def _chunked_get(self, url: str, params: Optional[Dict[str, Any]] = None,
265266
all_results.extend(chunk_data['results'])
266267

267268
except Exception as e:
268-
logging.error("Failed to process chunk %d: %s", i, e)
269-
logging.error("Traceback: %s", traceback.format_exc())
269+
logging.error("Failed to process chunk %d: %s", i, sanitize_error_message(str(e)))
270+
logging.error("Traceback: %s", sanitize_error_message(traceback.format_exc()))
270271
raise
271272

272273
# Create aggregated response
@@ -291,10 +292,64 @@ def create_from_config_file(cls, path: Optional[str] = None, domain: Optional[st
291292
"""
292293
Create a Pulp client from a standard configuration file that is
293294
used by the `pulp` CLI tool.
295+
296+
Args:
297+
path: Path to the config file (default: ~/.config/pulp/cli.toml)
298+
domain: Optional domain override
299+
namespace: Optional namespace override
300+
301+
Returns:
302+
PulpClient instance
303+
304+
Raises:
305+
FileNotFoundError: If the config file doesn't exist
306+
ValueError: If the config file is malformed or missing required sections
294307
"""
295308
config_path = Path(path or "~/.config/pulp/cli.toml").expanduser()
296-
with open(config_path, "rb") as fp:
297-
config = tomllib.load(fp)
309+
310+
# Check if config file exists
311+
if not config_path.exists():
312+
logging.error("Pulp config file not found: %s", config_path)
313+
raise FileNotFoundError(f"Pulp config file not found: {config_path}")
314+
315+
try:
316+
# Read and decode base64 if encoded
317+
_, decoded_content = read_file_with_base64_decode(str(config_path))
318+
config = tomllib.loads(decoded_content.decode('utf-8'))
319+
except OSError as e:
320+
# File system errors (FileNotFoundError, PermissionError, etc.)
321+
logging.error("Failed to read config file: %s", config_path)
322+
logging.error("Error: %s", sanitize_error_message(str(e)))
323+
raise FileNotFoundError(f"Pulp config file not found or cannot be read: {config_path}") from e
324+
except (ValueError, KeyError) as e:
325+
# TOML parsing errors (TOMLDecodeError is a subclass of ValueError in tomllib/tomli)
326+
error_msg = str(e)
327+
sanitized_error = sanitize_error_message(error_msg)
328+
error_type = type(e).__name__
329+
330+
if "TOMLDecodeError" in error_type or "Expected '='" in error_msg:
331+
logging.error("Failed to parse TOML config file: %s", config_path)
332+
logging.error("The config file appears to be malformed.")
333+
logging.error("Error type: %s", error_type)
334+
logging.error("Error message: %s", sanitized_error)
335+
logging.error("Please check the TOML syntax in the config file.")
336+
logging.error("Common issues:")
337+
logging.error(" - Missing '=' after a key in a key/value pair")
338+
logging.error(" - Incomplete key-value pairs")
339+
logging.error(" - Trailing syntax errors at the end of the file")
340+
logging.error(" - Invalid TOML structure")
341+
raise ValueError(f"Malformed TOML config file: {sanitized_error}") from e
342+
343+
logging.error("Failed to load Pulp client from config file: %s", config_path)
344+
logging.error("Error type: %s", error_type)
345+
logging.error("Error message: %s", sanitized_error)
346+
raise ValueError(f"Failed to load config file: {sanitized_error}") from e
347+
348+
# Validate that config has required 'cli' section
349+
if "cli" not in config:
350+
logging.error("Config file missing required 'cli' section: %s", config_path)
351+
raise ValueError(f"Config file missing required 'cli' section: {config_path}")
352+
298353
return cls(config["cli"], domain, namespace)
299354

300355
@property
@@ -415,26 +470,30 @@ def _check_response(self, response: Response, operation: str = "request") -> Non
415470
"""Check if a response is successful, raise exception if not."""
416471
if not response.ok:
417472
logging.error("Failed to %s: %s - %s", operation, response.status_code,
418-
response.text)
473+
sanitize_error_message(response.text))
419474

420475
# Enhanced error logging for server errors
421476
if response.status_code >= 500:
422477
logging.error("Server error details:")
423478
logging.error(" Status Code: %s", response.status_code)
424-
logging.error(" Headers: %s", dict(response.headers))
479+
# Sanitize headers to prevent credential leakage
480+
headers_dict = dict(response.headers)
481+
logging.error(" Headers: %s", sanitize_error_message(str(headers_dict)))
425482
logging.error(" URL: %s", response.url)
426483
logging.error(" Request Method: %s",
427484
response.request.method if response.request else "Unknown")
428485

429486
# Try to parse error details
430487
try:
431488
error_data = response.json()
432-
logging.error(" Error Data: %s", error_data)
489+
logging.error(" Error Data: %s", sanitize_error_message(str(error_data)))
433490
except (ValueError, json.JSONDecodeError):
434-
logging.error(" Raw Response: %s", response.text)
491+
logging.error(" Raw Response: %s", sanitize_error_message(response.text))
435492

493+
# Sanitize error message in exception
494+
sanitized_text = sanitize_error_message(response.text)
436495
raise requests.RequestException(
437-
f"Failed to {operation}: {response.status_code} - {response.text}"
496+
f"Failed to {operation}: {response.status_code} - {sanitized_text}"
438497
)
439498

440499
def check_response(self, response: Response, operation: str = "request") -> None:
@@ -498,12 +557,12 @@ def upload_content(self, file_path: str, labels: Dict[str, str],
498557
return response.json()["pulp_href"]
499558

500559
except requests.RequestException as e:
501-
logging.error("Request failed for %s %s: %s", file_type, file_path, e)
502-
logging.error("Traceback: %s", traceback.format_exc())
560+
logging.error("Request failed for %s %s: %s", file_type, file_path, sanitize_error_message(str(e)))
561+
logging.error("Traceback: %s", sanitize_error_message(traceback.format_exc()))
503562
raise
504563
except Exception as e:
505-
logging.error("Unexpected error uploading %s %s: %s", file_type, file_path, e)
506-
logging.error("Traceback: %s", traceback.format_exc())
564+
logging.error("Unexpected error uploading %s %s: %s", file_type, file_path, sanitize_error_message(str(e)))
565+
logging.error("Traceback: %s", sanitize_error_message(traceback.format_exc()))
507566
raise
508567

509568
def create_file_content(self, repository: str, content_or_path: Union[str, Path],
@@ -610,7 +669,7 @@ def wait_for_finished_task(self, task: str, timeout: int = DEFAULT_TASK_TIMEOUT)
610669
response = self._get_task(task)
611670

612671
if not response.ok:
613-
logging.error("Error processing task %s: %s", task, response.text)
672+
logging.error("Error processing task %s: %s", task, sanitize_error_message(response.text))
614673
return response
615674

616675
task_state = response.json().get("state")
@@ -708,9 +767,10 @@ def gather_content_data(self, build_id: str,
708767
logging.debug("Content response JSON: %s", resp_json)
709768
content_results = resp_json["results"]
710769
except Exception as e:
711-
logging.error("Failed to get content by build ID: %s", e)
712-
logging.error("Response text: %s", resp.text if 'resp' in locals() else "No response")
713-
logging.error("Traceback: %s", traceback.format_exc())
770+
logging.error("Failed to get content by build ID: %s", sanitize_error_message(str(e)))
771+
resp_text = resp.text if 'resp' in locals() else "No response"
772+
logging.error("Response text: %s", sanitize_error_message(resp_text))
773+
logging.error("Traceback: %s", sanitize_error_message(traceback.format_exc()))
714774
raise
715775

716776
if not content_results:

0 commit comments

Comments
 (0)