Skip to content

Commit 0fc5306

Browse files
committed
Adding environments changes
1 parent 36f1712 commit 0fc5306

File tree

7 files changed

+561
-30
lines changed

7 files changed

+561
-30
lines changed

src/agentex/lib/cli/commands/agents.py

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -243,10 +243,13 @@ def deploy(
243243
cluster: str = typer.Option(
244244
..., help="Target cluster name (must match kubectl context)"
245245
),
246+
environment: str = typer.Option(
247+
..., help="Environment name (dev, prod, etc.) - must be defined in environments.yaml"
248+
),
246249
manifest: str = typer.Option("manifest.yaml", help="Path to the manifest file"),
247250
namespace: str | None = typer.Option(
248251
None,
249-
help="Kubernetes namespace to deploy to (required in non-interactive mode)",
252+
help="Override Kubernetes namespace (defaults to namespace from environments.yaml)",
250253
),
251254
tag: str | None = typer.Option(None, help="Override the image tag for deployment"),
252255
repository: str | None = typer.Option(
@@ -272,23 +275,30 @@ def deploy(
272275
console.print(f"[red]Error:[/red] Manifest file not found: {manifest}")
273276
raise typer.Exit(1)
274277

275-
# In non-interactive mode, require namespace
276-
if not interactive and not namespace:
277-
console.print(
278-
"[red]Error:[/red] --namespace is required in non-interactive mode"
279-
)
278+
# Validate environments.yaml exists
279+
environments_file = manifest_path.parent / "environments.yaml"
280+
if not environments_file.exists():
281+
console.print(f"[red]Error:[/red] environments.yaml not found next to {manifest}")
282+
console.print("\n💡 To create one:")
283+
console.print(" agentex agents init-environments")
284+
console.print("\n📋 Why required:")
285+
console.print(" Environment-specific settings (auth, namespace, resources)")
286+
console.print(" must be separated from global manifest for proper isolation.")
280287
raise typer.Exit(1)
281288

282-
# Get namespace if not provided (only in interactive mode)
283-
if not namespace:
284-
namespace = questionary.text(
285-
"Enter Kubernetes namespace:", default="default"
286-
).ask()
287-
namespace = handle_questionary_cancellation(namespace, "namespace input")
289+
# Load and validate environment configuration
290+
try:
291+
from agentex.lib.sdk.config.environment_config import AgentEnvironmentsConfig
292+
environments_config = AgentEnvironmentsConfig.from_yaml(str(environments_file))
293+
agent_env_config = environments_config.get_config_for_env(environment)
294+
except Exception as e:
295+
console.print(f"[red]Error:[/red] Failed to load environment config: {e}")
296+
raise typer.Exit(1)
288297

289-
if not namespace:
290-
console.print("Deployment cancelled")
291-
raise typer.Exit(0)
298+
# Use namespace from environment config if not overridden
299+
if not namespace:
300+
namespace = agent_env_config.kubernetes.namespace
301+
console.print(f"[blue]ℹ[/blue] Using namespace from environments.yaml: {namespace}")
292302

293303
# Validate override file exists if provided
294304
if override_file:
@@ -305,6 +315,7 @@ def deploy(
305315
# Confirm deployment (only in interactive mode)
306316
console.print("\n[bold]Deployment Summary:[/bold]")
307317
console.print(f" Manifest: {manifest}")
318+
console.print(f" Environment: {environment}")
308319
console.print(f" Cluster: {cluster}")
309320
console.print(f" Namespace: {namespace}")
310321
if tag:
@@ -340,6 +351,7 @@ def deploy(
340351
namespace=namespace,
341352
deploy_overrides=deploy_overrides,
342353
override_file_path=override_file,
354+
environment_name=environment,
343355
)
344356

345357
# Use the already loaded manifest object

src/agentex/lib/cli/handlers/deploy_handlers.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from pydantic import BaseModel, Field
99
from rich.console import Console
1010

11-
from agentex.lib.cli.utils.auth_utils import _encode_principal_context
1211
from agentex.lib.cli.utils.exceptions import DeploymentError, HelmError
1312
from agentex.lib.cli.utils.kubectl_utils import check_and_switch_cluster_context
1413
from agentex.lib.cli.utils.path_utils import calculate_docker_acp_module, PathResolutionError
@@ -116,6 +115,7 @@ def add_acp_command_to_helm_values(helm_values: dict[str, Any], manifest: AgentM
116115

117116
def merge_deployment_configs(
118117
manifest: AgentManifest,
118+
agent_env_config,
119119
cluster_config: ClusterConfig | None,
120120
deploy_overrides: InputDeployOverrides,
121121
manifest_path: str,
@@ -193,10 +193,12 @@ def merge_deployment_configs(
193193
if agent_config.env:
194194
all_env_vars.update(agent_config.env)
195195

196-
# Add auth principal env var if manifest principal is set
197-
encoded_principal = _encode_principal_context(manifest)
198-
if encoded_principal:
199-
all_env_vars[EnvVarKeys.AUTH_PRINCIPAL_B64.value] = encoded_principal
196+
# Add auth principal env var if environment config is set
197+
if agent_env_config and agent_env_config.auth:
198+
from agentex.lib.cli.utils.auth_utils import _encode_principal_context_from_env_config
199+
encoded_principal = _encode_principal_context_from_env_config(agent_env_config.auth)
200+
if encoded_principal:
201+
all_env_vars[EnvVarKeys.AUTH_PRINCIPAL_B64.value] = encoded_principal
200202

201203
# Handle credentials and check for conflicts
202204
if agent_config.credentials:
@@ -278,6 +280,11 @@ def merge_deployment_configs(
278280
if cluster_config.additional_overrides:
279281
_deep_merge(helm_values, cluster_config.additional_overrides)
280282

283+
# Apply agent environment configuration overrides
284+
if agent_env_config:
285+
if agent_env_config.helm_overrides:
286+
_deep_merge(helm_values, agent_env_config.helm_overrides)
287+
281288
# Set final environment variables
282289
if all_env_vars:
283290
helm_values["env"] = convert_env_vars_dict_to_list(all_env_vars)
@@ -334,6 +341,7 @@ def deploy_agent(
334341
namespace: str,
335342
deploy_overrides: InputDeployOverrides,
336343
override_file_path: str | None = None,
344+
environment_name: str | None = None,
337345
) -> None:
338346
"""Deploy an agent using helm"""
339347

@@ -347,6 +355,17 @@ def deploy_agent(
347355
manifest = AgentManifest.from_yaml(file_path=manifest_path)
348356
override_config = load_override_config(override_file_path)
349357

358+
# Load agent environment configuration
359+
agent_env_config = None
360+
if environment_name:
361+
manifest_dir = Path(manifest_path).parent
362+
environments_config = manifest.load_environments_config(manifest_dir)
363+
if environments_config:
364+
agent_env_config = environments_config.get_config_for_env(environment_name)
365+
console.print(f"[green]✓[/green] Using environment config: {environment_name}")
366+
else:
367+
console.print(f"[yellow]⚠[/yellow] No environments.yaml found, skipping environment-specific config")
368+
350369
# Provide feedback about override configuration
351370
if override_config:
352371
console.print(f"[green]✓[/green] Using override config: {override_file_path}")
@@ -359,7 +378,7 @@ def deploy_agent(
359378
add_helm_repo()
360379

361380
# Merge configurations
362-
helm_values = merge_deployment_configs(manifest, override_config, deploy_overrides, manifest_path)
381+
helm_values = merge_deployment_configs(manifest, agent_env_config, override_config, deploy_overrides, manifest_path)
363382

364383
# Create values file
365384
values_file = create_helm_values_file(helm_values)

src/agentex/lib/cli/handlers/run_handlers.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from rich.console import Console
77
from rich.panel import Panel
88

9-
from agentex.lib.cli.utils.auth_utils import _encode_principal_context
109
from agentex.lib.cli.handlers.cleanup_handlers import (
1110
cleanup_agent_workflows,
1211
should_cleanup_on_restart
@@ -374,10 +373,13 @@ def create_agent_environment(manifest: AgentManifest) -> dict[str, str]:
374373
"ACP_PORT": str(manifest.local_development.agent.port),
375374
}
376375

377-
# Add authorization principal if set
376+
# Add authorization principal if set - for local development, auth is optional
377+
from agentex.lib.cli.utils.auth_utils import _encode_principal_context
378378
encoded_principal = _encode_principal_context(manifest)
379379
if encoded_principal:
380380
env_vars[EnvVarKeys.AUTH_PRINCIPAL_B64] = encoded_principal
381+
else:
382+
logger.info("No auth principal configured - agent will run without authentication context")
381383

382384
# Add description if available
383385
if manifest.agent.description:
Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,58 @@
11
import base64
22
import json
3+
from typing import Any, Dict
34

45
from agentex.lib.sdk.config.agent_manifest import AgentManifest
6+
from agentex.lib.sdk.config.environment_config import AgentAuthConfig
57

8+
# DEPRECATED: Old function for backward compatibility
9+
# Will be removed in future version
10+
def _encode_principal_context(manifest: AgentManifest) -> str | None:
11+
"""
12+
DEPRECATED: This function is deprecated as AgentManifest no longer contains auth.
13+
Use _encode_principal_context_from_env_config instead.
14+
15+
This function is kept temporarily for backward compatibility during migration.
16+
"""
17+
# AgentManifest no longer has auth field - this will always return None
18+
return None
619

7-
# Base 64 encode principal dictionary
8-
def _encode_principal_context(manifest: AgentManifest):
9-
if manifest.auth is None:
10-
return None
1120

12-
principal = manifest.auth.principal
21+
def _encode_principal_context_from_env_config(auth_config: "AgentAuthConfig | None") -> str | None:
22+
"""
23+
Encode principal context from environment configuration.
24+
25+
Args:
26+
auth_config: AgentAuthConfig containing principal configuration
27+
28+
Returns:
29+
Base64-encoded JSON string of the principal, or None if no principal
30+
"""
31+
if auth_config is None:
32+
return None
33+
34+
principal = auth_config.principal
1335
if principal is None:
1436
return None
1537

1638
json_str = json.dumps(principal, separators=(',', ':'))
1739
encoded_bytes = base64.b64encode(json_str.encode('utf-8'))
1840
return encoded_bytes.decode('utf-8')
41+
42+
43+
def _encode_principal_dict(principal: Dict[str, Any]) -> str | None:
44+
"""
45+
Encode principal dictionary directly.
46+
47+
Args:
48+
principal: Dictionary containing principal configuration
49+
50+
Returns:
51+
Base64-encoded JSON string of the principal, or None if principal is empty
52+
"""
53+
if not principal:
54+
return None
55+
56+
json_str = json.dumps(principal, separators=(',', ':'))
57+
encoded_bytes = base64.b64encode(json_str.encode('utf-8'))
58+
return encoded_bytes.decode('utf-8')

src/agentex/lib/sdk/config/agent_manifest.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
from agentex.lib.sdk.config.agent_config import AgentConfig
1717
from agentex.lib.sdk.config.build_config import BuildConfig
18-
from agentex.lib.sdk.config.deployment_config import DeploymentConfig, AuthenticationConfig
18+
from agentex.lib.sdk.config.deployment_config import DeploymentConfig
1919
from agentex.lib.sdk.config.local_development_config import LocalDevelopmentConfig
2020
from agentex.lib.utils.logging import make_logger
2121
from agentex.lib.utils.model_utils import BaseModel
@@ -36,7 +36,7 @@ class AgentManifest(BaseModel):
3636
deployment: DeploymentConfig | None = Field(
3737
default=None, description="Deployment configuration for the agent"
3838
)
39-
auth: AuthenticationConfig | None = Field(default=None, description="Authentication configuration")
39+
4040

4141
def context_manager(self, build_context_root: Path) -> BuildContextManager:
4242
"""
@@ -45,6 +45,23 @@ def context_manager(self, build_context_root: Path) -> BuildContextManager:
4545
return BuildContextManager(
4646
agent_manifest=self, build_context_root=build_context_root
4747
)
48+
49+
def load_environments_config(self, manifest_dir: Path) -> "AgentEnvironmentsConfig | None":
50+
"""Load environments.yaml from same directory as manifest.yaml.
51+
52+
Args:
53+
manifest_dir: Directory containing manifest.yaml
54+
55+
Returns:
56+
AgentEnvironmentsConfig if environments.yaml exists, None otherwise
57+
58+
Raises:
59+
ValueError: If environments.yaml exists but is invalid
60+
"""
61+
# Import here to avoid circular imports
62+
from agentex.lib.sdk.config.environment_config import load_environments_config_from_manifest_dir
63+
64+
return load_environments_config_from_manifest_dir(manifest_dir)
4865

4966

5067
class BuildContextManager:

0 commit comments

Comments
 (0)