From d76505767390fabdb98885c5454ca0a68c1d180d Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Fri, 5 Feb 2016 11:14:11 +0100 Subject: [PATCH] Implement WSGI support for mistral-api * Allow to deploy mistral-api in WSGI with apache. * Switch beaker tests to deploy WSGI by default. * Check if 8041 port is listening on apache Change-Id: I031198958fdd4147121fbd64b6510bdd564e03c3 --- manifests/api.pp | 48 +++++++-- manifests/params.pp | 36 ++++--- manifests/wsgi/apache.pp | 128 +++++++++++++++++++++++ metadata.json | 1 + spec/acceptance/basic_mistral_spec.rb | 10 +- spec/classes/mistral_api_spec.rb | 49 ++++++++- spec/classes/mistral_wsgi_apache_spec.rb | 124 ++++++++++++++++++++++ 7 files changed, 367 insertions(+), 29 deletions(-) create mode 100644 manifests/wsgi/apache.pp create mode 100644 spec/classes/mistral_wsgi_apache_spec.rb diff --git a/manifests/api.pp b/manifests/api.pp index ffe4845..abd1c85 100644 --- a/manifests/api.pp +++ b/manifests/api.pp @@ -29,6 +29,15 @@ # relationship with workflows. (boolean value). # Defaults to $::os_service_default. # +# [*service_name*] +# (optional) Name of the service that will be providing the +# server functionality of mistral-api. +# If the value is 'httpd', this means mistral-api will be a web +# service, and you must use another class to configure that +# web service. For example, use class { 'mistral::wsgi::apache'...} +# to make mistral-api be a web app using apache mod_wsgi. +# Defaults to '$::mistral::params::api_service_name' +# class mistral::api ( $package_ensure = present, $manage_service = true, @@ -36,13 +45,17 @@ class mistral::api ( $bind_host = $::os_service_default, $bind_port = $::os_service_default, $allow_action_execution_deletion = $::os_service_default, -) { + $service_name = $::mistral::params::api_service_name, +) inherits mistral::params { include ::mistral::params include ::mistral::policy + Mistral_config<||> ~> Service[$service_name] + Class['mistral::policy'] ~> Service[$service_name] Package['mistral-api'] -> Class['mistral::policy'] - Class['mistral::policy'] ~> Service['mistral-api'] + Package['mistral-api'] -> Service[$service_name] + Package['mistral-api'] -> Service['mistral-api'] package { 'mistral-api': ensure => $package_ensure, @@ -58,13 +71,30 @@ class mistral::api ( } } - service { 'mistral-api': - ensure => $service_ensure, - name => $::mistral::params::api_service_name, - enable => $enabled, - hasstatus => true, - hasrestart => true, - tag => 'mistral-service', + if $service_name == $::mistral::params::api_service_name { + service { 'mistral-api': + ensure => $service_ensure, + name => $::mistral::params::api_service_name, + enable => $enabled, + hasstatus => true, + hasrestart => true, + tag => 'mistral-service', + } + } elsif $service_name == 'httpd' { + include ::apache::params + service { 'mistral-api': + ensure => 'stopped', + name => $::mistral::params::api_service_name, + enable => false, + tag => 'mistral-service', + } + Class['mistral::db'] -> Service[$service_name] + Service <<| title == 'httpd' |>> { tag +> 'mistral-service' } + + # we need to make sure mistral-api s stopped before trying to start apache + Service['mistral-api'] -> Service[$service_name] + } else { + fail('Invalid service_name. Either mistral/openstack-mistral-api for running as a standalone service, or httpd for being run by a httpd server') } mistral_config { diff --git a/manifests/params.pp b/manifests/params.pp index a37439b..fbda3cd 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -10,24 +10,28 @@ class mistral::params { case $::osfamily { 'RedHat': { - $common_package_name = 'openstack-mistral-common' - $api_package_name = 'openstack-mistral-api' - $api_service_name = 'openstack-mistral-api' - $engine_package_name = 'openstack-mistral-engine' - $engine_service_name = 'openstack-mistral-engine' - $executor_package_name = 'openstack-mistral-executor' - $executor_service_name = 'openstack-mistral-executor' - $pymysql_package_name = undef + $common_package_name = 'openstack-mistral-common' + $api_package_name = 'openstack-mistral-api' + $api_service_name = 'openstack-mistral-api' + $engine_package_name = 'openstack-mistral-engine' + $engine_service_name = 'openstack-mistral-engine' + $executor_package_name = 'openstack-mistral-executor' + $executor_service_name = 'openstack-mistral-executor' + $mistral_wsgi_script_path = '/var/www/cgi-bin/mistral' + $mistral_wsgi_script_source = '/usr/lib/python2.7/site-packages/mistral/api/wsgi.py' + $pymysql_package_name = undef } 'Debian': { - $common_package_name = 'mistral' - $api_package_name = 'mistral-api' - $api_service_name = 'mistral-api' - $engine_package_name = 'mistral-engine' - $engine_service_name = 'mistral-engine' - $executor_package_name = 'mistral-executor' - $executor_service_name = 'mistral-executor' - $pymysql_package_name = 'python-pymysql' + $common_package_name = 'mistral' + $api_package_name = 'mistral-api' + $api_service_name = 'mistral-api' + $engine_package_name = 'mistral-engine' + $engine_service_name = 'mistral-engine' + $executor_package_name = 'mistral-executor' + $executor_service_name = 'mistral-executor' + $mistral_wsgi_script_path = '/usr/lib/cgi-bin/mistral' + $mistral_wsgi_script_source = '/usr/share/mistral-common/wsgi.py' + $pymysql_package_name = 'python-pymysql' } default: { fail("Unsupported osfamily: ${::osfamily} operatingsystem: \ diff --git a/manifests/wsgi/apache.pp b/manifests/wsgi/apache.pp new file mode 100644 index 0000000..1cc6d59 --- /dev/null +++ b/manifests/wsgi/apache.pp @@ -0,0 +1,128 @@ +# +# Copyright (C) 2015 eNovance SAS +# +# Author: Emilien Macchi +# +# 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. +# +# Class to serve mistral API with apache mod_wsgi in place of mistral-api service. +# +# Serving mistral API from apache is the recommended way to go for production +# because of limited performance for concurrent accesses when running eventlet. +# +# When using this class you should disable your mistral-api service. +# +# == Parameters +# +# [*servername*] +# The servername for the virtualhost. +# Optional. Defaults to $::fqdn +# +# [*port*] +# The port. +# Optional. Defaults to 8989 +# +# [*bind_host*] +# The host/ip address Apache will listen on. +# Optional. Defaults to undef (listen on all ip addresses). +# +# [*path*] +# The prefix for the endpoint. +# Optional. Defaults to '/' +# +# [*ssl*] +# Use ssl ? (boolean) +# Optional. Defaults to true +# +# [*workers*] +# Number of WSGI workers to spawn. +# Optional. Defaults to 1 +# +# [*priority*] +# (optional) The priority for the vhost. +# Defaults to '10' +# +# [*threads*] +# (optional) The number of threads for the vhost. +# Defaults to $::processorcount +# +# [*ssl_cert*] +# [*ssl_key*] +# [*ssl_chain*] +# [*ssl_ca*] +# [*ssl_crl_path*] +# [*ssl_crl*] +# [*ssl_certs_dir*] +# apache::vhost ssl parameters. +# Optional. Default to apache::vhost 'ssl_*' defaults. +# +# == Dependencies +# +# requires Class['apache'] & Class['mistral'] +# +# == Examples +# +# include apache +# +# class { 'mistral::wsgi::apache': } +# +class mistral::wsgi::apache ( + $servername = $::fqdn, + $port = 8989, + $bind_host = undef, + $path = '/', + $ssl = true, + $workers = 1, + $ssl_cert = undef, + $ssl_key = undef, + $ssl_chain = undef, + $ssl_ca = undef, + $ssl_crl_path = undef, + $ssl_crl = undef, + $ssl_certs_dir = undef, + $threads = $::processorcount, + $priority = '10', +) { + + include ::mistral::params + include ::apache + include ::apache::mod::wsgi + if $ssl { + include ::apache::mod::ssl + } + + ::openstacklib::wsgi::apache { 'mistral_wsgi': + bind_host => $bind_host, + bind_port => $port, + group => 'mistral', + path => $path, + priority => $priority, + servername => $servername, + ssl => $ssl, + ssl_ca => $ssl_ca, + ssl_cert => $ssl_cert, + ssl_certs_dir => $ssl_certs_dir, + ssl_chain => $ssl_chain, + ssl_crl => $ssl_crl, + ssl_crl_path => $ssl_crl_path, + ssl_key => $ssl_key, + threads => $threads, + user => 'mistral', + workers => $workers, + wsgi_daemon_process => 'mistral', + wsgi_process_group => 'mistral', + wsgi_script_dir => $::mistral::params::mistral_wsgi_script_path, + wsgi_script_file => 'app', + wsgi_script_source => $::mistral::params::mistral_wsgi_script_source, + } +} diff --git a/metadata.json b/metadata.json index ba44d93..fa59a0e 100644 --- a/metadata.json +++ b/metadata.json @@ -27,6 +27,7 @@ } ], "dependencies": [ + { "name": "puppetlabs/apache", "version_requirement": ">=1.0.0 <2.0.0" }, { "name": "puppetlabs/inifile", "version_requirement": ">=1.0.0 <2.0.0" }, { "name": "puppetlabs/stdlib", "version_requirement": ">= 4.0.0 <5.0.0" }, { "name": "openstack/openstacklib", "version_requirement": ">=7.0.0 <8.0.0" } diff --git a/spec/acceptance/basic_mistral_spec.rb b/spec/acceptance/basic_mistral_spec.rb index 649bdba..552e80a 100644 --- a/spec/acceptance/basic_mistral_spec.rb +++ b/spec/acceptance/basic_mistral_spec.rb @@ -48,7 +48,13 @@ describe 'basic mistral' do class { '::mistral::db::mysql': password => 'a_big_secret', } - class { '::mistral::api': } + class { '::mistral::api': + service_name => 'httpd', + } + include ::apache + class { '::mistral::wsgi::apache': + ssl => false, + } class { '::mistral::client': } class { '::mistral::engine': } class { '::mistral::executor': } @@ -65,7 +71,7 @@ describe 'basic mistral' do if os[:family].casecmp('RedHat') == 0 describe port(8989) do - it { is_expected.to be_listening.with('tcp') } + it { is_expected.to be_listening } end end diff --git a/spec/classes/mistral_api_spec.rb b/spec/classes/mistral_api_spec.rb index 6d08c0a..858fc7a 100644 --- a/spec/classes/mistral_api_spec.rb +++ b/spec/classes/mistral_api_spec.rb @@ -65,11 +65,49 @@ describe 'mistral::api' do end end + context 'when running mistral-api in wsgi' do + before do + params.merge!({ :service_name => 'httpd' }) + end + + let :pre_condition do + "include ::apache + include ::mistral::db + class { '::mistral': + keystone_password => 'foo', + rabbit_password => 'bar', + }" + end + + it 'configures mistral-api service with Apache' do + is_expected.to contain_service('mistral-api').with( + :ensure => 'stopped', + :name => platform_params[:api_service_name], + :enable => false, + :tag => ['mistral-service'], + ) + end + end + + context 'when service_name is not valid' do + before do + params.merge!({ :service_name => 'foobar' }) + end + + it_raises 'a Puppet::Error', /Invalid service_name/ + end end context 'on Debian platforms' do let :facts do - { :osfamily => 'Debian' } + @default_facts.merge({ + :osfamily => 'Debian', + :operatingsystem => 'Debian', + :operatingsystemrelease => '8.0', + :concat_basedir => '/var/lib/puppet/concat', + :fqdn => 'some.host.tld', + :processorcount => 2, + }) end let :platform_params do @@ -81,7 +119,14 @@ describe 'mistral::api' do context 'on RedHat platforms' do let :facts do - { :osfamily => 'RedHat' } + @default_facts.merge({ + :osfamily => 'RedHat', + :operatingsystem => 'RedHat', + :operatingsystemrelease => '7.1', + :fqdn => 'some.host.tld', + :concat_basedir => '/var/lib/puppet/concat', + :processorcount => 2, + }) end let :platform_params do diff --git a/spec/classes/mistral_wsgi_apache_spec.rb b/spec/classes/mistral_wsgi_apache_spec.rb new file mode 100644 index 0000000..65a59d3 --- /dev/null +++ b/spec/classes/mistral_wsgi_apache_spec.rb @@ -0,0 +1,124 @@ +require 'spec_helper' + +describe 'mistral::wsgi::apache' do + + let :global_facts do + @default_facts.merge({ + :processorcount => 42, + :concat_basedir => '/var/lib/puppet/concat', + :fqdn => 'some.host.tld', + }) + end + + shared_examples_for 'apache serving mistral with mod_wsgi' do + it { is_expected.to contain_service('httpd').with_name(platform_parameters[:httpd_service_name]) } + it { is_expected.to contain_class('mistral::params') } + it { is_expected.to contain_class('apache') } + it { is_expected.to contain_class('apache::mod::wsgi') } + + describe 'with default parameters' do + + it { is_expected.to contain_file("#{platform_parameters[:wsgi_script_path]}").with( + 'ensure' => 'directory', + 'owner' => 'mistral', + 'group' => 'mistral', + 'require' => 'Package[httpd]' + )} + + + it { is_expected.to contain_file('mistral_wsgi').with( + 'ensure' => 'file', + 'path' => "#{platform_parameters[:wsgi_script_path]}/app", + 'source' => platform_parameters[:wsgi_script_source], + 'owner' => 'mistral', + 'group' => 'mistral', + 'mode' => '0644' + )} + it { is_expected.to contain_file('mistral_wsgi').that_requires("File[#{platform_parameters[:wsgi_script_path]}]") } + + it { is_expected.to contain_apache__vhost('mistral_wsgi').with( + 'servername' => 'some.host.tld', + 'ip' => nil, + 'port' => '8989', + 'docroot' => "#{platform_parameters[:wsgi_script_path]}", + 'docroot_owner' => 'mistral', + 'docroot_group' => 'mistral', + 'ssl' => 'true', + 'wsgi_daemon_process' => 'mistral', + 'wsgi_process_group' => 'mistral', + 'wsgi_script_aliases' => { '/' => "#{platform_parameters[:wsgi_script_path]}/app" }, + 'require' => 'File[mistral_wsgi]' + )} + it { is_expected.to contain_file("#{platform_parameters[:httpd_ports_file]}") } + end + + describe 'when overriding parameters using different ports' do + let :params do + { + :servername => 'dummy.host', + :bind_host => '10.42.51.1', + :port => 12345, + :ssl => false, + :workers => 37, + } + end + + it { is_expected.to contain_apache__vhost('mistral_wsgi').with( + 'servername' => 'dummy.host', + 'ip' => '10.42.51.1', + 'port' => '12345', + 'docroot' => "#{platform_parameters[:wsgi_script_path]}", + 'docroot_owner' => 'mistral', + 'docroot_group' => 'mistral', + 'ssl' => 'false', + 'wsgi_daemon_process' => 'mistral', + 'wsgi_process_group' => 'mistral', + 'wsgi_script_aliases' => { '/' => "#{platform_parameters[:wsgi_script_path]}/app" }, + 'require' => 'File[mistral_wsgi]' + )} + + it { is_expected.to contain_file("#{platform_parameters[:httpd_ports_file]}") } + end + end + + context 'on RedHat platforms' do + let :facts do + global_facts.merge({ + :osfamily => 'RedHat', + :operatingsystemrelease => '7.0' + }) + end + + let :platform_parameters do + { + :httpd_service_name => 'httpd', + :httpd_ports_file => '/etc/httpd/conf/ports.conf', + :wsgi_script_path => '/var/www/cgi-bin/mistral', + :wsgi_script_source => '/usr/lib/python2.7/site-packages/mistral/api/wsgi.py' + } + end + + it_configures 'apache serving mistral with mod_wsgi' + end + + context 'on Debian platforms' do + let :facts do + global_facts.merge({ + :osfamily => 'Debian', + :operatingsystem => 'Debian', + :operatingsystemrelease => '7.0' + }) + end + + let :platform_parameters do + { + :httpd_service_name => 'apache2', + :httpd_ports_file => '/etc/apache2/ports.conf', + :wsgi_script_path => '/usr/lib/cgi-bin/mistral', + :wsgi_script_source => '/usr/share/mistral-common/wsgi.py' + } + end + + it_configures 'apache serving mistral with mod_wsgi' + end +end