Skip to content
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

flux-mini: add environment manipulation options #3150

Merged
merged 4 commits into from
Aug 21, 2020

Conversation

grondo
Copy link
Contributor

@grondo grondo commented Aug 19, 2020

Ok, I'm parking this experimental PR here before moving on to other things.

This PR adds --env=RULE, --env-filter=PATTERN, and --env-file=FILE options to flux-mini as discussed in #3141.

The PR is marked WIP for now because there were some concerns about the interface. Namely this interface doesn't allow exporting environment variables that begin with - or ^ explictly to jobs, but you can't do that with the shell anyway (at least most shells). As @trws demonstrated a user could run flux-mini under /usr/bin/env if an environment variable not already set in the environment needs to start with a - or ^.

That being said, this interface was just an experiment so I'd be fine trying something else.

Note that env variable values are expanded before being set by string.Template using a ChainMap of the current jobspec environment and os.environ, so this works as you'd expect:

$ flux mini run --dry-run --env-filter=* --env='PATH=/foo:${PATH}' --env='PATH=${PATH}:/foo' hostname | jq .attributes.system.environment
{
  "PATH": "/foo:/home/grondo/git/flux-core.git/src/cmd:/usr/bin:/bin:/foo"
}

Unfortunately, this requires that the variable expressions be quoted from the shell or else you only get the current environment expanded. Maybe that is too subtle for users?

It is nice when using an env file though, as it allows this to work:

-*
FOO=bar
BAR=${FOO}/baz

@trws
Copy link
Member

trws commented Aug 20, 2020

I like that a lot. Even if the technical rule is that one can put those in there, almost everyone treats them as though you cant, and as you say it's possible to work around anyway. The ability to do appends or prepends like that makes me really happy, it handles the corner case of having other separators without breaking a sweat even for programs written for windows, and those that should have known better like lua.

One question I couldn't parse out in a quick glance. Is there a way to express a negative rather than positive pattern? Like, I want to remove all variables matching pattern?

@grondo
Copy link
Contributor Author

grondo commented Aug 20, 2020

One question I couldn't parse out in a quick glance. Is there a way to express a negative rather than positive pattern? Like, I want to remove all variables matching pattern?

Sorry for not being clear @trws.

The current functionality --env-filter=PATTERN or --env=-PATTERN removes all variables matching a pattern. However, it occurs to me we could treat the normal VAR usage, e.g. --env=VAR with no "=" as a pattern rule instead of just an exact match.

So this would work, without affecting previous functionality:

$ flux mini run --dry-run --env-filter=* --env='FLUX_*_PATH' hostname | jq .attributes.system.environment
{
  "FLUX_CONNECTOR_PATH": "/home/grondo/git/flux-core.git/src/connectors",
  "FLUX_MODULE_PATH": "/home/grondo/git/flux-core.git/src/modules",
  "FLUX_EXEC_PATH": "/home/grondo/git/flux-core.git/src/broker:/home/grondo/git/flux-core.git/src/cmd",
  "FLUX_PMI_LIBRARY_PATH": "/home/grondo/git/flux-core.git/src/common/.libs/libpmi.so"
}

@trws
Copy link
Member

trws commented Aug 20, 2020

Ah, ok, sorry I had it completely backwards (this is what happens when I try to do too many things at once 🤦 ). So we have negative patterns, but not positive ones. Is that true for the regular expression form as well? I need to think about this a bit, but "filter" in most places I know of usually takes a pattern or lambda or whatever that when true keeps the item, so it might be worth considering another name, or having both positive and negative filters like you mentioned originally? The C++ std lib is the one place that does the negative pattern thing, they call it remove_if in case that sparks any ideas.

@trws
Copy link
Member

trws commented Aug 20, 2020 via email

@grondo
Copy link
Contributor Author

grondo commented Aug 20, 2020

Thanks! I was having the same thought about the name! I'm fine with --env-remove=PATTERN if you are!
If you have other ideas let me know.

Note that in my current proposal --env=PATTERN would be the positive match, where -PATTERN or --env-remove=PATTERN would be the negative match.

A subtlety though, --env=PATTERN doesn't only include environment variables that match a pattern. It would pull all matching variables from the current environment, so it only makes sense when starting with an empty environment. That is, --env=* could be considered an implied first '--env' argument for flux mini.

@trws
Copy link
Member

