Skip to content

Commit 8f4f02d

Browse files
authored
Merge pull request #39 from vsoch/master
modify build parameters and calculate size dynamically
2 parents ebdb5ff + e80ad89 commit 8f4f02d

File tree

8 files changed

+111
-14
lines changed

8 files changed

+111
-14
lines changed

MANIFEST.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
recursive-include singularity/templates *
22
recursive-include singularity/static *
3-
recursive-include singularity/scripts *
3+
recursive-include singularity/build *
44
recursive-include singularity/testing *

setup.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
name="singularity",
88

99
# Version number:
10-
version="0.37",
10+
version="0.39",
1111

1212
# Application author details:
1313
author="Vanessa Sochat",
@@ -18,7 +18,6 @@
1818

1919
# Data files
2020
include_package_data=True,
21-
2221
zip_safe=False,
2322

2423
# Details

singularity/build/google.py

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@
1111
api_post
1212
)
1313

14+
from singularity.build.utils import get_singularity_version
15+
1416
from singularity.package import (
1517
build_from_spec,
18+
estimate_image_size,
1619
package
1720
)
1821

@@ -59,7 +62,13 @@
5962
def get_storage_service():
6063
credentials = GoogleCredentials.get_application_default()
6164
return build('storage', 'v1', credentials=credentials)
65+
66+
67+
def get_compute_service():
68+
credentials = GoogleCredentials.get_application_default()
69+
return build('compute', 'v1', credentials=credentials)
6270

71+
6372
def get_bucket(storage_service,bucket_name):
6473
req = storage_service.buckets().get(bucket=bucket_name)
6574
return req.execute()
@@ -106,7 +115,7 @@ def list_bucket(bucket,storage_service):
106115

107116

108117
def run_build(build_dir=None,spec_file=None,repo_url=None,token=None,size=None,bucket_name=None,
109-
repo_id=None,commit=None,verbose=True,response_url=None,secret=None):
118+
repo_id=None,commit=None,verbose=True,response_url=None,secret=None,branch=None):
110119
'''run_build will generate the Singularity build from a spec_file from a repo_url.
111120
If no arguments are required, the metadata api is queried for the values.
112121
:param build_dir: directory to do the build in. If not specified,
@@ -115,13 +124,14 @@ def run_build(build_dir=None,spec_file=None,repo_url=None,token=None,size=None,b
115124
:param repo_url: the url to download the repo from
116125
:param repo_id: the repo_id to uniquely identify the repo (in case name changes)
117126
:param commit: the commit to checkout. If none provided, will use most recent.
118-
:param size: the size of the image to build. If none set, builds default 1024.
127+
:param size: the size of the image to build. If none set, builds folder size + 200MB padding
119128
:param bucket_name: the name of the bucket to send files to
120129
:param verbose: print out extra details as we go (default True)
121130
:param token: a token to send back to the server to authenticate the collection
122131
:param secret: a secret to match to the correct container
123132
:param response_url: the build url to send the response back to. Should also come
124133
from metadata. If not specified, no response is sent
134+
:param branch: the branch to checkout for the build.
125135
:: note: this function is currently configured to work with Google Compute
126136
Engine metadata api, and should (will) be customized if needed to work elsewhere
127137
'''
@@ -145,7 +155,9 @@ def run_build(build_dir=None,spec_file=None,repo_url=None,token=None,size=None,b
145155
{'key': 'token', 'value': token, 'return_text': False },
146156
{'key': 'commit', 'value': commit, 'return_text': True },
147157
{'key': 'secret', 'value': secret, 'return_text': True },
148-
{'key': 'size', 'value': size, 'return_text': True }]
158+
{'key': 'size', 'value': size, 'return_text': True },
159+
{'key': 'branch', 'value': branch, 'return_text': True },
160+
{'key': 'container_id', 'value': None, 'return_text': True }]
149161

150162
# Default spec file is Singularity
151163
if spec_file == None:
@@ -162,6 +174,10 @@ def run_build(build_dir=None,spec_file=None,repo_url=None,token=None,size=None,b
162174
destination=build_dir)
163175

