Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion airflow/plugins_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ def initialize_web_ui_plugins():
'blueprint': bp
} for bp in plugin.flask_blueprints])

if (admin_views and not flask_appbuilder_views) or (menu_links and flask_appbuilder_menu_links):
if (admin_views and not flask_appbuilder_views) or (menu_links and not flask_appbuilder_menu_links):
log.warning(
"Plugin \'%s\' may not be compatible with the current Airflow version. "
"Please contact the author of the plugin.",
Expand Down
112 changes: 75 additions & 37 deletions tests/plugins/test_plugins_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from airflow.hooks.base_hook import BaseHook
from airflow.plugins_manager import AirflowPlugin
from airflow.www import app as application
from tests.test_utils.mock_plugins import mock_plugin_manager


class TestPluginsRBAC(unittest.TestCase):
Expand Down Expand Up @@ -94,59 +95,52 @@ def test_entrypoint_plugin_errors_dont_raise_exceptions(self, mock_ep_plugins):


class TestPluginsManager(unittest.TestCase):
class AirflowTestPropertyPlugin(AirflowPlugin):

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved this class to the function to maintain one style of code. It's easier to find the right test when all the code is in one place.

name = "test_property_plugin"
def test_should_load_plugins_from_property(self):
class AirflowTestPropertyPlugin(AirflowPlugin):
name = "test_property_plugin"

@property
def operators(self):
from airflow.models.baseoperator import BaseOperator

class PluginPropertyOperator(BaseOperator):
pass

@property
def operators(self):
from airflow.models.baseoperator import BaseOperator
return [PluginPropertyOperator]

class PluginPropertyOperator(BaseOperator):
class TestNonPropertyHook(BaseHook):
pass

return [PluginPropertyOperator]
hooks = [TestNonPropertyHook]

class TestNonPropertyHook(BaseHook):
pass
with mock_plugin_manager(plugins=[AirflowTestPropertyPlugin()]):
from airflow import plugins_manager
plugins_manager.integrate_dag_plugins()

hooks = [TestNonPropertyHook]
self.assertIn('AirflowTestPropertyPlugin', str(plugins_manager.plugins))
self.assertIn('PluginPropertyOperator', str(plugins_manager.operators_modules[0].__dict__))
self.assertIn("TestNonPropertyHook", str(plugins_manager.hooks_modules[0].__dict__))

@mock.patch('airflow.plugins_manager.plugins', [AirflowTestPropertyPlugin()])
@mock.patch('airflow.plugins_manager.operators_modules', None)
@mock.patch('airflow.plugins_manager.sensors_modules', None)
@mock.patch('airflow.plugins_manager.hooks_modules', None)
@mock.patch('airflow.plugins_manager.macros_modules', None)
def test_should_load_plugins_from_property(self):
from airflow import plugins_manager
plugins_manager.integrate_dag_plugins()
self.assertIn('TestPluginsManager.AirflowTestPropertyPlugin', str(plugins_manager.plugins))
self.assertIn('PluginPropertyOperator', str(plugins_manager.operators_modules[0].__dict__))
self.assertIn("TestNonPropertyHook", str(plugins_manager.hooks_modules[0].__dict__))

@mock.patch('airflow.plugins_manager.plugins', [])
@mock.patch('airflow.plugins_manager.admin_views', None)
@mock.patch('airflow.plugins_manager.flask_blueprints', None)
@mock.patch('airflow.plugins_manager.menu_links', None)
@mock.patch('airflow.plugins_manager.flask_appbuilder_views', None)
@mock.patch('airflow.plugins_manager.flask_appbuilder_menu_links', None)
def test_should_warning_about_incompatible_plugins(self):
class AirflowDeprecatedAdminViewsPlugin(AirflowPlugin):
class AirflowAdminViewsPlugin(AirflowPlugin):
name = "test_admin_views_plugin"

admin_views = [mock.MagicMock()]

class AirflowDeprecatedAdminMenuLinksPlugin(AirflowPlugin):
class AirflowAdminMenuLinksPlugin(AirflowPlugin):
name = "test_menu_links_plugin"

menu_links = [mock.MagicMock()]

from airflow import plugins_manager
plugins_manager.plugins = [
AirflowDeprecatedAdminViewsPlugin(),
AirflowDeprecatedAdminMenuLinksPlugin()
]
with self.assertLogs(plugins_manager.log) as cm:
plugins_manager.initialize_web_ui_plugins()
with mock_plugin_manager(plugins=[
AirflowAdminViewsPlugin(),
AirflowAdminMenuLinksPlugin()
]):
from airflow import plugins_manager

# assert not logs
with self.assertLogs(plugins_manager.log) as cm:
plugins_manager.initialize_web_ui_plugins()

self.assertEqual(cm.output, [
'WARNING:airflow.plugins_manager:Plugin \'test_admin_views_plugin\' may not be '
Expand All @@ -156,3 +150,47 @@ class AirflowDeprecatedAdminMenuLinksPlugin(AirflowPlugin):
'compatible with the current Airflow version. Please contact the author of '
'the plugin.'
])

def test_should_not_warning_about_fab_plugins(self):
class AirflowAdminViewsPlugin(AirflowPlugin):
name = "test_admin_views_plugin"

appbuilder_views = [mock.MagicMock()]

class AirflowAdminMenuLinksPlugin(AirflowPlugin):
name = "test_menu_links_plugin"

appbuilder_menu_items = [mock.MagicMock()]

with mock_plugin_manager(plugins=[
AirflowAdminViewsPlugin(),
AirflowAdminMenuLinksPlugin()
]):
from airflow import plugins_manager

# assert not logs
with self.assertRaises(AssertionError), self.assertLogs(plugins_manager.log):
plugins_manager.initialize_web_ui_plugins()

def test_should_not_warning_about_fab_and_flask_admin_plugins(self):
class AirflowAdminViewsPlugin(AirflowPlugin):
name = "test_admin_views_plugin"

admin_views = [mock.MagicMock()]
appbuilder_views = [mock.MagicMock()]

class AirflowAdminMenuLinksPlugin(AirflowPlugin):
name = "test_menu_links_plugin"

menu_links = [mock.MagicMock()]
appbuilder_menu_items = [mock.MagicMock()]

with mock_plugin_manager(plugins=[
AirflowAdminViewsPlugin(),
AirflowAdminMenuLinksPlugin()
]):
from airflow import plugins_manager

# assert not logs
with self.assertRaises(AssertionError), self.assertLogs(plugins_manager.log):
plugins_manager.initialize_web_ui_plugins()