pungi/bin/pungi-config-dump
Lubomír Sedlář 32a6415e58 config: Keep known options defined on CLI
If the validation or dumping script is given some options, they should
only be removed if they are not valid. We have to remove the invalid
ones, otherwise that would cause a warning about unknown options.

Signed-off-by: Lubomír Sedlář <lsedlar@redhat.com>
2019-06-25 15:13:16 +02:00

210 lines
5.7 KiB
Python
Executable File

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
import argparse
import json
import os
import shutil
import sys
from six.moves import configparser
import kobo.conf
import pungi.checks
import pungi.util
from pungi_utils import config_utils
def load_file(source, conf):
try:
with open(source) as f:
conf.load_from_dict(json.load(f))
except ValueError:
conf.load_from_file(source)
def load_source(source, conf):
if os.path.isfile(source):
load_file(source, conf)
elif os.path.isdir(source):
load_file(os.path.join(source, "logs/global/config-dump.global.log"), conf)
else:
raise RuntimeError("Source %s is neither file nor directory." % source)
def dump_multi_config(conf_file, dest, **kwargs):
"""Given a multi compose config, clone it and all referenced files to a
given directory.
"""
parser = configparser.RawConfigParser()
parser.read(conf_file)
pungi.util.makedirs(dest)
basedir = os.path.dirname(conf_file)
def copy_local_files(config):
"""Helper function called on all loaded Pungi configs that copies local
variants or comps files (unless specified by absolute path).
"""
for opt in "comps_file", "variants_file":
val = config.get(opt)
if isinstance(val, str) and val[0] != "/":
shutil.copy2(os.path.join(basedir, val), dest)
for section in parser.sections():
if section == "general":
continue
file_path = parser.get(section, "config")
dest_file = os.path.splitext(os.path.join(dest, file_path))[0] + ".json"
with open(dest_file, "w") as fh:
if not process_file(
[os.path.join(basedir, file_path)],
out=fh,
callback=copy_local_files,
**kwargs
):
return False
parser.set(section, "config", os.path.basename(dest_file))
with open(os.path.join(dest, os.path.basename(conf_file)), "w") as fh:
parser.write(fh)
return True
def process_file(
sources,
out=sys.stdout,
callback=None,
defines=None,
just_dump=False,
event=None,
offline=False,
):
"""Load Pungi config file, validate it, optionally resolve Git references,
and dump created JSON to a given stream.
:param callable callback: a callable to call with parsed config
:param dict defines: mapping of values to define before parsing the config
:param bool just_dump: skip validation and adding default values
:param int event: Koji event to hardcode into the config
:param bool offline: skip resolving Git references
:returns: False if validation failed, True otherwise
"""
conf = kobo.conf.PyConfigParser()
conf.load_from_dict(defines or {})
for source in sources:
load_source(source, conf)
if not just_dump:
errors, _ = pungi.checks.validate(conf, offline=offline)
if errors:
for error in errors:
print(error, file=sys.stderr)
return False
if event:
conf["koji_event"] = event
# Clean up defines from the final final config. We don't want to keep them
# as they would cause warnings during validation.
config_utils.remove_unknown(conf, defines)
if callback:
callback(conf)
json.dump(conf, out, sort_keys=True, indent=4)
return True
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"sources",
metavar="SOURCE",
nargs="+",
help=(
"Source for the configuration; either a compose "
"or arbitrary number of config files."
),
)
parser.add_argument(
"--freeze-event",
metavar="ID",
type=pungi.util.parse_koji_event,
help=(
"Include this koji event in the created config; "
"takes either event ID or path to a compose"
),
)
parser.add_argument(
"-e",
"--define",
action="append",
default=[],
metavar="VAR=VALUE",
type=config_utils.validate_definition,
help=(
"Define a variable on command line and inject it into the config file. "
"Can be used multiple times."
),
)
group = parser.add_mutually_exclusive_group()
group.add_argument(
"--just-dump",
action="store_true",
help=(
"Do not transform the config in any way. Default values are not "
"added, git references are not resolved."
),
)
group.add_argument(
"--offline", action="store_true", help="Do not resolve git references."
)
parser.add_argument(
"--multi",
metavar="DIR",
help=(
"Treat source as config for pungi-orchestrate and store dump into "
"given directory."
),
)
args = parser.parse_args()
defines = config_utils.extract_defines(args.define)
if args.multi:
if len(args.sources) > 1:
parser.error("Only one multi config can be specified.")
return dump_multi_config(
args.sources[0],
dest=args.multi,
defines=defines,
just_dump=args.just_dump,
event=args.freeze_event,
offline=args.offline,
)
return process_file(
args.sources,
defines=defines,
just_dump=args.just_dump,
event=args.freeze_event,
offline=args.offline,
)
if __name__ == "__main__":
try:
if not main():
sys.exit(1)
except RuntimeError as exc:
print("Error", str(exc), file=sys.stderr)
sys.exit(2)