1+ import base64
12import json
23import os
34
89
910import metrics_utility .base as base
1011
11- from metrics_utility .exceptions import FailedToUploadPayload
12+ from metrics_utility .exceptions import FailedToUploadPayload , MetricsException
1213from metrics_utility .logger import logger
1314
1415
@@ -38,6 +39,11 @@ def _get_rh_password(self):
3839 def _get_http_request_headers (self ):
3940 return get_awx_http_client_headers ()
4041
42+ # only service_account was reachable in 0.6.0 without changing code; identity still isn't
43+ # FIXME: only use for service if needed, remove if not
44+ def _shipping_auth (self ):
45+ return os .getenv ('METRICS_UTILITY_SHIP_AUTH' , 'service_account' ) # identity | mutual_tls | service_account | user_pass
46+
4147 def is_shipping_configured (self ):
4248 if not self .tar_path :
4349 logger .error ('Insights for Ansible Automation Platform TAR not found' )
@@ -54,17 +60,19 @@ def is_shipping_configured(self):
5460 logger .error ('METRICS_UTILITY_CRC_INGRESS_URL is not set' )
5561 return False
5662
57- if not self ._get_sso_url ():
58- logger .error ('METRICS_UTILITY_CRC_SSO_URL is not set' )
59- return False
63+ if self ._shipping_auth () == 'service_account' :
64+ if not self ._get_sso_url ():
65+ logger .error ('METRICS_UTILITY_CRC_SSO_URL is not set' )
66+ return False
6067
61- if not self ._get_rh_user ():
62- logger .error ('METRICS_UTILITY_SERVICE_ACCOUNT_ID is not set' )
63- return False
68+ if self ._shipping_auth () in ('service_account' , 'user_pass' ):
69+ if not self ._get_rh_user ():
70+ logger .error ('METRICS_UTILITY_SERVICE_ACCOUNT_ID is not set' )
71+ return False
6472
65- if not self ._get_rh_password ():
66- logger .error ('METRICS_UTILITY_SERVICE_ACCOUNT_SECRET is not set' )
67- return False
73+ if not self ._get_rh_password ():
74+ logger .error ('METRICS_UTILITY_SERVICE_ACCOUNT_SECRET is not set' )
75+ return False
6876
6977 # _get_proxy_url is optional
7078
@@ -89,43 +97,110 @@ def ship(self):
8997 )
9098 }
9199
92- s = requests .Session ()
93- s .headers = self ._get_http_request_headers ()
94- s .headers .pop ('Content-Type' )
100+ response = self ._request (files )
101+
102+ # Accept 2XX status_codes
103+ if response .status_code >= 300 :
104+ raise FailedToUploadPayload (f'Upload failed with status { response .status_code } , { response .text } ' )
105+
106+ self .shipping_successful = True
107+ return True
108+
109+ def _session (self ):
110+ session = requests .Session ()
111+ session .headers = self ._get_http_request_headers ()
112+ session .headers .pop ('Content-Type' )
113+
114+ session .verify = self .CERT_PATH
115+ session .timeout = (31 , 31 )
95116
96- url = self ._get_ingress_url ()
97- self .shipping_successful = self ._send_data (url , files , s )
117+ return session
98118
99- return self .shipping_successful
119+ def _bearer (self ):
120+ response = requests .post (
121+ self ._get_sso_url (),
122+ data = {'client_id' : self ._get_rh_user (), 'client_secret' : self ._get_rh_password (), 'grant_type' : 'client_credentials' },
123+ headers = {'Content-Type' : 'application/x-www-form-urlencoded' },
124+ timeout = (31 , 31 ),
125+ verify = self .CERT_PATH ,
126+ )
100127
101- def _send_data (self , url , files , session ):
102- sso_url = self ._get_sso_url ()
103- headers = {'Content-Type' : 'application/x-www-form-urlencoded' }
128+ return json .loads (response .content )['access_token' ]
104129
105- data = {'client_id' : self ._get_rh_user (), 'client_secret' : self ._get_rh_password (), 'grant_type' : 'client_credentials' }
130+ def _proxies (self ):
131+ if not self ._get_proxy_url ():
132+ return {}
106133
107- r = requests .post (sso_url , headers = headers , data = data , verify = self .CERT_PATH , timeout = (31 , 31 ))
108- access_token = json .loads (r .content )['access_token' ]
134+ return {'https' : self ._get_proxy_url ()}
109135
110- # Query crc with bearer token
111- headers = session .headers
112- headers ['authorization' ] = f'Bearer { access_token } '
136+ def _identity (self , url , files ):
137+ session = self ._session ()
113138
114- proxies = {}
115- if self ._get_proxy_url ():
116- proxies = {'https' : self ._get_proxy_url ()}
139+ # FIXME: make parametrizable, if used
140+ identity = {
141+ 'identity' : {
142+ 'type' : 'User' ,
143+ 'account_number' : '0000001' ,
144+ 'user' : {'is_org_admin' : True },
145+ 'internal' : {'org_id' : '000001' },
146+ }
147+ }
148+ session .headers ['x-rh-identity' ] = base64 .b64encode (json .dumps (identity ).encode ('utf8' ))
117149
118- response = session .post (
150+ return session .post (
119151 url ,
120152 files = files ,
121- verify = self .CERT_PATH ,
122- proxies = proxies ,
123- headers = headers ,
124- timeout = (31 , 31 ),
153+ proxies = self ._proxies (),
125154 )
126155
127- # Accept 2XX status_codes
128- if response .status_code >= 300 :
129- raise FailedToUploadPayload (f'Upload failed with status { response .status_code } , { response .text } ' )
156+ def _mutual_tls (self , url , files ):
157+ session = self ._session ()
130158
131- return True
159+ # a single file (containing the private key and the certificate)
160+ # or a tuple of both files paths (cert_file, keyfile)
161+ session .cert = (
162+ '/etc/pki/consumer/cert.pem' ,
163+ '/etc/pki/consumer/key.pem' ,
164+ )
165+
166+ return session .post (
167+ url ,
168+ files = files ,
169+ proxies = self ._proxies (),
170+ )
171+
172+ def _service_account (self , url , files ):
173+ session = self ._session ()
174+
175+ access_token = self ._bearer ()
176+ session .headers ['authorization' ] = f'Bearer { access_token } '
177+
178+ return session .post (
179+ url ,
180+ files = files ,
181+ proxies = self ._proxies (),
182+ )
183+
184+ def _user_pass (self , url , files ):
185+ session = self ._session ()
186+ session .auth = (self ._get_rh_user (), self ._get_rh_password ())
187+
188+ return session .post (
189+ url ,
190+ files = files ,
191+ proxies = self ._proxies (),
192+ )
193+
194+ def _request (self , url , files ):
195+ url = self ._get_ingress_url ()
196+ mode = self ._shipping_auth ()
197+ if mode == 'identity' :
198+ return self ._identity (url , files )
199+ elif mode == 'mutual_tls' :
200+ return self ._mutual_tls (url , files )
201+ elif mode == 'service_account' :
202+ return self ._service_account (url , files )
203+ elif mode == 'user_pass' :
204+ return self ._user_pass (url , files )
205+ else :
206+ raise MetricsException (f'Invalid METRICS_UTILITY_SHIP_AUTH { mode } : identity | mutual_tls | service_account (default) | user_pass' )
0 commit comments