2015-11-23 15:09:01 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation; version 2 of the License.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU Library General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
2016-09-21 12:49:13 +00:00
|
|
|
# along with this program; if not, see <https://gnu.org/licenses/>.
|
2015-11-23 15:09:01 +00:00
|
|
|
|
2017-06-28 08:26:29 +00:00
|
|
|
from datetime import datetime
|
2015-11-23 15:09:01 +00:00
|
|
|
import json
|
2017-06-28 08:26:29 +00:00
|
|
|
import os
|
2015-11-23 15:09:01 +00:00
|
|
|
import threading
|
|
|
|
|
2017-03-24 21:20:55 +00:00
|
|
|
import pungi.util
|
2015-12-09 22:07:43 +00:00
|
|
|
|
2015-11-23 15:09:01 +00:00
|
|
|
from kobo import shortcuts
|
|
|
|
|
|
|
|
|
|
|
|
class PungiNotifier(object):
|
|
|
|
"""Wrapper around an external script for sending messages.
|
|
|
|
|
|
|
|
If no script is configured, the messages are just silently ignored. If the
|
|
|
|
script fails, a warning will be logged, but the compose process will not be
|
|
|
|
interrupted.
|
|
|
|
"""
|
2020-02-03 03:50:06 +00:00
|
|
|
|
2017-06-28 08:26:29 +00:00
|
|
|
def __init__(self, cmds):
|
|
|
|
self.cmds = cmds
|
2015-11-23 15:09:01 +00:00
|
|
|
self.lock = threading.Lock()
|
2016-10-20 07:23:10 +00:00
|
|
|
self.compose = None
|
2015-11-23 15:09:01 +00:00
|
|
|
|
|
|
|
def _update_args(self, data):
|
|
|
|
"""Add compose related information to the data."""
|
2016-10-20 07:23:10 +00:00
|
|
|
if not self.compose:
|
|
|
|
return
|
2020-02-03 03:50:06 +00:00
|
|
|
data.setdefault("compose_id", self.compose.compose_id)
|
2015-12-09 22:07:43 +00:00
|
|
|
|
|
|
|
# Publish where in the world this compose will end up living
|
2017-03-24 21:20:55 +00:00
|
|
|
location = pungi.util.translate_path(
|
2020-02-03 03:50:06 +00:00
|
|
|
self.compose, self.compose.paths.compose.topdir()
|
|
|
|
)
|
|
|
|
data.setdefault("location", location)
|
2015-11-23 15:09:01 +00:00
|
|
|
|
2017-10-31 09:47:01 +00:00
|
|
|
# Add information about the compose itself.
|
2020-02-03 03:50:06 +00:00
|
|
|
data.setdefault("compose_date", self.compose.compose_date)
|
|
|
|
data.setdefault("compose_type", self.compose.compose_type)
|
|
|
|
data.setdefault("compose_respin", self.compose.compose_respin)
|
|
|
|
data.setdefault("compose_label", self.compose.compose_label)
|
2020-06-05 06:48:13 +00:00
|
|
|
data.setdefault("compose_path", self.compose.topdir)
|
2020-02-03 03:50:06 +00:00
|
|
|
data.setdefault("release_short", self.compose.conf["release_short"])
|
|
|
|
data.setdefault("release_name", self.compose.conf["release_name"])
|
|
|
|
data.setdefault("release_version", self.compose.conf["release_version"])
|
|
|
|
data.setdefault("release_type", self.compose.conf["release_type"].lower())
|
|
|
|
data.setdefault("release_is_layered", False)
|
|
|
|
|
|
|
|
if self.compose.conf.get("base_product_name", ""):
|
|
|
|
data["release_is_layered"] = True
|
|
|
|
data["base_product_name"] = self.compose.conf["base_product_name"]
|
|
|
|
data["base_product_version"] = self.compose.conf["base_product_version"]
|
|
|
|
data["base_product_short"] = self.compose.conf["base_product_short"]
|
|
|
|
data["base_product_type"] = self.compose.conf["base_product_type"].lower()
|
2017-10-31 09:47:01 +00:00
|
|
|
|
2016-10-20 07:23:10 +00:00
|
|
|
def send(self, msg, workdir=None, **kwargs):
|
2015-11-23 15:09:01 +00:00
|
|
|
"""Send a message.
|
|
|
|
|
|
|
|
The actual meaning of ``msg`` depends on what the notification script
|
|
|
|
will be doing. The keyword arguments will be JSON-encoded and passed on
|
|
|
|
to standard input of the notification process.
|
|
|
|
|
|
|
|
Unless you specify it manually, a ``compose_id`` key with appropriate
|
|
|
|
value will be automatically added.
|
|
|
|
"""
|
2017-06-28 08:26:29 +00:00
|
|
|
if not self.cmds:
|
2015-11-23 15:09:01 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
self._update_args(kwargs)
|
|
|
|
|
2016-10-20 07:23:10 +00:00
|
|
|
if self.compose:
|
|
|
|
workdir = self.compose.paths.compose.topdir()
|
|
|
|
|
2015-11-23 15:09:01 +00:00
|
|
|
with self.lock:
|
2017-06-28 08:26:29 +00:00
|
|
|
for cmd in self.cmds:
|
|
|
|
self._run_script(cmd, msg, workdir, kwargs)
|
|
|
|
|
|
|
|
def _run_script(self, cmd, msg, workdir, kwargs):
|
|
|
|
"""Run a single notification script with proper logging."""
|
|
|
|
logfile = None
|
|
|
|
if self.compose:
|
2020-02-03 03:50:06 +00:00
|
|
|
self.compose.log_debug("Notification: %r %r, %r" % (cmd, msg, kwargs))
|
2017-06-28 08:26:29 +00:00
|
|
|
logfile = os.path.join(
|
|
|
|
self.compose.paths.log.topdir(),
|
2020-02-03 03:50:06 +00:00
|
|
|
"notifications",
|
|
|
|
"notification-%s.log" % datetime.utcnow().strftime("%Y-%m-%d_%H-%M-%S"),
|
2017-06-28 08:26:29 +00:00
|
|
|
)
|
|
|
|
pungi.util.makedirs(os.path.dirname(logfile))
|
|
|
|
|
2020-02-03 03:50:06 +00:00
|
|
|
ret, _ = shortcuts.run(
|
|
|
|
(cmd, msg),
|
|
|
|
stdin_data=json.dumps(kwargs),
|
|
|
|
can_fail=True,
|
|
|
|
workdir=workdir,
|
|
|
|
return_stdout=False,
|
|
|
|
show_cmd=True,
|
|
|
|
universal_newlines=True,
|
|
|
|
logfile=logfile,
|
|
|
|
)
|
2017-06-28 08:26:29 +00:00
|
|
|
if ret != 0:
|
2016-10-20 07:23:10 +00:00
|
|
|
if self.compose:
|
2020-02-03 03:50:06 +00:00
|
|
|
self.compose.log_warning("Failed to invoke notification script.")
|