From 4a5a1fd46dd6d83b45b6dd24eb5b30efa93ff9fa Mon Sep 17 00:00:00 2001 From: Thomas Herve Date: Tue, 21 Feb 2017 10:54:24 -0500 Subject: [PATCH] Allow deployment of Zaqar WSGI with Apache Change-Id: If91894176abdfae2820c2e13723c87f5ef8fb153 --- manifests/params.pp | 12 +- manifests/server.pp | 50 +++++-- manifests/wsgi/apache.pp | 133 ++++++++++++++++++ metadata.json | 4 + .../notes/apache_wsgi-4e0a5968cea688c1.yaml | 3 + spec/acceptance/zaqar_mongo_spec.rb | 10 +- spec/acceptance/zaqar_swift_spec.rb | 10 +- spec/classes/zaqar_wsgi_apache_spec.rb | 127 +++++++++++++++++ 8 files changed, 331 insertions(+), 18 deletions(-) create mode 100644 manifests/wsgi/apache.pp create mode 100644 releasenotes/notes/apache_wsgi-4e0a5968cea688c1.yaml create mode 100644 spec/classes/zaqar_wsgi_apache_spec.rb diff --git a/manifests/params.pp b/manifests/params.pp index b9d867c..d4ab958 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -9,12 +9,16 @@ class zaqar::params { case $::osfamily { 'RedHat': { - $package_name = 'openstack-zaqar' - $service_name = 'openstack-zaqar' + $package_name = 'openstack-zaqar' + $service_name = 'openstack-zaqar' + $zaqar_wsgi_script_source = '/usr/lib/python2.7/site-packages/zaqar/transport/wsgi/app.py' + $zaqar_wsgi_script_path = '/var/www/cgi-bin/zaqar' } 'Debian': { - $package_name = 'zaqar-server' - $service_name = 'zaqar-server' + $package_name = 'zaqar-server' + $service_name = 'zaqar-server' + $zaqar_wsgi_script_source = '/usr/lib/python2.7/dist-packages/zaqar/transport/wsgi/app.py' + $zaqar_wsgi_script_path = '/usr/lib/cgi-bin/zaqar' } default: { fail("Unsupported osfamily: ${::osfamily} operatingsystem: \ diff --git a/manifests/server.pp b/manifests/server.pp index f71256d..d734b4e 100644 --- a/manifests/server.pp +++ b/manifests/server.pp @@ -9,30 +9,60 @@ # [*manage_service*] # (Optional) Whether the service is managed by this puppet class. # Defaults to true. +# +# [*service_name*] +# (optional) Name of the service that will be providing the +# server functionality of zaqar-server +# If the value is 'httpd', this means zaqar-server will be a web +# service, and you must use another class to configure that +# web service. For example, use class { 'zaqar::wsgi::apache'...} +# to make zaqar-server be a web app using apache mod_wsgi. +# Defaults to '$::zaqar::params::service_name' + # class zaqar::server( $manage_service = true, $enabled = true, -) { + $service_name = $::zaqar::params::service_name, +) inherits zaqar::params { include ::zaqar include ::zaqar::deps include ::zaqar::params - if $manage_service { - if $enabled { - $service_ensure = 'running' - } else { - $service_ensure = 'stopped' + if $enabled { + if $manage_service { + $ensure = 'running' + } + } else { + if $manage_service { + $ensure = 'stopped' } } - if $manage_service { + if $service_name == $::zaqar::params::service_name { service { $::zaqar::params::service_name: - ensure => $service_ensure, - enable => $enabled, - tag => 'zaqar-service' + ensure => $ensure, + name => $::zaqar::params::service_name, + enable => $enabled, + hasstatus => true, + tag => 'zaqar-service', } + + } elsif $service_name == 'httpd' { + include ::apache::params + service { $::zaqar::params::service_name: + ensure => 'stopped', + name => $::zaqar::params::service_name, + enable => false, + tag => ['zaqar-service'], + } + + # we need to make sure zaqar-server is stopped before trying to start apache + Service[$::zaqar::params::service_name] -> Service[$service_name] + } else { + fail("Invalid service_name. Either zaqar-server/openstack-zaqar for \ +running as a standalone service, or httpd for being run by a httpd server") } } diff --git a/manifests/wsgi/apache.pp b/manifests/wsgi/apache.pp new file mode 100644 index 0000000..a65215e --- /dev/null +++ b/manifests/wsgi/apache.pp @@ -0,0 +1,133 @@ +# +# 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 zaqar 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 zaqar service. +# +# == Parameters +# +# [*servername*] +# The servername for the virtualhost. +# Optional. Defaults to $::fqdn +# +# [*port*] +# The port. +# Optional. Defaults to 8888 +# +# [*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['zaqar'] +# +# == Examples +# +# include apache +# +# class { 'zaqar::wsgi::apache': } +# +class zaqar::wsgi::apache ( + $servername = $::fqdn, + $port = 8888, + $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 ::zaqar::deps + include ::zaqar::params + include ::apache + include ::apache::mod::wsgi + if $ssl { + include ::apache::mod::ssl + } + + ::openstacklib::wsgi::apache { 'zaqar_wsgi': + bind_host => $bind_host, + bind_port => $port, + group => 'zaqar', + 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 => 'zaqar', + workers => $workers, + wsgi_daemon_process => 'zaqar-server', + wsgi_process_display_name => $wsgi_process_display_name, + wsgi_process_group => 'zaqar-server', + wsgi_script_dir => $::zaqar::params::zaqar_wsgi_script_path, + wsgi_script_file => 'zaqar-server', + wsgi_script_source => $::zaqar::params::zaqar_wsgi_script_source, + require => Anchor['zaqar::install::end'], + vhost_custom_fragment => 'WSGICallableObject app', + } +} diff --git a/metadata.json b/metadata.json index a764cb9..132adaa 100644 --- a/metadata.json +++ b/metadata.json @@ -66,6 +66,10 @@ { "name": "openstack/oslo", "version_requirement": ">=10.3.0 <11.0.0" + }, + { + "name": "puppetlabs/apache", + "version_requirement": ">=1.0.0 <2.0.0" } ] } diff --git a/releasenotes/notes/apache_wsgi-4e0a5968cea688c1.yaml b/releasenotes/notes/apache_wsgi-4e0a5968cea688c1.yaml new file mode 100644 index 0000000..690923c --- /dev/null +++ b/releasenotes/notes/apache_wsgi-4e0a5968cea688c1.yaml @@ -0,0 +1,3 @@ +--- +features: + - Add support for running Zaqar API with Apache. diff --git a/spec/acceptance/zaqar_mongo_spec.rb b/spec/acceptance/zaqar_mongo_spec.rb index 438da8d..9aed2e0 100644 --- a/spec/acceptance/zaqar_mongo_spec.rb +++ b/spec/acceptance/zaqar_mongo_spec.rb @@ -49,7 +49,13 @@ describe 'basic zaqar' do class {'::zaqar': unreliable => true, } - include ::zaqar::server + class {'::zaqar::server': + service_name => 'httpd', + } + include ::apache + class { '::zaqar::wsgi::apache': + ssl => false, + } # run a second instance using websockets, the Debian system does # not support the use of services to run a second instance. if $::osfamily == 'RedHat' { @@ -66,7 +72,7 @@ describe 'basic zaqar' do end describe port(8888) do - it { is_expected.to be_listening.with('tcp') } + it { is_expected.to be_listening } end end diff --git a/spec/acceptance/zaqar_swift_spec.rb b/spec/acceptance/zaqar_swift_spec.rb index b302f6a..cb57ed7 100644 --- a/spec/acceptance/zaqar_swift_spec.rb +++ b/spec/acceptance/zaqar_swift_spec.rb @@ -36,7 +36,13 @@ describe 'swift zaqar' do class {'::zaqar': unreliable => true, } - include ::zaqar::server + class {'::zaqar::server': + service_name => 'httpd', + } + include ::apache + class { '::zaqar::wsgi::apache': + ssl => false, + } # run a second instance using websockets, the Debian system does # not support the use of services to run a second instance. if $::osfamily == 'RedHat' { @@ -53,7 +59,7 @@ describe 'swift zaqar' do end describe port(8888) do - it { is_expected.to be_listening.with('tcp') } + it { is_expected.to be_listening } end end diff --git a/spec/classes/zaqar_wsgi_apache_spec.rb b/spec/classes/zaqar_wsgi_apache_spec.rb new file mode 100644 index 0000000..8c2e6f5 --- /dev/null +++ b/spec/classes/zaqar_wsgi_apache_spec.rb @@ -0,0 +1,127 @@ +require 'spec_helper' + +describe 'zaqar::wsgi::apache' do + + shared_examples_for 'apache serving zaqar with mod_wsgi' do + it { is_expected.to contain_service('httpd').with_name(platform_params[:httpd_service_name]) } + it { is_expected.to contain_class('zaqar::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_params[:wsgi_script_path]}").with( + 'ensure' => 'directory', + 'owner' => 'zaqar', + 'group' => 'zaqar', + 'require' => 'Package[httpd]' + )} + + + it { is_expected.to contain_file('zaqar_wsgi').with( + 'ensure' => 'file', + 'path' => "#{platform_params[:wsgi_script_path]}/zaqar-server", + 'source' => platform_params[:wsgi_script_source], + 'owner' => 'zaqar', + 'group' => 'zaqar', + 'mode' => '0644' + )} + it { is_expected.to contain_file('zaqar_wsgi').that_requires("File[#{platform_params[:wsgi_script_path]}]") } + + it { is_expected.to contain_apache__vhost('zaqar_wsgi').with( + 'servername' => 'some.host.tld', + 'ip' => nil, + 'port' => '8888', + 'docroot' => "#{platform_params[:wsgi_script_path]}", + 'docroot_owner' => 'zaqar', + 'docroot_group' => 'zaqar', + 'ssl' => 'true', + 'wsgi_daemon_process' => 'zaqar-server', + 'wsgi_daemon_process_options' => { + 'user' => 'zaqar', + 'group' => 'zaqar', + 'processes' => 1, + 'threads' => '42', + 'display-name' => 'zaqar_wsgi', + }, + 'wsgi_process_group' => 'zaqar-server', + 'wsgi_script_aliases' => { '/' => "#{platform_params[:wsgi_script_path]}/zaqar-server" }, + 'require' => 'File[zaqar_wsgi]', + 'custom_fragment' => 'WSGICallableObject app' + )} + it { is_expected.to contain_concat("#{platform_params[: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, + :wsgi_process_display_name => 'zaqar-server', + :workers => 37, + } + end + + it { is_expected.to contain_apache__vhost('zaqar_wsgi').with( + 'servername' => 'dummy.host', + 'ip' => '10.42.51.1', + 'port' => '12345', + 'docroot' => "#{platform_params[:wsgi_script_path]}", + 'docroot_owner' => 'zaqar', + 'docroot_group' => 'zaqar', + 'ssl' => 'false', + 'wsgi_daemon_process' => 'zaqar-server', + 'wsgi_daemon_process_options' => { + 'user' => 'zaqar', + 'group' => 'zaqar', + 'processes' => '37', + 'threads' => '42', + 'display-name' => 'zaqar-server', + }, + 'wsgi_process_group' => 'zaqar-server', + 'wsgi_script_aliases' => { '/' => "#{platform_params[:wsgi_script_path]}/zaqar-server" }, + 'require' => 'File[zaqar_wsgi]', + 'custom_fragment' => 'WSGICallableObject app' + )} + + it { is_expected.to contain_concat("#{platform_params[:httpd_ports_file]}") } + 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' + { + :httpd_service_name => 'apache2', + :httpd_ports_file => '/etc/apache2/ports.conf', + :wsgi_script_path => '/usr/lib/cgi-bin/zaqar', + :wsgi_script_source => '/usr/lib/python2.7/dist-packages/zaqar/transport/wsgi/app.py' + } + when 'RedHat' + { + :httpd_service_name => 'httpd', + :httpd_ports_file => '/etc/httpd/conf/ports.conf', + :wsgi_script_path => '/var/www/cgi-bin/zaqar', + :wsgi_script_source => '/usr/lib/python2.7/site-packages/zaqar/transport/wsgi/app.py' + } + end + end + + it_behaves_like 'apache serving zaqar with mod_wsgi' + end + end +end