Skip to content

Commit 5a3baa3

Browse files
committed
tmp 4
Signed-off-by: kvmw <mshamsi@broadcom.com>
1 parent 638bf6c commit 5a3baa3

7 files changed

Lines changed: 183 additions & 20 deletions

File tree

greeter-messages/app.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,8 @@
88

99
app = Flask(__name__)
1010

11-
port = int(os.getenv('PORT', '8080'))
12-
1311
try:
14-
init_client(port)
12+
init_client()
1513
logger.info("Eureka client initialized successfully")
1614
except Exception as e:
1715
logger.error(f"Failed to initialize Eureka client: {e}")
@@ -25,4 +23,5 @@ def greeting():
2523
return f"{salutation}, {name}!"
2624

2725
if __name__ == '__main__':
26+
port = int(os.getenv('PORT', '8080'))
2827
app.run(host='0.0.0.0', port=port)

greeter-messages/eureka.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def _on_err(err_type: str, err: Exception):
4242
if err_type in (eureka_client.ERROR_REGISTER, eureka_client.ERROR_DISCOVER):
4343
eureka_client.stop()
4444

45-
def init_client(port: int):
45+
def init_client():
4646
"""
4747
Initializes the eureka client.
4848
"""
@@ -63,9 +63,11 @@ def init_client(port: int):
6363
instance_ip=os.getenv('CF_INSTANCE_INTERNAL_IP'),
6464
instance_id=f"{env.uris[0]}:{env.app.get('instance_id') or '0'}",
6565
instance_unsecure_port_enabled=True,
66-
instance_port=port,
66+
instance_port=80,
6767
instance_secure_port_enabled=True,
68-
instance_secure_port=port,
68+
instance_secure_port=443,
69+
vip_adr='UNKNOWN',
70+
secure_vip_addr='UNKNOWN',
6971
eureka_server=credentials['uri'],
7072
on_error=_on_err
7173
)

greeter/app.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
11
import os
2+
import logging
23
from flask import Flask, request
3-
from shared import EurekaServerClient
4+
from eureka import init_client, call_service
5+
6+
logging.basicConfig(level=logging.INFO)
7+
logger = logging.getLogger(__name__)
48

59
app = Flask(__name__)
610

7-
@app.route('/greet')
8-
def greet():
9-
name = request.args.get('name', 'World')
10-
return f"Greetings, {name}!"
11+
try:
12+
init_client()
13+
logger.info("Eureka client initialized successfully")
14+
except Exception as e:
15+
logger.error(f"Failed to initialize Eureka client: {e}")
16+
17+
@app.route('/hello')
18+
def hello():
19+
salutation = request.args.get('salutation', 'Hello')
20+
name = request.args.get('name', 'Bob')
21+
22+
return call_service("GREETER-MESSAGES", f"/greeting?salutation={salutation}&name={name}")
1123

1224
if __name__ == '__main__':
13-
port = int(os.getenv("PORT", '8080'))
14-
eureka_client = EurekaServerClient(port)
15-
eureka_client.start()
16-
app.run(host='0.0.0.0', port=port)
25+
port = int(os.getenv('PORT', '8080'))
26+
app.run(host='0.0.0.0', port=port)

greeter/eureka.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import os
2+
import urllib.request
3+
import logging
4+
from cfenv import AppEnv
5+
from oauth2 import OAuth2Client
6+
import py_eureka_client.eureka_client as eureka_client
7+
import py_eureka_client.http_client as http_client
8+
from urllib.error import URLError
9+
from typing import Union
10+
11+
logger = logging.getLogger(__name__)
12+
13+
class _OAuth2HttpClient(http_client.HttpClient):
14+
"""
15+
A custom http client that uses OAuth2 to authenticate requests.
16+
"""
17+
18+
def __init__(self, credentials: dict):
19+
self.oauth_client = OAuth2Client(
20+
client_id=credentials['client_id'],
21+
client_secret=credentials['client_secret'],
22+
access_token_uri=credentials['access_token_uri']
23+
)
24+
25+
async def urlopen(self, request: Union[str, http_client.HttpRequest] = None,
26+
data: bytes = None, timeout: float = None) -> http_client.HttpResponse:
27+
if isinstance(request, http_client.HttpRequest):
28+
req = request
29+
elif isinstance(request, str):
30+
req = http_client.HttpRequest(request, headers={'Accept-Encoding': 'gzip'})
31+
else:
32+
raise URLError("Invalid URL")
33+
34+
req = req._to_urllib_request()
35+
req.add_header("Connection", "close")
36+
req.add_header("Authorization", f"Bearer {self.oauth_client.get_access_token()}")
37+
res = urllib.request.urlopen(req, data=data, timeout=timeout)
38+
return http_client.HttpResponse(res)
39+
40+
def _on_err(err_type: str, err: Exception):
41+
logger.error(f"Eureka client error - Type: {err_type}, Error: {err}")
42+
if err_type in (eureka_client.ERROR_REGISTER, eureka_client.ERROR_DISCOVER):
43+
eureka_client.stop()
44+
45+
def init_client():
46+
"""
47+
Initializes the eureka client.
48+
"""
49+
50+
env = AppEnv()
51+
52+
service_registry = env.get_service(label="p.service-registry")
53+
if not service_registry:
54+
raise ValueError("No 'p.service-registry' service instance found.")
55+
56+
credentials = service_registry.credentials
57+
58+
http_client.set_http_client(_OAuth2HttpClient(credentials))
59+
60+
eureka_client.init(
61+
app_name=env.name,
62+
instance_host=env.uris[0],
63+
instance_ip=os.getenv('CF_INSTANCE_INTERNAL_IP'),
64+
instance_id=f"{env.uris[0]}:{env.app.get('instance_id') or '0'}",
65+
instance_unsecure_port_enabled=True,
66+
instance_port=80,
67+
instance_secure_port_enabled=True,
68+
instance_secure_port=443,
69+
vip_adr='UNKNOWN',
70+
secure_vip_addr='UNKNOWN',
71+
eureka_server=credentials['uri'],
72+
on_error=_on_err
73+
)
74+
75+
def call_service(name: str, path: str):
76+
"""
77+
Calls a service with the given name and path.
78+
"""
79+
try:
80+
return eureka_client.do_service(app_name=name, service=path, prefer_https=True)
81+
except urllib.request.HTTPError as e:
82+
logger.error(f"Error calling service {name} at {path}: {e}")
83+
raise e

