Skip to content

Commit 95bae96

Browse files
committed
main is missing from the repo!
1 parent 8395f90 commit 95bae96

File tree

3 files changed

+241
-3
lines changed

3 files changed

+241
-3
lines changed

singularity/build/main.py

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
#!/usr/bin/env python
2+
3+
'''
4+
build/main.py: main runner for Singularity Hub builds
5+
6+
'''
7+
8+
from singularity.version import (
9+
__version__ as singularity_python_version
10+
)
11+
12+
from singularity.package import (
13+
build_from_spec,
14+
#estimate_image_size,
15+
package
16+
)
17+
18+
from singularity.build.utils import (
19+
get_singularity_version,
20+
stop_if_result_none,
21+
test_container
22+
)
23+
24+
from singularity.utils import download_repo
25+
from singularity.analysis.classify import (
26+
get_tags,
27+
get_diff,
28+
estimate_os,
29+
file_counts,
30+
extension_counts
31+
)
32+
33+
from datetime import datetime
34+
from glob import glob
35+
import io
36+
import json
37+
import os
38+
import pickle
39+
import re
40+
import requests
41+
42+
from retrying import retry
43+
# https://cloud.google.com/storage/docs/exponential-backoff
44+
45+
import shutil
46+
import sys
47+
import tempfile
48+
import time
49+
50+
shub_api = "http://www.singularity-hub.org/api"
51+
52+
from singularity.logman import bot
53+
54+
def run_build(build_dir,params,verbose=True):
55+
'''run_build takes a build directory and params dictionary, and does the following:
56+
- downloads repo to a temporary directory
57+
- changes branch or commit, if needed
58+
- creates and bootstraps singularity image from Singularity file
59+
- returns a dictionary with:
60+
image (path), image_package (path), metadata (dict)
61+
62+
The following must be included in params:
63+
spec_file, repo_url, branch, commit
64+
65+
Optional parameters
66+
size
67+
'''
68+
69+
# Download the repo and image
70+
download_repo(repo_url=params['repo_url'],
71+
destination=build_dir)
72+
73+
os.chdir(build_dir)
74+
if params['branch'] != None:
75+
bot.logger.info('Checking out branch %s',params['branch'])
76+
os.system('git checkout %s' %(params['branch']))
77+
78+
# Commit
79+
if params['commit'] not in [None,'']:
80+
bot.logger.info('Checking out commit %s',params['commit'])
81+
os.system('git checkout %s .' %(params['commit']))
82+
83+
# From here on out commit is used as a unique id, if we don't have one, we use current
84+
else:
85+
params['commit'] = os.popen('git log -n 1 --pretty=format:"%H"').read()
86+
bot.logger.warning("commit not specified, setting to current %s", params['commit'])
87+
88+
# Dump some params for the builder, in case it fails after this
89+
passing_params = "/tmp/params.pkl"
90+
pickle.dump(params,open(passing_params,'wb'))
91+
92+
if os.path.exists(params['spec_file']):
93+
bot.logger.info("Found spec file %s in repository",params['spec_file'])
94+
95+
# If size is None, set default of 800
96+
if params['size'] in [None,'']:
97+
bot.logger.info("""\n\n--------------------------------------------------------------
98+
Size not detected for build. Will use default of 800MB padding. If
99+
your build fails due to running out of disk space, you can adjust the
100+
size under collection --> edit builder
101+
---------------------------------------------------------------------
102+
\n""")
103+
104+
params['size'] = 800
105+
106+
# In future we can possibly add this back.
107+
#params['size'] = estimate_image_size(spec_file=os.path.abspath(params['spec_file']),
108+
# sudopw='',
109+
# padding=params['padding'])
110+
#bot.logger.info("Size estimated as %s",params['size'])
111+
112+
113+
# START TIMING
114+
start_time = datetime.now()
115+
image = build_from_spec(spec_file=params['spec_file'], # default will package the image
116+
size=params['size'],
117+
sudopw='', # with root should not need sudo
118+
build_dir=build_dir,
119+
debug=params['debug'])
120+
121+
final_time = (datetime.now() - start_time).seconds
122+
bot.logger.info("Final time of build %s seconds.",final_time)
123+
124+
# Did the container build successfully?
125+
test_result = test_container(image)
126+
if test_result['return_code'] == 255:
127+
bot.logger.error("Image failed to bootstrap, cancelling build.")
128+
sys.exit(1)
129+
130+
# Compress image
131+
compressed_image = "%s.img.gz" %image
132+
os.system('gzip -c -9 %s > %s' %(image,compressed_image))
133+
134+
# Package the image metadata (files, folders, etc)
135+
image_package = package(image_path=image,
136+
spec_path=params['spec_file'],
137+
output_folder=build_dir,
138+
sudopw='',
139+
remove_image=True,
140+
verbose=True)
141+
142+
# Derive software tags by subtracting similar OS
143+
diff = get_diff(image_package=image_package)
144+
145+
# Get singularity version
146+
singularity_version = get_singularity_version()
147+
148+
# Get tags for services, executables
149+
interesting_folders = ['init','init.d','bin','systemd']
150+
tags = get_tags(search_folders=interesting_folders,
151+
diff=diff)
152+
153+
# Count file types, and extensions
154+
counts = dict()
155+
counts['readme'] = file_counts(diff=diff)
156+
counts['copyright'] = file_counts(diff=diff,patterns=['copyright'])
157+
counts['authors-thanks-credit'] = file_counts(diff=diff,
158+
patterns=['authors','thanks','credit'])
159+
counts['todo'] = file_counts(diff=diff,patterns=['todo'])
160+
extensions = extension_counts(diff=diff)
161+
162+
os_sims = estimate_os(image_package=image_package,return_top=False)
163+
most_similar = os_sims['SCORE'].idxmax()
164+
165+
metrics = {'size': params['size'],
166+
'build_time_seconds':final_time,
167+
'singularity_version':singularity_version,
168+
'singularity_python_version':singularity_python_version,
169+
'estimated_os': most_similar,
170+
'os_sims':os_sims['SCORE'].to_dict(),
171+
'tags':tags,
172+
'file_counts':counts,
173+
'file_ext':extensions }
174+
175+
output = {'image':compressed_image,
176+
'image_package':image_package,
177+
'metadata':metrics,
178+
'params':params }
179+
180+
return output
181+
182+
else:
183+
# Tell the user what is actually there
184+
present_files = glob("*")
185+
bot.logger.error("Build file %s not found in repository",params['spec_file'])
186+
bot.logger.info("Found files are %s","\n".join(present_files))
187+
# Params have been exported, will be found by log
188+
sys.exit(1)
189+
190+
191+
@retry(wait_exponential_multiplier=1000, wait_exponential_max=10000)
192+
def send_build_data(build_dir,data,response_url=None,clean_up=True):
193+
'''finish build sends the build and data (response) to a response url
194+
:param build_dir: the directory of the build
195+
:response_url: where to send the response. If None, won't send
196+
:param data: the data object to send as a post
197+
:param clean_up: If true (default) removes build directory
198+
'''
199+
if response_url != None:
200+
finish = requests.post(response_url,data=data)
201+
else:
202+
bot.logger.warning("response_url set to None, skipping sending of build.")
203+
204+
if clean_up == True:
205+
shutil.rmtree(build_dir)
206+
207+
# Delay a bit, to give buffer between bringing instance down
208+
time.sleep(20)
209+
210+
211+
212+
@retry(wait_exponential_multiplier=1000, wait_exponential_max=10000,retry_on_result=stop_if_result_none)
213+
def send_build_close(params,response_url=None):
214+
'''send build close sends a final response (post) to the server to bring down
215+
the instance. The following must be included in params:
216+
217+
repo_url, logfile, repo_id, secret, log_file, token
218+
219+
if response_url is None, this is skipped entirely.
220+
'''
221+
222+
if response_url != None:
223+
224+
# Finally, package everything to send back to shub
225+
response = {"log": json.dumps(params['log_file']),
226+
"repo_url": params['repo_url'],
227+
"logfile": params['logfile'],
228+
"repo_id": params['repo_id'],
229+
"secret": params['secret']}
230+
231+
if params['token'] != None:
232+
response['token'] = params['token']
233+
234+
# Send it back!
235+
return requests.post(response_url,data=response)
236+
237+
bot.logger.warning("Response url set to none, cannot send build close.")
238+
return None

singularity/package.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,9 @@ def package(image_path,spec_path=None,output_folder=None,runscript=True,
119119
# Run create image and bootstrap with Singularity command line tool.
120120
if S == None:
121121
if sudopw != None:
122-
S = Singularity(sudopw=sudopw,verbose=verbose)
122+
S = Singularity(sudopw=sudopw,debug=verbose)
123123
else:
124-
S = Singularity(verbose=verbose) # This command will ask the user for sudo
124+
S = Singularity(debug=verbose) # This command will ask the user for sudo
125125
tmptar = S.export(image_path=image_path,pipe=False)
126126
tar = tarfile.open(tmptar)
127127
members = tar.getmembers()

singularity/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "1.00"
1+
__version__ = "1.0.2"
22
AUTHOR = 'Vanessa Sochat'
33
AUTHOR_EMAIL = '[email protected]'
44
NAME = 'singularity'

0 commit comments

Comments
 (0)