trws commented Aug 20, 2020

A subtlety though, --env=PATTERN doesn't only include environment variables that match a pattern. It would pull all matching variables from the current environment, so it only makes sense when starting with an empty environment. That is, --env=* could be considered an implied first '--env' argument for flux mini.

That's an interesting point. It kidna gets back to my earlier thought/question, if a user did --env-remove=* --env=OMP_* would that remove everything and start with no environment, or remove everything then whitelist the ones starting with OMP_? The former is maybe easier to describe, but the latter is what things like .gitignore do, and might be quite useful. It would probably mean it would have to process as: "filter into or out of effective environment from original environment" rather than from current state of effective environment... Not sure.

I think the env-remove naming makes it much more clear by the way, works for me!

@grondo
Copy link
Contributor Author

grondo commented Aug 20, 2020

would we then be able to have
the regex version be —env=/<exp>/ for positive rather than
—env=-/ for the negative?

Yes, though I was thinking of keeping both, unless you don't think we need a negative match?

@grondo
Copy link
Contributor Author

grondo commented Aug 20, 2020

unless you don't think we need a negative match?

Or, rather, I guess it is more technically "remove all matching entries" (I'm getting confused on my terminology)

@trws
Copy link
Member

trws commented Aug 20, 2020 via email

@grondo
Copy link
Contributor Author

grondo commented Aug 20, 2020

Yeah I was thinking have both, shouldn’t have said it as “rather
than” more meant “as opposed to” or “in addition to” or
something.

Ok. Let me push that small change here so people could play with it if they want. I'll also change --env-filter to --env-remove.

@grondo
Copy link
Contributor Author

grondo commented Aug 20, 2020

That's an interesting point. It kidna gets back to my earlier thought/question, if a user did --env-remove=* --env=OMP_* would that remove everything and start with no environment, or remove everything then whitelist the ones starting with OMP_?

In the current implementation it would be the latter. Both the "positive match" and the template expansion of $VAR use a ChainMap(env, os.environ), so lookups are from the built environment first, then fall back to the process environment. The "rules" are stored in a list and applied in the order they are found on the cmdline, with the first implied rule being * (so we start with a copy of the current environment).

The former is maybe easier to describe, but the latter is what things like .gitignore do, and might be quite useful. It would probably mean it would have to process as: "filter into or out of effective environment from original environment" rather than from current state of effective environment... Not sure.

If I understand you correctly, this is pretty much what is done now. I think it successfully follows the principle of least surprise, except where it might not be obvious that we always start with a full environment. (so --env=FOO alone actually does nothing -- maybe we need an --env-only=PATTERN option that is effectively the same as --env-remove=* --env=PATTERN?)

@trws
Copy link
Member

trws commented Aug 20, 2020 via email

@grondo
Copy link
Contributor Author

grondo commented Aug 20, 2020

Great, thanks for the valuable feedback @trws!

Copy link
Member

@SteVwonder SteVwonder left a comment

Choose a reason for hiding this comment

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

LGTM! Super minor nits on the help messages:

src/cmd/flux-mini.py Outdated Show resolved Hide resolved
src/cmd/flux-mini.py Outdated Show resolved Hide resolved
src/cmd/flux-mini.py Show resolved Hide resolved
src/cmd/flux-mini.py Outdated Show resolved Hide resolved
@grondo
Copy link
Contributor Author

grondo commented Aug 20, 2020

Thanks @SteVwonder and @trws! I'll apply @SteVwonder's suggestions, then if the interface looks basically Ok, I'll expand tests and then amend flux-mini(1) documentation.

@grondo
Copy link
Contributor Author

grondo commented Aug 20, 2020

Ok, I've force pushed another version here with some documentation, and the try catch block around the template substitution as suggested by @SteVwonder (still need to add tests for error handling there)

@grondo grondo changed the title WIP: flux-mini: add environment manipulation options flux-mini: add environment manipulation options Aug 21, 2020
@grondo
Copy link
Contributor Author

grondo commented Aug 21, 2020

I've pushed the changes to throw an exception when variable substitution fails as noted by @SteVwonder (thanks for catching that!!), added tests and an additional ENVIRONMENT section the flux-mini(1) manpage.

I've removed WIP for now as this is ready for a real review.

Problem: flux-mini always copies the entire current environment into
jobspec, and there is no way to filter, modify, or amend it.

Add a new function get_filtered_environment() to flux-mini.py which
accepts and array of environment modification "rules" to apply to the
current process environment in order to form the final environment
copied into the jobspec.

The rules are

 * If a rule begins with '-', then the rest of the rule is a pattern
   which removes matching environment variables. If the pattern
   starts with '/', it is a regex (optionally ending with '/'),
   otherwise the pattern is considered a shell glob(7).

    Example: "-*" filters all variables, creating an empty environment.

 * If a rule begins with '^' then the rest of the rule is a filename
   from which to read more rules, one per line. The ~ character is
   expanded in filename to be users directory.

    Example: "^~/envfile" reads from file $HOME/envfile.

 * If a rule is of the form `VAR=VAL`, then the variable VAR is set
   to VAL. Before being set, however, variables in VAL are expanded
   via string.Template().substitute, so any $VARIABLE within VAL will
   be expanded using first the generated environment, falling back
   to os.environ.

     Example: "PATH=/bin", "PATH=$PATH:/bin"

 * Otherwise, the rule is considered a pattern from which to match
   variables from os.environ if they do not alread exist in the
   generated environment. E.g. "PATH" will export PATH from the
   current environment (if it has not already been set in the
   generated environment), and "OMP*" would copy all environment
   variables that start with "OMP" and are not already set in the
   generated environment.

     Example: "PATH", "FLUX_*_PATH"

Rules are specified on the command line with a new `--env` option.
For convenience, `--env-remove=PATTERN` and `--env-file=FILE` are also
provided, which are equivalent to `--env=-PATTERN` and `--env=^FILE`,
respectively. All rules are applied in the order in which they are
specified.

The first implied rule is always `--env=*`, i.e. the generated
environment always starts as a copy of the current environment.

Since an env file is just a set of rules, it can be used in place of
multiple command line arguments, and can even load other env files:

-FLUX_URI
-LS_COLORS
-OMPI_*
PATH=$PATH:/some/other/path
^~/.flux/some-other-env-file
@grondo grondo force-pushed the issue#3141 branch 3 times, most recently from 5c25354 to 988190d Compare August 21, 2020 04:19
Add a set of basic functionality tests for the flux-mini(1)
--env, --env-remove, and --env-file options.
Copy link
Member

@SteVwonder SteVwonder left a comment

Choose a reason for hiding this comment

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

Thanks @grondo. LGTM! Just one suggestion in the man page. Feel free to set MWP after you take a look at the suggestion. (Unless @trws or @garlick want to take a pass).

doc/man1/flux-mini.rst Outdated Show resolved Hide resolved
@grondo
Copy link
Contributor Author

grondo commented Aug 21, 2020

Thanks!

Add an ENVIRONMENT section to flux-mini(1) documenting the
--env, --env-remove, and --env-file options along with how the
environment is copied into the jobspec before job submission.
@codecov-commenter
Copy link

Codecov Report

Merging #3150 into master will decrease coverage by 0.03%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##           master    #3150      +/-   ##
==========================================
- Coverage   81.18%   81.14%   -0.04%     
==========================================
  Files         286      285       -1     
  Lines       44538    44514      -24     
==========================================
- Hits        36159    36122      -37     
- Misses       8379     8392      +13     
Impacted Files Coverage Δ
src/cmd/flux-mini.py 88.92% <100.00%> (+2.94%) ⬆️
src/broker/module.c 74.94% <0.00%> (-2.04%) ⬇️
src/modules/job-archive/job-archive.c 58.89% <0.00%> (-0.80%) ⬇️
src/modules/resource/acquire.c 65.06% <0.00%> (-0.69%) ⬇️
src/modules/kvs/kvs.c 66.04% <0.00%> (-0.63%) ⬇️
src/broker/broker.c 74.00% <0.00%> (-0.11%) ⬇️
src/common/libutil/sha256.c
src/common/libsubprocess/subprocess.c 87.47% <0.00%> (+0.32%) ⬆️
src/common/libsubprocess/local.c 79.93% <0.00%> (+0.34%) ⬆️
src/broker/runat.c 84.58% <0.00%> (+0.39%) ⬆️
... and 1 more

@mergify mergify bot merged commit fad8759 into flux-framework:master Aug 21, 2020
@grondo grondo deleted the issue#3141 branch August 21, 2020 21:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants