Skip to content

Commit c3b2f03

Browse files
Create custom exceptions and link it to website (#1232)
* Create custom exceptions and link it to website
1 parent b1d093d commit c3b2f03

File tree

2 files changed

+53
-50
lines changed

2 files changed

+53
-50
lines changed

scripts/aws/ec2.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
import requests
1010
import signal
1111
import argparse
12-
from botocore.exceptions import ClientError
12+
from botocore.exceptions import ClientError, NoCredentialsError
1313
from typing import Dict
1414
import sys
1515
import time
1616
import yaml
1717

1818
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
19-
from confidential_compute import ConfidentialCompute, ConfidentialComputeConfig, SecretNotFoundException, ConfidentialComputeStartupException
19+
from confidential_compute import ConfidentialCompute, ConfidentialComputeConfig, MissingInstanceProfile, ConfigNotFound, InvalidConfigValue, ConfidentialComputeStartupException
2020

2121
class AWSConfidentialComputeConfig(ConfidentialComputeConfig):
2222
enclave_memory_mb: int
@@ -61,7 +61,7 @@ def __get_aws_token(self) -> str:
6161
)
6262
return response.text
6363
except requests.RequestException as e:
64-
raise RuntimeError(f"Failed to fetch aws token: {e}")
64+
raise RuntimeError(f"Failed to fetch AWS token: {e}")
6565

6666
def __get_current_region(self) -> str:
6767
"""Fetches the current AWS region from EC2 instance metadata."""
@@ -97,16 +97,15 @@ def add_defaults(configs: Dict[str, any]) -> AWSConfidentialComputeConfig:
9797

9898
region = self.__get_current_region()
9999
print(f"Running in {region}")
100-
try:
101-
client = boto3.client("secretsmanager", region_name=region)
102-
except Exception as e:
103-
raise RuntimeError("Please use IAM instance profile for your instance and make sure that has permission to access Secret Manager", e)
100+
client = boto3.client("secretsmanager", region_name=region)
104101
try:
105102
secret = add_defaults(json.loads(client.get_secret_value(SecretId=secret_identifier)["SecretString"]))
106103
self.__validate_aws_specific_config(secret)
107104
return secret
105+
except NoCredentialsError as _:
106+
raise MissingInstanceProfile(self.__class__.__name__)
108107
except ClientError as _:
109-
raise SecretNotFoundException(f"{secret_identifier} in {region}")
108+
raise ConfigNotFound(self.__class__.__name__, f"Secret Manager {secret_identifier} in {region}")
110109

111110
@staticmethod
112111
def __get_max_capacity():
@@ -255,5 +254,5 @@ def __kill_auxiliaries(self) -> None:
255254
except ConfidentialComputeStartupException as e:
256255
print("Failed starting up Confidential Compute. Please checks the logs for errors and retry \n", e)
257256
except Exception as e:
258-
print("Unknown failure while starting up Confidential Compute. Please contact UID support team with this log \n ", e)
257+
print("Unexpected failure while starting up Confidential Compute. Please contact UID support team with this log \n ", e)
259258

scripts/confidential_compute.py

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,40 @@ class ConfidentialComputeConfig(TypedDict):
1313
environment: str
1414
skip_validations: NotRequired[bool]
1515
debug_mode: NotRequired[bool]
16+
17+
class ConfidentialComputeStartupException(Exception):
18+
def __init__(self, error_name, provider, extra_message=None):
19+
urls = {
20+
"EC2": "https://unifiedid.com/docs/guides/operator-guide-aws-marketplace#uid2-operator-error-codes",
21+
"Azure": "https://unifiedid.com/docs/guides/operator-guide-azure-enclave#uid2-operator-error-codes",
22+
"GCP": "https://unifiedid.com/docs/guides/operator-private-gcp-confidential-space#uid2-operator-error-codes",
23+
}
24+
url = urls.get(provider)
25+
super().__init__(f"{error_name}\n" + (extra_message if extra_message else "") + f"\nVisit {url} for more details")
26+
27+
class MissingInstanceProfile(ConfidentialComputeStartupException):
28+
def __init__(self, cls):
29+
super().__init__(error_name=f"E01: {self.__class__.__name__}", provider=cls)
30+
31+
class ConfigNotFound(ConfidentialComputeStartupException):
32+
def __init__(self, cls, message = None):
33+
super().__init__(error_name=f"E02: {self.__class__.__name__}", provider=cls, extra_message=message)
34+
35+
class MissingConfig(ConfidentialComputeStartupException):
36+
def __init__(self, cls, missing_keys):
37+
super().__init__(error_name=f"E03: {self.__class__.__name__}", provider=cls, extra_message=', '.join(missing_keys))
38+
39+
class InvalidConfigValue(ConfidentialComputeStartupException):
40+
def __init__(self, cls, config_key = None):
41+
super().__init__(error_name=f"E04: {self.__class__.__name__} " , provider=cls, extra_message=config_key)
42+
43+
class InvalidOperatorKey(ConfidentialComputeStartupException):
44+
def __init__(self, cls):
45+
super().__init__(error_name=f"E05: {self.__class__.__name__}", provider=cls)
46+
47+
class UID2ServicesUnreachable(ConfidentialComputeStartupException):
48+
def __init__(self, cls, ip=None):
49+
super().__init__(error_name=f"E06: {self.__class__.__name__}", provider=cls, extra_message=ip)
1650

