orchestrator: Support generic pre- and post- scripts
Run arbitrary commands before and after the compose. The example config is updated to generate latest symlink with a post-compose script. The pre compose script runs always, post compose runs only if the compose is not doomed. JIRA: COMPOSE-3288 Signed-off-by: Lubomír Sedlář <lsedlar@redhat.com>
This commit is contained in:
parent
86fb93d603
commit
088ea7fe37
@ -47,6 +47,35 @@ General settings
|
|||||||
**kerberos_principal**
|
**kerberos_principal**
|
||||||
Kerberos principal for the ticket
|
Kerberos principal for the ticket
|
||||||
|
|
||||||
|
**pre_compose_script**
|
||||||
|
Commands to execute before first part is started. Can contain multiple
|
||||||
|
commands on separate lines.
|
||||||
|
**post_compose_script**
|
||||||
|
Commands to execute after the last part finishes and final status is
|
||||||
|
updated. Can contain multiple commands on separate lines. ::
|
||||||
|
|
||||||
|
post_compose_script =
|
||||||
|
compose-latest-symlink $COMPOSE_PATH
|
||||||
|
custom-post-compose-script.sh
|
||||||
|
|
||||||
|
Multiple environment variables are defined for the scripts:
|
||||||
|
|
||||||
|
* ``COMPOSE_PATH``
|
||||||
|
* ``COMPOSE_ID``
|
||||||
|
* ``COMPOSE_DATE``
|
||||||
|
* ``COMPOSE_TYPE``
|
||||||
|
* ``COMPOSE_RESPIN``
|
||||||
|
* ``COMPOSE_LABEL``
|
||||||
|
* ``RELEASE_ID``
|
||||||
|
* ``RELEASE_NAME``
|
||||||
|
* ``RELEASE_SHORT``
|
||||||
|
* ``RELEASE_VERSION``
|
||||||
|
* ``RELEASE_TYPE``
|
||||||
|
* ``RELEASE_IS_LAYERED`` – ``YES`` for layered products, empty otherwise
|
||||||
|
* ``BASE_PRODUCT_NAME`` – only set for layered products
|
||||||
|
* ``BASE_PRODUCT_SHORT`` – only set for layered products
|
||||||
|
* ``BASE_PRODUCT_VERSION`` – only set for layered products
|
||||||
|
* ``BASE_PRODUCT_TYPE`` – only set for layered products
|
||||||
|
|
||||||
Partial compose settings
|
Partial compose settings
|
||||||
------------------------
|
------------------------
|
||||||
|
@ -484,12 +484,58 @@ def run_kinit(config):
|
|||||||
atexit.register(os.remove, fname)
|
atexit.register(os.remove, fname)
|
||||||
|
|
||||||
|
|
||||||
|
def get_script_env(compose_path):
|
||||||
|
env = os.environ.copy()
|
||||||
|
env["COMPOSE_PATH"] = compose_path
|
||||||
|
try:
|
||||||
|
compose = productmd.compose.Compose(compose_path)
|
||||||
|
env.update({
|
||||||
|
"COMPOSE_ID": compose.info.compose.id,
|
||||||
|
"COMPOSE_DATE": compose.info.compose.date,
|
||||||
|
"COMPOSE_TYPE": compose.info.compose.type,
|
||||||
|
"COMPOSE_RESPIN": str(compose.info.compose.respin),
|
||||||
|
"COMPOSE_LABEL": compose.info.compose.label or "",
|
||||||
|
"RELEASE_ID": compose.info.release_id,
|
||||||
|
"RELEASE_NAME": compose.info.release.name,
|
||||||
|
"RELEASE_SHORT": compose.info.release.short,
|
||||||
|
"RELEASE_VERSION": compose.info.release.version,
|
||||||
|
"RELEASE_TYPE": compose.info.release.type,
|
||||||
|
"RELEASE_IS_LAYERED": "YES" if compose.info.release.is_layered else "",
|
||||||
|
})
|
||||||
|
if compose.info.release.is_layered:
|
||||||
|
env.update({
|
||||||
|
"BASE_PRODUCT_NAME": compose.info.base_product.name,
|
||||||
|
"BASE_PRODUCT_SHORT": compose.info.base_product.short,
|
||||||
|
"BASE_PRODUCT_VERSION": compose.info.base_product.version,
|
||||||
|
"BASE_PRODUCT_TYPE": compose.info.base_product.type,
|
||||||
|
})
|
||||||
|
except Exception as exc:
|
||||||
|
pass
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
def run_scripts(prefix, compose_dir, scripts):
|
||||||
|
env = get_script_env(compose_dir)
|
||||||
|
for idx, script in enumerate(scripts.strip().splitlines()):
|
||||||
|
command = script.strip()
|
||||||
|
logfile = os.path.join(compose_dir, "logs", "%s%s.log" % (prefix, idx))
|
||||||
|
log.debug("Running command: %r", command)
|
||||||
|
log.debug("See output in %s", logfile)
|
||||||
|
shortcuts.run(command, env=env, logfile=logfile)
|
||||||
|
|
||||||
|
|
||||||
def run(work_dir, main_config_file, args):
|
def run(work_dir, main_config_file, args):
|
||||||
config_dir = os.path.join(work_dir, "config")
|
config_dir = os.path.join(work_dir, "config")
|
||||||
shutil.copytree(os.path.dirname(main_config_file), config_dir)
|
shutil.copytree(os.path.dirname(main_config_file), config_dir)
|
||||||
|
|
||||||
# Read main config
|
# Read main config
|
||||||
parser = configparser.RawConfigParser(defaults={"kerberos": "false"})
|
parser = configparser.RawConfigParser(
|
||||||
|
defaults={
|
||||||
|
"kerberos": "false",
|
||||||
|
"pre_compose_script": "",
|
||||||
|
"post_compose_script": "",
|
||||||
|
}
|
||||||
|
)
|
||||||
parser.read(main_config_file)
|
parser.read(main_config_file)
|
||||||
|
|
||||||
# Create kerberos ticket
|
# Create kerberos ticket
|
||||||
@ -502,6 +548,8 @@ def run(work_dir, main_config_file, args):
|
|||||||
kobo.log.add_file_logger(log, os.path.join(target_dir, "logs", "orchestrator.log"))
|
kobo.log.add_file_logger(log, os.path.join(target_dir, "logs", "orchestrator.log"))
|
||||||
log.info("Composing %s", target_dir)
|
log.info("Composing %s", target_dir)
|
||||||
|
|
||||||
|
run_scripts("pre_compose_", target_dir, parser.get("general", "pre_compose_script"))
|
||||||
|
|
||||||
old_compose = find_old_compose(
|
old_compose = find_old_compose(
|
||||||
os.path.dirname(target_dir),
|
os.path.dirname(target_dir),
|
||||||
compose_info["release_short"],
|
compose_info["release_short"],
|
||||||
@ -536,7 +584,15 @@ def run(work_dir, main_config_file, args):
|
|||||||
if hasattr(args, "part"):
|
if hasattr(args, "part"):
|
||||||
setup_for_restart(global_config, parts, args.part)
|
setup_for_restart(global_config, parts, args.part)
|
||||||
|
|
||||||
return run_all(global_config, parts)
|
retcode = run_all(global_config, parts)
|
||||||
|
|
||||||
|
if retcode:
|
||||||
|
# Only run the script if we are not doomed.
|
||||||
|
run_scripts(
|
||||||
|
"post_compose_", target_dir, parser.get("general", "post_compose_script")
|
||||||
|
)
|
||||||
|
|
||||||
|
return retcode
|
||||||
|
|
||||||
|
|
||||||
def parse_args(argv):
|
def parse_args(argv):
|
||||||
|
@ -7,6 +7,9 @@ compose_type = nightly
|
|||||||
target = ../_composes/
|
target = ../_composes/
|
||||||
extra_args = --quiet
|
extra_args = --quiet
|
||||||
|
|
||||||
|
post_compose_script =
|
||||||
|
compose-latest-symlink $COMPOSE_PATH
|
||||||
|
|
||||||
[server]
|
[server]
|
||||||
config = server.conf
|
config = server.conf
|
||||||
|
|
||||||
|
@ -833,3 +833,60 @@ class TestRunKinit(BaseTestCase):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
register.call_args_list, [mock.call(os.remove, os.environ["KRB5CCNAME"])]
|
register.call_args_list, [mock.call(os.remove, os.environ["KRB5CCNAME"])]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch.dict("os.environ", {}, clear=True)
|
||||||
|
class TestGetScriptEnv(BaseTestCase):
|
||||||
|
def test_without_metadata(self):
|
||||||
|
env = o.get_script_env("/foobar")
|
||||||
|
self.assertEqual(env, {"COMPOSE_PATH": "/foobar"})
|
||||||
|
|
||||||
|
def test_with_metadata(self):
|
||||||
|
compose_dir = os.path.join(FIXTURE_DIR, "DP-1.0-20161013.t.4")
|
||||||
|
env = o.get_script_env(compose_dir)
|
||||||
|
self.maxDiff = None
|
||||||
|
self.assertEqual(
|
||||||
|
env,
|
||||||
|
{
|
||||||
|
"COMPOSE_PATH": compose_dir,
|
||||||
|
"COMPOSE_ID": "DP-1.0-20161013.t.4",
|
||||||
|
"COMPOSE_DATE": "20161013",
|
||||||
|
"COMPOSE_TYPE": "test",
|
||||||
|
"COMPOSE_RESPIN": "4",
|
||||||
|
"COMPOSE_LABEL": "",
|
||||||
|
"RELEASE_ID": "DP-1.0",
|
||||||
|
"RELEASE_NAME": "Dummy Product",
|
||||||
|
"RELEASE_SHORT": "DP",
|
||||||
|
"RELEASE_VERSION": "1.0",
|
||||||
|
"RELEASE_TYPE": "ga",
|
||||||
|
"RELEASE_IS_LAYERED": "",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestRunScripts(BaseTestCase):
|
||||||
|
@mock.patch("pungi_utils.orchestrator.get_script_env")
|
||||||
|
@mock.patch("kobo.shortcuts.run")
|
||||||
|
def test_run_scripts(self, run, get_env):
|
||||||
|
commands = """
|
||||||
|
date
|
||||||
|
env
|
||||||
|
"""
|
||||||
|
|
||||||
|
o.run_scripts("pref_", "/tmp/compose", commands)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
run.call_args_list,
|
||||||
|
[
|
||||||
|
mock.call(
|
||||||
|
"date",
|
||||||
|
logfile="/tmp/compose/logs/pref_0.log",
|
||||||
|
env=get_env.return_value,
|
||||||
|
),
|
||||||
|
mock.call(
|
||||||
|
"env",
|
||||||
|
logfile="/tmp/compose/logs/pref_1.log",
|
||||||
|
env=get_env.return_value,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user