Skip to content

Commit

Permalink
Merge pull request #230 from douglasjacobsen/package_manager_configs
Browse files Browse the repository at this point in the history
Add support for package manager configs, and remove spack module generation
  • Loading branch information
rfbgo authored Jul 31, 2023
2 parents 51476dd + 8a9b40e commit 2f588a6
Show file tree
Hide file tree
Showing 13 changed files with 315 additions and 25 deletions.
14 changes: 14 additions & 0 deletions lib/ramble/ramble/application_types/spack.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ def __init__(self, file_path):
def _long_print(self):
out_str = super()._long_print()

if hasattr(self, 'package_manager_configs'):
out_str.append('\n')
out_str.append(section_title('Package Manager Configs:\n'))
for name, config in self.package_manager_configs.items():
out_str.append(f'\t{name} = {config}\n')

for group in self._spec_groups:
if hasattr(self, group[0]):
out_str.append('\n')
Expand Down Expand Up @@ -135,6 +141,14 @@ def _create_spack_env(self, workspace):
else:
workspace.add_to_cache(cache_tupl)

package_manager_config_dicts = [self.package_manager_configs]
for mod_inst in self._modifier_instances:
package_manager_config_dicts.append(mod_inst.package_manager_configs)

for config_dict in package_manager_config_dicts:
for _, config in config_dict.items():
self.spack_runner.add_config(config)

try:
self.spack_runner.set_dry_run(workspace.dry_run)
self.spack_runner.create_env(self.expander.expand_var('{spack_env}'))
Expand Down
15 changes: 15 additions & 0 deletions lib/ramble/ramble/language/shared_language.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,21 @@ def _execute_software_spec(obj):
return _execute_software_spec


@shared_directive('package_manager_configs')
def package_manager_config(name, config, **kwargs):
"""Defines a config option to set within a package manager
Define a new config which will be passed to a package manager. The
resulting experiment instance will pass the config to the package manager,
which will control the logic of applying it.
"""

def _execute_package_manager_config(obj):
obj.package_manager_configs[name] = config

return _execute_package_manager_config


@shared_directive('required_packages')
def required_package(name):
"""Defines a new spack package that is required for this object
Expand Down
6 changes: 6 additions & 0 deletions lib/ramble/ramble/modifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ def _long_print(self):
out_str.append(rucolor.section_title('Executable Modifiers:\n'))
out_str.append('\t' + colified(self.executable_modifiers.keys(), tty=True) + '\n')

if hasattr(self, 'package_manager_configs'):
out_str.append(rucolor.section_title('Package Manager Configs:\n'))
for name, config in self.package_manager_configs.items():
out_str.append(f'\t{name} = {config}\n')
out_str.append('\n')

if hasattr(self, 'default_compilers'):
out_str.append(rucolor.section_title('Default Compilers:\n'))
for comp_name, comp_def in self.default_compilers.items():
Expand Down
61 changes: 38 additions & 23 deletions lib/ramble/ramble/spack_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ def __init__(self, shell='bash', dry_run=False):
self.dry_run = dry_run
self.concretized = False
self.compiler_config_dir = None
self.configs = []
self.configs_applied = False

def get_version(self):
"""Get spack's version"""
Expand Down Expand Up @@ -180,6 +182,12 @@ def configure_env(self, path):
# Ensure subsequent commands use the created env now.
self.env_path = path

def add_config(self, config):
"""
Add a config option to this spack environment.
"""
self.configs.append(config)

def create_env(self, path, output=None, error=None):
"""
Ensure a spack environment is created, and set the path to it within
Expand Down Expand Up @@ -375,6 +383,30 @@ def add_include_file(self, include_file):
if file_name in self._allowed_config_files:
self.includes.append(include_file)

def apply_configs(self):
"""
Add all defined configs to the environment
"""

if self.configs_applied:
return

self._check_active()

config_args = [
'config',
'add'
]

for config in self.configs:
args = config_args.copy()
args.append(config)
self.exe(*args)
if self.dry_run:
self._dry_run_print(args)

self.configs_applied = True

def copy_from_external_env(self, env_name_or_path):
"""
Copy an external spack environment file into the generated environment.
Expand Down Expand Up @@ -419,6 +451,9 @@ def copy_from_external_env(self, env_name_or_path):

shutil.copyfile(conf_file, os.path.join(self.env_path, 'spack.yaml'))

if self.configs:
self.apply_configs()

self.concretized = found_lock

def generate_env_file(self):
Expand Down Expand Up @@ -465,6 +500,9 @@ def generate_env_file(self):
with open(os.path.join(self.env_path, 'spack.yaml'), 'w+') as f:
syaml.dump_config(env_file, f, default_flow_style=False)

if self.configs:
self.apply_configs()

