From 9d2a0fb19836dda6a2c3b7ef7701064abe21c4c5 Mon Sep 17 00:00:00 2001 From: ZhongShengping Date: Tue, 15 Aug 2017 10:16:22 +0800 Subject: [PATCH] Add WSGI support for `watcher-api' Adds support to run watcher-api with mod-wsgi[0]. [0]https://review.openstack.org/#/c/450740/ Change-Id: Ie6bbde98bee19ccc4a938bba28b66b759ccd3307 Implements: blueprint wsgi-support --- manifests/api.pp | 51 +++++-- manifests/params.pp | 4 + manifests/wsgi/apache.pp | 132 ++++++++++++++++++ .../add_wsgi_support-98135d2375f323e8.yaml | 3 + ...er_spec.rb => watcher_wsgi_apache_spec.rb} | 9 +- spec/classes/watcher_api_spec.rb | 44 ++++++ spec/classes/watcher_wsgi_apache_spec.rb | 93 ++++++++++++ 7 files changed, 322 insertions(+), 14 deletions(-) create mode 100644 manifests/wsgi/apache.pp create mode 100644 releasenotes/notes/add_wsgi_support-98135d2375f323e8.yaml rename spec/acceptance/{watcher_spec.rb => watcher_wsgi_apache_spec.rb} (90%) create mode 100644 spec/classes/watcher_wsgi_apache_spec.rb diff --git a/manifests/api.pp b/manifests/api.pp index a9816f6..8c86f21 100644 --- a/manifests/api.pp +++ b/manifests/api.pp @@ -101,6 +101,15 @@ # (Optional) Authentication type to load. # Defaults to undef # +# [*service_name*] +# (optional) Name of the service that will be providing the +# server functionality of watcher-api. +# If the value is 'httpd', this means watcher-api will be a web +# service, and you must use another class to configure that +# web service. For example, use class { 'watcher::wsgi::apache'...} +# to make watcher-api be a web app using apache mod_wsgi. +# Defaults to '$::watcher::params::api_service_name' +# # === Watcher API service validation # # [*validation_options*] @@ -149,12 +158,12 @@ class watcher::api ( $watcher_client_keyfile = $::os_service_default, $watcher_client_auth_type = 'password', $validation_options = {}, + $service_name = $::watcher::params::api_service_name, $create_db_schema = false, $upgrade_db = false, $auth_strategy = 'keystone', -) { +) inherits watcher::params { - include ::watcher::params include ::watcher::policy include ::watcher::deps @@ -186,16 +195,34 @@ class watcher::api ( include ::watcher::db::upgrade } - # NOTE(danpawlik) Watcher doesn't support db_sync command. - service { 'watcher-api': - ensure => $service_ensure, - name => $::watcher::params::api_service_name, - enable => $enabled, - hasstatus => true, - hasrestart => true, - tag => [ 'watcher-service', - 'watcher-db-manage-create_schema', - 'watcher-db-manage-upgrade'], + if $service_name == $::watcher::params::api_service_name { + # NOTE(danpawlik) Watcher doesn't support db_sync command. + service { 'watcher-api': + ensure => $service_ensure, + name => $::watcher::params::api_service_name, + enable => $enabled, + hasstatus => true, + hasrestart => true, + tag => [ 'watcher-service', + 'watcher-db-manage-create_schema', + 'watcher-db-manage-upgrade'], + } + } elsif $service_name == 'httpd' { + include ::apache::params + service { 'watcher-api': + ensure => 'stopped', + name => $::watcher::params::api_service_name, + enable => false, + tag => [ 'watcher-service', + 'watcher-db-manage-create_schema', + 'watcher-db-manage-upgrade'], + } + + # we need to make sure watcher-api/eventlet is stopped before trying to start apache + Service['watcher-api'] -> Service[$service_name] + } else { + fail("Invalid service_name. Either watcher/openstack-watcher-api for running \ +as a standalone service, or httpd for being run by a httpd server") } if $enabled { diff --git a/manifests/params.pp b/manifests/params.pp index 829dba1..d312d81 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -13,6 +13,8 @@ class watcher::params { $decision_engine_package_name = 'openstack-watcher-decision-engine' $decision_engine_service_name = 'openstack-watcher-decision-engine' $client_package_name = undef + $watcher_wsgi_script_source = '/usr/lib/python2.7/site-packages/watcher/api/app.wsgi' + $watcher_wsgi_script_path = '/var/www/cgi-bin/watcher' } 'Debian': { $api_service_name = 'watcher-api' @@ -23,6 +25,8 @@ class watcher::params { $decision_engine_package_name = 'watcher-decision-engine' $decision_engine_service_name = 'watcher-decision-engine' $client_package_name = 'python-watcherclient' + $watcher_wsgi_script_source = '/usr/lib/python2.7/dist-packages/watcher/api/app.wsgi' + $watcher_wsgi_script_path = '/usr/lib/cgi-bin/watcher' } default: { fail("Unsupported osfamily: ${::osfamily} operatingsystem") diff --git a/manifests/wsgi/apache.pp b/manifests/wsgi/apache.pp new file mode 100644 index 0000000..bd154f1 --- /dev/null +++ b/manifests/wsgi/apache.pp @@ -0,0 +1,132 @@ +# +# 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 Cinder API with apache mod_wsgi in place of watcher service. +# +# Serving Cinder 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 watcher service. +# +# == Parameters +# +# [*servername*] +# The servername for the virtualhost. +# Optional. Defaults to $::fqdn +# +# [*port*] +# The port. +# Optional. Defaults to 9322 +# +# [*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 $::os_workers +# +# [*wsgi_process_display_name*] +# (optional) Name of the WSGI process display-name. +# Defaults to undef +# +# [*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['watcher'] +# +# == Examples +# +# include apache +# +# class { 'watcher::wsgi::apache': } +# +class watcher::wsgi::apache ( + $servername = $::fqdn, + $port = 9322, + $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, + $wsgi_process_display_name = undef, + $threads = $::os_workers, + $priority = '10', +) { + + include ::watcher::deps + include ::watcher::params + include ::apache + include ::apache::mod::wsgi + if $ssl { + include ::apache::mod::ssl + } + + ::openstacklib::wsgi::apache { 'watcher_wsgi': + bind_host => $bind_host, + bind_port => $port, + group => 'watcher', + 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 => 'watcher', + workers => $workers, + wsgi_daemon_process => 'watcher', + wsgi_process_display_name => $wsgi_process_display_name, + wsgi_process_group => 'watcher', + wsgi_script_dir => $::watcher::params::watcher_wsgi_script_path, + wsgi_script_file => 'app', + wsgi_script_source => $::watcher::params::watcher_wsgi_script_source, + require => Anchor['watcher::install::end'], + } +} diff --git a/releasenotes/notes/add_wsgi_support-98135d2375f323e8.yaml b/releasenotes/notes/add_wsgi_support-98135d2375f323e8.yaml new file mode 100644 index 0000000..ec97e0a --- /dev/null +++ b/releasenotes/notes/add_wsgi_support-98135d2375f323e8.yaml @@ -0,0 +1,3 @@ +--- +features: + - Add support to run watcher-api with mod-wsgi. diff --git a/spec/acceptance/watcher_spec.rb b/spec/acceptance/watcher_wsgi_apache_spec.rb similarity index 90% rename from spec/acceptance/watcher_spec.rb rename to spec/acceptance/watcher_wsgi_apache_spec.rb index e7708ab..01c291a 100644 --- a/spec/acceptance/watcher_spec.rb +++ b/spec/acceptance/watcher_wsgi_apache_spec.rb @@ -49,9 +49,14 @@ describe 'basic watcher' do default_transport_url => 'rabbit://watcher:my_secret@127.0.0.1:5672/', } class { '::watcher::api': + service_name => 'httpd', watcher_client_password => 'a_big_secret', - create_db_schema => true, - upgrade_db => true, + create_db_schema => true, + upgrade_db => true, + } + include ::apache + class { '::watcher::wsgi::apache': + ssl => false, } class { '::watcher::applier': applier_workers => '2', diff --git a/spec/classes/watcher_api_spec.rb b/spec/classes/watcher_api_spec.rb index 40ddcef..90b669f 100644 --- a/spec/classes/watcher_api_spec.rb +++ b/spec/classes/watcher_api_spec.rb @@ -135,6 +135,50 @@ describe 'watcher::api' do is_expected.to contain_watcher_config('watcher_clients_auth/keyfile').with_value( params[:watcher_client_keyfile] ) end end + + context 'when running watcher-api in wsgi' do + before do + params.merge!({ :service_name => 'httpd' }) + end + + let :pre_condition do + "include ::apache + include ::watcher::db + class { 'watcher': } + class { '::watcher::keystone::authtoken': + password => 'a_big_secret', + }" + end + + it 'configures watcher-api service with Apache' do + is_expected.to contain_service('watcher-api').with( + :ensure => 'stopped', + :name => platform_params[:api_service_name], + :enable => false, + :tag => ['watcher-service', + 'watcher-db-manage-create_schema', + 'watcher-db-manage-upgrade'], + ) + end + end + + context 'when service_name is not valid' do + before do + params.merge!({ :service_name => 'foobar' }) + end + + let :pre_condition do + "include ::apache + include ::watcher::db + class { 'watcher': } + class { '::watcher::keystone::authtoken': + password => 'a_big_secret', + }" + end + + it_raises 'a Puppet::Error', /Invalid service_name/ + end + end on_supported_os({ diff --git a/spec/classes/watcher_wsgi_apache_spec.rb b/spec/classes/watcher_wsgi_apache_spec.rb new file mode 100644 index 0000000..74d5fed --- /dev/null +++ b/spec/classes/watcher_wsgi_apache_spec.rb @@ -0,0 +1,93 @@ +require 'spec_helper' + +describe 'watcher::wsgi::apache' do + + shared_examples_for 'apache serving watcher with mod_wsgi' do + context 'with default parameters' do + it { is_expected.to contain_class('watcher::params') } + it { is_expected.to contain_class('apache') } + it { is_expected.to contain_class('apache::mod::wsgi') } + it { is_expected.to contain_class('apache::mod::ssl') } + it { is_expected.to contain_openstacklib__wsgi__apache('watcher_wsgi').with( + :bind_port => 9322, + :group => 'watcher', + :path => '/', + :servername => facts[:fqdn], + :ssl => true, + :threads => facts[:os_workers], + :user => 'watcher', + :workers => 1, + :wsgi_daemon_process => 'watcher', + :wsgi_process_group => 'watcher', + :wsgi_script_dir => platform_params[:wsgi_script_path], + :wsgi_script_file => 'app', + :wsgi_script_source => platform_params[:wsgi_script_source], + )} + end + + context 'when overriding parameters using different ports' do + let :params do + { + :servername => 'dummy.host', + :bind_host => '10.42.51.1', + :port => 12345, + :ssl => false, + :wsgi_process_display_name => 'watcher', + :workers => 37, + } + end + it { is_expected.to contain_class('watcher::params') } + it { is_expected.to contain_class('apache') } + it { is_expected.to contain_class('apache::mod::wsgi') } + it { is_expected.to_not contain_class('apache::mod::ssl') } + it { is_expected.to contain_openstacklib__wsgi__apache('watcher_wsgi').with( + :bind_host => '10.42.51.1', + :bind_port => 12345, + :group => 'watcher', + :path => '/', + :servername => 'dummy.host', + :ssl => false, + :threads => facts[:os_workers], + :user => 'watcher', + :workers => 37, + :wsgi_daemon_process => 'watcher', + :wsgi_process_display_name => 'watcher', + :wsgi_process_group => 'watcher', + :wsgi_script_dir => platform_params[:wsgi_script_path], + :wsgi_script_file => 'app', + :wsgi_script_source => platform_params[:wsgi_script_source], + )} + end + end + + on_supported_os({ + :supported_os => OSDefaults.get_supported_os + }).each do |os,facts| + context "on #{os}" do + let (:facts) do + facts.merge!(OSDefaults.get_facts({ + :os_workers => 42, + :concat_basedir => '/var/lib/puppet/concat', + :fqdn => 'some.host.tld', + })) + end + + let(:platform_params) do + case facts[:osfamily] + when 'Debian' + { + :wsgi_script_path => '/usr/lib/cgi-bin/watcher', + :wsgi_script_source => '/usr/lib/python2.7/dist-packages/watcher/api/app.wsgi' + } + when 'RedHat' + { + :wsgi_script_path => '/var/www/cgi-bin/watcher', + :wsgi_script_source => '/usr/lib/python2.7/site-packages/watcher/api/app.wsgi' + } + end + end + + it_behaves_like 'apache serving watcher with mod_wsgi' + end + end +end