Skip to content

Commit a9d6616

Browse files
authored
Merge pull request #715 from douglasjacobsen/workspace-precedence-docs
Update configuration precedence documentation
2 parents d49d842 + 403baf6 commit a9d6616

File tree

5 files changed

+140
-74
lines changed

5 files changed

+140
-74
lines changed

lib/ramble/docs/configuration_files.rst

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,24 @@ highest) Ramble contains the following scopes:
7171
5. **custom**: Stored in a custom directory, specified by ``--config-scope``.
7272
If multiple scopes are listed on the command line, they are ordered from lowest
7373
to highest precedence. Settings here override all previously defined scoped.
74-
6. **workspace configs dir**: Stored in ``$(workspace_root)/configs``
75-
generally as a ``<config_section>.yaml`` file (i.e. ``variables.yaml``). These
76-
settings apply to a specific workspace, and override all previous configuration
77-
scopes.
74+
6. **included files in workspace configuration file**: Paths referred to in the
75+
file from #7 above. For more information see the
76+
:ref:`documentation for including external configuration files<workspace_including_external_files>`.
7877
7. **workspace configuration file**: Stored in
7978
``$(workspace_root)/configs/ramble.yaml``. Configuration scopes defined within
8079
this config file override all previously defined configuration scopes.
81-
8. **command line**: Configuration options defined on the command line take
80+
8. **workspace configs dir**: Stored in ``$(workspace_root)/configs``
81+
generally as a ``<config_section>.yaml`` file (i.e. ``variables.yaml``). These
82+
settings apply to a specific workspace, and override all previous configuration
83+
scopes.
84+
9. **command line**: Configuration options defined on the command line take
8285
precedence over all other scopes.
86+
10. **application / workload / experiment scope sections**: Several
87+
configuration sections can be defined within the ``application``,
88+
``workload``, and ``experiment`` portions of the ``applications``
89+
configuration section. These will override all other scopes. See the
90+
:ref:`application section documentation<application-config>` for more
91+
details.
8392

8493
Each configuration directory may contain several configuration files, such as
8594
``config.yaml``, ``variables.yaml``, or ``modifiers.yaml``. When configurations

