diff --git a/LICENSE b/LICENSE index 81899d0fee17d..93a7b9e61837b 100644 --- a/LICENSE +++ b/LICENSE @@ -221,6 +221,7 @@ at licenses/LICENSE-[project].txt. (ALv2 License) jqclock v2.3.0 (https://github.com/JohnRDOrazio/jQuery-Clock-Plugin) (ALv2 License) bootstrap3-typeahead v4.0.2 (https://github.com/bassjobsen/Bootstrap-3-Typeahead) (ALv2 License) connexion v2.7.0 (https://github.com/zalando/connexion) + (ALv2 License) python-daemon v2.3.2(https://pagure.io/python-daemon/) ======================================================================== MIT licenses diff --git a/airflow/_vendor/README.md b/airflow/_vendor/README.md index e708f1e507160..da914825aacfb 100644 --- a/airflow/_vendor/README.md +++ b/airflow/_vendor/README.md @@ -10,7 +10,7 @@ the `_vendor` package. All Vendored libraries must follow these rules: 1. Vendored libraries must be pure Python--no compiling (so that we do not have to release multi-platform airflow packages on PyPI). -2. Source code for the libary is included in this directory. +2. Source code for the library is included in this directory. 3. License must be included in this repo and in the [LICENSE](../../LICENSE) file and in the [licenses](../../licenses) folder. 4. Requirements of the library become requirements of airflow core. diff --git a/airflow/_vendor/daemon/__init__.py b/airflow/_vendor/daemon/__init__.py new file mode 100644 index 0000000000000..991702edc5c9c --- /dev/null +++ b/airflow/_vendor/daemon/__init__.py @@ -0,0 +1,52 @@ +# daemon/__init__.py +# Part of ‘python-daemon’, an implementation of PEP 3143. +# +# This is free software, and you are welcome to redistribute it under +# certain conditions; see the end of this file for copyright +# information, grant of license, and disclaimer of warranty. + +""" Library to implement a well-behaved Unix daemon process. + + This library implements the well-behaved daemon specification of + :pep:`3143`, “Standard daemon process library”. + + A well-behaved Unix daemon process is tricky to get right, but the + required steps are much the same for every daemon program. A + `DaemonContext` instance holds the behaviour and configured + process environment for the program; use the instance as a context + manager to enter a daemon state. + + Simple example of usage:: + + import daemon + + from spam import do_main_program + + with daemon.DaemonContext(): + do_main_program() + + Customisation of the steps to become a daemon is available by + setting options on the `DaemonContext` instance; see the + documentation for that class for each option. + """ + +from .daemon import DaemonContext + + +__all__ = ['DaemonContext'] + + +# Copyright © 2009–2022 Ben Finney +# Copyright © 2006 Robert Niederreiter +# +# This is free software: you may copy, modify, and/or distribute this work +# under the terms of the Apache License, version 2.0 as published by the +# Apache Software Foundation. +# No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details. + + +# Local variables: +# coding: utf-8 +# mode: python +# End: +# vim: fileencoding=utf-8 filetype=python : diff --git a/airflow/_vendor/daemon/_metadata.py b/airflow/_vendor/daemon/_metadata.py new file mode 100644 index 0000000000000..16f8a01b89715 --- /dev/null +++ b/airflow/_vendor/daemon/_metadata.py @@ -0,0 +1,141 @@ +# daemon/_metadata.py +# Part of ‘python-daemon’, an implementation of PEP 3143. +# +# This is free software, and you are welcome to redistribute it under +# certain conditions; see the end of this file for copyright +# information, grant of license, and disclaimer of warranty. + +""" Package metadata for the ‘python-daemon’ distribution. """ + +import datetime +import json + +import pkg_resources + + +distribution_name = "python-daemon" +version_info_filename = "version_info.json" + + +def get_distribution(name): + """ Get the `Distribution` instance for distribution `name`. + + :param name: The distribution name for the query. + :return: The `pkg_resources.Distribution` instance, or + ``None`` if the distribution instance is not found. + """ + distribution = None + try: + distribution = pkg_resources.get_distribution(name) + except pkg_resources.DistributionNotFound: + pass + + return distribution + + +def get_distribution_version_info( + distribution, filename=version_info_filename): + """ Get the version info from the installed distribution. + + :param distribution: The `pkg_resources.Distribution` instance + representing the Python package to interrogate. + :param filename: Base filename of the version info resource. + :return: The version info as a mapping of fields. + + The version info is stored as a metadata file in the + `distribution`. + + If the `distribution` is ``None``, or the version info + metadata file is not found, the return value mapping fields + have placeholder values. + """ + version_info = { + 'release_date': "UNKNOWN", + 'version': "UNKNOWN", + 'maintainer': "UNKNOWN", + } + + if distribution is not None: + if distribution.has_metadata(filename): + content = distribution.get_metadata(filename) + version_info = json.loads(content) + + return version_info + + +distribution = get_distribution(distribution_name) +version_info = get_distribution_version_info(distribution) + +version_installed = version_info['version'] + + +author_name = "Ben Finney" +author_email = "ben+python@benfinney.id.au" +author = "{name} <{email}>".format(name=author_name, email=author_email) + + +class YearRange: + """ A range of years spanning a period. """ + + def __init__(self, begin, end=None): + self.begin = begin + self.end = end + + def __str__(self): + text = "{range.begin:04d}".format(range=self) + if self.end is not None: + if self.end > self.begin: + text = "{range.begin:04d}–{range.end:04d}".format(range=self) + return text + + +def make_year_range(begin_year, end_date=None): + """ Construct the year range given a start and possible end date. + + :param begin_year: The beginning year (text, 4 digits) for the + range. + :param end_date: The end date (text, ISO-8601 format) for the + range, or a non-date token string. + :return: The range of years as a `YearRange` instance. + + If the `end_date` is not a valid ISO-8601 date string, the + range has ``None`` for the end year. + """ + begin_year = int(begin_year) + + try: + end_date = datetime.datetime.strptime(end_date, "%Y-%m-%d") + except (TypeError, ValueError): + # Specified end_date value is not a valid date. + end_year = None + else: + end_year = end_date.year + + year_range = YearRange(begin=begin_year, end=end_year) + + return year_range + + +copyright_year_begin = "2001" +build_date = version_info['release_date'] +copyright_year_range = make_year_range(copyright_year_begin, build_date) + +copyright = "Copyright © {year_range} {author} and others".format( + year_range=copyright_year_range, author=author) +license = "Apache-2" +url = "https://pagure.io/python-daemon/" + + +# Copyright © 2008–2022 Ben Finney +# +# This is free software: you may copy, modify, and/or distribute this work +# under the terms of the Apache License, version 2.0 as published by the +# Apache Software Foundation. +# No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details. + + +# Local variables: +# coding: utf-8 +# mode: python +# End: +# vim: fileencoding=utf-8 filetype=python : diff --git a/airflow/_vendor/daemon/daemon.py b/airflow/_vendor/daemon/daemon.py new file mode 100644 index 0000000000000..ea6c671c24514 --- /dev/null +++ b/airflow/_vendor/daemon/daemon.py @@ -0,0 +1,1052 @@ +# daemon/daemon.py +# Part of ‘python-daemon’, an implementation of PEP 3143. +# +# This is free software, and you are welcome to redistribute it under +# certain conditions; see the end of this file for copyright +# information, grant of license, and disclaimer of warranty. + +""" Daemon process behaviour. """ + +import atexit +import errno +import os +import pwd +import resource +import signal +import socket +import sys +import warnings + + +class DaemonError(Exception): + """ Base exception class for errors from this module. """ + + +class DaemonOSEnvironmentError(DaemonError, OSError): + """ Exception raised when daemon OS environment setup receives error. """ + + +class DaemonProcessDetachError(DaemonError, OSError): + """ Exception raised when process detach fails. """ + + +class DaemonContext: + """ Context for turning the current program into a daemon process. + + A `DaemonContext` instance represents the behaviour settings and + process context for the program when it becomes a daemon. The + behaviour and environment is customised by setting options on the + instance, before calling the `open` method. + + Each option can be passed as a keyword argument to the `DaemonContext` + constructor, or subsequently altered by assigning to an attribute on + the instance at any time prior to calling `open`. That is, for + options named `wibble` and `wubble`, the following invocation:: + + foo = daemon.DaemonContext(wibble=bar, wubble=baz) + foo.open() + + is equivalent to:: + + foo = daemon.DaemonContext() + foo.wibble = bar + foo.wubble = baz + foo.open() + + The following options are defined. + + `files_preserve` + :Default: ``None`` + + List of files that should *not* be closed when starting the + daemon. If ``None``, all open file descriptors will be closed. + + Elements of the list are file descriptors (as returned by a file + object's `fileno()` method) or Python `file` objects. Each + specifies a file that is not to be closed during daemon start. + + `chroot_directory` + :Default: ``None`` + + Full path to a directory to set as the effective root directory of + the process. If ``None``, specifies that the root directory is not + to be changed. + + `working_directory` + :Default: ``'/'`` + + Full path of the working directory to which the process should + change on daemon start. + + Since a filesystem cannot be unmounted if a process has its + current working directory on that filesystem, this should either + be left at default or set to a directory that is a sensible “home + directory” for the daemon while it is running. + + `umask` + :Default: ``0`` + + File access creation mask (“umask”) to set for the process on + daemon start. + + A daemon should not rely on the parent process's umask value, + which is beyond its control and may prevent creating a file with + the required access mode. So when the daemon context opens, the + umask is set to an explicit known value. + + If the conventional value of 0 is too open, consider setting a + value such as 0o022, 0o027, 0o077, or another specific value. + Otherwise, ensure the daemon creates every file with an + explicit access mode for the purpose. + + `pidfile` + :Default: ``None`` + + Context manager for a PID lock file. When the daemon context opens + and closes, it enters and exits the `pidfile` context manager. + + `detach_process` + :Default: ``None`` + + If ``True``, detach the process context when opening the daemon + context; if ``False``, do not detach. + + If unspecified (``None``) during initialisation of the instance, + this will be set to ``True`` by default, and ``False`` only if + detaching the process is determined to be redundant; for example, + in the case when the process was started by `init`, by `initd`, or + by `inetd`. + + `signal_map` + :Default: system-dependent + + Mapping from operating system signals to callback actions. + + The mapping is used when the daemon context opens, and determines + the action for each signal's signal handler: + + * A value of ``None`` will ignore the signal (by setting the + signal action to ``signal.SIG_IGN``). + + * A string value will be used as the name of an attribute on the + ``DaemonContext`` instance. The attribute's value will be used + as the action for the signal handler. + + * Any other value will be used as the action for the + signal handler. See the ``signal.signal`` documentation + for details of the signal handler interface. + + The default value depends on which signals are defined on the + running system. Each item from the list below whose signal is + actually defined in the ``signal`` module will appear in the + default map: + + * ``signal.SIGTTIN``: ``None`` + + * ``signal.SIGTTOU``: ``None`` + + * ``signal.SIGTSTP``: ``None`` + + * ``signal.SIGTERM``: ``'terminate'`` + + Depending on how the program will interact with its child + processes, it may need to specify a signal map that + includes the ``signal.SIGCHLD`` signal (received when a + child process exits). See the specific operating system's + documentation for more detail on how to determine what + circumstances dictate the need for signal handlers. + + `uid` + :Default: ``os.getuid()`` + + `gid` + :Default: ``os.getgid()`` + + The user ID (“UID”) value and group ID (“GID”) value to switch + the process to on daemon start. + + The default values, the real UID and GID of the process, will + relinquish any effective privilege elevation inherited by the + process. + + `initgroups` + :Default: ``False`` + + If true, set the daemon process's supplementary groups as + determined by the specified `uid`. + + This will require that the current process UID has + permission to change the process's owning GID. + + `prevent_core` + :Default: ``True`` + + If true, prevents the generation of core files, in order to avoid + leaking sensitive information from daemons run as `root`. + + `stdin` + :Default: ``None`` + + `stdout` + :Default: ``None`` + + `stderr` + :Default: ``None`` + + Each of `stdin`, `stdout`, and `stderr` is a file-like object + which will be used as the new file for the standard I/O stream + `sys.stdin`, `sys.stdout`, and `sys.stderr` respectively. The file + should therefore be open, with a minimum of mode 'r' in the case + of `stdin`, and mimimum of mode 'w+' in the case of `stdout` and + `stderr`. + + If the object has a `fileno()` method that returns a file + descriptor, the corresponding file will be excluded from being + closed during daemon start (that is, it will be treated as though + it were listed in `files_preserve`). + + If ``None``, the corresponding system stream is re-bound to the + file named by `os.devnull`. + """ + + def __init__( + self, + chroot_directory=None, + working_directory="/", + umask=0, + uid=None, + gid=None, + initgroups=False, + prevent_core=True, + detach_process=None, + files_preserve=None, + pidfile=None, + stdin=None, + stdout=None, + stderr=None, + signal_map=None, + ): + """ Set up a new instance. """ + self.chroot_directory = chroot_directory + self.working_directory = working_directory + self.umask = umask + self.prevent_core = prevent_core + self.files_preserve = files_preserve + self.pidfile = pidfile + self.stdin = stdin + self.stdout = stdout + self.stderr = stderr + + if uid is None: + uid = os.getuid() + self.uid = uid + if gid is None: + gid = os.getgid() + self.gid = gid + self.initgroups = initgroups + + if detach_process is None: + detach_process = is_detach_process_context_required() + self.detach_process = detach_process + + if signal_map is None: + signal_map = make_default_signal_map() + self.signal_map = signal_map + + self._is_open = False + + @property + def is_open(self): + """ ``True`` if the instance is currently open. """ + return self._is_open + + def open(self): + """ Become a daemon process. + + :return: ``None``. + + Open the daemon context, turning the current program into a daemon + process. This performs the following steps: + + * If this instance's `is_open` property is true, return + immediately. This makes it safe to call `open` multiple times on + an instance. + + * If the `prevent_core` attribute is true, set the resource limits + for the process to prevent any core dump from the process. + + * If the `chroot_directory` attribute is not ``None``, set the + effective root directory of the process to that directory (via + `os.chroot`). + + This allows running the daemon process inside a “chroot gaol” + as a means of limiting the system's exposure to rogue behaviour + by the process. Note that the specified directory needs to + already be set up for this purpose. + + * Set the process owner (UID and GID) to the `uid` and `gid` + attribute values. + + If the `initgroups` attribute is true, also set the process's + supplementary groups to all the user's groups (i.e. those + groups whose membership includes the username corresponding + to `uid`). + + * Close all open file descriptors. This excludes those listed in + the `files_preserve` attribute, and those that correspond to the + `stdin`, `stdout`, or `stderr` attributes. + + * Change current working directory to the path specified by the + `working_directory` attribute. + + * Reset the file access creation mask to the value specified by + the `umask` attribute. + + * If the `detach_process` option is true, detach the current + process into its own process group, and disassociate from any + controlling terminal. + + * Set signal handlers as specified by the `signal_map` attribute. + + * If any of the attributes `stdin`, `stdout`, `stderr` are not + ``None``, bind the system streams `sys.stdin`, `sys.stdout`, + and/or `sys.stderr` to the files represented by the + corresponding attributes. Where the attribute has a file + descriptor, the descriptor is duplicated (instead of re-binding + the name). + + * If the `pidfile` attribute is not ``None``, enter its context + manager. + + * Mark this instance as open (for the purpose of future `open` and + `close` calls). + + * Register the `close` method to be called during Python's exit + processing. + + When the function returns, the running program is a daemon + process. + """ + if self.is_open: + return + + if self.chroot_directory is not None: + change_root_directory(self.chroot_directory) + + if self.prevent_core: + prevent_core_dump() + + change_file_creation_mask(self.umask) + change_working_directory(self.working_directory) + change_process_owner(self.uid, self.gid, self.initgroups) + + if self.detach_process: + detach_process_context() + + signal_handler_map = self._make_signal_handler_map() + set_signal_handlers(signal_handler_map) + + exclude_fds = self._get_exclude_file_descriptors() + close_all_open_files(exclude=exclude_fds) + + redirect_stream(sys.stdin, self.stdin) + redirect_stream(sys.stdout, self.stdout) + redirect_stream(sys.stderr, self.stderr) + + if self.pidfile is not None: + self.pidfile.__enter__() + + self._is_open = True + + register_atexit_function(self.close) + + def __enter__(self): + """ Context manager entry point. """ + self.open() + return self + + def close(self): + """ Exit the daemon process context. + + :return: ``None``. + + Close the daemon context. This performs the following steps: + + * If this instance's `is_open` property is false, return + immediately. This makes it safe to call `close` multiple times + on an instance. + + * If the `pidfile` attribute is not ``None``, exit its context + manager. + + * Mark this instance as closed (for the purpose of future `open` + and `close` calls). + """ + if not self.is_open: + return + + if self.pidfile is not None: + # Follow the interface for telling a context manager to exit, + # . + self.pidfile.__exit__(None, None, None) + + self._is_open = False + + def __exit__(self, exc_type, exc_value, traceback): + """ Context manager exit point. """ + self.close() + + def terminate(self, signal_number, stack_frame): + """ Signal handler for end-process signals. + + :param signal_number: The OS signal number received. + :param stack_frame: The frame object at the point the + signal was received. + :return: ``None``. + + Signal handler for the ``signal.SIGTERM`` signal. Performs the + following step: + + * Raise a ``SystemExit`` exception explaining the signal. + """ + exception = SystemExit( + "Terminating on signal {signal_number!r}".format( + signal_number=signal_number)) + raise exception + + def _get_exclude_file_descriptors(self): + """ Get the set of file descriptors to exclude closing. + + :return: A set containing the file descriptors for the + files to be preserved. + + The file descriptors to be preserved are those from the + items in `files_preserve`, and also each of `stdin`, + `stdout`, and `stderr`. For each item: + + * If the item is ``None``, omit it from the return set. + + * If the item's `fileno` method returns a value, include + that value in the return set. + + * Otherwise, include the item verbatim in the return set. + """ + files_preserve = self.files_preserve + if files_preserve is None: + files_preserve = [] + files_preserve.extend( + item for item in {self.stdin, self.stdout, self.stderr} + if hasattr(item, 'fileno')) + + exclude_descriptors = set() + for item in files_preserve: + if item is None: + continue + file_descriptor = _get_file_descriptor(item) + if file_descriptor is not None: + exclude_descriptors.add(file_descriptor) + else: + exclude_descriptors.add(item) + + return exclude_descriptors + + def _make_signal_handler(self, target): + """ Make the signal handler for a specified target object. + + :param target: A specification of the target for the + handler; see below. + :return: The value for use by `signal.signal()`. + + If `target` is ``None``, return ``signal.SIG_IGN``. If `target` + is a text string, return the attribute of this instance named + by that string. Otherwise, return `target` itself. + """ + if target is None: + result = signal.SIG_IGN + elif isinstance(target, str): + name = target + result = getattr(self, name) + else: + result = target + + return result + + def _make_signal_handler_map(self): + """ Make the map from signals to handlers for this instance. + + :return: The constructed signal map for this instance. + + Construct a map from signal numbers to handlers for this + context instance, suitable for passing to + `set_signal_handlers`. + """ + signal_handler_map = dict( + (signal_number, self._make_signal_handler(target)) + for (signal_number, target) in self.signal_map.items()) + return signal_handler_map + + +def get_stream_file_descriptors( + stdin=sys.stdin, + stdout=sys.stdout, + stderr=sys.stderr, + ): + """ Get the set of file descriptors for the process streams. + + :stdin: The input stream for the process (default: + `sys.stdin`). + :stdout: The ouput stream for the process (default: + `sys.stdout`). + :stderr: The diagnostic stream for the process (default: + `sys.stderr`). + :return: A `set` of each file descriptor (integer) for the + streams. + + The standard streams are the files `sys.stdin`, `sys.stdout`, + `sys.stderr`. + + Streams might in some circumstances be non-file objects. + Include in the result only those streams that actually have a + file descriptor (as returned by the `fileno` method). + """ + file_descriptors = set( + fd for fd in set( + _get_file_descriptor(stream) + for stream in {stdin, stdout, stderr}) + if fd is not None) + return file_descriptors + + +def _get_file_descriptor(obj): + """ Get the file descriptor, if the object has one. + + :param obj: The object expected to be a file-like object. + :return: The file descriptor iff the file supports it; otherwise + ``None``. + + The object may be a non-file object. It may also be a + file-like object with no support for a file descriptor. In + either case, return ``None``. + """ + file_descriptor = None + if hasattr(obj, 'fileno'): + try: + file_descriptor = obj.fileno() + except ValueError: + # The item doesn't support a file descriptor. + pass + + return file_descriptor + + +def change_working_directory(directory): + """ Change the working directory of this process. + + :param directory: The target directory path. + :return: ``None``. + """ + try: + os.chdir(directory) + except Exception as exc: + error = DaemonOSEnvironmentError( + "Unable to change working directory ({exc})".format(exc=exc)) + raise error from exc + + +def change_root_directory(directory): + """ Change the root directory of this process. + + :param directory: The target directory path. + :return: ``None``. + + Set the current working directory, then the process root directory, + to the specified `directory`. Requires appropriate OS privileges + for this process. + """ + try: + os.chdir(directory) + os.chroot(directory) + except Exception as exc: + error = DaemonOSEnvironmentError( + "Unable to change root directory ({exc})".format(exc=exc)) + raise error from exc + + +def change_file_creation_mask(mask): + """ Change the file creation mask for this process. + + :param mask: The numeric file creation mask to set. + :return: ``None``. + """ + try: + os.umask(mask) + except Exception as exc: + error = DaemonOSEnvironmentError( + "Unable to change file creation mask ({exc})".format(exc=exc)) + raise error from exc + + +def get_username_for_uid(uid): + """ Get the username for the specified UID. """ + passwd_entry = pwd.getpwuid(uid) + username = passwd_entry.pw_name + + return username + + +def change_process_owner(uid, gid, initgroups=False): + """ Change the owning UID, GID, and groups of this process. + + :param uid: The target UID for the daemon process. + :param gid: The target GID for the daemon process. + :param initgroups: If true, initialise the supplementary + groups of the process. + :return: ``None``. + + Sets the owning GID and UID of the process (in that order, to + avoid permission errors) to the specified `gid` and `uid` + values. + + If `initgroups` is true, the supplementary groups of the + process are also initialised, with those corresponding to the + username for the target UID. + + All these operations require appropriate OS privileges. If + permission is denied, a ``DaemonOSEnvironmentError`` is + raised. + """ + try: + username = get_username_for_uid(uid) + except KeyError: + # We don't have a username to pass to ‘os.initgroups’. + initgroups = False + + try: + if initgroups: + os.initgroups(username, gid) + else: + os.setgid(gid) + os.setuid(uid) + except Exception as exc: + error = DaemonOSEnvironmentError( + "Unable to change process owner ({exc})".format(exc=exc)) + raise error from exc + + +def prevent_core_dump(): + """ Prevent this process from generating a core dump. + + :return: ``None``. + + Set the soft and hard limits for core dump size to zero. On Unix, + this entirely prevents the process from creating core dump. + """ + core_resource = resource.RLIMIT_CORE + + try: + # Ensure the resource limit exists on this platform, by requesting + # its current value. + resource.getrlimit(core_resource) + except ValueError as exc: + error = DaemonOSEnvironmentError( + "System does not support RLIMIT_CORE resource limit" + " ({exc})".format(exc=exc)) + raise error from exc + + # Set hard and soft limits to zero, i.e. no core dump at all. + core_limit = (0, 0) + resource.setrlimit(core_resource, core_limit) + + +def detach_process_context(): + """ Detach the process context from parent and session. + + :return: ``None``. + + Detach from the parent process and session group, allowing the + parent to exit while this process continues running. + + Reference: “Advanced Programming in the Unix Environment”, + section 13.3, by W. Richard Stevens, published 1993 by + Addison-Wesley. + """ + + def fork_then_exit_parent(error_message): + """ Fork a child process, then exit the parent process. + + :param error_message: Message for the exception in case of a + detach failure. + :return: ``None``. + :raise DaemonProcessDetachError: If the fork fails. + """ + try: + pid = os.fork() + if pid > 0: + os._exit(0) + except OSError as exc: + error = DaemonProcessDetachError( + "{message}: [{exc.errno:d}] {exc.strerror}".format( + message=error_message, exc=exc)) + raise error from exc + + fork_then_exit_parent(error_message="Failed first fork") + os.setsid() + fork_then_exit_parent(error_message="Failed second fork") + + +def is_process_started_by_init(): + """ Determine whether the current process is started by `init`. + + :return: ``True`` iff the parent process is `init`; otherwise + ``False``. + + The `init` process is the one with process ID of 1. + """ + result = False + + init_pid = 1 + if os.getppid() == init_pid: + result = True + + return result + + +def is_socket(fd): + """ Determine whether the file descriptor is a socket. + + :param fd: The file descriptor to interrogate. + :return: ``True`` iff the file descriptor is a socket; otherwise + ``False``. + + Query the socket type of `fd`. If there is no error, the file is a + socket. + """ + warnings.warn( + DeprecationWarning("migrate to `is_socket_file` instead")) + + result = False + + try: + file_socket = socket.fromfd(fd, socket.AF_INET, socket.SOCK_RAW) + file_socket.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE) + except socket.error as exc: + exc_errno = exc.args[0] + if exc_errno == errno.ENOTSOCK: + # Socket operation on non-socket. + pass + else: + # Some other socket error. + result = True + else: + # No error getting socket type. + result = True + + return result + + +def is_socket_file(file): + """ Determine whether the `file` is a socket. + + :param file: The file (an `io.IOBase` instance) to interrogate. + :return: ``True`` iff `file` is a socket; otherwise ``False``. + + Query the socket type of the file descriptor of `file`. If there is no + error, the file is a socket. + """ + result = False + + try: + file_fd = file.fileno() + except ValueError: + # The file doesn't have a file descriptor. + file_fd = None + + try: + file_socket = socket.fromfd(file_fd, socket.AF_INET, socket.SOCK_RAW) + file_socket.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE) + except socket.error as exc: + exc_errno = exc.args[0] + if exc_errno == errno.ENOTSOCK: + # Socket operation on non-socket. + pass + else: + # Some other socket error. + result = True + else: + # No error getting socket type. + result = True + + return result + + +def is_process_started_by_superserver(): + """ Determine whether the current process is started by the superserver. + + :return: ``True`` if this process was started by the internet + superserver; otherwise ``False``. + + The internet superserver creates a network socket, and + attaches it to the standard streams of the child process. If + that is the case for this process, return ``True``, otherwise + ``False``. + """ + result = False + + if is_socket_file(sys.__stdin__): + result = True + + return result + + +def is_detach_process_context_required(): + """ Determine whether detaching the process context is required. + + :return: ``False`` iff the process is already detached; + otherwise ``True``. + + The process environment is interrogated for the following: + + * Process was started by `init`; or + + * Process was started by `inetd`. + + If any of the above are true, the process is deemed to be already + detached. + """ + result = True + if is_process_started_by_init() or is_process_started_by_superserver(): + result = False + + return result + + +def close_file_descriptor_if_open(fd): + """ Close a file descriptor if already open. + + :param fd: The file descriptor to close. + :return: ``None``. + + Close the file descriptor `fd`, suppressing an error in the + case the file was not open. + """ + try: + os.close(fd) + except EnvironmentError as exc: + if exc.errno == errno.EBADF: + # File descriptor was not open. + pass + else: + error = DaemonOSEnvironmentError( + "Failed to close file descriptor {fd:d} ({exc})".format( + fd=fd, exc=exc)) + raise error from exc + + +MAXFD = 2048 + + +def get_maximum_file_descriptors(): + """ Get the maximum number of open file descriptors for this process. + + :return: The number (integer) to use as the maximum number of open + files for this process. + + The maximum is the process hard resource limit of maximum number of + open file descriptors. If the limit is “infinity”, a default value + of ``MAXFD`` is returned. + """ + (__, hard_limit) = resource.getrlimit(resource.RLIMIT_NOFILE) + + result = hard_limit + if hard_limit == resource.RLIM_INFINITY: + result = MAXFD + + return result + + +_total_file_descriptor_range = (0, get_maximum_file_descriptors()) +_total_file_descriptor_set = set(range(*_total_file_descriptor_range)) + + +def _get_candidate_file_descriptors(exclude): + """ Get the collection of candidate file descriptors. + + :param exclude: A collection of file descriptors that should + be excluded from the return set. + :return: The collection (a `set`) of file descriptors that are + candidates for files that may be open in this process. + + Determine the set of all `int` values that could be open file + descriptors in this process. A file descriptor is a candidate + if it is within the range (0, `maxfd`), excluding those + integers in the `exclude` collection. + + The `maxfd` value is determined from the standard library + `resource` module. + """ + candidates = _total_file_descriptor_set.difference(exclude) + return candidates + + +def _get_candidate_file_descriptor_ranges(exclude): + """ Get the collection of candidate file descriptor ranges. + + :param exclude: A collection of file descriptors that should + be excluded from the return ranges. + :return: The collection (a `list`) of ranges that contain the + file descriptors that are candidates for files that may be + open in this process. + + Determine the ranges of all the candidate file descriptors. + Each range is a pair of `int` values (`low`, `high`). + + A value is a candidate if it could be an open file descriptor + in this process, excluding those integers in the `exclude` + collection. + """ + candidates_list = sorted(_get_candidate_file_descriptors(exclude)) + ranges = [] + + def append_range_if_needed(candidate_range): + (low, high) = candidate_range + if (low < high): + # The range is not empty. + ranges.append(candidate_range) + + this_range = ( + (min(candidates_list), (min(candidates_list) + 1)) + if candidates_list else (0, 0)) + for fd in candidates_list[1:]: + high = fd + 1 + if this_range[1] == fd: + # This file descriptor extends the current range. + this_range = (this_range[0], high) + else: + # The previous range has ended at a gap. + append_range_if_needed(this_range) + # This file descriptor begins a new range. + this_range = (fd, high) + append_range_if_needed(this_range) + return ranges + + +def _close_file_descriptor_ranges(ranges): + """ Close file descriptors described by `ranges`. + + :param ranges: A sequence of tuples `(low, high)`, each + describing a range of file descriptors to close. + :return: ``None``. + + Attempt to close each open file descriptor – starting from + `low` and ending before `high` – from each range in `ranges`. + """ + for range in ranges: + os.closerange(range[0], range[1]) + + +def close_all_open_files(exclude=None): + """ Close all open file descriptors. + + :param exclude: Collection of file descriptors to skip when closing + files. + :return: ``None``. + + Closes every file descriptor (if open) of this process. If + specified, `exclude` is a set of file descriptors to *not* + close. + """ + if exclude is None: + exclude = set() + fd_ranges = _get_candidate_file_descriptor_ranges(exclude=exclude) + _close_file_descriptor_ranges(ranges=fd_ranges) + + +def redirect_stream(system_stream, target_stream): + """ Redirect a system stream to a specified file. + + :param standard_stream: A file object representing a standard I/O + stream. + :param target_stream: The target file object for the redirected + stream, or ``None`` to specify the null device. + :return: ``None``. + + `system_stream` is a standard system stream such as + ``sys.stdout``. `target_stream` is an open file object that + should replace the corresponding system stream object. + + If `target_stream` is ``None``, defaults to opening the + operating system's null device and using its file descriptor. + """ + if target_stream is None: + target_fd = os.open(os.devnull, os.O_RDWR) + else: + target_fd = target_stream.fileno() + os.dup2(target_fd, system_stream.fileno()) + + +def make_default_signal_map(): + """ Make the default signal map for this system. + + :return: A mapping from signal number to handler object. + + The signals available differ by system. The map will not contain + any signals not defined on the running system. + """ + name_map = { + 'SIGTSTP': None, + 'SIGTTIN': None, + 'SIGTTOU': None, + 'SIGTERM': 'terminate', + } + signal_map = dict( + (getattr(signal, name), target) + for (name, target) in name_map.items() + if hasattr(signal, name)) + + return signal_map + + +def set_signal_handlers(signal_handler_map): + """ Set the signal handlers as specified. + + :param signal_handler_map: A map from signal number to handler + object. + :return: ``None``. + + See the `signal` module for details on signal numbers and signal + handlers. + """ + for (signal_number, handler) in signal_handler_map.items(): + signal.signal(signal_number, handler) + + +def register_atexit_function(func): + """ Register a function for processing at program exit. + + :param func: A callable function expecting no arguments. + :return: ``None``. + + The function `func` is registered for a call with no arguments + at program exit. + """ + atexit.register(func) + + +# Copyright © 2008–2022 Ben Finney +# Copyright © 2007–2008 Robert Niederreiter, Jens Klein +# Copyright © 2004–2005 Chad J. Schroeder +# Copyright © 2003 Clark Evans +# Copyright © 2002 Noah Spurrier +# Copyright © 2001 Jürgen Hermann +# +# This is free software: you may copy, modify, and/or distribute this work +# under the terms of the Apache License, version 2.0 as published by the +# Apache Software Foundation. +# No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details. + + +# Local variables: +# coding: utf-8 +# mode: python +# End: +# vim: fileencoding=utf-8 filetype=python : diff --git a/airflow/_vendor/daemon/pidfile.py b/airflow/_vendor/daemon/pidfile.py new file mode 100644 index 0000000000000..95891f170f2dd --- /dev/null +++ b/airflow/_vendor/daemon/pidfile.py @@ -0,0 +1,64 @@ +# daemon/pidfile.py +# Part of ‘python-daemon’, an implementation of PEP 3143. +# +# This is free software, and you are welcome to redistribute it under +# certain conditions; see the end of this file for copyright +# information, grant of license, and disclaimer of warranty. + +""" Lockfile behaviour implemented via Unix PID files. """ + +from lockfile.pidlockfile import PIDLockFile + + +class TimeoutPIDLockFile(PIDLockFile, object): + """ Lockfile with default timeout, implemented as a Unix PID file. + + This uses the ``PIDLockFile`` implementation, with the + following changes: + + * The `acquire_timeout` parameter to the initialiser will be + used as the default `timeout` parameter for the `acquire` + method. + """ + + def __init__(self, path, acquire_timeout=None, *args, **kwargs): + """ Set up the parameters of a TimeoutPIDLockFile. + + :param path: Filesystem path to the PID file. + :param acquire_timeout: Value to use by default for the + `acquire` call. + :return: ``None``. + """ + self.acquire_timeout = acquire_timeout + super().__init__(path, *args, **kwargs) + + def acquire(self, timeout=None, *args, **kwargs): + """ Acquire the lock. + + :param timeout: Specifies the timeout; see below for valid + values. + :return: ``None``. + + The `timeout` defaults to the value set during + initialisation with the `acquire_timeout` parameter. It is + passed to `PIDLockFile.acquire`; see that method for + details. + """ + if timeout is None: + timeout = self.acquire_timeout + super().acquire(timeout, *args, **kwargs) + + +# Copyright © 2008–2022 Ben Finney +# +# This is free software: you may copy, modify, and/or distribute this work +# under the terms of the Apache License, version 2.0 as published by the +# Apache Software Foundation. +# No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details. + + +# Local variables: +# coding: utf-8 +# mode: python +# End: +# vim: fileencoding=utf-8 filetype=python : diff --git a/airflow/_vendor/daemon/runner.py b/airflow/_vendor/daemon/runner.py new file mode 100644 index 0000000000000..877dcffa39b4c --- /dev/null +++ b/airflow/_vendor/daemon/runner.py @@ -0,0 +1,304 @@ +# daemon/runner.py +# Part of ‘python-daemon’, an implementation of PEP 3143. +# +# This is free software, and you are welcome to redistribute it under +# certain conditions; see the end of this file for copyright +# information, grant of license, and disclaimer of warranty. + +""" Daemon runner library. """ + +import errno +import os +import signal +import sys +import warnings + +import lockfile + +from . import pidfile +from .daemon import DaemonContext + + +warnings.warn( + "The ‘runner’ module is not a supported API for this library.", + DeprecationWarning) + + +class DaemonRunnerError(Exception): + """ Abstract base class for errors from DaemonRunner. """ + + +class DaemonRunnerInvalidActionError(DaemonRunnerError, ValueError): + """ Raised when specified action for DaemonRunner is invalid. """ + + +class DaemonRunnerStartFailureError(DaemonRunnerError, RuntimeError): + """ Raised when failure starting DaemonRunner. """ + + +class DaemonRunnerStopFailureError(DaemonRunnerError, RuntimeError): + """ Raised when failure stopping DaemonRunner. """ + + +class DaemonRunner: + """ Controller for a callable running in a separate background process. + + The first command-line argument is the action to take: + + * 'start': Become a daemon and call `app.run()`. + * 'stop': Exit the daemon process specified in the PID file. + * 'restart': Stop, then start. + """ + + start_message = "started with pid {pid:d}" + + def __init__(self, app): + """ Set up the parameters of a new runner. + + :param app: The application instance; see below. + :return: ``None``. + + The `app` argument must have the following attributes: + + * `stdin_path`, `stdout_path`, `stderr_path`: Filesystem paths + to open and replace the existing `sys.stdin`, `sys.stdout`, + `sys.stderr`. + + * `pidfile_path`: Absolute filesystem path to a file that will + be used as the PID file for the daemon. If ``None``, no PID + file will be used. + + * `pidfile_timeout`: Used as the default acquisition timeout + value supplied to the runner's PID lock file. + + * `run`: Callable that will be invoked when the daemon is + started. + """ + self.parse_args() + self.app = app + self.daemon_context = DaemonContext() + self._open_streams_from_app_stream_paths(app) + + self.pidfile = None + if app.pidfile_path is not None: + self.pidfile = make_pidlockfile( + app.pidfile_path, app.pidfile_timeout) + self.daemon_context.pidfile = self.pidfile + + def _open_streams_from_app_stream_paths(self, app): + """ Open the `daemon_context` streams from the paths specified. + + :param app: The application instance. + + Open the `daemon_context` standard streams (`stdin`, + `stdout`, `stderr`) as stream objects of the appropriate + types, from each of the corresponding filesystem paths + from the `app`. + """ + self.daemon_context.stdin = open(app.stdin_path, 'rt') + self.daemon_context.stdout = open(app.stdout_path, 'w+t') + self.daemon_context.stderr = open( + app.stderr_path, 'w+t', buffering=0) + + def _usage_exit(self, argv): + """ Emit a usage message, then exit. + + :param argv: The command-line arguments used to invoke the + program, as a sequence of strings. + :return: ``None``. + """ + progname = os.path.basename(argv[0]) + usage_exit_code = 2 + action_usage = "|".join(self.action_funcs.keys()) + message = "usage: {progname} {usage}".format( + progname=progname, usage=action_usage) + emit_message(message) + sys.exit(usage_exit_code) + + def parse_args(self, argv=None): + """ Parse command-line arguments. + + :param argv: The command-line arguments used to invoke the + program, as a sequence of strings. + + :return: ``None``. + + The parser expects the first argument as the program name, the + second argument as the action to perform. + + If the parser fails to parse the arguments, emit a usage + message and exit the program. + """ + if argv is None: + argv = sys.argv + + min_args = 2 + if len(argv) < min_args: + self._usage_exit(argv) + + self.action = str(argv[1]) + if self.action not in self.action_funcs: + self._usage_exit(argv) + + def _start(self): + """ Open the daemon context and run the application. + + :return: ``None``. + :raises DaemonRunnerStartFailureError: If the PID file cannot + be locked by this process. + """ + if is_pidfile_stale(self.pidfile): + self.pidfile.break_lock() + + try: + self.daemon_context.open() + except lockfile.AlreadyLocked as exc: + error = DaemonRunnerStartFailureError( + "PID file {pidfile.path!r} already locked".format( + pidfile=self.pidfile)) + raise error from exc + + pid = os.getpid() + message = self.start_message.format(pid=pid) + emit_message(message) + + self.app.run() + + def _terminate_daemon_process(self): + """ Terminate the daemon process specified in the current PID file. + + :return: ``None``. + :raises DaemonRunnerStopFailureError: If terminating the daemon + fails with an OS error. + """ + pid = self.pidfile.read_pid() + try: + os.kill(pid, signal.SIGTERM) + except OSError as exc: + error = DaemonRunnerStopFailureError( + "Failed to terminate {pid:d}: {exc}".format( + pid=pid, exc=exc)) + raise error from exc + + def _stop(self): + """ Exit the daemon process specified in the current PID file. + + :return: ``None``. + :raises DaemonRunnerStopFailureError: If the PID file is not + already locked. + """ + if not self.pidfile.is_locked(): + error = DaemonRunnerStopFailureError( + "PID file {pidfile.path!r} not locked".format( + pidfile=self.pidfile)) + raise error + + if is_pidfile_stale(self.pidfile): + self.pidfile.break_lock() + else: + self._terminate_daemon_process() + + def _restart(self): + """ Stop, then start. """ + self._stop() + self._start() + + action_funcs = { + 'start': _start, + 'stop': _stop, + 'restart': _restart, + } + + def _get_action_func(self): + """ Get the function for the specified action. + + :return: The function object corresponding to the specified + action. + :raises DaemonRunnerInvalidActionError: if the action is + unknown. + + The action is specified by the `action` attribute, which is set + during `parse_args`. + """ + try: + func = self.action_funcs[self.action] + except KeyError: + error = DaemonRunnerInvalidActionError( + "Unknown action: {action!r}".format( + action=self.action)) + raise error + return func + + def do_action(self): + """ Perform the requested action. + + :return: ``None``. + + The action is specified by the `action` attribute, which is set + during `parse_args`. + """ + func = self._get_action_func() + func(self) + + +def emit_message(message, stream=None): + """ Emit a message to the specified stream (default `sys.stderr`). """ + if stream is None: + stream = sys.stderr + stream.write("{message}\n".format(message=message)) + stream.flush() + + +def make_pidlockfile(path, acquire_timeout): + """ Make a PIDLockFile instance with the given filesystem path. """ + if not isinstance(path, str): + error = ValueError("Not a filesystem path: {path!r}".format( + path=path)) + raise error + if not os.path.isabs(path): + error = ValueError("Not an absolute path: {path!r}".format( + path=path)) + raise error + lockfile = pidfile.TimeoutPIDLockFile(path, acquire_timeout) + + return lockfile + + +def is_pidfile_stale(pidfile): + """ Determine whether a PID file is stale. + + :return: ``True`` iff the PID file is stale; otherwise ``False``. + + The PID file is “stale” if its contents are valid but do not + match the PID of a currently-running process. + """ + result = False + + pidfile_pid = pidfile.read_pid() + if pidfile_pid is not None: + try: + os.kill(pidfile_pid, signal.SIG_DFL) + except ProcessLookupError: + # The specified PID does not exist. + result = True + + return result + + +# Copyright © 2009–2021 Ben Finney +# Copyright © 2007–2008 Robert Niederreiter, Jens Klein +# Copyright © 2003 Clark Evans +# Copyright © 2002 Noah Spurrier +# Copyright © 2001 Jürgen Hermann +# +# This is free software: you may copy, modify, and/or distribute this work +# under the terms of the Apache License, version 2.0 as published by the +# Apache Software Foundation. +# No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details. + + +# Local variables: +# coding: utf-8 +# mode: python +# End: +# vim: fileencoding=utf-8 filetype=python : diff --git a/airflow/_vendor/vendor.md b/airflow/_vendor/vendor.md index ab102ab08353f..7d980c17ef60e 100644 --- a/airflow/_vendor/vendor.md +++ b/airflow/_vendor/vendor.md @@ -1,2 +1,3 @@ | Package | Version | File | SHA256 | |-----------|---------|------------------------|------------------------------------------------------------------| +| daemon | 2.3.2 | daemon | 80e389dbefdaa1ad38cd025ab25cf7d6be64f2d9 | diff --git a/airflow/cli/commands/celery_command.py b/airflow/cli/commands/celery_command.py index 0d3e1295dcd4e..9827b24f4309d 100644 --- a/airflow/cli/commands/celery_command.py +++ b/airflow/cli/commands/celery_command.py @@ -21,14 +21,14 @@ from contextlib import contextmanager from multiprocessing import Process -import daemon import psutil import sqlalchemy.exc from celery import maybe_patch_concurrency # type: ignore[attr-defined] -from daemon.pidfile import TimeoutPIDLockFile from lockfile.pidlockfile import read_pid_from_pidfile, remove_existing_pidfile +import airflow._vendor.daemon as daemon from airflow import settings +from airflow._vendor.daemon.pidfile import TimeoutPIDLockFile from airflow.configuration import conf from airflow.executors.celery_executor import app as celery_app from airflow.utils import cli as cli_utils diff --git a/airflow/cli/commands/dag_processor_command.py b/airflow/cli/commands/dag_processor_command.py index d96fb4f06f0dc..68bc8d72ac800 100644 --- a/airflow/cli/commands/dag_processor_command.py +++ b/airflow/cli/commands/dag_processor_command.py @@ -20,10 +20,9 @@ import logging from datetime import timedelta -import daemon -from daemon.pidfile import TimeoutPIDLockFile - +import airflow._vendor.daemon as daemon from airflow import settings +from airflow._vendor.daemon.pidfile import TimeoutPIDLockFile from airflow.configuration import conf from airflow.jobs.dag_processor_job import DagProcessorJob from airflow.utils import cli as cli_utils diff --git a/airflow/cli/commands/internal_api_command.py b/airflow/cli/commands/internal_api_command.py index 19ab7ab98501c..bf898c1ced3c2 100644 --- a/airflow/cli/commands/internal_api_command.py +++ b/airflow/cli/commands/internal_api_command.py @@ -27,9 +27,7 @@ from tempfile import gettempdir from time import sleep -import daemon import psutil -from daemon.pidfile import TimeoutPIDLockFile from flask import Flask from flask_appbuilder import SQLA from flask_caching import Cache @@ -37,7 +35,9 @@ from lockfile.pidlockfile import read_pid_from_pidfile from sqlalchemy.engine.url import make_url +import airflow._vendor.daemon as daemon from airflow import settings +from airflow._vendor.daemon.pidfile import TimeoutPIDLockFile from airflow.api_internal.internal_api_call import InternalApiConfig from airflow.cli.commands.webserver_command import GunicornMonitor from airflow.configuration import conf diff --git a/airflow/cli/commands/kerberos_command.py b/airflow/cli/commands/kerberos_command.py index 4bbe3f6df919d..36edb21006b4b 100644 --- a/airflow/cli/commands/kerberos_command.py +++ b/airflow/cli/commands/kerberos_command.py @@ -17,10 +17,9 @@ """Kerberos command.""" from __future__ import annotations -import daemon -from daemon.pidfile import TimeoutPIDLockFile - +import airflow._vendor.daemon as daemon from airflow import settings +from airflow._vendor.daemon.pidfile import TimeoutPIDLockFile from airflow.security import kerberos as krb from airflow.utils import cli as cli_utils from airflow.utils.cli import setup_locations diff --git a/airflow/cli/commands/scheduler_command.py b/airflow/cli/commands/scheduler_command.py index 2c7c7c0f1a9e1..505e057f70b94 100644 --- a/airflow/cli/commands/scheduler_command.py +++ b/airflow/cli/commands/scheduler_command.py @@ -21,10 +21,9 @@ from contextlib import contextmanager from multiprocessing import Process -import daemon -from daemon.pidfile import TimeoutPIDLockFile - +import airflow._vendor.daemon as daemon from airflow import settings +from airflow._vendor.daemon.pidfile import TimeoutPIDLockFile from airflow.api_internal.internal_api_call import InternalApiConfig from airflow.configuration import conf from airflow.executors.executor_loader import ExecutorLoader diff --git a/airflow/cli/commands/triggerer_command.py b/airflow/cli/commands/triggerer_command.py index 8bf0c2822ddae..81702bb5503cb 100644 --- a/airflow/cli/commands/triggerer_command.py +++ b/airflow/cli/commands/triggerer_command.py @@ -23,10 +23,9 @@ from multiprocessing import Process from typing import Generator -import daemon -from daemon.pidfile import TimeoutPIDLockFile - +import airflow._vendor.daemon as daemon from airflow import settings +from airflow._vendor.daemon.pidfile import TimeoutPIDLockFile from airflow.configuration import conf from airflow.jobs.triggerer_job import TriggererJob from airflow.utils import cli as cli_utils diff --git a/airflow/cli/commands/webserver_command.py b/airflow/cli/commands/webserver_command.py index a14f6a38e7357..32b72a5c8762a 100644 --- a/airflow/cli/commands/webserver_command.py +++ b/airflow/cli/commands/webserver_command.py @@ -29,12 +29,12 @@ from time import sleep from typing import NoReturn -import daemon import psutil -from daemon.pidfile import TimeoutPIDLockFile from lockfile.pidlockfile import read_pid_from_pidfile +import airflow._vendor.daemon as daemon from airflow import settings +from airflow._vendor.daemon.pidfile import TimeoutPIDLockFile from airflow.configuration import conf from airflow.exceptions import AirflowException, AirflowWebServerTimeout from airflow.utils import cli as cli_utils diff --git a/licenses/LICENSE-python-daemon.txt b/licenses/LICENSE-python-daemon.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/licenses/LICENSE-python-daemon.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/setup.cfg b/setup.cfg index ec9a5c8bd4ab3..14b1a190a9a7d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -121,7 +121,6 @@ install_requires = psutil>=4.2.0 pygments>=2.0.1 pyjwt>=2.0.0 - python-daemon>=2.2.4 python-dateutil>=2.3 python-nvd3>=0.15.0 python-slugify>=5.0