164176
os.chdir(build_dir)
177+
if params['branch'] != None:
178+
bot.logger.info('Checking out branch %s',params['branch'])
179+
os.system('git checkout %s' %(params['branch']))
180+
165181
if params['commit'] != None:
166182
bot.logger.info('Checking out commit %s',params['commit'])
167183
os.system('git checkout %s .' %(params['commit']))
@@ -175,6 +191,12 @@ def run_build(build_dir=None,spec_file=None,repo_url=None,token=None,size=None,b
175191
if os.path.exists(spec_file):
176192
bot.logger.info("Found spec file %s in repository",spec_file)
177193

194+
# If size is None, get from image + 200 padding
195+
if params['size'] == None:
196+
bot.logger.info("Size not detected for build. Will estimate with 200MB padding.")
197+
params['size'] = estimate_image_size(spec=spec_file,
198+
sudopw='')
199+
178200
image = build_from_spec(spec=spec_file, # default will package the image
179201
size=params['size'],
180202
sudopw='', # with root should not need sudo
@@ -232,7 +254,17 @@ def run_build(build_dir=None,spec_file=None,repo_url=None,token=None,size=None,b
232254
"repo_url": params['repo_url'],
233255
"commit": params['commit'],
234256
"repo_id": params['repo_id'],
235-
"secret": params['secret']}
257+
"secret": params['secret'],
258+
"container_id": params['container_id']}
259+
260+
# Did the user specify a specific log file?
261+
logfile = get_build_metadata(key='logfile')
262+
if logfile != None:
263+
response['logfile'] = logfile
264+
265+
if params['branch'] != None:
266+
response['branch'] = params['branch']
267+
236268

237269
if params['token'] != None:
238270
response['token'] = params['token']
@@ -252,7 +284,7 @@ def run_build(build_dir=None,spec_file=None,repo_url=None,token=None,size=None,b
252284
shutil.rmtree(build_dir)
253285

254286

255-
def finish_build(logfile,repo_url=None,bucket_name=None,commit=None,verbose=True,repo_id=None,
287+
def finish_build(logfile,singularity_version=None,repo_url=None,bucket_name=None,commit=None,verbose=True,repo_id=None,
256288
logging_response_url=None,secret=None,token=None):
257289
'''finish_build will finish the build by way of sending the log to the same bucket.
258290
:param build_dir: directory to do the build in. If not specified,
@@ -262,6 +294,7 @@ def finish_build(logfile,repo_url=None,bucket_name=None,commit=None,verbose=True
262294
:param repo_id: the repo_id to uniquely identify the repo (in case name changes)
263295
:param commit: the commit to checkout. If none provided, will use most recent.
264296
:param bucket_name: the name of the bucket to send files to
297+
:param singularity_version: the version of singularity installed
265298
:param verbose: print out extra details as we go (default True)
266299
:param secret: a secret to match to the correct container
267300
:param logging_response_url: the logging response url to send the response back to.
@@ -273,6 +306,9 @@ def finish_build(logfile,repo_url=None,bucket_name=None,commit=None,verbose=True
273306
if go == None:
274307
sys.exit(0)
275308

309+
if singularity_version == None:
310+
singularity_version = get_singularity_version(singularity_version)
311+
276312
# Get variables from the instance metadata API
277313
metadata = [{'key': 'logging_url', 'value': logging_response_url, 'return_text': True },
278314
{'key': 'repo_url', 'value': repo_url, 'return_text': False },

singularity/build/scripts/singularity-build-latest.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ sudo apt-get install -y build-essential libtool autotools-dev automake autoconf
55
sudo apt-get install -y python3-pip
66
cd /tmp && git clone http://www.github.com/singularityware/singularity
77
cd singularity && ./autogen.sh && ./configure --prefix=/usr/local && make && sudo make install
8-
SINGULARITY_VERSION=$(singularity --version)
8+
export SINGULARITY_VERSION=$(singularity --version)
99
sudo pip3 install --upgrade pip
1010
sudo pip3 install --upgrade google-api-python-client
1111
sudo pip3 install --upgrade google
@@ -14,5 +14,5 @@ sudo pip3 install gitpython
1414
cd /tmp && git clone https://github.com/singularityware/singularity-python
1515
cd singularity-python && python3 setup.py sdist && sudo python3 setup.py install
1616
python3 -c "from singularity.build.google import run_build; run_build()" > /tmp/.shub-log 2>&1
17-
command=$(echo "from singularity.build.google import finish_build; finish_build(logfile='/tmp/.shub-log',version='$SINGULARITY_VERSION')")
17+
command=$(echo "from singularity.build.google import finish_build; finish_build(logfile='/tmp/.shub-log')")
1818
python3 -c "$command"

singularity/build/utils.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414

1515
from singularity.utils import (
1616
get_installdir,
17-
read_file
17+
read_file,
18+
run_command
1819
)
1920

2021
import sys
@@ -59,3 +60,19 @@ def get_build_template(template_name,params=None,to_file=None):
5960
else:
6061
bot.logger.warning("Template %s not found.",template_file)
6162
return None
63+
64+
65+
def get_singularity_version(singularity_version=None):
66+
'''get_singularity_version will determine the singularity version for a build
67+
first, an environmental variable is looked at, followed by using the system
68+
version.
69+
'''
70+
if singularity_version == None:
71+
singularity_version = os.environ.get("SINGULARITY_VERSION",None)
72+
73+
# Next get from system
74+
if singularity_version == None:
75+
cmd = ['singularity','--version']
76+
singularity_version = run_command(cmd,error_message="Cannot determine Singularity version!").decode('utf-8').strip('\n')
77+
78+
return singularity_version

singularity/package.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from singularity.logman import bot
99

1010
from singularity.utils import (
11+
calculate_folder_size,
1112
format_container_name,
1213
read_file,
1314
zip_up
@@ -23,10 +24,34 @@
2324
import os
2425

2526

27+
def estimate_image_size(spec=None,sudopw=None,padding=200):
28+
'''estimate_image_size will generate an image in a directory, and add
29+
some padding to it to estimate the size of the image file to generate
30+
:param sudopw: the sudopw for Singularity, root should provide ''
31+
:param spec: the spec file, called "Singuarity"
32+
:param padding: the padding (MB) to add to the image
33+
'''
34+
size_dir = tempfile.mkdtemp()
35+
tmp_dir = tempfile.mkdtemp()
36+
image_folder = build_from_spec(spec=spec_file, # default will package the image
37+
sudopw=sudopw, # with root should not need sudo
38+
output_folder=size_dir,
39+
build_dir=tmp_dir,
40+
build_folder=True)
41+
original_size = calculate_folder_size(image_folder)
42+
bot.logger.debug("Original image size calculated as %s",original_size)
43+
padded_size = original_size + padding
44+
bot.logger.debug("Size with padding will be %s",padded_size)
45+
shutil.rmtree(size_dir)
46+
os.system('sudo rm -rf %s' %tmp_dir)
47+
return padded_size
48+
49+
2650
def build_from_spec(spec=None,build_dir=None,size=None,sudopw=None,
2751
output_folder=None,build_folder=False):
2852
'''build_from_spec will build a "spec" file in a "build_dir" and return the directory
2953
:param spec: the spec file, called "Singuarity"
54+
:param sudopw: the sudopw for Singularity, root should provide ''
3055
:param build_dir: the directory to build in. If not defined, will use tmpdir.
3156
:param size: the size of the image
3257
:param output_folder: where to output the image package

singularity/tests/test_package.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,6 @@ def test_packaging(self):
6262
self.assertTrue(os.path.exists(image1_extraction))
6363
shutil.rmtree(os.path.dirname(image1_extraction))
6464

65+
6566
if __name__ == '__main__':
6667
unittest.main()

singularity/utils.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,14 @@
3939
######################################################################################
4040

4141

42-
def check_install(software="singularity"):
42+
def check_install(software=None):
4343
'''check_install will attempt to run the singularity command, and return an error
4444
if not installed. The command line utils will not run without this check.
4545
'''
46-
46+
if software == None:
47+
software = "singularity"
4748
cmd = [software,'--version']
48-
version = run_command(cmd,error_message="Cannot find singularity. Is it installed?")
49+
version = run_command(cmd,error_message="Cannot find %s. Is it installed?" %software)
4950
if version != None:
5051
bot.logger.info("Found %s version %s",software.upper(),version)
5152
return True
@@ -228,6 +229,24 @@ def read_file(filename,mode="r"):
228229
return content
229230

230231

232+
############################################################################
233+
## OTHER MISC. #############################################################
234+
############################################################################
235+
236+
237+
def calculate_folder_size(folder_path):
238+
'''calculate_folder size recursively walks a directory to calculate
239+
a total size (in)
240+
:param folder_path: the path to calculate size for
241+
'''
242+
total_size = 0
243+
for dirpath, dirnames, filenames in os.walk(folder_path):
244+
for filey in filenames:
245+
fp = os.path.join(dirpath, filey)
246+
total_size += os.path.getsize(fp)
247+
return total_size
248+
249+
231250
def remove_unicode_dict(input_dict):
232251
'''remove unicode keys and values from dict, encoding in utf8
233252
'''

0 commit comments

Comments
 (0)