Added support for SSL connection in mistra-api server

Change-Id: I4e114a11191821f4ef9dec967fb76b0a34179119
Implements: blueprint mistral-api-server-https
This commit is contained in:
hardik 2016-07-20 13:00:11 +05:30
parent 3a7e91df4a
commit 4adc3cfb56
7 changed files with 188 additions and 29 deletions

55
mistral/api/service.py Normal file
View File

@ -0,0 +1,55 @@
# Copyright 2016 NEC Corporation. All rights reserved.
#
# 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_concurrency import processutils
from oslo_config import cfg
from oslo_service import service
from oslo_service import wsgi
from mistral.api import app
class WSGIService(service.ServiceBase):
"""Provides ability to launch Mistral API from wsgi app."""
def __init__(self, name):
self.name = name
self.app = app.setup_app()
self.workers = (cfg.CONF.api.api_workers or
processutils.get_worker_count())
self.server = wsgi.Server(
cfg.CONF,
name,
self.app,
host=cfg.CONF.api.host,
port=cfg.CONF.api.port,
use_ssl=cfg.CONF.api.enable_ssl_api
)
def start(self):
self.server.start()
def stop(self):
self.server.stop()
def wait(self):
self.server.wait()
def reset(self):
self.server.reset()
def process_launcher():
return service.ProcessLauncher(cfg.CONF)

View File

@ -27,7 +27,6 @@ eventlet.monkey_patch(
import os
import six
# If ../mistral/__init__.py exists, add ../ to Python search path, so that
@ -41,15 +40,7 @@ if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'mistral', '__init__.py')):
from oslo_config import cfg
from oslo_log import log as logging
if six.PY3 is True:
import socketserver
else:
import SocketServer as socketserver
from wsgiref import simple_server
from wsgiref.simple_server import WSGIServer
from mistral.api import app
from mistral.api import service as mistral_service
from mistral import config
from mistral.db.v2 import api as db_api
from mistral.engine import default_engine as def_eng
@ -139,25 +130,11 @@ def launch_event_engine():
print("Stopping event_engine service...")
class ThreadingWSGIServer(socketserver.ThreadingMixIn, WSGIServer):
pass
def launch_api():
host = cfg.CONF.api.host
port = cfg.CONF.api.port
api_server = simple_server.make_server(
host,
port,
app.setup_app(),
ThreadingWSGIServer
)
LOG.info("Mistral API is serving on http://%s:%s (PID=%s)" %
(host, port, os.getpid()))
api_server.serve_forever()
launcher = mistral_service.process_launcher()
server = mistral_service.WSGIService('mistral_api')
launcher.launch_service(server, workers=server.workers)
launcher.wait()
def launch_any(options):

View File

@ -65,6 +65,18 @@ api_opts = [
default=False,
help='Enables the ability to delete action_execution which '
'has no relationship with workflows.'
),
cfg.BoolOpt(
'enable_ssl_api',
default=False,
help='Enable the integrated stand-alone API to service requests'
'via HTTPS instead of HTTP.'
),
cfg.IntOpt(
'api_workers',
help='Number of workers for Mistral API service '
'default is equal to the number of CPUs available if that can '
'be determined, else a default worker count of 1 is returned.'
)
]

View File

@ -32,7 +32,7 @@ class TestCORSMiddleware(base.APITest):
cfg.CONF.register_opts(cors_middleware.CORS_OPTS, 'cors')
# Load up our valid domain values before the application is created.
cfg.CONF.set_override(
self.override_config(
"allowed_origin",
"http://valid.example.com",
group='cors'

View File

@ -0,0 +1,109 @@
# Copyright 2016 NEC Corporation. All rights reserved.
#
# 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.
import mock
from oslo_concurrency import processutils
from oslo_config import cfg
import pecan
from mistral.api import service
from mistral.tests.unit import base
class TestWSGIService(base.BaseTest):
@mock.patch('mistral.api.app.setup_app')
@mock.patch.object(service.wsgi, 'Server')
def test_workers_set_default(self, wsgi_server, mock_app):
service_name = "mistral_api"
mock_app.return_value = pecan.testing.load_test_app({
'app': {
'root': cfg.CONF.pecan.root,
'modules': cfg.CONF.pecan.modules,
'debug': cfg.CONF.pecan.debug,
'auth_enable': cfg.CONF.pecan.auth_enable,
'disable_cron_trigger_thread': True
}
})
test_service = service.WSGIService(service_name)
wsgi_server.assert_called_once_with(
cfg.CONF,
service_name,
test_service.app,
host='0.0.0.0',
port=8989,
use_ssl=False
)
@mock.patch('mistral.api.app.setup_app')
@mock.patch.object(service.wsgi, 'Server')
def test_workers_set_correct_setting(self, wsgi_server, mock_app):
self.override_config('api_workers', 8, group='api')
mock_app.return_value = pecan.testing.load_test_app({
'app': {
'root': cfg.CONF.pecan.root,
'modules': cfg.CONF.pecan.modules,
'debug': cfg.CONF.pecan.debug,
'auth_enable': cfg.CONF.pecan.auth_enable,
'disable_cron_trigger_thread': True
}
})
test_service = service.WSGIService("mistral_api")
self.assertEqual(8, test_service.workers)
@mock.patch('mistral.api.app.setup_app')
@mock.patch.object(service.wsgi, 'Server')
def test_workers_set_zero_setting(self, wsgi_server, mock_app):
self.override_config('api_workers', 0, group='api')
mock_app.return_value = pecan.testing.load_test_app({
'app': {
'root': cfg.CONF.pecan.root,
'modules': cfg.CONF.pecan.modules,
'debug': cfg.CONF.pecan.debug,
'auth_enable': cfg.CONF.pecan.auth_enable,
'disable_cron_trigger_thread': True
}
})
test_service = service.WSGIService("mistral_api")
self.assertEqual(processutils.get_worker_count(), test_service.workers)
@mock.patch('mistral.api.app.setup_app')
@mock.patch.object(service.wsgi, 'Server')
def test_wsgi_service_with_ssl_enabled(self, wsgi_server, mock_app):
self.override_config('enable_ssl_api', True, group='api')
mock_app.return_value = pecan.testing.load_test_app({
'app': {
'root': cfg.CONF.pecan.root,
'modules': cfg.CONF.pecan.modules,
'debug': cfg.CONF.pecan.debug,
'auth_enable': cfg.CONF.pecan.auth_enable,
'disable_cron_trigger_thread': True
}
})
service_name = 'mistral_api'
srv = service.WSGIService(service_name)
wsgi_server.assert_called_once_with(
cfg.CONF,
service_name,
srv.app,
host='0.0.0.0',
port=8989,
use_ssl=True
)

View File

@ -202,6 +202,11 @@ class BaseTest(base.BaseTestCase):
def _sleep(self, seconds):
time.sleep(seconds)
def override_config(self, name, override, group=None):
"""Cleanly override CONF variables."""
cfg.CONF.set_override(name, override, group)
self.addCleanup(cfg.CONF.clear_override, name, group)
class DbTestCase(BaseTest):
is_heavy_init_called = False

View File

@ -7,3 +7,4 @@ namespace = keystonemiddleware.auth_token
namespace = periodic.config
namespace = oslo.log
namespace = oslo.policy
namespace = oslo.service.sslutils