greeter/oauth2.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import time
2+
import logging
3+
from typing import Optional
4+
import requests
5+
6+
logger = logging.getLogger(__name__)
7+
8+
class OAuth2Client:
9+
"""OAuth 2.0 client for handling client credentials flow with token caching"""
10+
11+
def __init__(self, client_id: str, client_secret: str, access_token_uri: str):
12+
self.client_id = client_id
13+
self.client_secret = client_secret
14+
self.access_token_uri = access_token_uri
15+
self.access_token: Optional[str] = None
16+
self.token_expires_at: Optional[float] = None
17+
18+
def _is_token_expired(self, buffer_seconds: int = 30) -> bool:
19+
"""Check if the current token is expired or will expire soon"""
20+
if not self.access_token or not self.token_expires_at:
21+
return True
22+
23+
# Add buffer time to refresh token before it actually expires
24+
return time.time() >= (self.token_expires_at - buffer_seconds)
25+
26+
def _request_new_token(self) -> str:
27+
"""Request a new access token from the OAuth server"""
28+
# Prepare token request
29+
token_data = {
30+
'grant_type': 'client_credentials',
31+
'client_id': self.client_id,
32+
'client_secret': self.client_secret
33+
}
34+
35+
headers = {
36+
'Content-Type': 'application/x-www-form-urlencoded'
37+
}
38+
39+
try:
40+
response = requests.post(
41+
self.access_token_uri,
42+
data=token_data,
43+
headers=headers,
44+
timeout=30
45+
)
46+
response.raise_for_status()
47+
48+
token_response = response.json()
49+
self.access_token = token_response.get('access_token')
50+
51+
# Calculate expiration time
52+
expires_in = token_response.get('expires_in', 3600) # Default to 1 hour
53+
self.token_expires_at = time.time() + expires_in
54+
55+
if not self.access_token:
56+
logger.error("No access token received")
57+
raise ValueError("No access token received")
58+
59+
return self.access_token
60+
except requests.exceptions.RequestException as e:
61+
logger.error(f"Failed to get access token: {e}")
62+
raise ValueError(f"Failed to get access token: {e}") from e
63+
64+
def get_access_token(self) -> str:
65+
"""Get OAuth 2.0 access token using client credentials flow"""
66+
# Check if we have a valid cached token
67+
if not self._is_token_expired():
68+
return self.access_token
69+
# Token is expired or doesn't exist, get a new one
70+
return self._request_new_token()

greeter/requirements.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ cfenv==0.5.3
55
charset-normalizer==3.4.2
66
click==8.2.1
77
dill==0.4.0
8+
dnspython==2.7.0
89
Flask==3.1.1
910
furl==2.1.4
1011
gunicorn==23.0.0
1112
idna==3.10
13+
ifaddr==0.2.0
1214
isort==6.0.1
1315
itsdangerous==2.2.0
1416
Jinja2==3.1.6
@@ -17,10 +19,11 @@ mccabe==0.7.0
1719
orderedmultidict==1.0.1
1820
packaging==25.0
1921
platformdirs==4.3.8
22+
py_eureka_client==0.11.13
2023
pylint==3.3.7
2124
python-dotenv==1.0.0
2225
requests==2.31.0
2326
six==1.17.0
2427
tomlkit==0.13.3
2528
urllib3==2.5.0
26-
Werkzeug==3.1.3
29+
Werkzeug==3.1.3

manifest.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ applications:
66
path: greeter-messages
77
services:
88
- greeter-service-registry
9-
env:
10-
PROFILES: development
119

1210
- name: greeter
1311
random-route: true
@@ -16,5 +14,3 @@ applications:
1614
path: greeter
1715
services:
1816
- greeter-service-registry
19-
env:
20-
PROFILES: development

0 commit comments

Comments
 (0)