Skip to content

Commit 1247191

Browse files
Merge pull request #79 from IBM/cloud_manager_secret
Added cloud manager secret
2 parents c1689c6 + ffd3aa2 commit 1247191

File tree

1 file changed

+78
-15
lines changed

1 file changed

+78
-15
lines changed

manager_net.py

Lines changed: 78 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,16 @@ class ManagerNet(object):
2020
namespace = 'apic-management'
2121
username = ''
2222
password = ''
23+
cm_username = ''
24+
cm_password = ''
2325
hostname = ''
24-
client_id = "caa87d9a-8cd7-4686-8b6e-ee2cdc5ee267"
25-
client_secret = "3ecff363-7eb3-44be-9e07-6d4386c48b0b"
26+
default_client_id = "caa87d9a-8cd7-4686-8b6e-ee2cdc5ee267"
27+
default_client_secret = "3ecff363-7eb3-44be-9e07-6d4386c48b0b"
28+
client_id = default_client_id
29+
client_secret = default_client_secret
2630
grant_type = 'password'
31+
cm_token = None # Cloud manager access token
32+
cm_token_expires = 0 # Cloud manager access token expiration
2733
token = None
2834
token_expires = 0
2935
max_frequency = 600
@@ -43,11 +49,13 @@ def __init__(self, config, trawler):
4349
self.namespace = config.get('namespace', 'default')
4450
# Maximum frequency to pull data from APIC
4551
self.max_frequency = int(config.get('frequency', 600))
46-
self.grant_type = config.get('grant_type', 'password')
52+
#self.grant_type = config.get('grant_type', 'password')
4753
self.org_metrics = (config.get('process_org_metrics', 'true') == 'true')
4854
self.version = Gauge('apiconnect_build_info',
4955
"A metric with a constant '1' value labeled with API Connect version details",
5056
["version", "juhu_release"])
57+
58+
# Provider creds
5159
if 'secret' in config:
5260
# If config points to a secret, then load from that
5361
# either in this namespace, or the specified one
@@ -63,30 +71,52 @@ def __init__(self, config, trawler):
6371
else:
6472
# Load password from secret `cloudmanager_password`
6573
self.password = trawler.read_secret('cloudmanager_password')
74+
75+
# Cloud manager creds
76+
if 'cloud_manager_secret' in config:
77+
# If config points to a secret, then load from that
78+
# either in this namespace, or the specified one
79+
self.load_credentials_from_secret(
80+
config.get('cloud_manager_secret' ),
81+
config.get('cloud_manager_secret_namespace', self.namespace),
82+
cloud_manager = True)
83+
84+
6685
if self.password is None:
6786
# Use out of box default password
6887
self.password = 'admin'
88+
6989
self.hostname = self.find_hostname()
7090
logger.debug("Hostname found is {}".format(self.hostname))
7191
self.trawler = trawler
7292

73-
def load_credentials_from_secret(self, secret_name, namespace):
93+
def load_credentials_from_secret(self, secret_name, namespace, cloud_manager=False):
7494
try:
7595
if self.use_kubeconfig:
7696
config.load_kube_config()
7797
else:
7898
config.load_incluster_config()
99+
79100
v1 = client.CoreV1Api()
80101
logger.info("Loading cloud manager credentials from secret {} in namespace {}".format(secret_name, namespace))
81102
# Get credentials secret
82103
secrets_response = v1.read_namespaced_secret(name=secret_name, namespace=namespace)
83-
if 'password' in secrets_response.data:
84-
self.password = base64.b64decode(secrets_response.data['password']).decode('utf-8')
85-
self.username = base64.b64decode(secrets_response.data['username']).decode('utf-8')
86-
logger.info("Username to use is {}, password length is {}".format(self.username, len(self.password)))
104+
if cloud_manager:
105+
if 'password' in secrets_response.data:
106+
self.cm_password = base64.b64decode(secrets_response.data['password']).decode('utf-8')
107+
self.cm_username = base64.b64decode(secrets_response.data['username']).decode('utf-8')
108+
logger.info("Username to use is {}, password length is {}".format(self.cm_username, len(self.cm_password)))
109+
else:
110+
if 'password' in secrets_response.data:
111+
self.password = base64.b64decode(secrets_response.data['password']).decode('utf-8')
112+
self.username = base64.b64decode(secrets_response.data['username']).decode('utf-8')
113+
logger.info("Username to use is {}, password length is {}".format(self.username, len(self.password)))
114+
115+
# Client secret is not applicable to cloud manager so no test needed
87116
if 'client_secret' in secrets_response.data:
88117
self.client_secret = base64.b64decode(secrets_response.data['client_secret']).decode('utf-8')
89118
self.client_id = base64.b64decode(secrets_response.data['client_id']).decode('utf-8')
119+
self.grant_type = 'client_credentials'
90120
logger.info("Client ID to use is {}, Client Secret length is {}".format(self.client_id, len(self.client_secret)))
91121

