diff --git a/flower-0.9.3.tar.gz b/flower-0.9.3.tar.gz deleted file mode 100644 index 8d9d47b..0000000 --- a/flower-0.9.3.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7f45acb297ab7cf3dd40140816143a2588f6938dbd70b8c46b59c7d8d1e93d55 -size 1290081 diff --git a/flower-0.9.7.tar.gz b/flower-0.9.7.tar.gz new file mode 100644 index 0000000..0debecc --- /dev/null +++ b/flower-0.9.7.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cf27a254268bb06fd4972408d0518237fcd847f7da4b4cd8055e228150ace8f3 +size 3526930 diff --git a/pr_1021.patch b/pr_1021.patch new file mode 100644 index 0000000..2384127 --- /dev/null +++ b/pr_1021.patch @@ -0,0 +1,543 @@ +From f52fea63d97eb16409f174acc2ded66403c66aac Mon Sep 17 00:00:00 2001 +From: avikam +Date: Tue, 1 Sep 2020 13:32:18 -0400 +Subject: [PATCH 01/10] start porting to 5.0.0 + +--- + flower/__main__.py | 14 +-- + flower/command.py | 240 ++++++++++++++++++++------------------- + requirements/default.txt | 3 +- + setup.py | 3 - + 4 files changed, 130 insertions(+), 130 deletions(-) + +diff --git a/flower/__main__.py b/flower/__main__.py +index b0e47c81..9d645588 100644 +--- a/flower/__main__.py ++++ b/flower/__main__.py +@@ -1,15 +1,11 @@ +-from flower.command import FlowerCommand +-from flower.utils import bugreport ++import sys + + + def main(): +- try: +- flower = FlowerCommand() +- flower.execute_from_commandline() +- except Exception: +- import sys +- print(bugreport(app=flower.app), file=sys.stderr) +- raise ++ from celery.bin.celery import main as _main, celery ++ from flower.command import flower ++ celery.add_command(flower) ++ sys.exit(_main()) + + + if __name__ == "__main__": +diff --git a/flower/command.py b/flower/command.py +index 9a716362..6c75a524 100644 +--- a/flower/command.py ++++ b/flower/command.py +@@ -8,10 +8,11 @@ + + from logging import NullHandler + ++import click + from tornado.options import options + from tornado.options import parse_command_line, parse_config_file + from tornado.log import enable_pretty_logging +-from celery.bin.base import Command ++from celery.bin.base import CeleryCommand + + from . import __version__ + from .app import Flower +@@ -21,127 +22,134 @@ + + + logger = logging.getLogger(__name__) ++ENV_VAR_PREFIX = 'FLOWER_' + + +-class FlowerCommand(Command): +- ENV_VAR_PREFIX = 'FLOWER_' ++def print_banner(app, ssl): ++ if not options.unix_socket: ++ logger.info( ++ "Visit me at http%s://%s:%s", 's' if ssl else '', ++ options.address or 'localhost', options.port ++ ) ++ else: ++ logger.info("Visit me via unix socket file: %s", options.unix_socket) ++ ++ logger.info('Broker: %s', app.connection().as_uri()) ++ logger.info( ++ 'Registered tasks: \n%s', ++ pformat(sorted(app.tasks.keys())) ++ ) ++ logger.debug('Settings: %s', pformat(settings)) ++ ++ ++@click.command(cls=CeleryCommand, ++ context_settings={'allow_extra_args': True}) ++@click.argument("torando_argv", nargs=-1, type=click.UNPROCESSED) ++@click.pass_context ++def flower(ctx, torando_argv): ++ apply_env_options() ++ apply_options(sys.argv[0], torando_argv) ++ ++ extract_settings() ++ setup_logging() ++ ++ app = ctx.obj.app ++ flower = Flower(capp=app, options=options, **settings) ++ ++ atexit.register(flower.stop) ++ ++ def sigterm_handler(signal, frame): ++ logger.info('SIGTERM detected, shutting down') ++ sys.exit(0) ++ ++ signal.signal(signal.SIGTERM, sigterm_handler) ++ print_banner(app, 'ssl_options' in settings) ++ try: ++ flower.start() ++ except (KeyboardInterrupt, SystemExit): ++ pass ++ ++ ++def apply_env_options(): ++ "apply options passed through environment variables" ++ env_options = filter(is_flower_envvar, os.environ) ++ for env_var_name in env_options: ++ name = env_var_name.replace(ENV_VAR_PREFIX, '', 1).lower() ++ value = os.environ[env_var_name] ++ try: ++ option = options._options[name] ++ except KeyError: ++ option = options._options[name.replace('_', '-')] ++ if option.multiple: ++ value = [option.type(i) for i in value.split(',')] ++ else: ++ value = option.type(value) ++ setattr(options, name, value) + +- def run_from_argv(self, prog_name, argv=None, **_kwargs): +- self.apply_env_options() +- self.apply_options(prog_name, argv) + +- self.extract_settings() +- self.setup_logging() ++def apply_options(prog_name, argv): ++ "apply options passed through the configuration file" ++ argv = list(filter(is_flower_option, argv)) ++ # parse the command line to get --conf option ++ parse_command_line([prog_name] + argv) ++ try: ++ parse_config_file(os.path.abspath(options.conf), final=False) ++ parse_command_line([prog_name] + argv) ++ except IOError: ++ if os.path.basename(options.conf) != DEFAULT_CONFIG_FILE: ++ raise + +- self.app.loader.import_default_modules() +- flower = Flower(capp=self.app, options=options, **settings) +- atexit.register(flower.stop) + +- def sigterm_handler(signal, frame): +- logger.info('SIGTERM detected, shutting down') +- sys.exit(0) +- signal.signal(signal.SIGTERM, sigterm_handler) ++def setup_logging(): ++ if options.debug and options.logging == 'info': ++ options.logging = 'debug' ++ enable_pretty_logging() ++ else: ++ logging.getLogger("tornado.access").addHandler(NullHandler()) ++ logging.getLogger("tornado.access").propagate = False + +- self.print_banner('ssl_options' in settings) + +- try: +- flower.start() +- except (KeyboardInterrupt, SystemExit): +- pass +- +- def handle_argv(self, prog_name, argv=None): +- return self.run_from_argv(prog_name, argv) +- +- def apply_env_options(self): +- "apply options passed through environment variables" +- env_options = filter(self.is_flower_envvar, os.environ) +- for env_var_name in env_options: +- name = env_var_name.replace(self.ENV_VAR_PREFIX, '', 1).lower() +- value = os.environ[env_var_name] +- try: +- option = options._options[name] +- except KeyError: +- option = options._options[name.replace('_', '-')] +- if option.multiple: +- value = [option.type(i) for i in value.split(',')] +- else: +- value = option.type(value) +- setattr(options, name, value) +- +- def apply_options(self, prog_name, argv): +- "apply options passed through the configuration file" +- argv = list(filter(self.is_flower_option, argv)) +- # parse the command line to get --conf option +- parse_command_line([prog_name] + argv) +- try: +- parse_config_file(os.path.abspath(options.conf), final=False) +- parse_command_line([prog_name] + argv) +- except IOError: +- if os.path.basename(options.conf) != DEFAULT_CONFIG_FILE: +- raise +- +- def setup_logging(self): +- if options.debug and options.logging == 'info': +- options.logging = 'debug' +- enable_pretty_logging() +- else: +- logging.getLogger("tornado.access").addHandler(NullHandler()) +- logging.getLogger("tornado.access").propagate = False +- +- def extract_settings(self): +- settings['debug'] = options.debug +- +- if options.cookie_secret: +- settings['cookie_secret'] = options.cookie_secret +- +- if options.url_prefix: +- for name in ['login_url', 'static_url_prefix']: +- settings[name] = prepend_url(settings[name], options.url_prefix) +- +- if options.auth: +- settings['oauth'] = { +- 'key': options.oauth2_key or os.environ.get('FLOWER_OAUTH2_KEY'), +- 'secret': options.oauth2_secret or os.environ.get('FLOWER_OAUTH2_SECRET'), +- 'redirect_uri': options.oauth2_redirect_uri or os.environ.get('FLOWER_OAUTH2_REDIRECT_URI'), +- } +- +- if options.certfile and options.keyfile: +- settings['ssl_options'] = dict(certfile=abs_path(options.certfile), +- keyfile=abs_path(options.keyfile)) +- if options.ca_certs: +- settings['ssl_options']['ca_certs'] = abs_path(options.ca_certs) +- +- def early_version(self, argv): +- if '--version' in argv: +- if '--debug' in argv: +- from flower.utils import bugreport +- print(bugreport(), file=self.stdout) +- +- print(__version__, file=self.stdout) +- super(FlowerCommand, self).early_version(argv) +- +- @staticmethod +- def is_flower_option(arg): +- name, _, _ = arg.lstrip('-').partition("=") +- name = name.replace('-', '_') +- return hasattr(options, name) +- +- def is_flower_envvar(self, name): +- return name.startswith(self.ENV_VAR_PREFIX) and\ +- name[len(self.ENV_VAR_PREFIX):].lower() in default_options +- +- def print_banner(self, ssl): +- if not options.unix_socket: +- logger.info( +- "Visit me at http%s://%s:%s", 's' if ssl else '', +- options.address or 'localhost', options.port +- ) +- else: +- logger.info("Visit me via unix socket file: %s", options.unix_socket) ++def extract_settings(): ++ settings['debug'] = options.debug + +- logger.info('Broker: %s', self.app.connection().as_uri()) +- logger.info( +- 'Registered tasks: \n%s', +- pformat(sorted(self.app.tasks.keys())) +- ) +- logger.debug('Settings: %s', pformat(settings)) ++ if options.cookie_secret: ++ settings['cookie_secret'] = options.cookie_secret ++ ++ if options.url_prefix: ++ for name in ['login_url', 'static_url_prefix']: ++ settings[name] = prepend_url(settings[name], options.url_prefix) ++ ++ if options.auth: ++ settings['oauth'] = { ++ 'key': options.oauth2_key or os.environ.get('FLOWER_OAUTH2_KEY'), ++ 'secret': options.oauth2_secret or os.environ.get('FLOWER_OAUTH2_SECRET'), ++ 'redirect_uri': options.oauth2_redirect_uri or os.environ.get('FLOWER_OAUTH2_REDIRECT_URI'), ++ } ++ ++ if options.certfile and options.keyfile: ++ settings['ssl_options'] = dict(certfile=abs_path(options.certfile), ++ keyfile=abs_path(options.keyfile)) ++ if options.ca_certs: ++ settings['ssl_options']['ca_certs'] = abs_path(options.ca_certs) ++ ++ ++def early_version(self, argv): ++ if '--version' in argv: ++ if '--debug' in argv: ++ from flower.utils import bugreport ++ print(bugreport(), file=self.stdout) ++ ++ print(__version__, file=self.stdout) ++ super(FlowerCommand, self).early_version(argv) ++ ++ ++@staticmethod ++def is_flower_option(arg): ++ name, _, _ = arg.lstrip('-').partition("=") ++ name = name.replace('-', '_') ++ return hasattr(options, name) ++ ++ ++def is_flower_envvar(name): ++ return name.startswith(ENV_VAR_PREFIX) and\ ++ name[len(ENV_VAR_PREFIX):].lower() in default_options + +From 5d69f8638aad3caa208b338f6fed3386260e27d6 Mon Sep 17 00:00:00 2001 +From: avikam +Date: Tue, 1 Sep 2020 13:36:03 -0400 +Subject: [PATCH 02/10] change friendly + +--- + flower/command.py | 45 +++++++++++++++++---------------------------- + 1 file changed, 17 insertions(+), 28 deletions(-) + +diff --git a/flower/command.py b/flower/command.py +index 6c75a524..95cd4c9d 100644 +--- a/flower/command.py ++++ b/flower/command.py +@@ -25,23 +25,6 @@ + ENV_VAR_PREFIX = 'FLOWER_' + + +-def print_banner(app, ssl): +- if not options.unix_socket: +- logger.info( +- "Visit me at http%s://%s:%s", 's' if ssl else '', +- options.address or 'localhost', options.port +- ) +- else: +- logger.info("Visit me via unix socket file: %s", options.unix_socket) +- +- logger.info('Broker: %s', app.connection().as_uri()) +- logger.info( +- 'Registered tasks: \n%s', +- pformat(sorted(app.tasks.keys())) +- ) +- logger.debug('Settings: %s', pformat(settings)) +- +- + @click.command(cls=CeleryCommand, + context_settings={'allow_extra_args': True}) + @click.argument("torando_argv", nargs=-1, type=click.UNPROCESSED) +@@ -133,17 +116,6 @@ def extract_settings(): + settings['ssl_options']['ca_certs'] = abs_path(options.ca_certs) + + +-def early_version(self, argv): +- if '--version' in argv: +- if '--debug' in argv: +- from flower.utils import bugreport +- print(bugreport(), file=self.stdout) +- +- print(__version__, file=self.stdout) +- super(FlowerCommand, self).early_version(argv) +- +- +-@staticmethod + def is_flower_option(arg): + name, _, _ = arg.lstrip('-').partition("=") + name = name.replace('-', '_') +@@ -153,3 +125,20 @@ def is_flower_option(arg): + def is_flower_envvar(name): + return name.startswith(ENV_VAR_PREFIX) and\ + name[len(ENV_VAR_PREFIX):].lower() in default_options ++ ++ ++def print_banner(app, ssl): ++ if not options.unix_socket: ++ logger.info( ++ "Visit me at http%s://%s:%s", 's' if ssl else '', ++ options.address or 'localhost', options.port ++ ) ++ else: ++ logger.info("Visit me via unix socket file: %s", options.unix_socket) ++ ++ logger.info('Broker: %s', app.connection().as_uri()) ++ logger.info( ++ 'Registered tasks: \n%s', ++ pformat(sorted(app.tasks.keys())) ++ ) ++ logger.debug('Settings: %s', pformat(settings)) +\ No newline at end of file + +From 25e1cb7b26b9350cd90d4c820e7e6808d8470f07 Mon Sep 17 00:00:00 2001 +From: avikam +Date: Tue, 1 Sep 2020 13:50:35 -0400 +Subject: [PATCH 03/10] forwarding arguments to tornado + +--- + flower/command.py | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/flower/command.py b/flower/command.py +index 95cd4c9d..57082f2d 100644 +--- a/flower/command.py ++++ b/flower/command.py +@@ -20,13 +20,14 @@ + from .utils import abs_path, prepend_url + from .options import DEFAULT_CONFIG_FILE, default_options + +- + logger = logging.getLogger(__name__) + ENV_VAR_PREFIX = 'FLOWER_' + + + @click.command(cls=CeleryCommand, +- context_settings={'allow_extra_args': True}) ++ context_settings={ ++ 'ignore_unknown_options': True ++ }) + @click.argument("torando_argv", nargs=-1, type=click.UNPROCESSED) + @click.pass_context + def flower(ctx, torando_argv): +@@ -123,7 +124,7 @@ def is_flower_option(arg): + + + def is_flower_envvar(name): +- return name.startswith(ENV_VAR_PREFIX) and\ ++ return name.startswith(ENV_VAR_PREFIX) and \ + name[len(ENV_VAR_PREFIX):].lower() in default_options + + +@@ -141,4 +142,4 @@ def print_banner(app, ssl): + 'Registered tasks: \n%s', + pformat(sorted(app.tasks.keys())) + ) +- logger.debug('Settings: %s', pformat(settings)) +\ No newline at end of file ++ logger.debug('Settings: %s', pformat(settings)) + +From 10e1447ee8f44c1bb2a700ab65de577c08b74b9a Mon Sep 17 00:00:00 2001 +From: avikam +Date: Sat, 14 Nov 2020 14:22:21 -0500 +Subject: [PATCH 05/10] fixing tornado options checking + +--- + tests/unit/test_command.py | 25 +++++++++---------------- + 1 file changed, 9 insertions(+), 16 deletions(-) + +diff --git a/tests/unit/test_command.py b/tests/unit/test_command.py +index d5fe9a5f..2c007110 100644 +--- a/tests/unit/test_command.py ++++ b/tests/unit/test_command.py +@@ -4,7 +4,7 @@ + import unittest + import subprocess + +-from flower.command import FlowerCommand ++from flower.command import apply_options + from tornado.options import options + from tests.unit import AsyncHTTPTestCase + +@@ -12,35 +12,30 @@ + class TestFlowerCommand(AsyncHTTPTestCase): + def test_port(self): + with self.mock_option('port', 5555): +- command = FlowerCommand() +- command.apply_options('flower', argv=['--port=123']) ++ apply_options('flower', argv=['--port=123']) + self.assertEqual(123, options.port) + + def test_address(self): + with self.mock_option('address', '127.0.0.1'): +- command = FlowerCommand() +- command.apply_options('flower', argv=['--address=foo']) ++ apply_options('flower', argv=['--address=foo']) + self.assertEqual('foo', options.address) + + + class TestConfOption(AsyncHTTPTestCase): + def test_error_conf(self): + with self.mock_option('conf', None): +- command = FlowerCommand() +- self.assertRaises(IOError, command.apply_options, ++ self.assertRaises(IOError, apply_options, + 'flower', argv=['--conf=foo']) +- self.assertRaises(IOError, command.apply_options, ++ self.assertRaises(IOError, apply_options, + 'flower', argv=['--conf=/tmp/flower/foo']) + + def test_default_option(self): +- command = FlowerCommand() +- command.apply_options('flower', argv=[]) ++ apply_options('flower', argv=[]) + self.assertEqual('flowerconfig.py', options.conf) + + def test_empty_conf(self): + with self.mock_option('conf', None): +- command = FlowerCommand() +- command.apply_options('flower', argv=['--conf=/dev/null']) ++ apply_options('flower', argv=['--conf=/dev/null']) + self.assertEqual('/dev/null', options.conf) + + def test_conf_abs(self): +@@ -48,8 +43,7 @@ def test_conf_abs(self): + with self.mock_option('conf', cf.name), self.mock_option('debug', False): + cf.write('debug=True\n'.encode('utf-8')) + cf.flush() +- command = FlowerCommand() +- command.apply_options('flower', argv=['--conf=%s' % cf.name]) ++ apply_options('flower', argv=['--conf=%s' % cf.name]) + self.assertEqual(cf.name, options.conf) + self.assertTrue(options.debug) + +@@ -58,8 +52,7 @@ def test_conf_relative(self): + with self.mock_option('conf', cf.name), self.mock_option('debug', False): + cf.write('debug=True\n'.encode('utf-8')) + cf.flush() +- command = FlowerCommand() +- command.apply_options('flower', argv=['--conf=%s' % os.path.basename(cf.name)]) ++ apply_options('flower', argv=['--conf=%s' % os.path.basename(cf.name)]) + self.assertTrue(options.debug) + + @unittest.skipUnless(not sys.platform.startswith("win"), 'skip windows') + +From 9aade51671a2c1f0c1d0c1c81c7ca9bb49837f83 Mon Sep 17 00:00:00 2001 +From: avikam +Date: Mon, 25 Jan 2021 09:55:35 -0500 +Subject: [PATCH 10/10] s/torando/tornado/ typo + +--- + flower/command.py | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/flower/command.py b/flower/command.py +index 57082f2d..840d0990 100644 +--- a/flower/command.py ++++ b/flower/command.py +@@ -28,11 +28,11 @@ + context_settings={ + 'ignore_unknown_options': True + }) +-@click.argument("torando_argv", nargs=-1, type=click.UNPROCESSED) ++@click.argument("tornado_argv", nargs=-1, type=click.UNPROCESSED) + @click.pass_context +-def flower(ctx, torando_argv): ++def flower(ctx, tornado_argv): + apply_env_options() +- apply_options(sys.argv[0], torando_argv) ++ apply_options(sys.argv[0], tornado_argv) + + extract_settings() + setup_logging() diff --git a/python-flower.changes b/python-flower.changes index 8599c4b..f71efb3 100644 --- a/python-flower.changes +++ b/python-flower.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Tue Feb 2 00:18:01 UTC 2021 - John Vandenberg + +- Add pr_1021.patch for Celery 5.0 support +- Update to v0.9.7 + * See https://github.com/mher/flower/compare/v0.9.3...v0.9.7 + ------------------------------------------------------------------- Mon Apr 20 12:03:43 UTC 2020 - Tomáš Chvátal diff --git a/python-flower.spec b/python-flower.spec index ed6f249..88ba1cc 100644 --- a/python-flower.spec +++ b/python-flower.spec @@ -1,7 +1,7 @@ # # spec file for package python-flower # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2021 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %bcond_without python2 Name: python-flower -Version: 0.9.3 +Version: 0.9.7 Release: 0 Summary: A web frontend for monitoring and administrating Celery clusters License: BSD-3-Clause @@ -27,21 +27,26 @@ URL: https://github.com/mher/flower Source: https://files.pythonhosted.org/packages/source/f/flower/flower-%{version}.tar.gz # Tornado 5+ update blocked by salt, so backport the missing piece Patch0: backport_run_in_executor.patch +Patch1: pr_1021.patch BuildRequires: %{python_module Babel >= 1.0} -BuildRequires: %{python_module celery >= 3.1.0} +BuildRequires: %{python_module celery >= 5.0.0} BuildRequires: %{python_module certifi} +BuildRequires: %{python_module humanize} BuildRequires: %{python_module kombu} BuildRequires: %{python_module mock} +BuildRequires: %{python_module prometheus_client >= 0.8.0} BuildRequires: %{python_module pytest} BuildRequires: %{python_module pytz} BuildRequires: %{python_module setuptools} -BuildRequires: %{python_module tornado >= 4.2.0} +BuildRequires: %{python_module tornado >= 5.0.0} BuildRequires: fdupes Requires: python-Babel >= 1.0 -Requires: python-celery >= 3.1.0 +Requires: python-celery >= 5.0.0 Requires: python-certifi +Requires: python-humanize +Requires: python-prometheus_client >= 0.8.0 Requires: python-pytz -Requires: python-tornado >= 4.2.0 +Requires: python-tornado >= 5.0.0 Requires(post): update-alternatives Requires(postun): update-alternatives BuildArch: noarch @@ -59,6 +64,7 @@ Flower is a web based tool for monitoring and administrating Celery clusters. %prep %setup -q -n flower-%{version} %patch0 -p1 +%patch1 -p1 %build %python_build