Add Sahara API WSGI support

Adds support for running the Sahara API under WSGI with Apache.
Ubuntu Bionic has removed support for eventlet which means you must
use WSGI now. See bug [1] [2]

[1] https://bugs.launchpad.net/ubuntu/+source/sahara/+bug/1786214
[2] https://bugs.launchpad.net/puppet-sahara/+bug/1765453

Closes-Bug: 1765453
Change-Id: If035283b5880a49f34c5552fd2146d9653bd1e61
This commit is contained in:
Tobias Urdin 2018-08-09 16:44:08 +02:00 committed by Tobias Urdin
parent a25bf8d4b3
commit 3765a48f0c
14 changed files with 598 additions and 125 deletions

View File

@ -15,22 +15,26 @@ class sahara::params {
case $::osfamily {
'RedHat': {
$common_package_name = 'openstack-sahara-common'
$all_package_name = 'openstack-sahara'
$api_package_name = 'openstack-sahara-api'
$engine_package_name = 'openstack-sahara-engine'
$all_service_name = 'openstack-sahara-all'
$api_service_name = 'openstack-sahara-api'
$engine_service_name = 'openstack-sahara-engine'
$common_package_name = 'openstack-sahara-common'
$all_package_name = 'openstack-sahara'
$api_package_name = 'openstack-sahara-api'
$engine_package_name = 'openstack-sahara-engine'
$all_service_name = 'openstack-sahara-all'
$api_service_name = 'openstack-sahara-api'
$engine_service_name = 'openstack-sahara-engine'
$sahara_wsgi_script_path = '/var/www/cgi-bin/sahara'
$sahara_wsgi_script_source = '/usr/bin/sahara-wsgi-api'
}
'Debian': {
$common_package_name = 'sahara-common'
$all_package_name = 'sahara'
$api_package_name = 'sahara-api'
$engine_package_name = 'sahara-engine'
$all_service_name = 'sahara'
$api_service_name = 'sahara-api'
$engine_service_name = 'sahara-engine'
$common_package_name = 'sahara-common'
$all_package_name = 'sahara'
$api_package_name = 'sahara-api'
$engine_package_name = 'sahara-engine'
$all_service_name = 'sahara'
$api_service_name = 'sahara-api'
$engine_service_name = 'sahara-engine'
$sahara_wsgi_script_path = '/usr/lib/cgi-bin/sahara'
$sahara_wsgi_script_source = '/usr/bin/sahara-wsgi-api'
}
default: {
fail("Unsupported osfamily: ${::osfamily} operatingsystem: ${::operatingsystem}")

View File

@ -48,5 +48,4 @@ class sahara::service::all (
hasrestart => true,
tag => 'sahara-service',
}
}

View File

@ -21,16 +21,29 @@
# (Optional) Ensure state for package.
# Defaults to 'present'
#
# [*service_name*]
# (Optional) Name of the service that will be providing the
# server functionality of sahara-api.
# If the value is 'httpd', this means sahara-api will be a web
# service, and you must use another class to configure that
# web service. For example, use class { 'sahara::wsgi::apache'...}
# to make sahara-api be a web app using apache mod_wsgi.
# Defaults to '$::sahara::params::api_service_name'
#
class sahara::service::api (
$api_workers = $::os_workers,
$enabled = true,
$manage_service = true,
$package_ensure = 'present',
) {
$service_name = $::sahara::params::api_service_name,
) inherits ::sahara::params {
include ::sahara::deps
include ::sahara::policy
include ::sahara::params
if $::operatingsystem == 'Ubuntu' and $service_name == $::sahara::params::api_service_name {
fail('The Sahara API must be run with WSGI on Ubuntu')
}
package { 'sahara-api':
ensure => $package_ensure,
@ -50,13 +63,27 @@ class sahara::service::api (
}
}
service { 'sahara-api':
ensure => $service_ensure,
name => $::sahara::params::api_service_name,
enable => $enabled,
hasstatus => true,
hasrestart => true,
tag => 'sahara-service',
}
if $service_name == $::sahara::params::api_service_name {
service { 'sahara-api':
ensure => $service_ensure,
name => $::sahara::params::api_service_name,
enable => $enabled,
hasstatus => true,
hasrestart => true,
tag => 'sahara-service',
}
} elsif $service_name == 'httpd' {
include ::apache::params
if $::operatingsystem != 'Ubuntu' {
service { 'sahara-api':
ensure => 'stopped',
name => $::sahara::params::api_service_name,
enable => false,
tag => 'sahara-service',
}
Service['sahara-api'] -> Service[$service_name]
}
Service<| title == 'httpd' |> { tag +> 'sahara-service' }
}
}

View File

@ -47,5 +47,4 @@ class sahara::service::engine (
hasrestart => true,
tag => 'sahara-service',
}
}

156
manifests/wsgi/apache.pp Normal file
View File

@ -0,0 +1,156 @@
#
# Copyright (C) 2018 Binero
#
# Author: Tobias Urdin <tobias.urdin@binero.se>
#
# 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: sahara::wsgi::apache
#
# Install Sahara API under apache with mod_wsgi.
#
# == Parameters:
#
# [*servername*]
# (Optional) The servername for the virtualhost.
# Defaults to $::fqdn
#
# [*port*]
# (Optional) The port.
# Defaults to 8386
#
# [*bind_host*]
# (Optional) The host/ip address Apache will listen on.
# Defaults to undef (listen on all ip addresses).
#
# [*path*]
# (Optional) The prefix for the endpoint.
# Defaults to '/'
#
# [*ssl*]
# (Optional) Use ssl.
# Defaults to false
#
# [*workers*]
# (Optional) Number of WSGI workers to spawn.
# Defaults to $::os_workers
#
# [*priority*]
# (Optional) The priority for the vhost.
# Defaults to '10'
#
# [*threads*]
# (Optional) The number of threads for the vhost.
# Defaults to 1
#
# [*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*]
# (Optional) apache::vhost ssl parameters.
# Default to apache::vhost 'ssl_*' defaults
#
# [*access_log_file*]
# (Optional) The log file name for the virtualhost.
# Defaults to false
#
# [*access_log_format*]
# (Optional) The log format for the virtualhost.
# Defaults to false
#
# [*error_log_file*]
# (Optional) The error log file name for the virtualhost.
# Defaults to undef
#
# [*custom_wsgi_process_options*]
# (Optional) gives you the oportunity to add custom process options or to
# overwrite the default options for the WSGI main process.
# eg. to use a virtual python environment for the WSGI process
# you could set it to:
# { python-path => '/my/python/virtualenv' }
# Defaults to {}
#
# == Example:
#
# include apache
# class { 'sahara::wsgi::apache': }
#
class sahara::wsgi::apache (
$servername = $::fqdn,
$port = 8386,
$bind_host = undef,
$path = '/',
$ssl = false,
$workers = $::os_workers,
$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 = 1,
$priority = '10',
$access_log_file = false,
$access_log_format = false,
$error_log_file = undef,
$custom_wsgi_process_options = {},
) {
include ::sahara::deps
include ::sahara::params
include ::apache
include ::apache::mod::wsgi
if $ssl {
include ::apache::mod::ssl
}
::openstacklib::wsgi::apache { 'sahara_wsgi':
bind_host => $bind_host,
bind_port => $port,
group => 'sahara',
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 => 'sahara',
workers => $workers,
wsgi_daemon_process => 'sahara',
wsgi_process_display_name => $wsgi_process_display_name,
wsgi_process_group => 'sahara',
wsgi_script_dir => $::sahara::params::sahara_wsgi_script_path,
wsgi_script_file => 'app',
wsgi_script_source => $::sahara::params::sahara_wsgi_script_source,
access_log_file => $access_log_file,
access_log_format => $access_log_format,
error_log_file => $error_log_file,
custom_wsgi_process_options => $custom_wsgi_process_options,
require => Anchor['sahara::install::end'],
}
}

View File

@ -0,0 +1,9 @@
---
features:
- |
Added new class sahara::wsgi::apache, you can now run the API under Apache
with mod_wsgi.
other:
- |
Added support for running Sahara API in WSGI, like on Ubuntu where eventlet
is not supported.

View File

@ -1,9 +1,58 @@
require 'spec_helper'
describe 'sahara::service::all' do
shared_examples 'sahara::service::all' do
context 'with default params' do
it {
should contain_class('sahara::deps')
should contain_class('sahara::policy')
should contain_class('sahara::params')
}
it { should contain_package('sahara-all').with(
:ensure => 'present',
:name => platform_params[:all_package_name],
:tag => ['openstack', 'sahara-package'],
)}
it { should contain_service('sahara-all').with(
:ensure => 'running',
:name => platform_params[:all_service_name],
:enable => true,
:hasstatus => true,
:hasrestart => true,
:tag => 'sahara-service',
)}
end
context 'with custom params' do
let :params do
{
:enabled => false,
:manage_service => false,
:package_ensure => 'absent',
}
end
it { should contain_package('sahara-all').with(
:ensure => 'absent',
:name => platform_params[:all_package_name],
:tag => ['openstack', 'sahara-package'],
)}
it { should contain_service('sahara-all').with(
:ensure => nil,
:name => platform_params[:all_service_name],
:enable => false,
:hasstatus => true,
:hasrestart => true,
:tag => 'sahara-service',
)}
end
end
on_supported_os({
:supported_os => OSDefaults.get_supported_os
:supported_os => OSDefaults.get_supported_os
}).each do |os,facts|
context "on #{os}" do
let (:facts) do
@ -12,18 +61,22 @@ describe 'sahara::service::all' do
case facts[:osfamily]
when 'Debian'
platform_params = {
:name => 'sahara-all',
:package_name => 'sahara',
:service_name => 'sahara' }
let (:platform_params) do
{
:all_package_name => 'sahara',
:all_service_name => 'sahara'
}
end
when 'RedHat'
platform_params = {
:name => 'sahara-all',
:package_name => 'openstack-sahara',
:service_name => 'openstack-sahara-all' }
let (:platform_params) do
{
:all_package_name => 'openstack-sahara',
:all_service_name => 'openstack-sahara-all'
}
end
end
it_behaves_like 'generic sahara service', platform_params
it_behaves_like 'sahara::service::all'
end
end

View File

@ -2,26 +2,119 @@ require 'spec_helper'
describe 'sahara::service::api' do
shared_examples_for 'sahara-api' do
shared_examples 'sahara-api eventlet' do
context 'with default params' do
context 'default params' do
it { is_expected.to contain_sahara_config('DEFAULT/api_workers').with_value(2) }
it {
should contain_class('sahara::deps')
should contain_class('sahara::policy')
should contain_class('sahara::params')
}
it { should contain_package('sahara-api').with(
:ensure => 'present',
:name => platform_params[:api_package_name],
:tag => ['openstack', 'sahara-package'],
)}
it { should contain_sahara_config('DEFAULT/api_workers').with_value(2) }
it { should contain_service('sahara-api').with(
:ensure => 'running',
:name => platform_params[:api_service_name],
:enable => true,
:hasstatus => true,
:hasrestart => true,
:tag => 'sahara-service',
)}
end
context 'passing params' do
context 'with custom params' do
let :params do
{
:api_workers => '4',
}
{
:api_workers => '4',
:enabled => false,
:package_ensure => 'absent',
:manage_service => false,
}
end
it { is_expected.to contain_sahara_config('DEFAULT/api_workers').with_value(4) }
it { should contain_package('sahara-api').with(
:ensure => 'absent',
:name => platform_params[:api_package_name],
:tag => ['openstack', 'sahara-package'],
)}
it { should contain_sahara_config('DEFAULT/api_workers').with_value(4) }
it { should contain_service('sahara-api').with(
:ensure => nil,
:name => platform_params[:api_service_name],
:enable => false,
:hasstatus => true,
:hasrestart => true,
:tag => 'sahara-service',
)}
end
end
shared_examples 'sahara-api eventlet ubuntu' do
context 'with default params' do
it { should raise_error(Puppet::Error, /The Sahara API must be run with WSGI on Ubuntu/) }
end
end
shared_examples 'sahara-api wsgi' do
let :pre_condition do
'include ::apache'
end
let :params do
{
:service_name => 'httpd',
}
end
context 'with default params' do
it { should contain_package('sahara-api').with(
:ensure => 'present',
:name => platform_params[:api_package_name],
:tag => ['openstack', 'sahara-package'],
)}
it { should contain_service('sahara-api').with(
:ensure => 'stopped',
:name => platform_params[:api_service_name],
:enable => false,
:tag => 'sahara-service',
)}
end
end
shared_examples 'sahara-api wsgi ubuntu' do
let :pre_condition do
'include ::apache'
end
let :params do
{
:service_name => 'httpd',
}
end
context 'with default params' do
it { should contain_package('sahara-api').with(
:ensure => 'present',
:name => platform_params[:api_package_name],
:tag => ['openstack', 'sahara-package'],
)}
it { should_not contain_service('sahara-api') }
end
end
on_supported_os({
:supported_os => OSDefaults.get_supported_os
:supported_os => OSDefaults.get_supported_os
}).each do |os,facts|
context "on #{os}" do
let (:facts) do
@ -30,19 +123,29 @@ describe 'sahara::service::api' do
case facts[:osfamily]
when 'Debian'
platform_params = {
:name => 'sahara-api',
:package_name => 'sahara-api',
:service_name => 'sahara-api' }
let (:platform_params) do
{
:api_package_name => 'sahara-api',
:api_service_name => 'sahara-api'
}
end
when 'RedHat'
platform_params = {
:name => 'sahara-api',
:package_name => 'openstack-sahara-api',
:service_name => 'openstack-sahara-api' }
let (:platform_params) do
{
:api_package_name => 'openstack-sahara-api',
:api_service_name => 'openstack-sahara-api'
}
end
end
if facts[:operatingsystem] == 'Ubuntu'
it_behaves_like 'sahara-api eventlet ubuntu'
it_behaves_like 'sahara-api wsgi ubuntu'
else
it_behaves_like 'sahara-api eventlet'
it_behaves_like 'sahara-api wsgi'
end
it_configures 'sahara-api'
it_behaves_like 'generic sahara service', platform_params
end
end

View File

@ -70,7 +70,7 @@ describe 'sahara::db' do
{ :database_connection => 'sqlite://sahara:sahara@localhost/sahara', }
end
it_raises 'a Puppet::Error', /validate_re/
it { should raise_error(Puppet::Error, /validate_re/) }
end
context 'with incorrect database_connection string' do
@ -78,7 +78,7 @@ describe 'sahara::db' do
{ :database_connection => 'foo+pymysql://sahara:sahara@localhost/sahara', }
end
it_raises 'a Puppet::Error', /validate_re/
it { should raise_error(Puppet::Error, /validate_re/) }
end
end

View File

@ -1,9 +1,57 @@
require 'spec_helper'
describe 'sahara::service::engine' do
shared_examples 'sahara::service::engine' do
context 'with default params' do
it {
should contain_class('sahara::deps')
should contain_class('sahara::params')
}
it { should contain_package('sahara-engine').with(
:ensure => 'present',
:name => platform_params[:engine_package_name],
:tag => ['openstack', 'sahara-package'],
)}
it { should contain_service('sahara-engine').with(
:ensure => 'running',
:name => platform_params[:engine_service_name],
:enable => true,
:hasstatus => true,
:hasrestart => true,
:tag => 'sahara-service',
)}
end
context 'with custom params' do
let :params do
{
:enabled => false,
:manage_service => false,
:package_ensure => 'absent',
}
end
it { should contain_package('sahara-engine').with(
:ensure => 'absent',
:name => platform_params[:engine_package_name],
:tag => ['openstack', 'sahara-package'],
)}
it { should contain_service('sahara-engine').with(
:ensure => nil,
:name => platform_params[:engine_service_name],
:enable => false,
:hasstatus => true,
:hasrestart => true,
:tag => 'sahara-service',
)}
end
end
on_supported_os({
:supported_os => OSDefaults.get_supported_os
:supported_os => OSDefaults.get_supported_os
}).each do |os,facts|
context "on #{os}" do
let (:facts) do
@ -12,18 +60,22 @@ describe 'sahara::service::engine' do
case facts[:osfamily]
when 'Debian'
platform_params = {
:name => 'sahara-engine',
:package_name => 'sahara-engine',
:service_name => 'sahara-engine' }
let (:platform_params) do
{
:engine_package_name => 'sahara-engine',
:engine_service_name => 'sahara-engine'
}
end
when 'RedHat'
platform_params = {
:name => 'sahara-engine',
:package_name => 'openstack-sahara-engine',
:service_name => 'openstack-sahara-engine' }
let (:platform_params) do
{
:engine_package_name => 'openstack-sahara-engine',
:engine_service_name => 'openstack-sahara-engine'
}
end
end
it_behaves_like 'generic sahara service', platform_params
it_behaves_like 'sahara::service::engine'
end
end

View File

@ -230,7 +230,8 @@ describe 'sahara' do
:use_ssl => true,
}
end
it_raises 'a Puppet::Error', /The cert_file parameter is required when use_ssl is set to true/
it { should raise_error(Puppet::Error, /The cert_file parameter is required when use_ssl is set to true/) }
end
context 'with ssl but without key_file' do
@ -240,7 +241,8 @@ describe 'sahara' do
:cert_file => '/tmp/cert_file',
}
end
it_raises 'a Puppet::Error', /The key_file parameter is required when use_ssl is set to true/
it { should raise_error(Puppet::Error, /The key_file parameter is required when use_ssl is set to true/) }
end
end

