-
-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement singularity-compose.yml override configuration feature #48
Changes from 2 commits
5f7bb40
8c7a592
5491961
2285aba
e2fbef0
7deee7d
746fb07
41731fa
e7e6694
e1f5bf6
14996ab
0fa8477
35388e9
7a35b99
6f9544f
01a522a
2d10fe3
a40e8f4
900d8ee
949b088
86c0905
acea351
22dd789
37f9e7e
2d75868
779456a
352ee85
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -64,8 +64,8 @@ def set_filename(self, filename): | |
========== | ||
filename: the singularity-compose.yml file to use | ||
""" | ||
self.filename = filename or "singularity-compose.yml" | ||
self.working_dir = os.path.dirname(os.path.abspath(self.filename)) | ||
self.filename = filename | ||
self.working_dir = os.getcwd() | ||
|
||
def set_name(self, name): | ||
"""set the filename to read the recipe from. If not provided, defaults | ||
|
@@ -75,11 +75,9 @@ def set_name(self, name): | |
========== | ||
name: if a customize name is provided, use it | ||
""" | ||
pwd = os.path.basename(os.path.dirname(os.path.abspath(self.filename))) | ||
self.name = (name or pwd).lower() | ||
self.name = (name or self.working_dir).lower() | ||
|
||
# Listing | ||
|
||
def ps(self): | ||
"""ps will print a table of instances, including pids and names.""" | ||
instance_names = self.get_instance_names() | ||
|
@@ -131,7 +129,6 @@ def get_instance(self, name): | |
return instance | ||
|
||
# Loading Functions | ||
|
||
def get_already_running(self): | ||
"""Since a user can bring select instances up and down, we need to | ||
derive a list of already running instances to include | ||
|
@@ -148,15 +145,59 @@ def get_already_running(self): | |
def load(self): | ||
"""load a singularity-compose.yml recipe, and validate it.""" | ||
|
||
if not os.path.exists(self.filename): | ||
bot.error("%s does not exist." % self.filename) | ||
sys.exit(1) | ||
|
||
try: | ||
self.config = read_yaml(self.filename, quiet=True) | ||
yaml_files = [] | ||
|
||
for f in self.filename: | ||
# ensure file exists | ||
if not os.path.exists(f): | ||
bot.error("%s does not exist." % f) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please move the multiple load of yaml files outside of the project.py - there should ideally be a separate utils function or set of config functions (some config.py or similar) that takes the list. and returns the single config here. Also - self.filename implies one file, and there should either be a self.filenames or self.filename to match the arity. |
||
sys.exit(1) | ||
# read yaml file | ||
yaml_files.append(read_yaml(f, quiet=True)) | ||
|
||
# merge/override yaml properties where applicable | ||
self.config = self.deep_merge(yaml_files) | ||
except: # ParserError | ||
bot.exit("Cannot parse %s, invalid yaml." % self.filename) | ||
|
||
def deep_merge(self, yaml_files): | ||
"""merge singularity-compose.yml files into a single dict""" | ||
if len(yaml_files) == 1: | ||
# nothing to merge as the user specified a single file | ||
return yaml_files[0] | ||
|
||
base_yaml = None | ||
for idx, item in enumerate(yaml_files): | ||
if idx == 0: | ||
base_yaml = item | ||
else: | ||
base_yaml = self.merge(base_yaml, item) | ||
|
||
return base_yaml | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All of this logic should not be in project.py - it's for merging yaml. |
||
def merge(self, a, b): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move this too. |
||
"""merge dict b into a""" | ||
for key in b: | ||
if key in a: | ||
# merge dicts recursively | ||
if isinstance(a[key], dict) and isinstance(b[key], dict): | ||
a[key] = self.merge(a[key], b[key]) | ||
|
||
# if types are equal, b takes precedence | ||
elif isinstance(a[key], type(b[key])): | ||
a[key] = b[key] | ||
|
||
# if nothing matches then this means a conflict of types which shouldn't exist in the first place | ||
else: | ||
bot.error( | ||
"key %s has property type mismatch in different files." % key | ||
) | ||
sys.exit(1) | ||
else: | ||
a[key] = b[key] | ||
return a | ||
|
||
def parse(self): | ||
"""parse a loaded config""" | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would leave the default here to "singularity-compose.yml" - and if you are changing this to require a list, then please support the old behavior to still provide a single filename, and have it checked if it's a list, single string, or None, and process accordingly. And then if your preference is to store in a list, the variable should be self.filenames to indicate plural / multiple files.
And if I understand correctly, the working directory is changed to os.getcwd() to account for possibly different compose files in different directories. Of course the issue here is that all compose files need to then have their context relevant to that directory (is that your intention?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason why I removed it is because this default value comes from the argparse as well, so I thought it was redundant. Quick question, the reason why you want the default value here as well is because you are envisioning devs instantiating this class programatically instead of using the CLI, is that correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes correct! We wouldn't want to break previous scripts that are providing a string. The simple solution I think is to allow either.