92122
except client.rest.ApiException as e:
@@ -128,15 +158,20 @@ def find_hostname(self):
128158
logger.exception(e)
129159

130160
def get_webhook_status(self):
131-
logger.info("Getting webhook data from API Manager")
161+
"""Get the webhook data from the API Manager
162+
This requires cloud manager access
163+
"""
164+
if not self.cm_token:
165+
logger.debug("No cloud manager token available. Not getting webhook data")
166+
logger.info("Getting webhook data from Cloud Manager")
132167
try:
133168
url = "https://{}/api/cloud/webhooks".format(self.hostname)
134169
response = requests.get(
135170
url=url,
136171
headers={
137172
"Accept": "application/json",
138173
"Content-Type": "application/json",
139-
"Authorization": "Bearer {}".format(self.token),
174+
"Authorization": "Bearer {}".format(self.cm_token),
140175
},
141176
verify=False
142177
)
@@ -200,6 +235,10 @@ def fish(self):
200235
logger.debug("Disabled because a fatal error already occurred")
201236
return
202237

238+
# Allow 10 seconds to run
239+
if self.cm_token_expires - 10 < time.time():
240+
self.get_token(self.hostname, cloud_manager=True)
241+
203242
# Allow 10 seconds to run
204243
if self.token_expires - 10 < time.time():
205244
self.get_token(self.hostname)
@@ -214,6 +253,7 @@ def fish(self):
214253
logger.debug(self.data)
215254
else:
216255
logger.warning("No token")
256+
217257
if 'counts' in self.data:
218258
for object_type in self.data['counts']:
219259
logger.debug("Type: {}, Value: {}".format(object_type, self.data['counts'][object_type]))
@@ -223,7 +263,9 @@ def fish(self):
223263
if org['org_type'] != 'admin':
224264
for catalog in org['catalogs']['results']:
225265
self.process_org_metrics(org['name'], catalog['name'])
226-
self.get_webhook_status()
266+
267+
if self.cm_token:
268+
self.get_webhook_status()
227269

228270
@alog.timed_function(logger.trace)
229271
def process_org_metrics(self, org_name, catalog_name):
@@ -273,7 +315,13 @@ def process_org_metrics(self, org_name, catalog_name):
273315

274316
# Get the authorization bearer token
275317
# See https://chrisphillips-cminion.github.io/apiconnect/2019/09/18/GettingoAuthTokenFromAPIC.html
276-
def get_token(self, host):
318+
def get_token(self, host, cloud_manager=False):
319+
"""Get the auth token from API Manager or Cloud Manager
320+
API Manager requires client credentials, client_id and client_secret
321+
Cloud Manager requires username and password, along with the known client_id and client_secret
322+
The secret in the config could be either grant_type client_credentials or password,
323+
but cloud manager access is always requried to be grant_type password.
324+
"""
277325
logger.debug("Getting bearer token")
278326

279327
headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
@@ -284,6 +332,12 @@ def get_token(self, host):
284332
data['username'] = self.username
285333
data['password'] = self.password
286334
data['realm'] = 'admin/default-idp-1'
335+
if cloud_manager:
336+
data['client_id'] = self.default_client_id
337+
data['client_secret'] = self.default_client_secret
338+
data['username'] = self.cm_username
339+
data['password'] = self.cm_password
340+
data['realm'] = 'admin/default-idp-1'
287341

288342
url = "https://{}/api/token".format(host)
289343
response = requests.post(
@@ -293,10 +347,19 @@ def get_token(self, host):
293347
verify=False)
294348

295349
if response.status_code == 200:
350+
token_expires = 0
351+
token_type = "Token"
296352
json_data = response.json()
297-
self.token = json_data['access_token']
298-
self.token_expires = json_data['expires_in'] + time.time()
299-
logger.info("Token expires at {} UTC".format(datetime.datetime.utcfromtimestamp(int(self.token_expires))))
353+
if cloud_manager:
354+
self.cm_token = json_data['access_token']
355+
self.cm_token_expires = json_data['expires_in'] + time.time()
356+
token_expires = self.cm_token_expires
357+
token_type = "Cloud Manager Token"
358+
else:
359+
self.token = json_data['access_token']
360+
self.token_expires = json_data['expires_in'] + time.time()
361+
token_expires = self.token_expires
362+
logger.info("{} expires at {} UTC".format(token_type, datetime.datetime.utcfromtimestamp(int(token_expires))))
300363
else:
301364
logger.error("Disabled manager net as failed to get bearer token: {}".format(response.status_code))
302365
self.errored = True

0 commit comments

Comments
 (0)