View File

@ -0,0 +1,124 @@
require 'spec_helper'
describe 'sahara::wsgi::apache' do
shared_examples 'sahara::wsgi::apache' do
context 'with default params' do
it {
should contain_class('sahara::deps')
should contain_class('sahara::params')
should contain_class('apache')
should contain_class('apache::mod::wsgi')
should_not contain_class('apache::mod::ssl')
}
it { should contain_openstacklib__wsgi__apache('sahara_wsgi').with(
:bind_port => 8386,
:group => 'sahara',
:path => '/',
:servername => facts[:fqdn],
:ssl => false,
:threads => 1,
:user => 'sahara',
:workers => facts[:os_workers],
:wsgi_daemon_process => 'sahara',
:wsgi_process_group => 'sahara',
:wsgi_script_dir => platform_params[:wsgi_script_path],
:wsgi_script_file => 'app',
:wsgi_script_source => platform_params[:wsgi_script_source],
:custom_wsgi_process_options => {},
:access_log_file => false,
:access_log_format => false,
)}
end
context 'when overriding paramters using different ports' do
let :params do
{
:servername => 'dummy.host',
:bind_host => '10.42.51.1',
:port => 12345,
:ssl => false,
:workers => 8,
:wsgi_process_display_name => 'sahara',
:threads => 2,
:custom_wsgi_process_options => {
'python_path' => '/my/python/path',
},
:access_log_file => '/var/log/httpd/access_log',
:access_log_format => 'some format',
:error_log_file => '/var/log/httpd/error_log'
}
end
it { is_expected.to contain_openstacklib__wsgi__apache('sahara_wsgi').with(
:bind_host => '10.42.51.1',
:bind_port => 12345,
:group => 'sahara',
:path => '/',
:servername => 'dummy.host',
:ssl => false,
:threads => 2,
:user => 'sahara',
:workers => 8,
:wsgi_daemon_process => 'sahara',
:wsgi_process_display_name => 'sahara',
:wsgi_process_group => 'sahara',
:wsgi_script_dir => platform_params[:wsgi_script_path],
:wsgi_script_file => 'app',
:wsgi_script_source => platform_params[:wsgi_script_source],
:custom_wsgi_process_options => {
'python_path' => '/my/python/path',
},
:access_log_file => '/var/log/httpd/access_log',
:access_log_format => 'some format',
:error_log_file => '/var/log/httpd/error_log'
)}
end
context 'with ssl' do
let :params do
{
:ssl => true,
}
end
it { should contain_class('apache::mod::ssl') }
it { should contain_openstacklib__wsgi__apache('sahara_wsgi').with_ssl(true) }
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 => 4,
:concat_basedir => '/var/lib/puppet/concat',
:fqdn => 'some.host.tld',
}))
end
let(:platform_params) do
case facts[:osfamily]
when 'Debian'
{
:httpd_service_name => 'apache2',
:wsgi_script_path => '/usr/lib/cgi-bin/sahara',
:wsgi_script_source => '/usr/bin/sahara-wsgi-api'
}
when 'RedHat'
{
:httpd_service_name => 'httpd',
:wsgi_script_path => '/var/www/cgi-bin/sahara',
:wsgi_script_source => '/usr/bin/sahara-wsgi-api'
}
end
end
it_behaves_like 'sahara::wsgi::apache'
end
end
end

