diff --git a/ironic/api/app.py b/ironic/api/app.py index 7a55def795..42c0fcd622 100644 --- a/ironic/api/app.py +++ b/ironic/api/app.py @@ -18,6 +18,7 @@ import keystonemiddleware.audit as audit_middleware from oslo_config import cfg import oslo_middleware.cors as cors_middleware +from oslo_middleware import healthcheck import osprofiler.web as osprofiler_web import pecan @@ -103,6 +104,14 @@ def setup_app(pecan_config=None, extra_hooks=None): if CONF.profiler.enabled: app = osprofiler_web.WsgiMiddleware(app) + # add in the healthcheck middleware if enabled + # NOTE(jroll) this is after the auth token middleware as we don't want auth + # in front of this, and WSGI works from the outside in. Requests to + # /healthcheck will be handled and returned before the auth middleware + # is reached. + if CONF.healthcheck.enabled: + app = healthcheck.Healthcheck(app, CONF) + # Create a CORS wrapper, and attach ironic-specific defaults that must be # included in all CORS responses. app = IronicCORS(app, CONF) diff --git a/ironic/conf/__init__.py b/ironic/conf/__init__.py index da095e2ba7..0370f5ae9c 100644 --- a/ironic/conf/__init__.py +++ b/ironic/conf/__init__.py @@ -29,6 +29,7 @@ from ironic.conf import deploy from ironic.conf import dhcp from ironic.conf import drac from ironic.conf import glance +from ironic.conf import healthcheck from ironic.conf import ilo from ironic.conf import inspector from ironic.conf import ipmi @@ -62,6 +63,7 @@ deploy.register_opts(CONF) drac.register_opts(CONF) dhcp.register_opts(CONF) glance.register_opts(CONF) +healthcheck.register_opts(CONF) ilo.register_opts(CONF) inspector.register_opts(CONF) ipmi.register_opts(CONF) diff --git a/ironic/conf/healthcheck.py b/ironic/conf/healthcheck.py new file mode 100644 index 0000000000..8b1be85757 --- /dev/null +++ b/ironic/conf/healthcheck.py @@ -0,0 +1,29 @@ +# 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. + +from oslo_config import cfg + +from ironic.common.i18n import _ + +opts = [ + cfg.BoolOpt('enabled', + default=False, + help=_('Enable the health check endpoint at /healthcheck. ' + 'Note that this is unauthenticated. More information ' + 'is available at ' + 'https://docs.openstack.org/oslo.middleware/latest/' + 'reference/healthcheck_plugins.html.')), +] + + +def register_opts(conf): + conf.register_opts(opts, group='healthcheck') diff --git a/ironic/conf/opts.py b/ironic/conf/opts.py index 4dd446b88c..0034b58a68 100644 --- a/ironic/conf/opts.py +++ b/ironic/conf/opts.py @@ -47,6 +47,7 @@ _opts = [ ('dhcp', ironic.conf.dhcp.opts), ('drac', ironic.conf.drac.opts), ('glance', ironic.conf.glance.list_opts()), + ('healthcheck', ironic.conf.healthcheck.opts), ('ilo', ironic.conf.ilo.opts), ('inspector', ironic.conf.inspector.list_opts()), ('ipmi', ironic.conf.ipmi.opts), diff --git a/ironic/tests/unit/api/test_healthcheck.py b/ironic/tests/unit/api/test_healthcheck.py new file mode 100644 index 0000000000..364ba8cf87 --- /dev/null +++ b/ironic/tests/unit/api/test_healthcheck.py @@ -0,0 +1,39 @@ +# 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. +""" +Tests to assert that audit middleware works as expected. +""" + +import mock +from oslo_config import cfg +from oslo_middleware import healthcheck + +from ironic.tests.unit.api import base + + +CONF = cfg.CONF + + +class TestHealthcheckMiddleware(base.BaseApiTest): + """Provide a basic smoke test to ensure healthcheck middleware works.""" + + @mock.patch.object(healthcheck, 'Healthcheck') + def test_enable(self, mock_healthcheck): + CONF.set_override('enabled', True, group='healthcheck') + self._make_app() + mock_healthcheck.assert_called_once_with(mock.ANY, CONF) + + @mock.patch.object(healthcheck, 'Healthcheck') + def test_disable(self, mock_healthcheck): + CONF.set_override('enabled', False, group='healthcheck') + self._make_app() + self.assertFalse(mock_healthcheck.called) diff --git a/releasenotes/notes/add-healthcheck-middleware-86120fa07a7c8151.yaml b/releasenotes/notes/add-healthcheck-middleware-86120fa07a7c8151.yaml new file mode 100644 index 0000000000..aaa8011515 --- /dev/null +++ b/releasenotes/notes/add-healthcheck-middleware-86120fa07a7c8151.yaml @@ -0,0 +1,10 @@ +--- +features: + - | + Adds the healthcheck middleware from oslo, configurable via the + ``[healthcheck]/enabled`` option. This middleware adds a status check at + `/healthcheck`. This is useful for load balancers to determine if a service + is up (and add or remove it from rotation), or for monitoring tools to see + the health of the server. This endpoint is unauthenticated, as not all load + balancers or monitoring tools support authenticating with a health check + endpoint. diff --git a/tools/config/ironic-config-generator.conf b/tools/config/ironic-config-generator.conf index f738cc4061..5412c9159f 100644 --- a/tools/config/ironic-config-generator.conf +++ b/tools/config/ironic-config-generator.conf @@ -10,6 +10,7 @@ namespace = ironic_lib.utils namespace = oslo.db namespace = oslo.messaging namespace = oslo.middleware.cors +namespace = oslo.middleware.healthcheck namespace = oslo.concurrency namespace = oslo.policy namespace = oslo.log