Skip to content

config: Overrides for system and user scopes#26735

Merged
tgamblin merged 1 commit intospack:developfrom
haampie:feature/add-SPACK_x_CONFIG_PATH-env-variable
Oct 27, 2021
Merged

config: Overrides for system and user scopes#26735
tgamblin merged 1 commit intospack:developfrom
haampie:feature/add-SPACK_x_CONFIG_PATH-env-variable

Conversation

@haampie
Copy link
Copy Markdown
Member

@haampie haampie commented Oct 14, 2021

Closes #11919.
Closes #11951.
Closes #13057.
Closes #12892.
Closes #26341.
Closes #23760.
Closes #23596.
Closes #25547.
Closes #26833.

Short story:

Spack now provides three environment variables that allow you to override or opt out of configuration locations:

  • SPACK_USER_CONFIG_PATH: Override the path to use for the user (~/.spack) scope.
  • SPACK_SYSTEM_CONFIG_PATH: Override the path to use for the system (/etc/spack) scope.
  • SPACK_DISABLE_LOCAL_CONFIG: set this environment variable to a non-empty value to completely disable both the system and user configuration directories. Spack will only consider its own defaults and site configuration locations.

And one that allows you to move the default cache location:

  • SPACK_USER_CACHE_PATH: Override the default path to use for user data (misc_cache, tests, reports, etc.)

With these settings, if you want to isolate Spack in a CI environment, you can do this:

export SPACK_DISABLE_LOCAL_CONFIG=true
export SPACK_USER_CACHE_PATH=/tmp/spack

This PR is the effective minimal and hopefully least controversial changeset to
address common complaints voiced many times on Slack and Github about
hard-coded config paths in Spack without a way to opt out.

Pinging @trws, @healther, @tjfulle, @jw500, @sleak-lbl, @bartlettroscoe, @dremerb,
@huqy, @eugeneswalker, @mathstuf, @mpbelhorn, @mamelara, @marcmengel who
all contributed to issues and prs addressing something along these lines.

It makes it possible for the user to change the default location of
the user config ~/.spack and system config /etc/spack folders.

This is a stop-gap approach until we have figured out how to deal with
the system and user config scopes more generally, as there are plans to
potentially / eventually get rid of them.

Why make this change?

User config

Spack is a bit of a pain when you have:

  • a shared $HOME folder across different systems.
  • multiple Spack versions on the same system.

System config

  • On shared systems with a versioned programming environment / toolkit,
    system administrators want to provide config for each version (e.g.
    21.09, 21.10) of the programming environment, and the user Spack
    instance should be able to pick this up without a steep learning
    curve.
  • On shared systems the user should be able to opt out of the
    hard-coded config scope in /etc/spack, since it may be incompatible with their
    particular instance. Currently Spack can only opt out of all config
    scopes through overrides with "config:":, "packages:":, but that
    also drops the defaults config, which would have to be repeated, which
    is undesirable, especially the lengthy packages.yaml.

An example use case is: having config in this folder:

/path/to/programming/environment/{version}/{compilers,packages}.yaml

and have module load spack-system-config set the variable

SPACK_SYSTEM_CONFIG_PATH=/path/to/programming/environment/{version}

where the user no longer has to worry about what {version} they are
on.

Continuous integration

Finally, there is the use case of continuous integration, which may
clone an arbitrary Spack version, which optimally should not pick up
system or user config from the previous run (like may happen in
classical bare metal non-containerized filesystem stateful
jenkins pipelines). In fact this is very similar to how spack itself
tries to avoid picking up system dependencies during builds...

But environments solve this?

  • You could do includes in environment files to get similar behavior
    to the spack_system_config_path example, but environments
    1. require you to list paths to individual config files, not directories;
    2. fail if the listed config file does not exist.
  • They allow you to override config scopes, but this is generally too
    rigorous, as it requires you to repeat the default config, in
    particular packages.yaml, and just defies the point of layered config.

@trws
Copy link
Copy Markdown
Contributor

trws commented Oct 20, 2021

FWIW, I'm all for this. Admittedly I would prefer that it go further, especially making it possible to make a spack self-contained with a file on disk, but any step in this direction seems like a good one to me.

trws
trws previously approved these changes Oct 20, 2021
Copy link
Copy Markdown
Contributor

@trws trws left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have follow-on PRs to this, but have no issue with anything here.

