Coverage for ocp_resources/utils/utils.py: 66%
64 statements
« prev ^ index » next coverage.py v7.10.1, created at 2025-07-29 12:31 +0300
« prev ^ index » next coverage.py v7.10.1, created at 2025-07-29 12:31 +0300
1import yaml
2from simple_logger.logger import get_logger
4LOGGER = get_logger(name=__name__)
7def skip_existing_resource_creation_teardown(resource, export_str, user_exported_args, check_exists=True):
8 """
9 Args:
10 resource (Resource): Resource to match against.
11 export_str (str): The user export str. (REUSE_IF_RESOURCE_EXISTS or SKIP_RESOURCE_TEARDOWN)
12 user_exported_args (str): Value of export_str. (os.environ.get)
13 check_exists (bool): Check if resource exists before return. (applied only for REUSE_IF_RESOURCE_EXISTS)
15 Returns:
16 Resource or None: If resource match.
17 """
19 def _return_resource(_resource, _check_exists, _msg):
20 """
21 Return the resource if exists when got _check_exists else return None.
22 If _check_exists=False returns the resource.
23 """
24 if not _check_exists: # In case of SKIP_RESOURCE_TEARDOWN
25 return _resource
27 elif _resource.exists: # In case of REUSE_IF_RESOURCE_EXISTS
28 LOGGER.warning(_msg)
29 return _resource
31 resource.to_dict()
32 resource_name = resource.res["metadata"]["name"]
33 resource_namespace = resource.res["metadata"].get("namespace")
34 skip_create_warn_msg = (
35 f"Skip resource {resource.kind} {resource_name} creation, using existing one."
36 f" Got {export_str}={user_exported_args}"
37 )
38 user_args = yaml.safe_load(stream=user_exported_args)
39 if resource.kind in user_args:
40 _resource_args = user_args[resource.kind]
41 if not _resource_args:
42 # Match only by kind, user didn't send name and/or namespace.
43 return _return_resource(
44 _resource=resource,
45 _check_exists=check_exists,
46 _msg=skip_create_warn_msg,
47 )
49 for _name, _namespace in _resource_args.items():
50 if resource_name == _name and (resource_namespace == _namespace or not (resource_namespace and _namespace)):
51 return _return_resource(
52 _resource=resource,
53 _check_exists=check_exists,
54 _msg=skip_create_warn_msg,
55 )
58def convert_camel_case_to_snake_case(name: str) -> str:
59 """
60 Converts a camel case string to snake case.
62 Args:
63 name (str): The camel case string to convert.
65 Returns:
66 str: The snake case representation of the input string.
68 Examples:
69 >>> convert_camel_case_to_snake_case(string_="allocateLoadBalancerNodePorts")
70 'allocate_load_balancer_node_ports'
71 >>> convert_camel_case_to_snake_case(string_="clusterIPs")
72 'cluster_ips'
73 >>> convert_camel_case_to_snake_case(string_="additionalCORSAllowedOS")
74 'additional_cors_allowed_os'
76 Notes:
77 - This function assumes that the input string adheres to camel case conventions.
78 - If the input string contains acronyms (e.g., "XMLHttpRequest"), they will be treated as separate words
79 (e.g., "xml_http_request").
80 - The function handles both single-word camel case strings (e.g., "Service") and multi-word camel case strings
81 (e.g., "myCamelCaseString").
82 """
83 import re
85 do_not_process_list = ["oauth", "kubevirt"]
87 # If the input string is in the do_not_proccess_list, return it as it is.
88 if name.lower() in do_not_process_list:
89 return name.lower()
91 formatted_str: str = ""
93 if name.islower():
94 return name
96 # For single words, e.g "Service" or "SERVICE"
97 if name.istitle() or name.isupper():
98 return name.lower()
100 # To decide if underscore is needed before a char, keep the last char format.
101 # If previous char is uppercase, underscode should not be added. Also applied for the first char in the string.
102 last_capital_char: bool | None = None
104 # To decide if there are additional words ahead; if found, there is at least one more word ahead, else this is the
105 # last word. Underscore should be added before it and all chars from here should be lowercase.
106 following_capital_chars: re.Match | None = None
108 str_len_for_idx_check = len(name) - 1
110 for idx, char in enumerate(name):
111 # If lower case, append to formatted string
112 if char.islower():
113 formatted_str += char
114 last_capital_char = False
116 # If first char is uppercase
117 elif idx == 0:
118 formatted_str += char.lower()
119 last_capital_char = True
121 else:
122 if idx < str_len_for_idx_check:
123 following_capital_chars = re.search(r"[A-Z]", "".join(name[idx + 1 :]))
124 if last_capital_char:
125 if idx < str_len_for_idx_check and name[idx + 1].islower():
126 if following_capital_chars:
127 formatted_str += f"_{char.lower()}"
128 last_capital_char = True
129 continue
131 remaining_str = "".join(name[idx:])
132 # The 2 letters in the string; uppercase char followed by lowercase char.
133 # Example: `clusterIPs`, handle `Ps` at this point
134 if idx + 1 == str_len_for_idx_check:
135 formatted_str += remaining_str.lower()
136 break
138 # The last word in the string; uppercase followed by multiple lowercase chars
139 # Example: `dataVolumeTTLSeconds`, handle `Seconds` at this point
140 elif remaining_str.istitle():
141 formatted_str += f"_{remaining_str.lower()}"
142 break
144 else:
145 formatted_str += char.lower()
146 last_capital_char = True
148 else:
149 formatted_str += char.lower()
150 last_capital_char = True
152 else:
153 formatted_str += f"_{char.lower()}"
154 last_capital_char = True
156 return formatted_str