Skip to content

Commit 739f025

Browse files
authored
[ROB-2024] Irsa cross account (#33)
* add-cross-account-support * made it a bit cleaner * safer code * bump version * added arn to prometheus config * add assume role to custom connect * updates, compatibility with newer pydantics
1 parent 956ec89 commit 739f025

File tree

6 files changed

+426
-74
lines changed

6 files changed

+426
-74
lines changed

poetry.lock

Lines changed: 363 additions & 69 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

prometrix/connect/aws_connect.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
1+
import os
12
from datetime import datetime
2-
from typing import Any, Dict, Optional, List
3+
from typing import Any, Dict, Optional
4+
import logging
35

46
import requests
57
import boto3
68
from botocore.auth import S3SigV4Auth
79
from botocore.awsrequest import AWSRequest
810
from botocore.credentials import Credentials
911
from prometheus_api_client import PrometheusApiClientException
12+
from botocore.exceptions import BotoCoreError, ClientError
1013

1114
from prometrix.connect.custom_connect import CustomPrometheusConnect
1215

16+
SA_TOKEN_PATH = os.environ.get("SA_TOKEN_PATH", "/var/run/secrets/eks.amazonaws.com/serviceaccount/token")
17+
AWS_ASSUME_ROLE = os.environ.get("AWS_ASSUME_ROLE")
1318

1419
class AWSPrometheusConnect(CustomPrometheusConnect):
1520
def __init__(
@@ -19,6 +24,7 @@ def __init__(
1924
region: str,
2025
service_name: str,
2126
token: Optional[str] = None,
27+
assume_role_arn: Optional[str] = None,
2228
**kwargs,
2329
):
2430
super().__init__(**kwargs)
@@ -36,6 +42,45 @@ def __init__(
3642
raise RuntimeError("No AWS credentials found (neither static keys nor IRSA)")
3743
self._credentials = creds
3844

45+
role_to_assume = assume_role_arn or AWS_ASSUME_ROLE
46+
if role_to_assume:
47+
self._assume_role_with_web_identity(role_to_assume)
48+
49+
def _assume_role_with_web_identity(self, role_arn: str) -> None:
50+
"""Assume the given role using the pod's service-account web identity token."""
51+
try:
52+
with open(SA_TOKEN_PATH, "r", encoding="utf-8") as f:
53+
web_identity_token = f.read().strip()
54+
except FileNotFoundError:
55+
raise RuntimeError(f"Service Account token not found at {SA_TOKEN_PATH}")
56+
57+
try:
58+
sts = boto3.client("sts", region_name=self.region)
59+
resp = sts.assume_role_with_web_identity(
60+
RoleArn=role_arn,
61+
RoleSessionName="amp-auto",
62+
WebIdentityToken=web_identity_token,
63+
)
64+
65+
credentials = resp.get("Credentials")
66+
if not credentials:
67+
logging.error("Invalid assume role response {resp}")
68+
return
69+
70+
required_fields = ["AccessKeyId", "SecretAccessKey", "SessionToken"]
71+
missing = [f for f in required_fields if not credentials.get(f)]
72+
if missing:
73+
logging.error("Missing required credential fields: {missing}. Raw response: {resp}")
74+
raise Exception(f"Failed to assume role: missing fields {missing}")
75+
76+
self._credentials = Credentials(
77+
credentials["AccessKeyId"],credentials["SecretAccessKey"], credentials["SessionToken"]
78+
)
79+
except (ClientError, BotoCoreError, Exception) as e:
80+
raise Exception(
81+
f"Failed to assume role {role_arn} with web identity: {str(e)}"
82+
)
83+
3984
def _build_auth(self) -> S3SigV4Auth:
4085
"""Builds fresh SigV4 auth with current credentials (handles rotation)."""
4186
frozen = self._credentials.get_frozen_credentials()
@@ -53,6 +98,7 @@ def signed_request(
5398
headers=dict(request.headers),
5499
verify=verify,
55100
data=data,
101+
params=params,
56102
)
57103

58104
def _custom_query(self, query: str, params: dict = None):

prometrix/models/prometheus_config.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
from enum import Enum
22
from typing import Dict, List, Optional
33

4-
from pydantic import BaseModel, SecretStr
4+
try:
5+
# Works if Pydantic v2 is installed
6+
from pydantic.v1 import BaseModel, SecretStr
7+
except ImportError:
8+
# Fallback if running under Pydantic v1
9+
from pydantic import BaseModel, SecretStr
510

611

712
class PrometheusApis(Enum):
@@ -35,6 +40,7 @@ class AWSPrometheusConfig(PrometheusConfig):
3540
token: Optional[str] = None
3641
service_name: str = "aps"
3742
aws_region: str
43+
assume_role_arn: Optional[str] = None
3844
supported_apis: List[PrometheusApis] = [
3945
PrometheusApis.QUERY,
4046
PrometheusApis.QUERY_RANGE,

prometrix/utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def get_custom_prometheus_connect(
3636
secret_key=prom_config.secret_access_key,
3737
service_name=prom_config.service_name,
3838
region=prom_config.aws_region,
39+
assume_role_arn=prom_config.assume_role_arn,
3940
config=prom_config,
4041
token=prom_config.token,
4142
)

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "prometrix"
3-
version = "0.2.2"
3+
version = "0.2.3"
44
authors = ["Avi Kotlicky <[email protected]>"]
55
readme = "README.md"
66
packages = [{include = "prometrix"}]
@@ -14,7 +14,7 @@ description = "A Python Prometheus client for all Prometheus instances."
1414
python = "^3.8"
1515
boto3 = "^1.28.15"
1616
botocore = "^1.31.15"
17-
pydantic = "^1.8.1"
17+
pydantic = ">=1.8.1,<3"
1818
prometheus-api-client = "^0.5.3"
1919
pillow = "^10.3.0" # added to Pin transitive dependency, not needed directly
2020
fonttools = "^4.43.0" # added to Pin transitive dependency, not needed directly

tests/main.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@
55

66
import yaml
77
from prometheus_api_client import PrometheusApiClientException
8-
from pydantic import ValidationError
8+
9+
try:
10+
from pydantic.v1 import ValidationError
11+
except ImportError:
12+
from pydantic import ValidationError
13+
914
from pytimeparse.timeparse import timeparse
1015

1116
from prometrix import (AWSPrometheusConfig, AzurePrometheusConfig,

0 commit comments

Comments
 (0)