diff --git a/hooks/heat_context.py b/hooks/heat_context.py index cf98646..70bbb79 100644 --- a/hooks/heat_context.py +++ b/hooks/heat_context.py @@ -23,6 +23,7 @@ from charmhelpers.core.hookenv import ( from charmhelpers.contrib.hahelpers.cluster import ( determine_apache_port, determine_api_port, + https, ) HEAT_PATH = '/var/lib/heat/' @@ -89,12 +90,24 @@ class HeatHAProxyContext(context.OSContextGenerator): apache_cfn_port = determine_apache_port(haproxy_cfn_port, singlenode_mode=True) + healthcheck = [{ + 'option': 'httpchk GET /healthcheck', + 'http-check': 'expect status 200', + }] + + backend_options = { + 'heat_api': healthcheck, + 'heat_cfn_api': healthcheck + } + ctxt = { 'service_ports': {'heat_api': [haproxy_port, apache_port], 'heat_cfn_api': [haproxy_cfn_port, apache_cfn_port]}, 'api_listen_port': api_port, 'api_cfn_listen_port': api_cfn_port, + 'backend_options': backend_options, + 'https': https(), } return ctxt diff --git a/templates/queens/api-paste.ini b/templates/queens/api-paste.ini index e01847c..dca3433 100644 --- a/templates/queens/api-paste.ini +++ b/templates/queens/api-paste.ini @@ -1,7 +1,7 @@ # heat-api pipeline [pipeline:heat-api] -pipeline = cors request_id faultwrap http_proxy_to_wsgi versionnegotiation osprofiler authurl authtoken context apiv1app +pipeline = healthcheck cors request_id faultwrap http_proxy_to_wsgi versionnegotiation osprofiler authurl authtoken context apiv1app # heat-api pipeline for standalone heat # ie. uses alternative auth backend that authenticates users against keystone @@ -12,7 +12,7 @@ pipeline = cors request_id faultwrap http_proxy_to_wsgi versionnegotiation ospro # flavor = standalone # [pipeline:heat-api-standalone] -pipeline = cors request_id faultwrap http_proxy_to_wsgi versionnegotiation authurl authpassword context apiv1app +pipeline = healthcheck cors request_id faultwrap http_proxy_to_wsgi versionnegotiation authurl authpassword context apiv1app # heat-api pipeline for custom cloud backends # i.e. in heat.conf: @@ -20,32 +20,32 @@ pipeline = cors request_id faultwrap http_proxy_to_wsgi versionnegotiation authu # flavor = custombackend # [pipeline:heat-api-custombackend] -pipeline = cors request_id faultwrap versionnegotiation context custombackendauth apiv1app +pipeline = healthcheck cors request_id faultwrap versionnegotiation context custombackendauth apiv1app # To enable, in heat.conf: # [paste_deploy] # flavor = noauth # [pipeline:heat-api-noauth] -pipeline = cors request_id faultwrap http_proxy_to_wsgi versionnegotiation noauth context apiv1app +pipeline = healthcheck cors request_id faultwrap http_proxy_to_wsgi versionnegotiation noauth context apiv1app # heat-api-cfn pipeline [pipeline:heat-api-cfn] -pipeline = cors http_proxy_to_wsgi cfnversionnegotiation osprofiler ec2authtoken authtoken context apicfnv1app +pipeline = healthcheck cors http_proxy_to_wsgi cfnversionnegotiation osprofiler ec2authtoken authtoken context apicfnv1app # heat-api-cfn pipeline for standalone heat # relies exclusively on authenticating with ec2 signed requests [pipeline:heat-api-cfn-standalone] -pipeline = cors http_proxy_to_wsgi cfnversionnegotiation ec2authtoken context apicfnv1app +pipeline = healthcheck cors http_proxy_to_wsgi cfnversionnegotiation ec2authtoken context apicfnv1app # heat-api-cloudwatch pipeline [pipeline:heat-api-cloudwatch] -pipeline = cors versionnegotiation osprofiler ec2authtoken authtoken context apicwapp +pipeline = healthcheck cors versionnegotiation osprofiler ec2authtoken authtoken context apicwapp # heat-api-cloudwatch pipeline for standalone heat # relies exclusively on authenticating with ec2 signed requests [pipeline:heat-api-cloudwatch-standalone] -pipeline = cors versionnegotiation ec2authtoken context apicwapp +pipeline = healthcheck cors versionnegotiation ec2authtoken context apicwapp [app:apiv1app] paste.app_factory = heat.common.wsgi:app_factory @@ -115,3 +115,5 @@ paste.filter_factory = oslo_middleware.request_id:RequestId.factory [filter:osprofiler] paste.filter_factory = osprofiler.web:WsgiMiddleware.factory +[filter:healthcheck] +paste.filter_factory = oslo_middleware:Healthcheck.factory diff --git a/tox.ini b/tox.ini index ae4d124..2cb6ca1 100644 --- a/tox.ini +++ b/tox.ini @@ -25,7 +25,7 @@ setenv = VIRTUAL_ENV={envdir} commands = stestr run --slowest {posargs} allowlist_externals = charmcraft - rename.sh + {toxinidir}/rename.sh passenv = HOME TERM diff --git a/unit_tests/test_heat_context.py b/unit_tests/test_heat_context.py index e3b1e3b..b51dd67 100644 --- a/unit_tests/test_heat_context.py +++ b/unit_tests/test_heat_context.py @@ -78,6 +78,38 @@ class TestHeatContext(CharmTestCase): self.test_config.set('max-stacks-per-tenant', '999') self.assertEqual(heat_context.QuotaConfigurationContext()(), expected) + @patch('charmhelpers.contrib.hahelpers.cluster.https') + @patch('heat_context.https') + def test_haproxy_context(self, mock_https, mock_ch_https): + for https_mode in [False, True]: + api_cfn_listen_port = 7990 + api_listen_port = 7994 + if https_mode: + api_cfn_listen_port = 7980 + api_listen_port = 7984 + mock_https.return_value = https_mode + mock_ch_https.return_value = https_mode + haproxy_context = heat_context.HeatHAProxyContext() + healthcheck = [{ + 'option': 'httpchk GET /healthcheck', + 'http-check': 'expect status 200', + }] + backend_options = { + 'heat_api': healthcheck, + 'heat_cfn_api': healthcheck + } + expected = { + 'service_ports': { + 'heat_api': [8004, 7994], + 'heat_cfn_api': [8000, 7990] + }, + 'api_listen_port': api_listen_port, + 'api_cfn_listen_port': api_cfn_listen_port, + 'backend_options': backend_options, + 'https': https_mode, + } + self.assertEqual(expected, haproxy_context()) + class HeatPluginContextTest(CharmTestCase):