Ejemplo n.º 1
0
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)
            )
Ejemplo n.º 2
0
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]
Ejemplo n.º 3
0
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)
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
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()
Ejemplo n.º 7
0
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()