diff --git a/doc/man1/flux.rst b/doc/man1/flux.rst index dd6fd831e168..0a780a1c4803 100644 --- a/doc/man1/flux.rst +++ b/doc/man1/flux.rst @@ -37,6 +37,11 @@ OPTIONS :envvar:`FLUX_KVS_NAMESPACE` if current instance is confined to a KVS namespace in the parent. This option may be specified multiple times. +.. option:: -r, --root + + Like :option:`--parent`, but connect to the top-level, or root, instance. + This overrides any other uses of the :option:`--parent` option. + .. option:: -v, --verbose Display command environment, and the path search for *CMD*. diff --git a/src/cmd/flux.c b/src/cmd/flux.c index 7334f9b05bca..33c73212a82f 100644 --- a/src/cmd/flux.c +++ b/src/cmd/flux.c @@ -38,17 +38,29 @@ void setup_keydir (struct environment *env, int flags); static void print_environment (struct environment *env); static void register_builtin_subcommands (optparse_t *p); static void push_parent_environment (optparse_t *p, struct environment *env); +static int current_instance_level (optparse_t *p); static struct optparse_option opts[] = { - { .name = "verbose", .key = 'v', .has_arg = 0, + { .name = "verbose", + .key = 'v', + .has_arg = 0, .usage = "Be verbose about environment and command search", }, - { .name = "version", .key = 'V', .has_arg = 0, + { .name = "version", + .key = 'V', + .has_arg = 0, .usage = "Display command and component versions", }, - { .name = "parent", .key = 'p', .has_arg = 0, + { .name = "parent", + .key = 'p', + .has_arg = 0, .usage = "Set environment of parent instead of current instance", }, + { .name = "root", + .key = 'r', + .has_arg = 0, + .usage = "Set environment of root instead of current instance", + }, OPTPARSE_TABLE_END }; @@ -64,7 +76,8 @@ void usage (optparse_t *p) const char *val = getenv ("FLUX_CMDHELP_PATTERN"); const char *def = default_cmdhelp_pattern (p); - if (asprintf (&help_pattern, "%s%s%s", + if (asprintf (&help_pattern, + "%s%s%s", def ? def : "", val ? ":" : "", val ? val : "") < 0) @@ -121,6 +134,7 @@ int main (int argc, char *argv[]) const char *argv0 = argv[0]; int flags = FLUX_CONF_INSTALLED; int optindex; + int n; log_init ("flux"); @@ -221,9 +235,14 @@ int main (int argc, char *argv[]) environment_apply (env); - /* If --parent, push parent environment for each occurrence + /* If --parent, push parent environment for each occurrence. + * If --root, act as if --parent was used instance-level times. */ - for (int n = optparse_getopt (p, "parent", NULL); n > 0; n--) { + if (optparse_hasopt (p, "root")) + n = current_instance_level (p); + else + n = optparse_getopt (p, "parent", NULL); + while (n-- > 0) { push_parent_environment (p, env); environment_apply (env); } @@ -317,8 +336,10 @@ void setup_path (struct environment *env, const char *argv0) /* Check for a flux-.py in dir and execute it under the configured * PYTHON_INTERPRETER if found. */ -void exec_subcommand_py (bool vopt, const char *dir, - int argc, char *argv[], +void exec_subcommand_py (bool vopt, + const char *dir, + int argc, + char *argv[], const char *prefix) { char *path = xasprintf ("%s%s%s%s.py", @@ -345,13 +366,16 @@ void exec_subcommand_py (bool vopt, const char *dir, free (path); } -void exec_subcommand_dir (bool vopt, const char *dir, char *argv[], - const char *prefix) +void exec_subcommand_dir (bool vopt, + const char *dir, + char *argv[], + const char *prefix) { char *path = xasprintf ("%s%s%s%s", dir ? dir : "", dir ? "/" : "", - prefix ? prefix : "", argv[0]); + prefix ? prefix : "", + argv[0]); if (vopt) log_msg ("trying to exec %s", path); execvp (path, argv); /* no return if successful */ @@ -381,7 +405,7 @@ void exec_subcommand (const char *searchpath, bool vopt, int argc, char *argv[]) } } -static flux_t *flux_open_internal (optparse_t *p, const char *uri) +static flux_t *flux_open_internal (optparse_t *p) { flux_t *h = NULL; if ((h = optparse_get_data (p, "flux_t"))) @@ -400,11 +424,32 @@ static void flux_close_internal (optparse_t *p) } } +static int current_instance_level (optparse_t *p) +{ + const char *s; + char *endptr; + long int l; + flux_t *h; + + if (!(h = flux_open_internal (p))) + log_err_exit ("flux_open"); + + if (!(s = flux_attr_get (h, "instance-level"))) + log_err_exit ("failed to get instance-level attribute"); + + errno = 0; + l = strtol (s, &endptr, 10); + if (errno != 0 || *endptr != '\0') + log_err_exit ("got invalid instance-level attribute: %s", s); + + return (int) l; +} + static void push_parent_environment (optparse_t *p, struct environment *env) { const char *uri; const char *ns; - flux_t *h = flux_open_internal (p, NULL); + flux_t *h = flux_open_internal (p); if (h == NULL) log_err_exit ("flux_open"); @@ -425,11 +470,10 @@ static void push_parent_environment (optparse_t *p, struct environment *env) else environment_unset (env, "FLUX_KVS_NAMESPACE"); - /* Now close current handle and connect to parent URI. + /* Now close current handle. Next call to `flux_open()` will + * have FLUX_URI set to parent after environment_apply() is called. */ flux_close_internal (p); - if (!(h = flux_open_internal (p, uri))) - log_err_exit ("flux_open (parent)"); } static void print_environment (struct environment *env) @@ -442,7 +486,7 @@ static void print_environment (struct environment *env) flux_t *builtin_get_flux_handle (optparse_t *p) { - return flux_open_internal (p, NULL); + return flux_open_internal (p); } static void register_builtin_subcommands (optparse_t *p) diff --git a/t/t3100-flux-in-flux.t b/t/t3100-flux-in-flux.t index c54dbe2222c7..d469223b58ef 100755 --- a/t/t3100-flux-in-flux.t +++ b/t/t3100-flux-in-flux.t @@ -50,6 +50,19 @@ test_expect_success "flux --parent --parent works in subinstance" ' test_cmp guest2.test.exp guest2.test ' +test_expect_success "flux --root works in subinstance" ' + id=$(flux batch -n1 --wrap \ + flux run flux start ${ARGS} \ + flux --root getattr instance-level) && + flux job wait-event -vt 30 $id clean && + test_debug "cat flux-${id}.out" && + test "$(cat flux-${id}.out)" -eq 0 +' + +test_expect_success 'flux --root returns current instance at depth 0' ' + test $(flux --root getattr instance-level) -eq 0 +' + test_expect_success "instance-level attribute = 0 in new standalone instance" ' flux start ${ARGS} flux getattr instance-level >level_new.out && echo 0 >level_new.exp &&