Option to support XDG and non-sysv system config path setups for example would be nice, but with this we can emulate all of that in the meantime.

@becker33, @alalazo, @tgamblin?

@tjfulle
Copy link
Copy Markdown
Contributor

tjfulle commented Oct 21, 2021

I've been submitting pull requests for several years about this issue and would be happy to see something done about it. I haven't understood the reluctance of the Spack team to make the user config path overridable by an environment variable - this is a defector standard way of customizing applications in the Linux/HPC worlds. I've got two comments on this specific PR:

  • this seems overly complicated. Why not just do

    user_config_path = os.getenv('SPACK_USER_CONFIG_PATH', os.path.expanduser('~/'))

    This can be simply tested with the following test:

    import os
    from spack.util.executable import Executable
    import spack.paths
    def test_user_config_path():
        spack_script = os.path.join(spack.paths.bin_path, 'spack')
        spack_python = Executable('{0} python'.format(spack_script))
        env = os.environ.copy()
        user_config_path ='foo/baz'
        env['SPACK_USER_CONFIG_PATH'] = user_config_path
        spack_python(
            '-c',
            'import spack.paths; assert spack.paths.user_config_path == {0!r}'.format(user_config_path),
            allow_failure=True,
            env=env,
        )
        assert spack_python.returncode == 0
  • this PR does not go far enough. It needs to replace occurrences of ~/.spack in $spack/etc/spack/defaults/config.yaml with $user_config_path. @trws has an alternative PR that does this. Without this, spack will still write to ./spack

@haampie
Copy link
Copy Markdown
Member Author

haampie commented Oct 21, 2021

for several years

this PR does not go far enough.

Please read the first paragraph, the PR is simple to get it in, hopefully before 0.17 branches off, for which the window is narrowing.

