11import os
22import json
33import httpx
4+ from string import Template
45from psycopg import AsyncConnection
56
67DEFAULT_HEADERS = {"Content-Type" : "application/json" }
@@ -96,60 +97,46 @@ async def sync_alerting(context: dict, token: dict, userinfo: dict):
9697 )
9798
9899
99- async def sync_datasource (context : dict , token : dict , userinfo : dict ):
100- username = userinfo [ "preferred_username" ]
101- prometheus_url = f" { DRYCC_CONTROLLER_API_URL } /v2/prometheus/ { username } "
102- datasource_name = "Prometheus on Drycc"
100+ async def sync_datasources (context : dict , token : dict , userinfo : dict ):
101+ headers = api_headers ( context , userinfo )
102+ datasources_path = os . path . join ( os . path . dirname ( __file__ ), ".." , "datasources" )
103+ created , drycc_token = await _get_or_create_drycc_token ( userinfo [ "preferred_username" ], token )
103104 async with httpx .AsyncClient () as client :
104- resp = await client .get (
105- api_url ("/api/datasources/name/{datasource_name}" ),
106- headers = api_headers (context , userinfo ))
107- if resp .status_code == 200 :
108- datasource = resp .json ()
109- drycc_token_uuid = datasource .get ("jsonData" , {}).get ("tokenId" , None )
110- drycc_token = await _get_or_create_drycc_token (drycc_token_uuid , token )
111- if "token" in drycc_token :
112- datasource ["jsonData" ]["tokenId" ] = drycc_token ["uuid" ]
113- datasource ["secureJsonData" ] = {
114- "httpHeaderValue1" : f"Token { drycc_token ["token" ]} " ,
115- }
116- await _set_datasource_read_only (False , datasource ["id" ])
117- await client .put (
118- api_url (f"/api/datasources/uid/{ datasource ["uid" ]} " ),
119- headers = api_headers (context , userinfo ),
120- json = datasource ,
121- )
122- await _set_datasource_read_only (True , datasource ["id" ])
123- return
124- drycc_token = await _get_or_create_drycc_token (None , token )
125- resp = await client .post (
126- api_url ("/api/datasources" ),
127- headers = api_headers (context , userinfo ),
128- json = {
129- "name" : datasource_name ,
130- "type" : "prometheus" ,
131- "url" : prometheus_url ,
132- "access" : "proxy" ,
133- "basicAuth" : False ,
134- "jsonData" : {
135- "tokenId" : drycc_token ["uuid" ],
136- "httpHeaderName1" : "Authorization" ,
137- "httpMethod" : "GET" ,
138- "timeInterval" : DRYCC_GRAFANA_REFRESH ,
139- },
140- "secureJsonData" : {
141- "httpHeaderValue1" : f"Token { drycc_token ["token" ]} " ,
142- },
143- },
144- )
145- await _set_datasource_read_only (True , resp .json ()["datasource" ]["id" ])
146-
147-
148- async def sync_dashboard (context : dict , token : dict , userinfo : dict ):
149- dashboard_path = os .path .join (os .path .dirname (__file__ ), ".." , "dashboard" )
105+ for filename in os .listdir (datasources_path ):
106+ with open (os .path .join (datasources_path , filename )) as f :
107+ template = Template (f .read ())
108+ datasource = json .loads (template .substitute (
109+ controller_api_url = DRYCC_CONTROLLER_API_URL ,
110+ username = userinfo ["preferred_username" ],
111+ time_interval = DRYCC_GRAFANA_REFRESH ,
112+ token = drycc_token
113+ ))
114+ resp = await client .get (
115+ api_url (f"/api/datasources/name/{ datasource ["name" ]} " ), headers = headers )
116+ if resp .status_code == 200 :
117+ if created :
118+ datasource = resp .json ()
119+ datasource ["secureJsonData" ] = {
120+ "httpHeaderValue1" : f"Token { drycc_token } " ,
121+ }
122+ await _set_datasource_read_only (False , datasource ["id" ])
123+ await client .put (
124+ api_url (f"/api/datasources/uid/{ datasource ["uid" ]} " ),
125+ headers = headers , json = datasource )
126+ await _set_datasource_read_only (True , datasource ["id" ])
127+ elif resp .status_code == 404 :
128+ resp = await client .post (
129+ api_url ("/api/datasources" ), headers = headers , json = datasource )
130+ await _set_datasource_read_only (True , resp .json ()["datasource" ]["id" ])
131+ else :
132+ raise ValueError (f"grafana returned an unexpected status: { resp .status_code } " )
133+
134+
135+ async def sync_dashboards (context : dict , token : dict , userinfo : dict ):
136+ dashboards_path = os .path .join (os .path .dirname (__file__ ), ".." , "dashboards" )
150137 async with httpx .AsyncClient () as client :
151- for filename in os .listdir (dashboard_path ):
152- with open (os .path .join (dashboard_path , filename )) as f :
138+ for filename in os .listdir (dashboards_path ):
139+ with open (os .path .join (dashboards_path , filename )) as f :
153140 dashboard = json .load (f )
154141 dashboard .update ({"id" : None , "refresh" : DRYCC_GRAFANA_REFRESH })
155142 await client .post (
@@ -172,21 +159,37 @@ async def _set_datasource_read_only(read_only: bool, id: str):
172159 await conn .commit ()
173160
174161
175- async def _get_or_create_drycc_token (drycc_token_uuid : str , token : dict ):
176- headers = {"Authorization" : f"Bearer { token ["access_token" ]} " }
177- async with httpx .AsyncClient () as client :
178- if drycc_token_uuid :
179- resp = await client .get (
180- f"{ DRYCC_CONTROLLER_API_URL } /v2/tokens/{ drycc_token_uuid } " , headers = headers )
181- if resp .status_code == 200 :
182- return resp .json ()
183- elif resp .status_code != 404 :
184- raise ValueError (
185- f"the Drycc controller returned an unsupported status code { resp .status_code } "
162+ async def _get_or_create_drycc_token (username , token : dict ):
163+ async def _check_or_create_drycc_token (drycc_token , token ):
164+ async with httpx .AsyncClient () as client :
165+ created = False if drycc_token else True
166+ if drycc_token :
167+ headers = {"Authorization" : f"Token { drycc_token } " }
168+ resp = await client .get (
169+ f"{ DRYCC_CONTROLLER_API_URL } /v2/auth/whoami" , headers = headers )
170+ if resp .status_code in [401 , 403 ]:
171+ created = True
172+ if created :
173+ headers = {"Authorization" : f"Bearer { token ["access_token" ]} " }
174+ data = (await client .post (
175+ f"{ DRYCC_CONTROLLER_API_URL } /v2/auth/token/?alias=grafana-datasource" ,
176+ headers = headers , json = token )).json ()
177+ drycc_token = data ["token" ]
178+ return created , drycc_token
179+
180+ async with await AsyncConnection .connect (os .environ .get ("GF_DATABASE_URL" )) as conn :
181+ async with conn .cursor () as cursor :
182+ await cursor .execute (
183+ "SELECT o_auth_id_token FROM user_auth WHERE auth_module=%s AND auth_id=%s" ,
184+ ("authproxy" , username )
185+ )
186+ drycc_token = (await cursor .fetchone ())[0 ]
187+ created , drycc_token = await _check_or_create_drycc_token (drycc_token , token )
188+ if created :
189+ async with conn .cursor () as cursor :
190+ await cursor .execute (
191+ "UPDATE user_auth SET o_auth_id_token=%s WHERE auth_module=%s AND auth_id=%s" ,
192+ (drycc_token , "authproxy" , username )
186193 )
187- resp = await client .post (
188- f"{ DRYCC_CONTROLLER_API_URL } /v2/auth/token/?alias=grafana-datasource" ,
189- headers = headers ,
190- json = token ,
191- )
192- return resp .json ()
194+ await conn .commit ()
195+ return created , drycc_token
0 commit comments