View File

@ -1,54 +0,0 @@
shared_examples_for "a Puppet::Error" do |description|
it "with message matching #{description.inspect}" do
expect { is_expected.to have_class_count(1) }.to raise_error(Puppet::Error, description)
end
end
shared_examples 'generic sahara service' do |service|
context 'with default parameters' do
it 'installs package and service' do
is_expected.to contain_package(service[:name]).with({
:name => service[:package_name],
:ensure => 'present',
})
is_expected.to contain_service(service[:name]).with({
:name => service[:service_name],
:ensure => 'running',
:hasstatus => true,
:enable => true
})
end
end
context 'with overridden parameters' do
let :params do
{ :enabled => true,
:package_ensure => '2014.2-1' }
end
it 'installs package and service' do
is_expected.to contain_package(service[:name]).with({
:name => service[:package_name],
:ensure => '2014.2-1',
})
is_expected.to contain_service(service[:name]).with({
:name => service[:service_name],
:ensure => 'running',
:hasstatus => true,
:enable => true
})
end
end
context 'while not managing service state' do
let :params do
{ :enabled => false,
:manage_service => false }
end
it 'does not control service state' do
is_expected.to contain_service(service[:name]).without_ensure
end
end
end

View File

@ -1,7 +1,6 @@
# Load libraries from openstacklib here to simulate how they live together in a real puppet run (for provider unit tests)
$LOAD_PATH.push(File.join(File.dirname(__FILE__), 'fixtures', 'modules', 'openstacklib', 'lib'))
require 'puppetlabs_spec_helper/module_spec_helper'
require 'shared_examples'
require 'puppet-openstack_spec_helper/facts'
RSpec.configure do |c|
@ -9,4 +8,4 @@ RSpec.configure do |c|
c.alias_it_should_behave_like_to :it_raises, 'raises'
end
at_exit { RSpec::Puppet::Coverage.report! }
at_exit { RSpec::Puppet::Coverage.report! }