Skip to content

Commit a0c443e

Browse files
authored
Merge pull request #332 from scaleapi/akam/agentex-SA-integration
add service account id as option to register agentex agent
2 parents f17ad00 + 1e0156a commit a0c443e

3 files changed

Lines changed: 97 additions & 6 deletions

File tree

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,17 @@ def get_configs_for_env(self, env_target: str) -> dict[str, AgentEnvironmentConf
165165
account_id: 6887f093600ecd59bbbd3095
166166
helm_overrides:
167167
168+
The principal must contain exactly one of `user_id` or `service_account_id`.
169+
Use `service_account_id` to register an agent under a service account
170+
instead of a personal user identity:
171+
dev:
172+
kubernetes:
173+
namespace: "sgp-000-hello-acp"
174+
auth:
175+
principal:
176+
service_account_id: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d
177+
account_id: 6887f093600ecd59bbbd3095
178+
168179
if the environment field is not explicitly set, we assume its the same as
169180
the name of the environment
170181
Args:

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,14 @@ def _validate_single_environment_config(env_name: str, env_config: AgentEnvironm
103103

104104
# Validate auth principal
105105
principal = env_config.auth.principal
106-
if not principal.get("user_id"):
107-
raise ValueError("Auth principal must contain non-empty 'user_id'")
106+
user_id = principal.get("user_id")
107+
service_account_id = principal.get("service_account_id")
108+
if not user_id and not service_account_id:
109+
raise ValueError("Auth principal must contain non-empty 'user_id' or 'service_account_id'")
110+
if user_id and service_account_id:
111+
raise ValueError("Auth principal must contain only one of 'user_id' or 'service_account_id', not both")
108112

109113
# Check for environment-specific user_id patterns
110-
user_id = principal["user_id"]
111114
if isinstance(user_id, str):
112115
if not any(env_name.lower() in user_id.lower() for env_name in ["dev", "prod", "staging", env_name]):
113116
logger.warning(
@@ -233,11 +236,12 @@ def generate_helpful_error_message(error: Exception, context: str = "") -> str:
233236
"1. Check file location: should be next to manifest.yaml\n"
234237
"2. Verify file permissions"
235238
)
236-
elif "user_id" in base_msg.lower():
239+
elif "user_id" in base_msg.lower() or "service_account_id" in base_msg.lower():
237240
base_msg += (
238241
"\n\n💡 Auth Principal Tips:\n"
239-
"- user_id should be unique per environment\n"
240-
"- Include environment name (e.g., 'dev_my_agent')\n"
242+
"- Set exactly one of 'user_id' or 'service_account_id'\n"
243+
"- The id should be unique per environment\n"
244+
"- For user_id, include environment name (e.g., 'dev_my_agent')\n"
241245
"- Use consistent naming convention across agents"
242246
)
243247
elif "namespace" in base_msg.lower():

tests/lib/cli/test_validation.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"""Tests for the auth-principal portion of the environments.yaml validator.
2+
3+
Covers the rule that an env config's principal must carry exactly one of
4+
`user_id` or `service_account_id` — the same shape that downstream services
5+
(agentex-auth, SGP) expect on the wire.
6+
"""
7+
8+
import pytest
9+
10+
from agentex.lib.sdk.config.validation import (
11+
EnvironmentsValidationError,
12+
validate_environments_config,
13+
)
14+
from agentex.lib.sdk.config.environment_config import (
15+
AgentAuthConfig,
16+
AgentKubernetesConfig,
17+
AgentEnvironmentConfig,
18+
AgentEnvironmentsConfig,
19+
)
20+
21+
22+
def _config_with_principal(principal: dict) -> AgentEnvironmentsConfig:
23+
return AgentEnvironmentsConfig(
24+
schema_version="v1",
25+
environments={
26+
"dev": AgentEnvironmentConfig(
27+
kubernetes=AgentKubernetesConfig(namespace="dev-ns"),
28+
auth=AgentAuthConfig(principal=principal),
29+
)
30+
},
31+
)
32+
33+
34+
def test_user_only_principal_passes():
35+
"""Existing user_id-only configs continue to validate (backwards compat)."""
36+
config = _config_with_principal({"user_id": "73d0c8bd-4726-434c-9686-eb627d89f078", "account_id": "acct-1"})
37+
38+
validate_environments_config(config)
39+
40+
41+
def test_service_account_only_principal_passes():
42+
"""New service_account_id-only configs validate."""
43+
config = _config_with_principal(
44+
{"service_account_id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", "account_id": "acct-1"}
45+
)
46+
47+
validate_environments_config(config)
48+
49+
50+
def test_principal_with_neither_id_is_rejected():
51+
"""A principal with no identity id fails fast with a clear error."""
52+
config = _config_with_principal({"account_id": "acct-1"})
53+
54+
with pytest.raises(EnvironmentsValidationError) as exc_info:
55+
validate_environments_config(config)
56+
57+
msg = str(exc_info.value)
58+
assert "user_id" in msg
59+
assert "service_account_id" in msg
60+
61+
62+
def test_principal_with_both_ids_is_rejected():
63+
"""Setting both ids is a config error — the principal must commit to one identity type."""
64+
config = _config_with_principal(
65+
{
66+
"user_id": "73d0c8bd-4726-434c-9686-eb627d89f078",
67+
"service_account_id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
68+
"account_id": "acct-1",
69+
}
70+
)
71+
72+
with pytest.raises(EnvironmentsValidationError) as exc_info:
73+
validate_environments_config(config)
74+
75+
msg = str(exc_info.value)
76+
assert "only one of" in msg.lower() or "not both" in msg.lower()

0 commit comments

Comments
 (0)