lib/ramble/ramble/test/cmd/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,7 @@ def test_config_remove_from_workspace(mutable_empty_config, mutable_mock_workspa
714714
config("rm", "config:dirty")
715715
output = config("get")
716716

717-
expected = ramble.workspace.default_config_yaml()
717+
expected = ramble.workspace.Workspace._default_config_yaml()
718718
expected += """ config: {}
719719
"""
720720
for line in io.StringIO(expected).readlines():

lib/ramble/ramble/test/cmd/workspace.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2055,3 +2055,60 @@ def test_workspace_simplify():
20552055
# ensure apps/pkgs/envs are not merged into workspace config from other config files
20562056
assert search_files_for_string([ws_config_path], "app_not_in_ws_config") is False
20572057
assert search_files_for_string([ws_config_path], "pkg_not_in_ws_config") is False
2058+
2059+
2060+
def write_variables_config_file(file_path, levels, value):
2061+
with open(file_path, "w+") as f:
2062+
f.write("variables:\n")
2063+
for i in range(0, levels):
2064+
f.write(f" scope{i}: {value}\n")
2065+
2066+
2067+
def test_workspace_config_precedence(request, tmpdir):
2068+
workspace_name = request.node.name
2069+
ws = ramble.workspace.create(workspace_name)
2070+
2071+
global_args = ["-w", workspace_name]
2072+
2073+
# Highest precedence, experiment scope
2074+
workspace(
2075+
"manage",
2076+
"experiments",
2077+
"basic",
2078+
"--wf",
2079+
"test_wl",
2080+
"-e",
2081+
"unit-test",
2082+
"-v",
2083+
"n_nodes=1",
2084+
"-v",
2085+
"n_ranks=1",
2086+
"-v",
2087+
"scope0=experiment",
2088+
global_args=global_args,
2089+
)
2090+
2091+
# 2nd highest precedence, included (in an included path)
2092+
included_path = os.path.join(ws.root, "variables.yaml")
2093+
with open(ws.config_file_path, "a") as f:
2094+
f.write(" include:\n")
2095+
f.write(f" - {included_path}\n")
2096+
2097+
write_variables_config_file(included_path, 2, "include_path")
2098+
2099+
# 3rd highest precedence, workspace overrides (in configs/variables.yaml)
2100+
workspace_overrides = os.path.join(ws.config_dir, "variables.yaml")
2101+
write_variables_config_file(workspace_overrides, 3, "workspace_overrides")
2102+
2103+
# 4th highest precedence, workspace (in ramble.yaml)
2104+
config("add", "variables:scope0:workspace", global_args=global_args)
2105+
config("add", "variables:scope1:workspace", global_args=global_args)
2106+
config("add", "variables:scope2:workspace", global_args=global_args)
2107+
config("add", "variables:scope3:workspace", global_args=global_args)
2108+
2109+
output = workspace("info", "-vv", global_args=global_args)
2110+
2111+
assert "scope0 = experiment" in output
2112+
assert "scope1 = include_path" in output
2113+
assert "scope2 = workspace_override" in output
2114+
assert "scope3 = workspace" in output

lib/ramble/ramble/workspace/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
config_dict,
2828
create,
2929
deactivate,
30-
default_config_yaml,
3130
exists,
3231
is_workspace_dir,
3332
get_workspace_path,
@@ -67,7 +66,6 @@
6766
"config_dict",
6867
"create",
6968
"deactivate",
70-
"default_config_yaml",
7169
"exists",
7270
"is_workspace_dir",
7371
"get_workspace_path",

lib/ramble/ramble/workspace/workspace.py

Lines changed: 68 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -106,74 +106,10 @@
106106
config_file_name = "ramble.yaml"
107107
licenses_file_name = "licenses.yaml"
108108

109-
110-
def default_config_yaml():
111-
"""default ramble.yaml file to put in new workspaces"""
112-
return """\
113-
# This is a ramble workspace config file.
114-
#
115-
# It describes the experiments, the software stack
116-
# and all variables required for ramble to configure
117-
# experiments.
118-
# As an example, experiments can be defined as follows.
119-
# applications:
120-
# hostname: # Application name, as seen in `ramble list`
121-
# variables:
122-
# iterations: '5'
123-
# workloads:
124-
# serial: # Workload name, as seen in `ramble info <app>`
125-
# variables:
126-
# type: 'test'
127-
# experiments:
128-
# single_node: # Arbitrary experiment name
129-
# variables:
130-
# n_ranks: '{processes_per_node}'
131-
132-
ramble:
133-
env_vars:
134-
set:
135-
OMP_NUM_THREADS: '{n_threads}'
136-
variables:
137-
mpi_command: mpirun -n {n_ranks}
138-
batch_submit: '{execute_experiment}'
139-
processes_per_node: 1
140-
applications: {}
141-
software:
142-
packages: {}
143-
environments: {}
144-
"""
145-
146-
147109
workspace_all_experiments_file = "all_experiments"
148110

149111
workspace_execution_template = "execute_experiment" + workspace_template_extension
150112

151-
shell = ramble.config.get("config:shell")
152-
shell_path = os.path.join("/bin/", shell)
153-
template_execute_script = (
154-
f"#!{shell_path}\n"
155-
+ """\
156-
# This is a template execution script for
157-
# running the execute pipeline.
158-
#
159-
# Variables surrounded by curly braces will be expanded
160-
# when generating a specific execution script.
161-
# Some example variables are:
162-
# - experiment_run_dir (Will be replaced with the experiment directory)
163-
# - command (Will be replaced with the command to run the experiment)
164-
# - log_dir (Will be replaced with the logs directory)
165-
# - experiment_name (Will be replaced with the name of the experiment)
166-
# - workload_run_dir (Will be replaced with the directory of the workload
167-
# - application_name (Will be repalced with the name of the application)
168-
# - n_nodes (Will be replaced with the required number of nodes)
169-
# Any experiment parameters will be available as variables as well.
170-
171-
cd "{experiment_run_dir}"
172-
173-
{command}
174-
"""
175-
)
176-
177113
#: Name of lockfile within a workspace
178114
lockfile_name = "ramble.lock"
179115

@@ -557,7 +493,7 @@ def _read(self):
557493

558494
read_default = not os.path.exists(self.config_file_path)
559495
if read_default:
560-
self._read_config(config_section, default_config_yaml())
496+
self._read_config(config_section, self._default_config_yaml())
561497
else:
562498
with open(self.config_file_path) as f:
563499
self._read_config(config_section, f)
@@ -588,7 +524,73 @@ def _read(self):
588524

589525
if read_default_script:
590526
template_name = workspace_execution_template[0:-ext_len]
591-
self._read_template(template_name, template_execute_script)
527+
self._read_template(template_name, self._template_execute_script())
528+
529+
@classmethod
530+
def _template_execute_script(self):
531+
shell = ramble.config.get("config:shell")
532+
shell_path = os.path.join("/bin/", shell)
533+
script = (
534+
f"#!{shell_path}\n"
535+
+ """\
536+
# This is a template execution script for
537+
# running the execute pipeline.
538+
#
539+
# Variables surrounded by curly braces will be expanded
540+
# when generating a specific execution script.
541+
# Some example variables are:
542+
# - experiment_run_dir (Will be replaced with the experiment directory)
543+
# - command (Will be replaced with the command to run the experiment)
544+
# - log_dir (Will be replaced with the logs directory)
545+
# - experiment_name (Will be replaced with the name of the experiment)
546+
# - workload_run_dir (Will be replaced with the directory of the workload
547+
# - application_name (Will be repalced with the name of the application)
548+
# - n_nodes (Will be replaced with the required number of nodes)
549+
# Any experiment parameters will be available as variables as well.
550+
551+
cd "{experiment_run_dir}"
552+
553+
{command}
554+
"""
555+
)
556+
557+
return script
558+
559+
@classmethod
560+
def _default_config_yaml(self):
561+
return """\
562+
# This is a ramble workspace config file.
563+
#
564+
# It describes the experiments, the software stack
565+
# and all variables required for ramble to configure
566+
# experiments.
567+
# As an example, experiments can be defined as follows.
568+
# applications:
569+
# hostname: # Application name, as seen in `ramble list`
570+
# variables:
571+
# iterations: '5'
572+
# workloads:
573+
# serial: # Workload name, as seen in `ramble info <app>`
574+
# variables:
575+
# type: 'test'
576+
# experiments:
577+
# single_node: # Arbitrary experiment name
578+
# variables:
579+
# n_ranks: '{processes_per_node}'
580+
581+
ramble:
582+
env_vars:
583+
set:
584+
OMP_NUM_THREADS: '{n_threads}'
585+
variables:
586+
mpi_command: mpirun -n {n_ranks}
587+
batch_submit: '{execute_experiment}'
588+
processes_per_node: 1
589+
applications: {}
590+
software:
591+
packages: {}
592+
environments: {}
593+
"""
592594

593595
def _read_application_config(self, path, f, raw_yaml=None):
594596
"""Read an application configuration file"""

0 commit comments

Comments
 (0)