11import argparse
2- import json
2+ import datetime
33import os
4+ import socket
45from urllib .parse import urljoin
6+ from uuid import uuid4
57
8+ import pytz
69import requests
10+ from datalake import GzippingFile
11+
12+
13+ class UTC (object ):
14+ # TODO factor this out into a proper library
15+
16+ def __init__ (self , d = "now" ):
17+ if d == "now" :
18+ self ._d = datetime .datetime .utcnow ()
19+ elif isinstance (d , datetime .datetime ):
20+ if not d .tzinfo :
21+ # naive, assume UTC
22+ d = d .replace (tzinfo = pytz .utc )
23+ elif d .tzinfo == pytz .utc :
24+ pass
25+ else :
26+ d = d .astimezone (pytz .utc )
27+ self ._d = d
28+ else :
29+ # TODO convert strings, etc
30+ raise NotImplementedError ()
31+
32+ @property
33+ def iso (self ):
34+ d = self ._d .isoformat ('T' , 'microseconds' )
35+ return d .replace ("+00:00" , "Z" )
736
837
938class MCAPI (object ):
@@ -13,6 +42,17 @@ def __init__(self, mc_base, jwt=None):
1342 self .s = requests .session ()
1443 self .jwt = None
1544
45+ @classmethod
46+ def from_environ (cls , ignore_ssl = False ):
47+ mc_api = cls (os .environ ['MC_BASE' ])
48+ if ignore_ssl :
49+ mc_api .s .verify = False
50+ if os .environ .get ('MC_JWT' ):
51+ mc_api .login (jwt = os .environ ['MC_JWT' ])
52+ else :
53+ mc_api .login (username = os .environ ['MC_USERNAME' ], password = os .environ ['MC_PASSWORD' ])
54+ return mc_api
55+
1656 def get (self , path , * args , ** kwargs ):
1757 r = self .s .get (urljoin (self .mc_base , path ), * args , ** kwargs )
1858 r .raise_for_status ()
@@ -22,6 +62,16 @@ def getj(self, path, *args, **kwargs):
2262 r = self .get (path , * args , ** kwargs )
2363 return r .json ()
2464
65+ def post (self , path , * args , ** kwargs ):
66+ r = self .s .post (urljoin (self .mc_base , path ), * args , ** kwargs )
67+ r .raise_for_status ()
68+ return r
69+
70+ def postj (self , path , * args , ** kwargs ):
71+ ret = self .s .post (urljoin (self .mc_base , path ), * args , ** kwargs )
72+ ret .raise_for_status ()
73+ return ret .json ()
74+
2575 def put (self , path , * args , ** kwargs ):
2676 r = self .s .put (urljoin (self .mc_base , path ), * args , ** kwargs )
2777 r .raise_for_status ()
@@ -145,6 +195,94 @@ def get_pass_task_stack(self, uuid, **kwargs):
145195 json = kwargs
146196 )
147197
198+ def get_latest_file (self , what , where , ** kwargs ):
199+ return self .getj (
200+ f'/api/v0/files/latest/{ what } /{ where } /' ,
201+ params = kwargs
202+ )
203+
204+ def get_files (self , what , ** kwargs ):
205+ kwargs .update ({"what" : what })
206+ return self .getj (
207+ f'/api/v0/files/search/' ,
208+ params = kwargs
209+ )
210+
211+ def get_files_by_cid (self , cid , ** kwargs ):
212+ return self .getj (
213+ f'/api/v0/files/cid/{ cid } /' ,
214+ params = kwargs
215+ )
216+
217+ def get_files_by_work_id (self , work_id , ** kwargs ):
218+ return self .getj (
219+ f'/api/v0/files/work-id/{ work_id } /' ,
220+ params = kwargs
221+ )
222+
223+ def get_file (self , uuid ):
224+ return self .getj (
225+ f'/api/v0/files/{ uuid } /'
226+ )
227+
228+ def download_file (self , uuid ):
229+ return self .get (
230+ f'/api/v0/files/{ uuid } /data/'
231+ )
232+
233+ def download_cid (self , cid ):
234+ return self .get (
235+ f'/api/v0/raw-file/{ cid } /data/'
236+ )
237+
238+ def upload_file (self , path , what , uuid = None , where = None , start = None ,
239+ end = None , work_id = None , content_type = None ):
240+
241+ if uuid is None :
242+ uuid = str (uuid4 ())
243+
244+ if start is None :
245+ start = UTC ("now" ).iso
246+ else :
247+ start = UTC (start ).iso
248+
249+ if where is None :
250+ where = socket .getfqdn ()
251+
252+ f = GzippingFile .from_filename (
253+ path ,
254+ what = what ,
255+ where = where ,
256+ start = start ,
257+ work_id = work_id
258+ )
259+
260+ # get signed upload
261+ signed = self .postj (
262+ f'/api/v0/files/presign/' ,
263+ json = f .metadata
264+ )
265+
266+ file_tuple = ("file" , f )
267+ if content_type is not None :
268+ file_tuple += (content_type ,)
269+
270+ # upload file
271+ if "url" in signed :
272+ signed ["fields" ]["Content-Encoding" ] = "gzip"
273+ resp = requests .post (
274+ signed ["url" ],
275+ data = signed ["fields" ],
276+ files = [file_tuple ]
277+ )
278+ resp .raise_for_status ()
279+
280+ # upload metadata
281+ return self .putj (
282+ f'/api/v0/files/{ uuid } /' ,
283+ json = f .metadata
284+ )
285+
148286 def login (self , username = None , password = None , jwt = None ):
149287 if username is not None and jwt is not None :
150288 raise ValueError ("Can't give both a username and a jwt" )
@@ -213,6 +351,7 @@ def handle_default_args(args):
213351 args .mc_api .login (username = args .username , password = args .password )
214352
215353def from_environ (ignore_ssl = False ):
354+ # deprecated, use MCAPI.from_environ()
216355 mc_api = MCAPI (os .environ ['MC_BASE' ])
217356 if ignore_ssl :
218357 mc_api .s .verify = False
@@ -221,4 +360,4 @@ def from_environ(ignore_ssl=False):
221360 else :
222361 mc_api .login (username = os .environ ['MC_USERNAME' ], password = os .environ ['MC_PASSWORD' ])
223362
224- return mc_api
363+ return mc_api
0 commit comments