11import argparse
2+ import datetime
3+ import io
24import json
35import os
6+ import socket
7+ import zlib
48from urllib .parse import urljoin
9+ from uuid import uuid4
510
11+ import pytz
612import requests
13+ from datalake import GzippingFile
14+
15+
16+ class UTC (object ):
17+ # TODO factor this out into a proper library
18+
19+ def __init__ (self , d = "now" ):
20+ if d == "now" :
21+ self ._d = datetime .datetime .utcnow ()
22+ elif isinstance (d , datetime .datetime ):
23+ if not d .tzinfo :
24+ # naive, assume UTC
25+ d = d .replace (tzinfo = pytz .utc )
26+ elif d .tzinfo == pytz .utc :
27+ pass
28+ else :
29+ d = d .astimezone (pytz .utc )
30+ self ._d = d
31+ else :
32+ # TODO convert strings, etc
33+ raise NotImplementedError ()
34+
35+ @property
36+ def iso (self ):
37+ d = self ._d .isoformat ('T' , 'microseconds' )
38+ return d .replace ("+00:00" , "Z" )
739
840
941class MCAPI (object ):
@@ -13,6 +45,17 @@ def __init__(self, mc_base, jwt=None):
1345 self .s = requests .session ()
1446 self .jwt = None
1547
48+ @classmethod
49+ def from_environ (cls , ignore_ssl = False ):
50+ mc_api = cls (os .environ ['MC_BASE' ])
51+ if ignore_ssl :
52+ mc_api .s .verify = False
53+ if os .environ .get ('MC_JWT' ):
54+ mc_api .login (jwt = os .environ ['MC_JWT' ])
55+ else :
56+ mc_api .login (username = os .environ ['MC_USERNAME' ], password = os .environ ['MC_PASSWORD' ])
57+ return mc_api
58+
1659 def get (self , path , * args , ** kwargs ):
1760 r = self .s .get (urljoin (self .mc_base , path ), * args , ** kwargs )
1861 r .raise_for_status ()
@@ -22,6 +65,16 @@ def getj(self, path, *args, **kwargs):
2265 r = self .get (path , * args , ** kwargs )
2366 return r .json ()
2467
68+ def post (self , path , * args , ** kwargs ):
69+ r = self .s .post (urljoin (self .mc_base , path ), * args , ** kwargs )
70+ r .raise_for_status ()
71+ return r
72+
73+ def postj (self , path , * args , ** kwargs ):
74+ ret = self .s .post (urljoin (self .mc_base , path ), * args , ** kwargs )
75+ ret .raise_for_status ()
76+ return ret .json ()
77+
2578 def put (self , path , * args , ** kwargs ):
2679 r = self .s .put (urljoin (self .mc_base , path ), * args , ** kwargs )
2780 r .raise_for_status ()
@@ -145,6 +198,94 @@ def get_pass_task_stack(self, uuid, **kwargs):
145198 json = kwargs
146199 )
147200
201+ def get_latest_file (self , what , where , ** kwargs ):
202+ return self .getj (
203+ f'/api/v0/files/latest/{ what } /{ where } /' ,
204+ params = kwargs
205+ )
206+
207+ def get_files (self , what , ** kwargs ):
208+ kwargs .update ({"what" : what })
209+ return self .getj (
210+ f'/api/v0/files/search/' ,
211+ params = kwargs
212+ )
213+
214+ def get_files_by_cid (self , cid , ** kwargs ):
215+ return self .getj (
216+ f'/api/v0/files/cid/{ cid } /' ,
217+ params = kwargs
218+ )
219+
220+ def get_files_by_work_id (self , work_id , ** kwargs ):
221+ return self .getj (
222+ f'/api/v0/files/work-id/{ work_id } /' ,
223+ params = kwargs
224+ )
225+
226+ def get_file (self , uuid ):
227+ return self .getj (
228+ f'/api/v0/files/{ uuid } /'
229+ )
230+
231+ def download_file (self , uuid ):
232+ return self .get (
233+ f'/api/v0/files/{ uuid } /data/'
234+ )
235+
236+ def download_cid (self , cid ):
237+ return self .get (
238+ f'/api/v0/raw-file/{ cid } /data/'
239+ )
240+
241+ def upload_file (self , path , what , uuid = None , where = None , start = None ,
242+ end = None , work_id = None , content_type = None ):
243+
244+ if uuid is None :
245+ uuid = str (uuid4 ())
246+
247+ if start is None :
248+ start = UTC ("now" ).iso
249+ else :
250+ start = UTC (start ).iso
251+
252+ if where is None :
253+ where = socket .getfqdn ()
254+
255+ f = GzippingFile .from_filename (
256+ path ,
257+ what = what ,
258+ where = where ,
259+ start = start ,
260+ work_id = work_id
261+ )
262+
263+ # get signed upload
264+ signed = self .postj (
265+ f'/api/v0/files/presign/' ,
266+ json = f .metadata
267+ )
268+
269+ file_tuple = ("file" , f )
270+ if content_type is not None :
271+ file_tuple += (content_type ,)
272+
273+ # upload file
274+ if "url" in signed :
275+ signed ["fields" ]["Content-Encoding" ] = "gzip"
276+ resp = requests .post (
277+ signed ["url" ],
278+ data = signed ["fields" ],
279+ files = [file_tuple ]
280+ )
281+ resp .raise_for_status ()
282+
283+ # upload metadata
284+ return self .putj (
285+ f'/api/v0/files/{ uuid } /' ,
286+ json = f .metadata
287+ )
288+
148289 def login (self , username = None , password = None , jwt = None ):
149290 if username is not None and jwt is not None :
150291 raise ValueError ("Can't give both a username and a jwt" )
@@ -213,6 +354,7 @@ def handle_default_args(args):
213354 args .mc_api .login (username = args .username , password = args .password )
214355
215356def from_environ (ignore_ssl = False ):
357+ # deprecated, use MCAPI.from_environ()
216358 mc_api = MCAPI (os .environ ['MC_BASE' ])
217359 if ignore_ssl :
218360 mc_api .s .verify = False
@@ -221,4 +363,4 @@ def from_environ(ignore_ssl=False):
221363 else :
222364 mc_api .login (username = os .environ ['MC_USERNAME' ], password = os .environ ['MC_PASSWORD' ])
223365
224- return mc_api
366+ return mc_api
0 commit comments