def concretize(self):
"""
Concretize a spack environment.
Expand Down Expand Up @@ -526,29 +564,6 @@ def install(self):
'install'
]
args.extend(install_flags.split())
if not self.dry_run:
self.exe(*args)
else:
self._dry_run_print(args)

for mod_type in ['tcl', 'lmod']:
args = [
'module',
mod_type,
'refresh',
'-y'
]

if not self.dry_run:
self.exe(*args)
else:
self._dry_run_print(args)

args = [
'env',
'loads'
]

if not self.dry_run:
self.exe(*args)
else:
Expand Down
1 change: 1 addition & 0 deletions lib/ramble/ramble/test/application_language.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def test_application_type_features(app_class):
assert hasattr(test_app, 'software_specs')
assert hasattr(test_app, 'required_packages')
assert hasattr(test_app, 'maintainers')
assert hasattr(test_app, 'package_manager_configs')


def add_workload(app_inst, wl_num=1):
Expand Down
18 changes: 18 additions & 0 deletions lib/ramble/ramble/test/cmd/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,25 @@ def test_spack_info_software(app_query):
'Tags:',
'spack_spec =',
'compiler =',
)

out = info(app_query)

for field in expected_fields:
assert field in out


@pytest.mark.parametrize('app_query', [
'zlib-configs',
])
def test_mock_spack_info_software(mock_applications, app_query):
expected_fields = (
'Description:',
'Setup Pipeline Phases:',
'Analyze Pipeline Phases:',
'Tags:',
'Package Manager Configs:',
'spack_spec =',
)

out = info(app_query)
Expand Down
2 changes: 1 addition & 1 deletion lib/ramble/ramble/test/cmd/mods.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
def check_info(output):
expected_sections = ['Tags', 'Mode', 'Builtin Executables',
'Executable Modifiers', 'Default Compilers',
'Software Specs']
'Software Specs', 'Package Manager Configs']

for section in expected_sections:
assert section in output
Expand Down
71 changes: 71 additions & 0 deletions lib/ramble/ramble/test/end_to_end/package_manager_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Copyright 2022-2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
# option. This file may not be copied, modified, or distributed
# except according to those terms.

import os

import pytest

import ramble.workspace
import ramble.config
import ramble.software_environments
from ramble.main import RambleCommand


pytestmark = pytest.mark.usefixtures('mutable_config',
'mutable_mock_workspace_path')

workspace = RambleCommand('workspace')


def test_package_manager_config_zlib(mock_applications):
test_config = """
ramble:
variables:
mpi_command: ''
batch_submit: 'batch_submit {execute_experiment}'
processes_per_node: '1'
n_ranks: '1'
applications:
zlib-configs:
workloads:
ensure_installed:
experiments:
test:
variables: {}
spack:
concretized: true
packages:
zlib:
spack_spec: 'zlib'
environments:
zlib-configs:
packages:
- zlib
"""

workspace_name = 'test_package_manager_config_zlib'
with ramble.workspace.create(workspace_name) as ws:
ws.write()

config_path = os.path.join(ws.config_dir, ramble.workspace.config_file_name)

with open(config_path, 'w+') as f:
f.write(test_config)
ws._re_read()

workspace('setup', '--dry-run', global_args=['-w', workspace_name])

spack_yaml = os.path.join(ws.software_dir, 'zlib-configs.ensure_installed',
'spack.yaml')

assert os.path.isfile(spack_yaml)

with open(spack_yaml, 'r') as f:
data = f.read()
assert 'config:' in data
assert 'debug: true' in data
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Copyright 2022-2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
# option. This file may not be copied, modified, or distributed
# except according to those terms.

import os

import pytest

from ramble.test.dry_run_helpers import dry_run_config, SCOPES
import ramble.test.modifier_functionality.modifier_helpers as modifier_helpers
import ramble.workspace
from ramble.main import RambleCommand

workspace = RambleCommand('workspace')


@pytest.mark.parametrize(
'scope',
[
SCOPES.workspace,
SCOPES.application,
SCOPES.workload,
SCOPES.experiment,
]
)
def test_gromacs_mock_spack_config_mod(mutable_mock_workspace_path,
mutable_applications,
mock_modifiers,
scope):
workspace_name = 'test_gromacs_mock_spack_config_mod'

test_modifiers = [
(scope, modifier_helpers.named_modifier('spack-mod')),
]

with ramble.workspace.create(workspace_name) as ws1:
ws1.write()

config_path = os.path.join(ws1.config_dir, ramble.workspace.config_file_name)

dry_run_config('modifiers', test_modifiers, config_path, 'gromacs', 'water_bare')

ws1._re_read()

workspace('concretize', global_args=['-D', ws1.root])
workspace('setup', '--dry-run', global_args=['-D', ws1.root])
exp_script = os.path.join(ws1.experiment_dir, 'gromacs', 'water_bare',
'test_exp', 'execute_experiment')

assert os.path.isfile(exp_script)

spack_yaml = os.path.join(ws1.software_dir, 'gromacs.water_bare',
'spack.yaml')
assert os.path.isfile(spack_yaml)

with open(spack_yaml, 'r') as f:
data = f.read()

assert 'debug: true' in data
1 change: 1 addition & 0 deletions lib/ramble/ramble/test/modifier_language.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def test_modifier_type_features(mod_class):
assert hasattr(test_mod, 'executable_modifiers')
assert hasattr(test_mod, 'env_var_modifications')
assert hasattr(test_mod, 'maintainers')
assert hasattr(test_mod, 'package_manager_configs')


def add_mode(mod_inst, mode_num=1):
Expand Down
Loading

0 comments on commit 2f588a6

Please sign in to comment.