1751
class ConfidentialCompute(ABC):
1852

@@ -25,36 +59,26 @@ def validate_configuration(self):
2559
def validate_operator_key():
2660
""" Validates the operator key format and its environment alignment."""
2761
operator_key = self.configs.get("api_token")
28-
if not operator_key:
29-
raise ValueError("API token is missing from the configuration.")
3062
pattern = r"^(UID2|EUID)-.\-(I|P|L)-\d+-.*$"
3163
if re.match(pattern, operator_key):
3264
env = self.configs.get("environment", "").lower()
3365
debug_mode = self.configs.get("debug_mode", False)
3466
expected_env = "I" if debug_mode or env == "integ" else "P"
35-
3667
if operator_key.split("-")[2] != expected_env:
37-
raise ValueError(
38-
f"Operator key does not match the expected environment ({expected_env})."
39-
)
68+
raise InvalidOperatorKey(self.__class__.__name__)
4069
print("Validated operator key matches environment")
4170
else:
4271
print("Skipping operator key validation")
4372

4473
def validate_url(url_key, environment):
4574
"""URL should include environment except in prod"""
4675
if environment != "prod" and environment not in self.configs[url_key]:
47-
raise ValueError(
48-
f"{url_key} must match the environment. Ensure the URL includes '{environment}'."
49-
)
76+
raise InvalidConfigValue(self.__class__.__name__, url_key)
5077
parsed_url = urlparse(self.configs[url_key])
5178
if parsed_url.scheme != 'https' and parsed_url.path:
52-
raise ValueError(
53-
f"{url_key} is invalid. Ensure {self.configs[url_key]} follows HTTPS, and doesn't have any path specified."
54-
)
79+
raise InvalidConfigValue(self.__class__.__name__, url_key)
5580
print(f"Validated {self.configs[url_key]} matches other config parameters")
5681

57-
5882
def validate_connectivity() -> None:
5983
""" Validates that the core URL is accessible."""
6084
try:
@@ -63,32 +87,29 @@ def validate_connectivity() -> None:
6387
requests.get(core_url, timeout=5)
6488
print(f"Validated connectivity to {core_url}")
6589
except (requests.ConnectionError, requests.Timeout) as e:
66-
raise RuntimeError(
67-
f"Failed to reach required URLs. Consider enabling {core_ip} in the egress firewall."
68-
)
90+
raise UID2ServicesUnreachable(self.__class__.__name__, core_ip)
6991
except Exception as e:
70-
raise Exception("Failed to reach the URLs.") from e
92+
raise UID2ServicesUnreachable(self.__class__.__name__)
93+
7194
type_hints = get_type_hints(ConfidentialComputeConfig, include_extras=True)
7295
required_keys = [field for field, hint in type_hints.items() if "NotRequired" not in str(hint)]
7396
missing_keys = [key for key in required_keys if key not in self.configs]
7497
if missing_keys:
75-
raise MissingConfigError(missing_keys)
76-
98+
raise MissingConfig(self.__class__.__name__, missing_keys)
99+
77100
environment = self.configs["environment"]
78-
79101
if environment not in ["integ", "prod"]:
80-
raise ValueError("Environment must be either prod/integ. It is currently set to", environment)
102+
raise InvalidConfigValue(self.__class__.__name__, "environment")
81103

82104
if self.configs.get("debug_mode") and environment == "prod":
83-
raise ValueError("Debug mode cannot be enabled in the production environment.")
105+
raise InvalidConfigValue(self.__class__.__name__, "debug_mode")
84106

85107
validate_url("core_base_url", environment)
86108
validate_url("optout_base_url", environment)
87109
validate_operator_key()
88110
validate_connectivity()
89111
print("Completed static validation of confidential compute config values")
90112

91-
92113
@abstractmethod
93114
def _get_secret(self, secret_identifier: str) -> ConfidentialComputeConfig:
94115
"""
@@ -124,21 +145,4 @@ def run_command(command, seperate_process=False):
124145
subprocess.run(command,check=True)
125146
except Exception as e:
126147
print(f"Failed to run command: {str(e)}")
127-
raise RuntimeError (f"Failed to start {' '.join(command)} ")
128-
129-
class ConfidentialComputeStartupException(Exception):
130-
def __init__(self, message):
131-
super().__init__(message)
132-
133-
class MissingConfigError(ConfidentialComputeStartupException):
134-
"""Custom exception to handle missing config keys."""
135-
def __init__(self, missing_keys):
136-
self.missing_keys = missing_keys
137-
self.message = f"\n Missing configuration keys: {', '.join(missing_keys)} \n"
138-
super().__init__(self.message)
139-
140-
class SecretNotFoundException(ConfidentialComputeStartupException):
141-
"""Custom exception if secret manager is not found"""
142-
def __init__(self, name):
143-
self.message = f"Secret manager not found - {name}. Please check if secret exist and the Instance Profile has permission to read it"
144-
super().__init__(self.message)
148+
raise RuntimeError (f"Failed to start {' '.join(command)} ")

0 commit comments

Comments
 (0)