def test_merge_rose_cylc_suite_install_conf(old, new, expect): loader = ConfigLoader() old = loader.load(StringIO(old)) new = loader.load(StringIO(new)) assert (loader.load(StringIO(expect)) == merge_rose_cylc_suite_install_conf(old, new) )
def test_get_cli_opts_node(opt_confs, defines, rose_template_vars, expect): opts = SimpleNamespace(opt_conf_keys=opt_confs, defines=defines, rose_template_vars=rose_template_vars) loader = ConfigLoader() expect = loader.load(StringIO(expect)) result = get_cli_opts_node(opts) for item in ['env', 'template variables', 'opts']: assert result[item] == expect[item]
def test_get_cli_opts_node(opt_confs, defines, define_suites, expect): opts = SimpleNamespace(opt_conf_keys=opt_confs, defines=defines, define_suites=define_suites) loader = ConfigLoader() expect = loader.load(StringIO(expect)) result = get_cli_opts_node(opts) for item in ['env', 'jinja2:suite.rc', 'opts']: assert result[item] == expect[item]
def test_functional_record_cylc_install_options(monkeypatch, tmp_path, opts, files, env_inserts): """It works the way the proposal says it should. TODO: Once the the dump of the final rose-suite.conf is done then this should be expanded to test that too. """ # Pin down the results of the function used to provide a timestamp. def fake(*arg, **kwargs): return '18151210T0000Z' monkeypatch.setattr(DateTimeOperator, 'process_time_point_str', fake) testdir = tmp_path / 'test' refdir = tmp_path / 'ref' # Set up existing files, should these exist: for fname, content in files.items(): path = tmp_path / fname path.parent.mkdir(parents=True, exist_ok=True) path.write_text(content) # Set any environment variables we require: for envvar, val in env_inserts.items(): monkeypatch.setenv(envvar, val) loader = ConfigLoader() # Run the entry point top-level function: rose_suite_cylc_install_node, rose_suite_opts_node = \ record_cylc_install_options( rundir=testdir, opts=opts, srcdir=testdir ) rose_fileinstall(rundir=testdir, opts=opts, srcdir=testdir) ritems = sorted([i.relative_to(refdir) for i in refdir.rglob('*')]) titems = sorted([i.relative_to(testdir) for i in testdir.rglob('*')]) assert titems == ritems for counter, item in enumerate(titems): output = testdir / item reference = refdir / ritems[counter] if output.is_file(): assert_rose_conf_full_equal(loader.load(str(output)), loader.load(str(reference)), no_ignore=False)
def record_cylc_install_options( rundir=None, opts=None, srcdir=None, ): """Create/modify files recording Cylc install config options. Creates a new config based on CLI options and writes it to the workflow install location as ``rose-suite-cylc-install.conf``. If ``rose-suite-cylc-install.conf`` already exists over-writes changed items, except for ``!opts=`` which is merged and simplified. If ``!opts=`` have been changed these are appended to those that have been written in the installed ``rose-suite.conf``. Args: srcdir (pathlib.Path): Used to check whether the source directory contains a rose config. opts: Cylc option parser object - we want to extract the following values: - opt_conf_keys (list or str): Equivelent of ``rose suite-run --option KEY`` - defines (list of str): Equivelent of ``rose suite-run --define KEY=VAL`` - suite_defines (list of str): Equivelent of ``rose suite-run --define-suite KEY=VAL`` rundir (pathlib.Path): Path to dump the rose-suite-cylc-conf Returns: cli_config - Config Node which has been dumped to ``rose-suite-cylc-install.conf``. rose_suite_conf['opts'] - Opts section of the config node dumped to installed ``rose-suite.conf``. """ # Create a config based on command line options: cli_config = get_cli_opts_node(opts) # Leave now if there is nothing to do: if not cli_config: return False # Construct path objects representing our target files. (Path(rundir) / 'opt').mkdir(exist_ok=True) conf_filepath = Path(rundir) / 'opt/rose-suite-cylc-install.conf' rose_conf_filepath = Path(rundir) / 'rose-suite.conf' dumper = ConfigDumper() loader = ConfigLoader() # If file exists we need to merge with our new config, over-writing with # new items where there are duplicates. if conf_filepath.is_file(): if opts.clear_rose_install_opts: conf_filepath.unlink() else: oldconfig = loader.load(str(conf_filepath)) cli_config = merge_rose_cylc_suite_install_conf( oldconfig, cli_config) cli_config.comments = [' This file records CLI Options.'] dumper.dump(cli_config, str(conf_filepath)) # Merge the opts section of the rose-suite.conf with those set by CLI: if not rose_conf_filepath.is_file(): rose_conf_filepath.touch() rose_suite_conf = loader.load(str(rose_conf_filepath)) rose_suite_conf = add_cylc_install_to_rose_conf_node_opts( rose_suite_conf, cli_config) dumper(rose_suite_conf, rose_conf_filepath) return cli_config, rose_suite_conf
def main(): """Implement the "rose config" command.""" opt_parser = RoseOptionParser() opt_parser.add_my_options("default", "env_var_process_mode", "files", "keys", "meta", "meta_key", "no_ignore", "no_opts", "print_conf_mode") opts, args = opt_parser.parse_args() report = Reporter(opts.verbosity - opts.quietness) metomi.rose.macro.add_meta_paths() if opts.meta_key: opts.meta = True if opts.files and opts.meta_key: report(Exception("Cannot specify both a file and meta key.")) sys.exit(1) config_loader = ConfigLoader() sources = [] if opts.files: root_node = ConfigNode() for fname in opts.files: if fname == "-": sources.append(sys.stdin) else: if opts.meta: try: root_node = config_loader.load(fname) except ConfigSyntaxError as exc: report(exc) sys.exit(1) rel_path = os.sep.join(fname.split(os.sep)[:-1]) fpath = get_meta_path(root_node, rel_path) if fpath is None: report(MetadataNotFoundEvent(fname)) else: sources.append(fpath) else: sources.append(fname) elif opts.meta: root_node = ConfigNode() if opts.meta_key: root_node.set(["meta"], opts.meta_key) else: fname = os.path.join(os.getcwd(), metomi.rose.SUB_CONFIG_NAME) try: root_node = config_loader.load(fname) except ConfigSyntaxError as exc: report(exc) sys.exit(1) fpath = get_meta_path(root_node, meta_key=opts.meta_key) root_node.unset(["meta"]) if fpath is None: report(Exception("Metadata not found")) sys.exit(1) else: sources.append(fpath) else: root_node = ResourceLocator.default().get_conf() for source in sources: try: if opts.meta or opts.no_opts: config_loader.load(source, root_node) else: config_loader.load_with_opts(source, root_node) except (ConfigSyntaxError, IOError) as exc: report(exc) sys.exit(1) if source is sys.stdin: source.close() if opts.quietness: sys.exit(root_node.get(args, opts.no_ignore) is None) if opts.keys_mode: try: keys = list(root_node.get(args, opts.no_ignore).value) except AttributeError: sys.exit(1) keys.sort() for key in keys: print(key) sys.exit() conf_dump = ConfigDumper() if len(args) == 0: conf_dump(root_node, concat_mode=opts.print_conf_mode) sys.exit() node = root_node.get(args, opts.no_ignore) if node is not None and isinstance(node.value, dict): if opts.print_conf_mode: conf_dump(ConfigNode().set(args, node.value), concat_mode=True) sys.exit() keys = list(node.value) keys.sort() for key in keys: node_of_key = node.get([key], opts.no_ignore) if node_of_key: value = node_of_key.value state = node_of_key.state string = "%s%s=%s" % (state, key, value) lines = string.splitlines() print(lines[0]) i_equal = len(state + key) + 1 for line in lines[1:]: print(" " * i_equal + line) sys.exit() if node is None: if opts.default is None: sys.exit(1) value = opts.default elif opts.env_var_process_mode: value = env_var_process(node.value) else: value = node.value if opts.print_conf_mode: conf_dump(ConfigNode().set(args, value), concat_mode=True) else: print(value) sys.exit()
def main(): """Implement the "rose config" command.""" opt_parser = RoseOptionParser(description=''' Parse and print rose configuration files. With no option and no argument, print the rose site + user configuration. EXAMPLES # Print the value of OPTION in SECTION. rose config SECTION OPTION # Print the value of OPTION in SECTION in FILE. rose config --file=FILE SECTION OPTION # Print the value of OPTION in SECTION if exists, or VALUE otherwise. rose config --default=VALUE SECTION OPTION # Print the OPTION=VALUE pairs in SECTION. rose config SECTION # Print the value of a top level OPTION. rose config OPTION # Print the OPTION keys in SECTION. rose config --keys SECTION # Print the SECTION keys. rose config --keys # Exit with 0 if OPTION exists in SECTION, or 1 otherwise. rose config -q SECTION OPTION # Exit with 0 if SECTION exists, or 1 otherwise. rose config -q SECTION # Combine the configurations in FILE1 and FILE2, and dump the result. rose config --file=FILE1 --file=FILE2 # Print the value of OPTION in SECTION of the metadata associated with # the specified config FILE rose config --file=FILE --meta SECTION OPTION # Print the value of a specified metadata KEY rose config --meta-key=KEY ''', epilog=''' ENVIRONMENT VARIABLES optional ROSE_META_PATH Prepend `$ROSE_META_PATH` to the metadata search path. ''') opt_parser.add_my_options( "default", "env_var_process_mode", "files", "keys", "meta", "meta_key", "no_ignore", "no_opts", "print_conf_mode", ) # the quietness argument is non-standard for this command opt_parser.modify_option( 'quietness', help=("Exit with 0 if the specified `SECTION` and/or `OPTION` exist in" " the configuration, or 1 otherwise."), ) opts, args = opt_parser.parse_args() report = Reporter(opts.verbosity - opts.quietness) metomi.rose.macro.add_meta_paths() if opts.meta_key: opts.meta = True if opts.files and opts.meta_key: report(Exception("Cannot specify both a file and meta key.")) sys.exit(1) config_loader = ConfigLoader() sources = [] if opts.files: root_node = ConfigNode() for fname in opts.files: if fname == "-": sources.append(sys.stdin) else: if opts.meta: try: root_node = config_loader.load(fname) except ConfigSyntaxError as exc: report(exc) sys.exit(1) rel_path = os.sep.join(fname.split(os.sep)[:-1]) fpath = get_meta_path(root_node, rel_path) if fpath is None: report(MetadataNotFoundEvent(fname)) else: sources.append(fpath) else: sources.append(fname) elif opts.meta: root_node = ConfigNode() if opts.meta_key: root_node.set(["meta"], opts.meta_key) else: fname = os.path.join(os.getcwd(), metomi.rose.SUB_CONFIG_NAME) try: root_node = config_loader.load(fname) except ConfigSyntaxError as exc: report(exc) sys.exit(1) fpath = get_meta_path(root_node, meta_key=opts.meta_key) root_node.unset(["meta"]) if fpath is None: report(Exception("Metadata not found")) sys.exit(1) else: sources.append(fpath) else: root_node = ResourceLocator.default().get_conf() for source in sources: try: if opts.meta or opts.no_opts: config_loader.load(source, root_node) else: config_loader.load_with_opts(source, root_node) except (ConfigSyntaxError, IOError) as exc: report(exc) sys.exit(1) if source is sys.stdin: source.close() if opts.quietness: sys.exit(root_node.get(args, opts.no_ignore) is None) if opts.keys_mode: try: keys = list(root_node.get(args, opts.no_ignore).value) except AttributeError: sys.exit(1) keys.sort() for key in keys: print(key) sys.exit() conf_dump = ConfigDumper() if len(args) == 0: conf_dump(root_node, concat_mode=opts.print_conf_mode) sys.exit() node = root_node.get(args, opts.no_ignore) if node is not None and isinstance(node.value, dict): if opts.print_conf_mode: conf_dump(ConfigNode().set(args, node.value), concat_mode=True) sys.exit() keys = list(node.value) keys.sort() for key in keys: node_of_key = node.get([key], opts.no_ignore) if node_of_key: value = node_of_key.value state = node_of_key.state string = "%s%s=%s" % (state, key, value) lines = string.splitlines() print(lines[0]) i_equal = len(state + key) + 1 for line in lines[1:]: print(" " * i_equal + line) sys.exit() if node is None: if opts.default is None: sys.exit(1) value = opts.default elif opts.env_var_process_mode: value = env_var_process(node.value) else: value = node.value if opts.print_conf_mode: conf_dump(ConfigNode().set(args, value), concat_mode=True) else: print(value) sys.exit()