Adding $user_config_path adds a new feature people will start to rely on. That may reduce chances of getting this in, since there are plans to drop the config scopes in the future (and I don't want to get into that in this PR). Note that the only relevant config setting you're talking about is misc_cache: ~/.spack/cache. After you've moved your user config to $SPACK_USER_CONFIG_PATH/config.yaml, just set the config:misc_cache value there.

Why not just do

See the reply above, there are plans to gradually get rid of all the spack globals and put them in some config/context object, which allows us to do unit testing of its initialization without side effects. Your test works too and is side effect free but starting a python subprocess was again something I feared would spark discussion blocking the actual contribution. Unit testing _get_system_config_path at least resembles the future unit test for default settings in spack.

@tjfulle
Copy link
Copy Markdown
Contributor

tjfulle commented Oct 21, 2021

Thanks for the reply @haampie - I'll just register my opinion that I think pulling in SPACK_USER_CONFIG_PATH without the corresponding change to the default misc_cache is a step backwards. I have a fork of Spack that I administer that applied a similar patch to your PR. User's were very angry that Spack kept trying to write to ~/.spack when they set SPACK_USER_CONFIG_PATH to something else. These user's were happy with the default configuration and did not want to write a config.yaml just to get spack to write the misc. cache to user_config_path. After applying a patch to write misc. cache where it should be (user_config_path) the complaints stopped.

I still maintain this fork of spack and many dozen users at Sandia use it - once this issue is resolved we will all happily ditch the fork and use the mainline spack.

@haampie
Copy link
Copy Markdown
Member Author

haampie commented Oct 21, 2021

Calling it a step back is not helpful if you really want something along these lines to be merged. And it really is not, cause: (a) it's enabling you to opt out of ~/.spack as a source where config is read from, which was previously impossible. And (b) you can give better defaults to your angry users by setting config:misc_cache:[some path] e.g. ~/.spack/cluster-name/cache, $scratch/spack-cache, or $SPACK_USER_CONFIG_PATH/.spack/cache in $SPACK_SYSTEM_CONFIG/config.yaml.

For further discussion please resort to #12892.

@haampie haampie force-pushed the feature/add-SPACK_x_CONFIG_PATH-env-variable branch from aa389f6 to 5e4f648 Compare October 21, 2021 21:53
@tgamblin tgamblin self-assigned this Oct 25, 2021
@tgamblin tgamblin changed the title Make system and user config path overridable config: Overrides for system and user scopes Oct 26, 2021
@spackbot-app spackbot-app bot added defaults documentation Improvements or additions to documentation labels Oct 26, 2021
Spack's `system` and `user` scopes provide ways for administrators and
users to set global defaults for all Spack instances, but for use cases
where one wants a clean Spack installation, these scopes can be undesirable.
For example, users may want to opt out of global system configuration, or
they may want to ignore their own home directory settings when running in
a continuous integration environment.

Spack also, by default, keeps various caches and user data in `~/.spack`,
but users may want to override these locations.

Spack provides three environment variables that allow you to override or
opt out of configuration locations:

 * `SPACK_USER_CONFIG_PATH`: Override the path to use for the
   `user` (`~/.spack`) scope.

 * `SPACK_SYSTEM_CONFIG_PATH`: Override the path to use for the
   `system` (`/etc/spack`) scope.

 * `SPACK_DISABLE_LOCAL_CONFIG`: set this environment variable to completely
   disable *both* the system and user configuration directories. Spack will
   only consider its own defaults and `site` configuration locations.

And one that allows you to move the default cache location:

 * `SPACK_USER_CACHE_PATH`: Override the default path to use for user data
   (misc_cache, tests, reports, etc.)

With these settings, if you want to isolate Spack in a CI environment, you can do this:

   export SPACK_DISABLE_LOCAL_CONFIG=true
   export SPACK_USER_CACHE_PATH=/tmp/spack

This is a stop-gap approach until we have figured out how to deal with
the system and user config scopes more generally, as there are plans to
potentially / eventually get rid of them.

**User config**

Spack is a bit of a pain when you have:

- a shared $HOME folder across different systems.
- multiple Spack versions on the same system.

**System config**

- On shared systems with a versioned programming environment / toolkit,
  system administrators want to provide config for each version (e.g.
  21.09, 21.10) of the programming environment, and the user Spack
  instance should be able to pick this up without a steep learning
  curve.
- On shared systems the user should be able to opt out of the
  hard-coded config scope in /etc/spack, since it may be incompatible
  with their particular instance. Currently Spack can only opt out of all
  config scopes through overrides with `"config:":`, `"packages:":`, but that
  also drops the defaults config, which would have to be repeated, which
  is undesirable, especially the lengthy packages.yaml.

An example use case is: having config in this folder:

```
/path/to/programming/environment/{version}/{compilers,packages}.yaml
```

and have `module load spack-system-config` set the variable

```
SPACK_SYSTEM_CONFIG_PATH=/path/to/programming/environment/{version}
```

where the user no longer has to worry about what `{version}` they are
on.

**Continuous integration**

Finally, there is the use case of continuous integration, which may
clone an arbitrary Spack version, which optimally should not pick up
system or user config from the previous run (like may happen in
classical bare metal non-containerized filesystem side effect ridden
jenkins pipelines). In fact this is very similar to how spack itself
tries to avoid picking up system dependencies during builds...

**But environments solve this?**

- You could do `include`s in environment files to get similar behavior
  to the spack_system_config_path example, but environments require you
  to:
  1) require paths to individual config files, not directories.
  2) fail if the listed config file does not exist
- They allow you to override config scopes, but this is generally too
  rigurous, as it requires you to repeat the default config, in
  particular packages.yaml, and just defies the point of layered config.

Co-authored-by: Tom Scogland <[email protected]>
Co-authored-by: Tim Fuller <[email protected]>
Co-authored-by: Steve Leak <[email protected]>
Co-authored-by: Todd Gamblin <[email protected]>
@haampie haampie force-pushed the feature/add-SPACK_x_CONFIG_PATH-env-variable branch from 109e35e to 5d66b08 Compare October 26, 2021 21:50
@tgamblin tgamblin enabled auto-merge (rebase) October 26, 2021 21:53
@haampie
Copy link
Copy Markdown
Member Author

haampie commented Oct 26, 2021

github actions doesn't want it to be merged, maybe just drop the pr?

=================================== FAILURES ===================================
________________________ test_list_format_version_json _________________________

    @pytest.mark.maybeslow
    def test_list_format_version_json():
        output = list('--format', 'version_json')
>       assert '  {"name": "cloverleaf3d",' in output
E       assert '  {"name": "cloverleaf3d",' in '[\n  {"name": "3dtk",\n   "latest_version": "1.2",\n   "versions": ["trunk", "1.2"],\n   "homepage": "http://slam6d.s...topcf",\n   "latest_version": "1.0.5",\n   "versions": ["1.0.5"],\n   "homepage": "https://cgit.freedesktop.org/x\n]\n'

