Skip to content

Commit 30fd3aa

Browse files
committed
updating client to include additional commands for labels, etc.
1 parent 49aabee commit 30fd3aa

File tree

5 files changed

+126
-12
lines changed

5 files changed

+126
-12
lines changed

examples/singularity_hub/compare_builds.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,42 @@
99
'vsoch/pefinder']
1010

1111
from singularity.hub.client import Client
12+
from singularity.package import get_image_hash
1213

13-
client = Client()
14+
import tempfile
15+
import os
16+
import demjson
17+
import pandas
18+
import shutil
19+
20+
shub = Client() # Singularity Hub Client
1421
results = dict()
1522

23+
# Let's keep images in a temporary folder
24+
storage = tempfile.mkdtemp()
25+
os.chdir(storage)
26+
27+
# We will keep a table of information
28+
columns = ['name','build_time_seconds','hash','size','commit','estimated_os']
29+
df = pandas.DataFrame(columns=columns)
30+
1631
for container_name in container_names:
1732

1833
# Retrieve the container based on the name
19-
collection = client.get_collection(container_name)
34+
collection = shub.get_collection(container_name)
2035
container_ids = collection['container_set']
2136
containers = []
2237
for container_id in container_ids:
23-
container = client.get_container(container_id)
24-
containers.append(container)
38+
manifest = shub.get_container(container_id)
39+
containers.append(manifest)
40+
image = shub.pull_container(manifest,
41+
download_folder=storage,
42+
name="%s.img.gz" %(manifest['version']))
43+
# Get hash of file
44+
hashes.append(get_image_hash(image))
45+
df.loc['%s-%s' %(container_name,manifest['version'])]
46+
47+
results[container_name] = {'collection':collection,
48+
'containers':containers}
49+
50+
shutil.rmtree(storage)

singularity/hub/base.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
api_post
1414
)
1515

16+
import os
17+
import sys
18+
from singularity.logman import bot
19+
1620
api_base = "https://singularity-hub.org/api"
1721

1822
def get_template(container_name,get_type=None):
@@ -36,6 +40,56 @@ def get_template(container_name,get_type=None):
3640
if image['repo_tag'] is not None and get_type is not "collection":
3741
url = "%s:%s" %(url,image['repo_tag'])
3842

39-
result = api_get(url)
43+
result = api_get(url)
4044

4145
return result
46+
47+
48+
def download_image(manifest,download_folder=None,extract=True,name=None):
49+
'''download_image will download a singularity image from singularity
50+
hub to a download_folder, named based on the image version (commit id)
51+
:param manifest: the manifest obtained with get_manifest
52+
:param download_folder: the folder to download to, if None, will be pwd
53+
:param extract: if True, will extract image to .img and return that.
54+
:param name: if defined, use custom set image name instead of default
55+
'''
56+
if name is not None:
57+
image_file = name
58+
else:
59+
image_file = get_image_name(manifest)
60+
61+
print("Found image %s:%s" %(manifest['name'],manifest['branch']))
62+
print("Downloading image... %s" %(image_file))
63+
64+
if download_folder != None:
65+
image_file = "%s/%s" %(download_folder,image_file)
66+
url = manifest['image']
67+
image_file = api_get(url,stream_to=image_file)
68+
if extract == True:
69+
print("Decompressing %s" %image_file)
70+
os.system('gzip -d -f %s' %(image_file))
71+
image_file = image_file.replace('.gz','')
72+
return image_file
73+
74+
75+
# Various Helpers ---------------------------------------------------------------------------------
76+
def get_image_name(manifest,extension='img.gz',use_hash=False):
77+
'''get_image_name will return the image name for a manifest
78+
:param manifest: the image manifest with 'image' as key with download link
79+
:param use_hash: use the image hash instead of name
80+
'''
81+
if not use_hash:
82+
image_name = "%s-%s.%s" %(manifest['name'].replace('/','-'),
83+
manifest['branch'].replace('/','-'),
84+
extension)
85+
else:
86+
image_url = os.path.basename(unquote(manifest['image']))
87+
image_name = re.findall(".+[.]%s" %(extension),image_url)
88+
if len(image_name) > 0:
89+
image_name = image_name[0]
90+
else:
91+
bot.logger.error("Singularity Hub Image not found with expected extension %s, exiting.",extension)
92+
sys.exit(1)
93+
94+
bot.logger.info("Singularity Hub Image: %s", image_name)
95+
return image_name

singularity/hub/client.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,12 @@
1010
)
1111

1212
from singularity.logman import bot
13-
from singularity.hub.base import get_template
13+
from singularity.hub.base import (
14+
get_template,
15+
download_image
16+
)
1417

18+
import demjson
1519

1620
class Client(object):
1721

@@ -31,13 +35,27 @@ def update_headers(self, headers):
3135
for key,value in headers.items():
3236
self.client.headers[key] = item
3337

38+
def load_metrics(self,manifest):
39+
'''load metrics about a container build from the manifest
40+
'''
41+
return demjson.decode(manifest['metrics'])
42+
3443

3544
def get_container(self,container_name):
3645
'''get a container or return None.
3746
'''
3847
return get_template(container_name,"container")
3948

4049

50+
def pull_container(self,manifest,download_folder=None,extract=True,name=None):
51+
'''pull a container to the local machine'''
52+
return download_image(manifest=manifest,
53+
download_folder=download_folder,
54+
extract=extract,
55+
name=name)
56+
57+
58+
4159
def get_collection(self,container_name):
4260
'''get a container collection or return None.
4361
'''

singularity/hub/utils.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,29 +13,44 @@
1313
import sys
1414

1515

16-
def api_get(url,headers=None,token=None,data=None, return_json=True):
16+
def api_get(url,headers=None,token=None,data=None, return_json=True, stream_to=None):
1717
'''api_get will use requests to get a particular url
1818
:param url: the url to send file to
1919
:param headers: a dictionary with headers for the request
2020
:param putdata: additional data to add to the request
2121
:param return_json: return json if successful
22+
:param stream_to: stream the response to file
2223
'''
2324
bot.logger.debug("GET %s",url)
2425

26+
stream = False
27+
if stream_to is not None:
28+
stream = True
29+
2530
if headers == None:
2631
headers = get_headers(token=token)
32+
2733
if data == None:
2834
response = requests.get(url,
29-
headers=headers)
35+
headers=headers,
36+
stream=stream)
3037
else:
3138
response = requests.get(url,
3239
headers=headers,
33-
json=data)
40+
json=data,
41+
stream=stream)
3442

35-
if response.status_code == 200 and return_json:
43+
if response.status_code == 200 and return_json and not stream:
3644
return response.json()
3745

38-
return response
46+
47+
chunk_size = 1 << 20
48+
with open(stream_to,'wb') as filey:
49+
for chunk in response.iter_content(chunk_size=chunk_size):
50+
filey.write(chunk)
51+
52+
return stream_to
53+
3954

4055

4156
def api_put(url,headers=None,token=None,data=None, return_json=True):
@@ -116,7 +131,7 @@ def parse_container_name(image):
116131

117132
# If the user provided a number (unique id for an image), return it
118133
if is_number(image) == True:
119-
logger.info("Numeric image ID %s found.", image)
134+
bot.logger.info("Numeric image ID %s found.", image)
120135
return int(image)
121136

122137
image = image.split('/')

singularity/version.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
INSTALL_REQUIRES = (
1111

12+
('demjson', {'min_version': None}),
1213
('flask', {'min_version': '0.12'}),
1314
('flask-restful', {'min_version': None}),
1415
('pandas', {'min_version': '0.19.2'}),

0 commit comments

Comments
 (0)