77
88from __future__ import annotations
99
10- from typing import Any , Dict , override
10+ from typing import Any , Dict , override , List
1111from pathlib import Path
1212
1313import yaml
1818
1919class AgentAuthConfig (BaseModel ):
2020 """Authentication configuration for an agent in a specific environment."""
21-
21+
2222 principal : Dict [str , Any ] = Field (
23- ...,
24- description = "Principal configuration for agent authorization and registration"
23+ ..., description = "Principal configuration for agent authorization and registration"
2524 )
26-
27- @field_validator (' principal' )
25+
26+ @field_validator (" principal" )
2827 @classmethod
2928 def validate_principal_required_fields (cls , v : Any ) -> Dict [str , Any ]:
3029 """Ensure principal has required fields for agent registration."""
@@ -35,142 +34,177 @@ def validate_principal_required_fields(cls, v: Any) -> Dict[str, Any]:
3534
3635class AgentKubernetesConfig (BaseModel ):
3736 """Kubernetes configuration for an agent in a specific environment."""
38-
39- namespace : str = Field (
40- ...,
41- description = "Kubernetes namespace where the agent will be deployed"
42- )
43-
44- @field_validator ('namespace' )
37+
38+ namespace : str = Field (..., description = "Kubernetes namespace where the agent will be deployed" )
39+
40+ @field_validator ("namespace" )
4541 @classmethod
4642 def validate_namespace_format (cls , v : str ) -> str :
4743 """Ensure namespace follows Kubernetes naming conventions."""
4844 if not v or not v .strip ():
4945 raise ValueError ("Namespace cannot be empty" )
50-
46+
5147 # Basic Kubernetes namespace validation
5248 namespace = v .strip ().lower ()
53- if not namespace .replace ('-' , '' ).replace ('.' , '' ).isalnum ():
54- raise ValueError (
55- f"Namespace '{ v } ' must contain only lowercase letters, numbers, "
56- "hyphens, and periods"
57- )
58-
49+ if not namespace .replace ("-" , "" ).replace ("." , "" ).isalnum ():
50+ raise ValueError (f"Namespace '{ v } ' must contain only lowercase letters, numbers, hyphens, and periods" )
51+
5952 if len (namespace ) > 63 :
6053 raise ValueError (f"Namespace '{ v } ' cannot exceed 63 characters" )
61-
54+
6255 return namespace
6356
6457
6558class AgentEnvironmentConfig (BaseModel ):
6659 """Complete configuration for an agent in a specific environment."""
67-
68- kubernetes : AgentKubernetesConfig | None = Field (
69- default = None ,
70- description = "Kubernetes deployment configuration"
71- )
72- auth : AgentAuthConfig = Field (
73- ...,
74- description = "Authentication and authorization configuration"
75- )
76- helm_repository_name : str = Field (
77- default = "scale-egp" ,
78- description = "Helm repository name for the environment"
60+
61+ kubernetes : AgentKubernetesConfig | None = Field (default = None , description = "Kubernetes deployment configuration" )
62+ environment : str | None = Field (
63+ default = None ,
64+ description = "The environment keyword that this specific environment maps to: either dev, staging, prod" ,
7965 )
66+ auth : AgentAuthConfig = Field (..., description = "Authentication and authorization configuration" )
67+ helm_repository_name : str = Field (default = "scale-egp" , description = "Helm repository name for the environment" )
8068 helm_repository_url : str = Field (
81- default = "https://scale-egp-helm-charts-us-west-2.s3.amazonaws.com/charts" ,
82- description = "Helm repository url for the environment"
69+ default = "https://scale-egp-helm-charts-us-west-2.s3.amazonaws.com/charts" ,
70+ description = "Helm repository url for the environment" ,
8371 )
8472 helm_overrides : Dict [str , Any ] = Field (
85- default_factory = dict ,
86- description = "Helm chart value overrides for environment-specific tuning"
73+ default_factory = dict , description = "Helm chart value overrides for environment-specific tuning"
8774 )
8875
8976
9077class AgentEnvironmentsConfig (UtilsBaseModel ):
9178 """All environment configurations for an agent."""
92-
93- schema_version : str = Field (
94- default = "v1" ,
95- description = "Schema version for validation and compatibility"
96- )
79+
80+ schema_version : str = Field (default = "v1" , description = "Schema version for validation and compatibility" )
9781 environments : Dict [str , AgentEnvironmentConfig ] = Field (
98- ...,
99- description = "Environment-specific configurations (dev, prod, etc.)"
82+ ..., description = "Environment-specific configurations (dev, prod, etc.)"
10083 )
101-
102- @field_validator (' schema_version' )
84+
85+ @field_validator (" schema_version" )
10386 @classmethod
10487 def validate_schema_version (cls , v : str ) -> str :
10588 """Ensure schema version is supported."""
106- supported_versions = ['v1' ]
89+ supported_versions = ["v1" ]
10790 if v not in supported_versions :
108- raise ValueError (
109- f"Schema version '{ v } ' not supported. "
110- f"Supported versions: { ', ' .join (supported_versions )} "
111- )
91+ raise ValueError (f"Schema version '{ v } ' not supported. Supported versions: { ', ' .join (supported_versions )} " )
11292 return v
113-
114- @field_validator (' environments' )
93+
94+ @field_validator (" environments" )
11595 @classmethod
11696 def validate_environments_not_empty (cls , v : Dict [str , AgentEnvironmentConfig ]) -> Dict [str , AgentEnvironmentConfig ]:
11797 """Ensure at least one environment is defined."""
11898 if not v :
11999 raise ValueError ("At least one environment must be defined" )
120100 return v
121-
101+
122102 def get_config_for_env (self , env_name : str ) -> AgentEnvironmentConfig :
123103 """Get configuration for a specific environment.
124-
104+
125105 Args:
126106 env_name: Name of the environment (e.g., 'dev', 'prod')
127-
107+
128108 Returns:
129109 AgentEnvironmentConfig for the specified environment
130-
110+
131111 Raises:
132112 ValueError: If environment is not found
133113 """
134114 if env_name not in self .environments :
135- available_envs = ', ' .join (self .environments .keys ())
115+ available_envs = ", " .join (self .environments .keys ())
136116 raise ValueError (
137- f"Environment '{ env_name } ' not found in environments.yaml. "
138- f"Available environments: { available_envs } "
117+ f"Environment '{ env_name } ' not found in environments.yaml. Available environments: { available_envs } "
139118 )
140119 return self .environments [env_name ]
141-
120+
121+ def get_configs_for_env (self , env : str ) -> List [AgentEnvironmentConfig ]:
122+ """Get configuration for a specific environment based on the expected mapping.
123+ The environment is either:
124+ 1. explicitly specified like so using a key-map in the environments conifg:
125+ environments:
126+ dev-aws:
127+ environment: "dev"
128+ kubernetes:
129+ namespace: "sgp-000-hello-acp"
130+ auth:
131+ principal:
132+ user_id: 73d0c8bd-4726-434c-9686-eb627d89f078
133+ account_id: 6887f093600ecd59bbbd3095
134+ helm_overrides:
135+
136+ or: it it can be defined at the top level:
137+ dev:
138+ kubernetes:
139+ namespace: "sgp-000-hello-acp"
140+ auth:
141+ principal:
142+ user_id: 73d0c8bd-4726-434c-9686-eb627d89f078
143+ account_id: 6887f093600ecd59bbbd3095
144+ helm_overrides:
145+
146+ if its not explicitly defined, we assumed the level key (environment name) is the environment
147+ Args:
148+ env_name: Name of the environment (e.g., 'dev', 'prod')
149+
150+ Returns:
151+ AgentEnvironmentConfig for the specified environment
152+
153+ Raises:
154+ ValueError: If environment is not found
155+ """
156+ envs_to_deploy = []
157+ if env in self .environments :
158+ # this supports if the top-level key is just "dev, staging, etc" and matches
159+ # the environment name exactly without any explicit mapping
160+ envs_to_deploy .append (self .environments [env ])
161+
162+ for _ , config in self .environments .items ():
163+ if config .environment == env :
164+ envs_to_deploy .append (config )
165+
166+ if len (envs_to_deploy ) == 0 :
167+ available_envs = [env .environment for _ , env in self .environments .items ()] + [
168+ env_name for env_name in self .environments
169+ ]
170+ raise ValueError (
171+ f"Environment '{ envs_to_deploy } ' not found in environments.yaml. Available environments: { available_envs } "
172+ )
173+
174+ return envs_to_deploy
175+
142176 def list_environments (self ) -> list [str ]:
143177 """Get list of all configured environment names."""
144178 return list (self .environments .keys ())
145-
179+
146180 @classmethod
147181 @override
148182 def from_yaml (cls , file_path : str ) -> "AgentEnvironmentsConfig" :
149183 """Load configuration from environments.yaml file.
150-
184+
151185 Args:
152186 file_path: Path to environments.yaml file
153-
187+
154188 Returns:
155189 Parsed and validated AgentEnvironmentsConfig
156-
190+
157191 Raises:
158192 FileNotFoundError: If file doesn't exist
159193 ValueError: If file is invalid or doesn't validate
160194 """
161195 path = Path (file_path )
162196 if not path .exists ():
163197 raise FileNotFoundError (f"environments.yaml not found: { file_path } " )
164-
198+
165199 try :
166- with open (path , 'r' ) as f :
200+ with open (path , "r" ) as f :
167201 data = yaml .safe_load (f )
168-
202+
169203 if not data :
170204 raise ValueError ("environments.yaml file is empty" )
171-
205+
172206 return cls .model_validate (data )
173-
207+
174208 except yaml .YAMLError as e :
175209 raise ValueError (f"Invalid YAML format in { file_path } : { e } " ) from e
176210 except Exception as e :
@@ -179,18 +213,18 @@ def from_yaml(cls, file_path: str) -> "AgentEnvironmentsConfig":
179213
180214def load_environments_config_from_manifest_dir (manifest_dir : Path ) -> AgentEnvironmentsConfig | None :
181215 """Helper function to load environments.yaml from same directory as manifest.yaml.
182-
216+
183217 Args:
184218 manifest_dir: Directory containing manifest.yaml
185-
219+
186220 Returns:
187221 AgentEnvironmentsConfig if environments.yaml exists, None otherwise
188-
222+
189223 Raises:
190224 ValueError: If environments.yaml exists but is invalid
191225 """
192226 environments_file = manifest_dir / "environments.yaml"
193227 if not environments_file .exists ():
194228 return None
195-
229+
196230 return AgentEnvironmentsConfig .from_yaml (str (environments_file ))
0 commit comments