lib/spack/spack/test/cmd/list.py:58: AssertionError
!!!!!!!!!!!!!!!!!!!! Interrupted: stopping after 1 failures !!!!!!!!!!!!!!!!!!!!
== 1 failed, 2429 passed, 7 skipped, 22 xfailed, 5 xpassed in 1106.24 seconds ==

same issue in another pr of mine

@haampie haampie closed this Oct 26, 2021
auto-merge was automatically disabled October 26, 2021 23:12

Pull request was closed

@haampie haampie reopened this Oct 26, 2021
@tgamblin tgamblin enabled auto-merge (rebase) October 26, 2021 23:47
@tgamblin tgamblin mentioned this pull request Oct 27, 2021
2 tasks
@tgamblin tgamblin merged commit ae6e83b into spack:develop Oct 27, 2021
@haampie haampie deleted the feature/add-SPACK_x_CONFIG_PATH-env-variable branch October 27, 2021 07:50
@tgamblin
Copy link
Copy Markdown
Member

It’s done y’all!

@haampie
Copy link
Copy Markdown
Member Author

haampie commented Oct 27, 2021

yay

@mathstuf
Copy link
Copy Markdown
Contributor

So I tested this out while also linking ~/.spack -> /dev/null to catch anything else. I end up with this error:

==> Error: [Errno 20] Not a directory: '/Users/svc-dashboard/.spack/bootstrap'

So there are still bits that want to touch ~/.spack around.

@tgamblin
Copy link
Copy Markdown
Member

@mathstuf Did you use both SPACK_DISABLE_LOCAL_CONFIG and SPACK_USER_CACHE_PATH? If you don’t use the latter, caches like bootstrap store will still default to the home directory.

@mathstuf
Copy link
Copy Markdown
Contributor

Yep:

        SPACK_USER_CACHE_PATH: "$GIT_CLONE_PATH/.gitlab/spack-cache"
        SPACK_DISABLE_LOCAL_CONFIG: "true"

@haampie
Copy link
Copy Markdown
Member Author

haampie commented Oct 28, 2021

So, this is because $user_config_path is not magically gone if you use SPACK_DISABLE_LOCAL_CONFIG 🙃

This means that you still have to override all config settings that use $user_config_path or use SPACK_USER_CONFIG_PATH.

I think the better solution now is to get rid of SPACK_DISABLE_LOCAL_CONFIG, it's mostly confusing.

Instead stick to SPACK_USER_CONFIG_PATH, SPACK_SYSTEM_CONFIG_PATH and SPACK_USER_CACHE_PATH.

@tgamblin
Copy link
Copy Markdown
Member

tgamblin commented Oct 28, 2021

@mathstuf: can you send a debug trace for the failure you mentioned above? (run with spack -d)

Do you refer to user_config_path in your configuration? Is that where it's coming from?

@trws
Copy link
Copy Markdown
Contributor

trws commented Oct 28, 2021

My guess would be it's this biting us: https://github.com/spack/spack/pull/26735/files#diff-24ac473c44f26728fd3da17d5dd741c4073fa0d8aec651971c2aaf7d17747c9fR7

That still needs to go somewhere, probably fall back to the next config scope like the rest of spack does?

@mathstuf
Copy link
Copy Markdown
Contributor

@mathstuf: can you send a debug trace for the failure you mentioned above? (run with spack -d)

I'll try and get a run with this in.

Do you refer to user_config_path in your configuration? Is that where it's coming from?

Not that I know of. The entirety of the Spack on the machine (besides the ~/.spack tripwire) is in this script (I removed the noisy Linux setup parts):

Spack setup script

Environment:

        SPACK_USER_CACHE_PATH: "$GIT_CLONE_PATH/.gitlab/spack-cache"
        SPACK_DISABLE_LOCAL_CONFIG: "true"
#!/bin/sh

set -e

readonly spackroot="$CI_PROJECT_DIR/.gitlab/spack"

# Clone Spack itself.
git clone --depth 1 https://github.com/spack/spack "$spackroot/src"
export SPACK_ROOT="$spackroot/src"
export PATH="$PATH:$SPACK_ROOT/bin"

# Search for the compiler.
spack compiler find

# Clone mochi package repository.
git clone --depth 1 https://github.com/mochi-hpc/mochi-spack-packages "$spackroot/mochi"

