i9
changed/i9/yum-plugin-universal-hooks-0.1-13.el9.inferit
commit
46c201516b
@ -0,0 +1,5 @@
|
||||
[main]
|
||||
enabled=1
|
||||
|
||||
# base_dir can be set to a different location than /etc/[yum|dnf]/universal-hooks
|
||||
# base_dir=/my/slot-script/directory
|
@ -0,0 +1,186 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Copyright (c) 2020, cPanel, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import abc
|
||||
import glob
|
||||
import logging
|
||||
import os
|
||||
from os import path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from dnf import Plugin
|
||||
|
||||
# this logger is configured by the dnf CLI, but error() is not shown by default (but is with -v)
|
||||
# LOG = logging.getLogger("dnf")
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
LOG.setLevel(logging.ERROR)
|
||||
LOG.addHandler(logging.StreamHandler(sys.stderr))
|
||||
|
||||
|
||||
class UniversalHooksPlugin(Plugin):
|
||||
name = 'universal-hooks'
|
||||
|
||||
def __init__(self, base, cli):
|
||||
super().__init__(base, cli)
|
||||
self.hook_root = '/etc/dnf/universal-hooks'
|
||||
|
||||
def pre_config(self):
|
||||
_run_dir(path.join(self.hook_root, self.pre_config.__name__), LOG)
|
||||
|
||||
def config(self):
|
||||
_run_dir(path.join(self.hook_root, self.config.__name__), LOG)
|
||||
|
||||
def resolved(self):
|
||||
_run_dir(path.join(self.hook_root, self.resolved.__name__), LOG)
|
||||
|
||||
def sack(self):
|
||||
_run_dir(path.join(self.hook_root, self.sack.__name__), LOG)
|
||||
|
||||
def pre_transaction(self):
|
||||
name = self.pre_transaction.__name__
|
||||
_run_pkg_dirs(self.hook_root, LOG, name, DnfTransactionInfo(self.base.transaction))
|
||||
_run_dir(path.join(self.hook_root, name), LOG)
|
||||
|
||||
def transaction(self):
|
||||
name = self.transaction.__name__
|
||||
_run_pkg_dirs(self.hook_root, LOG, name, DnfTransactionInfo(self.base.transaction))
|
||||
_run_dir(path.join(self.hook_root, name), LOG)
|
||||
|
||||
|
||||
class FileSystem(metaclass=abc.ABCMeta):
|
||||
@abc.abstractmethod
|
||||
def glob(self, pathname):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def isdir(self, pathname):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def access(self, path, mode):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def NamedTemporaryFile(self, mode, encoding):
|
||||
pass
|
||||
|
||||
|
||||
class RealFileSystem(FileSystem):
|
||||
def glob(self, pathname):
|
||||
return glob.glob(pathname)
|
||||
|
||||
def isdir(self, pathname):
|
||||
return path.isdir(pathname)
|
||||
|
||||
def access(self, path, mode):
|
||||
return os.access(path, mode)
|
||||
|
||||
def NamedTemporaryFile(self, mode, encoding):
|
||||
return tempfile.NamedTemporaryFile(mode=mode, encoding=encoding)
|
||||
|
||||
|
||||
fs = RealFileSystem()
|
||||
|
||||
|
||||
def _run_dir(hook_dir, log, args=''):
|
||||
if not fs.isdir(hook_dir):
|
||||
return None
|
||||
|
||||
for script in sorted(fs.glob(hook_dir + "/*")):
|
||||
if fs.isdir(script):
|
||||
continue
|
||||
|
||||
if fs.access(script, os.X_OK):
|
||||
cmdline = f'{script} {args}'
|
||||
completed = subprocess.run(cmdline, shell=True) # todo change args to a list, shell=False
|
||||
if 0 != completed.returncode:
|
||||
log.error("!!! %s did not exit cleanly: %d", cmdline, completed.returncode)
|
||||
else:
|
||||
log.error("!!! %s is not executable", script)
|
||||
|
||||
|
||||
class TransactionInfo(metaclass=abc.ABCMeta):
|
||||
@abc.abstractmethod
|
||||
def getMembers(self):
|
||||
pass
|
||||
|
||||
|
||||
class DnfTransactionInfo(TransactionInfo):
|
||||
def __init__(self, transaction) -> None:
|
||||
self.transaction = transaction
|
||||
|
||||
def getMembers(self):
|
||||
return self.transaction
|
||||
|
||||
|
||||
def _run_pkg_dirs(base_dir, log, slot, tinfo):
|
||||
"""
|
||||
|
||||
:param str base_dir:
|
||||
:param logging.Logger log:
|
||||
:param str slot:
|
||||
:param TransactionInfo tinfo:
|
||||
"""
|
||||
|
||||
wildcard_path = path.join(base_dir, 'multi_pkgs', slot)
|
||||
dir_matchers = _make_dir_matchers(wildcard_path)
|
||||
wildcard_to_run = {}
|
||||
|
||||
with fs.NamedTemporaryFile(mode='w', encoding='utf-8') as temp_pkg_file:
|
||||
members_seen = {}
|
||||
members = tinfo.getMembers()
|
||||
for member in sorted(set(members), key=lambda m: m.name):
|
||||
pkg = member.name
|
||||
if pkg in members_seen:
|
||||
continue
|
||||
|
||||
members_seen[pkg] = 1
|
||||
|
||||
temp_pkg_file.write(pkg + "\n")
|
||||
|
||||
_run_dir(path.join(base_dir, 'pkgs', pkg, slot), log)
|
||||
|
||||
for wildcard_dir, matcher in dir_matchers.items():
|
||||
if matcher.search(pkg):
|
||||
wildcard_to_run[wildcard_dir] = 1
|
||||
|
||||
# the file may be used by a subprocess, so make sure it is flushed to kernel
|
||||
temp_pkg_file.flush()
|
||||
|
||||
for wildcard_dir in wildcard_to_run:
|
||||
_run_dir(path.join(wildcard_path, wildcard_dir), log, "--pkg_list=" + temp_pkg_file.name)
|
||||
|
||||
|
||||
def _make_dir_matchers(wc_slot_dir):
|
||||
dir_matchers = {}
|
||||
for pth in fs.glob(wc_slot_dir + "/*"):
|
||||
if fs.isdir(pth):
|
||||
pth = path.basename(path.normpath(pth))
|
||||
dir_matchers[pth] = _regex_from_dir(pth)
|
||||
return dir_matchers
|
||||
|
||||
|
||||
def _regex_from_dir(path):
|
||||
expr = path.replace("__WILDCARD__", ".*")
|
||||
return re.compile("^" + expr + "$")
|
@ -0,0 +1,112 @@
|
||||
Name: yum-plugin-universal-hooks
|
||||
Version: 0.1
|
||||
# Doing release_prefix this way for Release allows for OBS-proof versioning, See EA-4598 for more details
|
||||
%define release_prefix 13
|
||||
Release: %{release_prefix}%{?dist}.inferit
|
||||
Summary: Yum plugin to run arbitrary commands at any slot. For slots involving package transactions it can be limited to a specific name or glob.
|
||||
|
||||
Group: Development/Tools
|
||||
License: BSD 2-Clause
|
||||
Requires: yum-utils
|
||||
Source0: universal-hooks.conf
|
||||
Source1: universal-hooks.py
|
||||
|
||||
%if 0%{?rhel} == 8 || 0%{?msvsphere} == 8
|
||||
BuildRequires: python36 dnf python3-dnf python3-libdnf
|
||||
Requires: python36 dnf python3-dnf python3-libdnf
|
||||
Provides: dnf-plugin-universal-hooks
|
||||
%endif
|
||||
|
||||
%if 0%{?rhel} == 9 || 0%{?msvsphere} == 9
|
||||
BuildRequires: python3 dnf python3-dnf python3-libdnf
|
||||
Requires: python3 dnf python3-dnf python3-libdnf
|
||||
Provides: dnf-plugin-universal-hooks
|
||||
%endif
|
||||
|
||||
%if 0%{?rhel} == 9 || 0%{?msvsphere} == 9
|
||||
%define dnf_pluginslib /usr/lib/python3.9/site-packages/dnf-plugins/
|
||||
%else
|
||||
%define dnf_pluginslib /usr/lib/python3.6/site-packages/dnf-plugins/
|
||||
%endif
|
||||
|
||||
%description
|
||||
This plugin allows us to drop scripts into certain paths in order to run arbitrary actions during any slot dnf or yum supports. It can be for all packages or, if the slot involves a transaction with packages involved, for specific packages or packages that match a certain wildcard patterns.
|
||||
|
||||
|
||||
%install
|
||||
rm -rf %{buildroot}
|
||||
|
||||
mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/dnf/plugins
|
||||
mkdir -p $RPM_BUILD_ROOT%{dnf_pluginslib}/__pycache__/
|
||||
install -m 644 %{S:0} $RPM_BUILD_ROOT%{_sysconfdir}/dnf/plugins/universal_hooks.conf
|
||||
install -m 755 %{S:1} $RPM_BUILD_ROOT%{dnf_pluginslib}/universal_hooks.py
|
||||
mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/dnf/universal-hooks
|
||||
|
||||
|
||||
%clean
|
||||
rm -rf %{buildroot}
|
||||
|
||||
|
||||
%files
|
||||
%defattr(-,root,root,-)
|
||||
|
||||
%{dnf_pluginslib}/universal_hooks.py
|
||||
|
||||
%if 0%{?rhel} == 9 || 0%{?msvsphere} == 9
|
||||
%{dnf_pluginslib}__pycache__/universal_hooks.cpython-39.opt-1.pyc
|
||||
%{dnf_pluginslib}__pycache__/universal_hooks.cpython-39.pyc
|
||||
%else
|
||||
%{dnf_pluginslib}__pycache__/universal_hooks.cpython-36.opt-1.pyc
|
||||
%{dnf_pluginslib}__pycache__/universal_hooks.cpython-36.pyc
|
||||
%endif
|
||||
|
||||
%config(noreplace) %{_sysconfdir}/dnf/plugins/universal_hooks.conf
|
||||
%{_sysconfdir}/dnf/universal-hooks
|
||||
|
||||
|
||||
%changelog
|
||||
* Thu Apr 18 2024 Sergey Cherevko <s.cherevko@msvsphere-os.ru> - 0.1-13.inferit
|
||||
- Rebuilt for MSVSphere 9.4
|
||||
|
||||
* Thu Sep 29 2022 Julian Brown <julian.brown@cpanel.net> - 0.1-13
|
||||
- ZC-10009: Add changes so that it builds on AlmaLinux 9
|
||||
|
||||
* Mon Jul 06 2020 Dan Muey <dan@cpanel.net> - 0.1-12
|
||||
- ZC-7100: install dnf version on C8 and above
|
||||
|
||||
* Tue May 26 2020 Julian Brown <julian.brown@cpanel.net> - 0.1-11
|
||||
- ZC-6880: Build on C8
|
||||
|
||||
* Mon Sep 09 2019 Dan Muey <dan@cpanel.net> - 0.1-10
|
||||
- ZC-5357: skip duplicate members to avoid running a hook more than once for no reason
|
||||
|
||||
* Fri Sep 16 2016 Darren Mobley <darren@cpanel.net> - 0.1-9
|
||||
- HB-1952: Added support for sending an argument of --pkglist=/path/to/file
|
||||
that has a line by line list of each rpm package being handled by the
|
||||
current operation to the wildcard scripts
|
||||
|
||||
* Mon Jun 20 2016 Dan Muey <dan@cpanel.net> - 0.1-8
|
||||
- EA-4383: Update Release value to OBS-proof versioning
|
||||
|
||||
* Wed Jun 03 2015 Darren Mobley <darren@cpanel.net> - 0.1-7
|
||||
- Added sort function to glob to ensure scripts are run in expected order
|
||||
|
||||
* Thu May 07 2015 Dan Muey <dan@cpanel.net> - 0.1-6
|
||||
- Add Vendor field
|
||||
- Add README.md version of internal wiki doc
|
||||
- Overlooked name updates
|
||||
|
||||
* Wed May 06 2015 Dan Muey <dan@cpanel.net> - 0.1-5
|
||||
- Rename to a more descriptive, non-cpanel specific name (since it can be used on any server)
|
||||
|
||||
* Wed May 06 2015 Dan Muey <dan@cpanel.net> - 0.1-4
|
||||
- Update license from cpanel to BSD 2-Clause
|
||||
|
||||
* Tue Mar 10 2015 Dan Muey <dan@cpanel.net> - 0.1-3
|
||||
- use yum_pluginslib instead of _libdir for the plugins path
|
||||
|
||||
* Fri Mar 06 2015 Dan Muey <dan@cpanel.net> - 0.1-2
|
||||
- path fixes
|
||||
|
||||
* Thu Mar 05 2015 Dan Muey <dan@cpanel.net> - 0.1-1
|
||||
- implement spec file
|
Loading…
Reference in new issue