# Add mochi repo to Spack.
spack repo add "$spackroot/mochi"

# Inject CI tools to avoid builds.
mkdir -p "$spackroot/config"
cat > "$spackroot/config/packages.yaml" <<EOF
packages:
  all:
    target: [$( uname -m )]
  cmake:
    buildable: false
    externals:
    - spec: cmake@$( cmake --version | head -n1 | cut -d' ' -f3 )
      prefix: $CI_PROJECT_DIR/.gitlab/cmake
EOF

readonly spec="mochi-thallium@main^libfabric fabrics=tcp,rxm"

# Install thallium.
spack -C "$spackroot/config" install $spec

# Clean up the directory.
spack -C "$spackroot/config" clean

tgamblin added a commit that referenced this pull request Oct 28, 2021
There were some loose ends left in ##26735 that cause errors when
using `SPACK_DISABLE_LOCAL_CONFIG`.

- [x] Fix hard-coded `~/.spack` references in `install_test.py` and `monitor.py`

Also, if `SPACK_DISABLE_LOCAL_CONFIG` is used, there is the issue that
`$user_config_path`, when used in configuration files, makes no sense,
because there is no user config scope.

Since we already have `$user_cache_path` in configuration files, and since there
really shouldn't be *any* data stored in a configuration scope (which is what
you'd configure in `config.yaml`/`bootstrap.yaml`/etc., this just removes
`$user_config_path`.

There will *always* be a `$user_cache_path`, as Spack needs to write files, but
we shouldn't rely on the existence of a particular configuration scope in the
Spack code, as scopes are configurable, both in number and location.

- [x] Remove `$user_config_path` substitution.
- [x] Fix reference to `$user_config_path` in `etc/spack/deaults/bootstrap.yaml`
      to refer to `$user_cache_path`, which is where it was intended to be.
tgamblin added a commit that referenced this pull request Oct 28, 2021
There were some loose ends left in ##26735 that cause errors when
using `SPACK_DISABLE_LOCAL_CONFIG`.

- [x] Fix hard-coded `~/.spack` references in `install_test.py` and `monitor.py`

Also, if `SPACK_DISABLE_LOCAL_CONFIG` is used, there is the issue that
`$user_config_path`, when used in configuration files, makes no sense,
because there is no user config scope.

Since we already have `$user_cache_path` in configuration files, and since there
really shouldn't be *any* data stored in a configuration scope (which is what
you'd configure in `config.yaml`/`bootstrap.yaml`/etc., this just removes
`$user_config_path`.

There will *always* be a `$user_cache_path`, as Spack needs to write files, but
we shouldn't rely on the existence of a particular configuration scope in the
Spack code, as scopes are configurable, both in number and location.

- [x] Remove `$user_config_path` substitution.
- [x] Fix reference to `$user_config_path` in `etc/spack/deaults/bootstrap.yaml`
      to refer to `$user_cache_path`, which is where it was intended to be.
@tgamblin
Copy link
Copy Markdown
Member

@mathstuf: I think I've fixed this over in #27022 -- gripping around for hard-coded .spack references. I assumed the variables in spack.paths were actually being used as defaults here and that was clearly wrong.

tgamblin added a commit that referenced this pull request Oct 28, 2021
…27022)

There were some loose ends left in ##26735 that cause errors when
using `SPACK_DISABLE_LOCAL_CONFIG`.

- [x] Fix hard-coded `~/.spack` references in `install_test.py` and `monitor.py`

Also, if `SPACK_DISABLE_LOCAL_CONFIG` is used, there is the issue that
`$user_config_path`, when used in configuration files, makes no sense,
because there is no user config scope.

Since we already have `$user_cache_path` in configuration files, and since there
really shouldn't be *any* data stored in a configuration scope (which is what
you'd configure in `config.yaml`/`bootstrap.yaml`/etc., this just removes
`$user_config_path`.

There will *always* be a `$user_cache_path`, as Spack needs to write files, but
we shouldn't rely on the existence of a particular configuration scope in the
Spack code, as scopes are configurable, both in number and location.

- [x] Remove `$user_config_path` substitution.
- [x] Fix reference to `$user_config_path` in `etc/spack/deaults/bootstrap.yaml`
      to refer to `$user_cache_path`, which is where it was intended to be.
@alalazo alalazo mentioned this pull request May 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment