diff --git a/apache/.gitignore b/apache/.gitignore index e8f52b445..b5b7a00d6 100644 --- a/apache/.gitignore +++ b/apache/.gitignore @@ -1,7 +1,7 @@ -.pkg +pkg/ Gemfile.lock -vendor -spec/fixtures -.rspec_system -.bundle -.*sw* +vendor/ +spec/fixtures/ +.vagrant/ +.bundle/ +coverage/ diff --git a/apache/.puppet-lint.rc b/apache/.puppet-lint.rc index df733ca81..0b3cfad25 100644 --- a/apache/.puppet-lint.rc +++ b/apache/.puppet-lint.rc @@ -1,5 +1,5 @@ +--relative --no-single_quote_string_with_variables-check --no-80chars-check --no-class_inherits_from_params_class-check ---no-class_parameter_defaults-check --no-documentation-check diff --git a/apache/.sync.yml b/apache/.sync.yml new file mode 100644 index 000000000..96d3c2bdb --- /dev/null +++ b/apache/.sync.yml @@ -0,0 +1,12 @@ +--- +.travis.yml: + extras: + - rvm: 1.9.3 + env: PUPPET_GEM_VERSION="~> 3.5.0" STRICT_VARIABLES="yes" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 3.5.0" STRICT_VARIABLES="yes" +Rakefile: + extra_disabled_lint_checks: + - 'disable_only_variable_string' +spec/spec_helper.rb: + unmanaged: true diff --git a/apache/.travis.yml b/apache/.travis.yml index 5efc64fa7..86222c281 100644 --- a/apache/.travis.yml +++ b/apache/.travis.yml @@ -1,40 +1,21 @@ --- -branches: - only: - - master language: ruby bundler_args: --without development -script: "bundle exec rake spec SPEC_OPTS='--format documentation'" -after_success: - - git clone -q git://github.com/puppetlabs/ghpublisher.git .forge-releng - - .forge-releng/publish -rvm: - - 1.8.7 - - 1.9.3 - - 2.0.0 -env: - matrix: - - PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.6.0" - - PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.7.0" - - PUPPET_GEM_VERSION="~> 3.0" - global: - - PUBLISHER_LOGIN=puppetlabs - - secure: |- - MO4pB4bqBQJjm2yFHf3Mgho+y0Qv4GmMxTMhzI02tGy1V0HMtruZbR7EBN0i - n2CiR7V9V0mNR7/ymzDMF9yVBcgqyXMsp/C6u992Dd0U63ZwFpbRWkxuAeEY - ioupWBkiczjVEo+sxn+gVOnx28pcH/X8kDWbr6wFOMIjO03K66Y= +script: "bundle exec rake validate && bundle exec rake lint && bundle exec rake spec SPEC_OPTS='--format documentation'" matrix: fast_finish: true - exclude: - - rvm: 1.9.3 - env: PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.6.0" - - rvm: 1.9.3 - env: PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.7.0" - - rvm: 2.0.0 - env: PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.6.0" - - rvm: 2.0.0 - env: PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.7.0" - - rvm: 1.8.7 - env: PUPPET_GEM_VERSION="~> 3.2.0" + include: + - rvm: 1.8.7 + env: PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.6.0" + - rvm: 1.8.7 + env: PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.7.0" + - rvm: 1.9.3 + env: PUPPET_GEM_VERSION="~> 3.0" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 3.0" + - rvm: 1.9.3 + env: PUPPET_GEM_VERSION="~> 3.5.0" STRICT_VARIABLES="yes" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 3.5.0" STRICT_VARIABLES="yes" notifications: email: false diff --git a/apache/CHANGELOG.md b/apache/CHANGELOG.md index 57d62e4c1..c2ccb4c6c 100644 --- a/apache/CHANGELOG.md +++ b/apache/CHANGELOG.md @@ -1,14 +1,195 @@ +##2014-09-30 - Supported Release 1.2.0 +###Summary + +This release features many improvements and bugfixes, including several new defines, a reworking of apache::vhost for more extensibility, and many new parameters for more customization. This release also includes improved support for strict variables and the future parser. + +####Features +- Convert apache::vhost to use concat for easier extensions +- Test improvements +- Synchronize files with modulesync +- Strict variable and future parser support +- Added apache::custom_config defined type to allow validation of configs before they are created +- Added bool2httpd function to convert true/false to apache 'On' and 'Off'. Intended for internal use in the module. +- Improved SCL support + - allow overriding of the mod_ssl package name +- Add support for reverse_urls/ProxyPassReverse in apache::vhost +- Add satisfy directive in apache::vhost::directories +- Add apache::fastcgi::server defined type +- New parameters - apache + - allow_encoded_slashes + - apache_name + - conf_dir + - default_ssl_crl_check + - docroot + - logroot_mode + - purge_vhost_dir +- New parameters - apache::vhost + - add_default_charset + - allow_encoded_slashes + - logroot_ensure + - logroot_mode + - manage_docroot + - passenger_app_root + - passenger_min_instances + - passenger_pre_start + - passenger_ruby + - passenger_start_timeout + - proxy_preserve_host + - redirectmatch_dest + - ssl_crl_check + - wsgi_chunked_request + - wsgi_pass_authorization +- Add support for ScriptAlias and ScriptAliasMatch in the apache::vhost::aliases parameter +- Add support for rewrites in the apache::vhost::directories parameter +- If the service_ensure parameter in apache::service is set to anything other than true, false, running, or stopped, ensure will not be passed to the service resource, allowing for the service to not be managed by puppet +- Turn of SSLv3 by default +- Improvements to apache::mod* + - Add restrict_access parameter to apache::mod::info + - Add force_language_priority and language_priority parameters to apache::mod::negotiation + - Add threadlimit parameter to apache::mod::worker + - Add content, template, and source parameters to apache::mod::php + - Add mod_authz_svn support via the authz_svn_enabled parameter in apache::mod::dav_svn + - Add loadfile_name parameter to apache::mod + - Add apache::mod::deflate class + - Add options parameter to apache::mod::fcgid + - Add timeouts parameter to apache::mod::reqtimeout + - Add apache::mod::shib + - Add apache_version parameter to apache::mod::ldap + - Add magic_file parameter to apache::mod::mime_magic + - Add apache_version parameter to apache::mod::pagespeed + - Add passenger_default_ruby parameter to apache::mod::passenger + - Add content, template, and source parameters to apache::mod::php + - Add apache_version parameter to apache::mod::proxy + - Add loadfiles parameter to apache::mod::proxy_html + - Add ssl_protocol and package_name parameters to apache::mod::ssl + - Add apache_version parameter to apache::mod::status + - Add apache_version parameter to apache::mod::userdir + - Add apache::mod::version class + +####Bugfixes +- Set osfamily defaults for wsgi_socket_prefix +- Support multiple balancermembers with the same url +- Validate apache::vhost::custom_fragment +- Add support for itk with mod_php +- Allow apache::vhost::ssl_certs_dir to not be set +- Improved passenger support for Debian +- Improved 2.4 support without mod_access_compat +- Support for more than one 'Allow from'-directive in _directories.erb +- Don't load systemd on Amazon linux based on CentOS6 with apache 2.4 +- Fix missing newline in ModPagespeed filter and memcached servers directive +- Use interpolated strings instead of numbers where required by future parser +- Make auth_require take precedence over default with apache 2.4 +- Lint fixes +- Set default for php_admin_flags and php_admin_values to be empty hash instead of empty array +- Correct typo in mod::pagespeed +- spec_helper fixes +- Install mod packages before dealing with the configuration +- Use absolute scope to check class definition in apache::mod::php +- Fix dependency loop in apache::vhost +- Properly scope variables in the inline template in apache::balancer +- Documentation clarification, typos, and formatting +- Set apache::mod::ssl::ssl_mutex to default for debian on apache >= 2.4 +- Strict variables fixes +- Add authn_core mode to Ubuntu trusty defaults +- Keep default loadfile for authz_svn on Debian +- Remove '.conf' from the site-include regexp for better Ubuntu/Debian support +- Load unixd before fcgid for EL7 +- Fix RedirectMatch rules +- Fix misleading error message in apache::version + +####Known Bugs +* By default, the version of Apache that ships with Ubuntu 10.04 does not work with `wsgi_import_script`. +* SLES is unsupported. + +##2014-07-15 - Supported Release 1.1.1 +###Summary + +This release merely updates metadata.json so the module can be uninstalled and +upgraded via the puppet module command. + +## 2014-04-14 Supported Release 1.1.0 + +###Summary + +This release primarily focuses on extending the httpd 2.4 support, tested +through adding RHEL7 and Ubuntu 14.04 support. It also includes Passenger +4 support, as well as several new modules and important bugfixes. + +####Features + +- Add support for RHEL7 and Ubuntu 14.04 +- More complete apache24 support +- Passenger 4 support +- Add support for max_keepalive_requests and log_formats parameters +- Add mod_pagespeed support +- Add mod_speling support +- Added several parameters for mod_passenger +- Added ssl_cipher parameter to apache::mod::ssl +- Improved examples in documentation +- Added docroot_mode, action, and suexec_user_group parameters to apache::vhost +- Add support for custom extensions for mod_php +- Improve proxy_html support for Debian + +####Bugfixes + +- Remove NameVirtualHost directive for apache >= 2.4 +- Order proxy_set option so it doesn't change between runs +- Fix inverted SSL compression +- Fix missing ensure on concat::fragment resources +- Fix bad dependencies in apache::mod and apache::mod::mime + +####Known Bugs +* By default, the version of Apache that ships with Ubuntu 10.04 does not work with `wsgi_import_script`. +* SLES is unsupported. + +## 2014-03-04 Supported Release 1.0.1 +###Summary + +This is a supported release. This release removes a testing symlink that can +cause trouble on systems where /var is on a seperate filesystem from the +modulepath. + +####Features +####Bugfixes +####Known Bugs +* By default, the version of Apache that ships with Ubuntu 10.04 does not work with `wsgi_import_script`. +* SLES is unsupported. + +## 2014-03-04 Supported Release 1.0.0 +###Summary + +This is a supported release. This release introduces Apache 2.4 support for +Debian and RHEL based osfamilies. + +####Features + +- Add apache24 support +- Add rewrite_base functionality to rewrites +- Updated README documentation +- Add WSGIApplicationGroup and WSGIImportScript directives + +####Bugfixes + +- Replace mutating hashes with merge() for Puppet 3.5 +- Fix WSGI import_script and mod_ssl issues on Lucid + +####Known Bugs +* By default, the version of Apache that ships with Ubuntu 10.04 does not work with `wsgi_import_script`. +* SLES is unsupported. + +--- + ## 2014-01-31 Release 0.11.0 ### Summary: This release adds preliminary support for Windows compatibility and multiple rewrite support. -### Backwards-incompatible Changes: +#### Backwards-incompatible Changes: - The rewrite_rule parameter is deprecated in favor of the new rewrite parameter and will be removed in a future release. -### Features: +#### Features: - add Match directive - quote paths for windows compatibility @@ -25,7 +206,7 @@ This release adds preliminary support for Windows compatibility and multiple rew - Convert spec tests to beaker. - Support php_admin_(flag|value)s -### Bugfixes: +#### Bugfixes: - directories are either a Hash or an Array of Hashes - Configure Passenger in separate .conf file on RH so PassengerRoot isn't lost @@ -40,7 +221,7 @@ This release adds preliminary support for Windows compatibility and multiple rew This release adds FreeBSD osfamily support and various other improvements to some mods. -### Features: +#### Features: - Add suPHP_UserGroup directive to directory context - Add support for ScriptAliasMatch directives @@ -68,7 +249,7 @@ This release adds FreeBSD osfamily support and various other improvements to som - Add documentation about $ip - Add ability to pass ip (instead of wildcard) in default vhost files -### Bugfixes: +#### Bugfixes: - Don't listen on port or set NameVirtualHost for non-existent vhost - only apply Directory defaults when provider is a directory @@ -80,7 +261,7 @@ This release adds more parameters to the base apache class and apache defined resource to make the module more flexible. It also adds or enhances SuPHP, WSGI, and Passenger mod support, and support for the ITK mpm module. -### Backwards-incompatible Changes: +#### Backwards-incompatible Changes: - Remove many default mods that are not normally needed. - Remove `rewrite_base` `apache::vhost` parameter; did not work anyway. - Specify dependencies on stdlib >=2.4.0 (this was already the case, but @@ -88,7 +269,7 @@ making explicit) - Deprecate `a2mod` in favor of the `apache::mod::*` classes and `apache::mod` defined resource. -### Features: +#### Features: - `apache` class - Add `httpd_dir` parameter to change the location of the configuration files. @@ -123,7 +304,7 @@ dependency chaining of `Class['apache'] -> ~> Class['apache::service']` - Added `apache::mod::proxy_balancer` class for `apache::balancer` -### Bugfixes: +#### Bugfixes: - Change dependency to puppetlabs-concat - Fix ruby 1.9 bug for `a2mod` - Change servername to be `$::hostname` if there is no `$::fqdn` @@ -133,17 +314,17 @@ Class['apache::service']` array. ## 2013-07-26 Release 0.8.1 -### Bugfixes: +#### Bugfixes: - Update `apache::mpm_module` detection for worker/prefork - Update `apache::mod::cgi` and `apache::mod::cgid` detection for worker/prefork ## 2013-07-16 Release 0.8.0 -### Features: +#### Features: - Add `servername` parameter to `apache` class - Add `proxy_set` parameter to `apache::balancer` define -### Bugfixes: +#### Bugfixes: - Fix ordering for multiple `apache::balancer` clusters - Fix symlinking for sites-available on Debian-based OSs - Fix dependency ordering for recursive confdir management @@ -151,13 +332,13 @@ worker/prefork - Documentation updates ## 2013-07-09 Release 0.7.0 -### Changes: +#### Changes: - Essentially rewrite the module -- too many to list - `apache::vhost` has many abilities -- see README.md for details - `apache::mod::*` classes provide httpd mod-loading capabilities - `apache` base class is much more configurable -### Bugfixes: +#### Bugfixes: - Many. And many more to come ## 2013-03-2 Release 0.6.0 @@ -166,44 +347,44 @@ worker/prefork - make purging of vhost dir configurable ## 2012-08-24 Release 0.4.0 -### Changes: +#### Changes: - `include apache` is now required when using `apache::mod::*` -### Bugfixes: +#### Bugfixes: - Fix syntax for validate_re - Fix formatting in vhost template - Fix spec tests such that they pass - 2012-05-08 Puppet Labs - 0.0.4 - e62e362 Fix broken tests for ssl, vhost, vhost::* - 42c6363 Changes to match style guide and pass puppet-lint without error - 42bc8ba changed name => path for file resources in order to name namevar by it's name - 72e13de One end too much - 0739641 style guide fixes: 'true' <> true, $operatingsystem needs to be $::operatingsystem, etc. - 273f94d fix tests - a35ede5 (#13860) Make a2enmod/a2dismo commands optional - 98d774e (#13860) Autorequire Package['httpd'] - 05fcec5 (#13073) Add missing puppet spec tests - 541afda (#6899) Remove virtual a2mod definition - 976cb69 (#13072) Move mod python and wsgi package names to params - 323915a (#13060) Add .gitignore to repo - fdf40af (#13060) Remove pkg directory from source tree - fd90015 Add LICENSE file and update the ModuleFile - d3d0d23 Re-enable local php class - d7516c7 Make management of firewalls configurable for vhosts - 60f83ba Explicitly lookup scope of apache_name in templates. - f4d287f (#12581) Add explicit ordering for vdir directory - 88a2ac6 (#11706) puppetlabs-apache depends on puppetlabs-firewall - a776a8b (#11071) Fix to work with latest firewall module - 2b79e8b (#11070) Add support for Scientific Linux - 405b3e9 Fix for a2mod - 57b9048 Commit apache::vhost::redirect Manifest - 8862d01 Commit apache::vhost::proxy Manifest - d5c1fd0 Commit apache::mod::wsgi Manifest - a825ac7 Commit apache::mod::python Manifest - b77062f Commit Templates - 9a51b4a Vhost File Declarations - 6cf7312 Defaults for Parameters - 6a5b11a Ensure installed - f672e46 a2mod fix - 8a56ee9 add pthon support to apache +##2012-05-08 Puppet Labs - 0.0.4 +* e62e362 Fix broken tests for ssl, vhost, vhost::* +* 42c6363 Changes to match style guide and pass puppet-lint without error +* 42bc8ba changed name => path for file resources in order to name namevar by it's name +* 72e13de One end too much +* 0739641 style guide fixes: 'true' <> true, $operatingsystem needs to be $::operatingsystem, etc. +* 273f94d fix tests +* a35ede5 (#13860) Make a2enmod/a2dismo commands optional +* 98d774e (#13860) Autorequire Package['httpd'] +* 05fcec5 (#13073) Add missing puppet spec tests +* 541afda (#6899) Remove virtual a2mod definition +* 976cb69 (#13072) Move mod python and wsgi package names to params +* 323915a (#13060) Add .gitignore to repo +* fdf40af (#13060) Remove pkg directory from source tree +* fd90015 Add LICENSE file and update the ModuleFile +* d3d0d23 Re-enable local php class +* d7516c7 Make management of firewalls configurable for vhosts +* 60f83ba Explicitly lookup scope of apache_name in templates. +* f4d287f (#12581) Add explicit ordering for vdir directory +* 88a2ac6 (#11706) puppetlabs-apache depends on puppetlabs-firewall +* a776a8b (#11071) Fix to work with latest firewall module +* 2b79e8b (#11070) Add support for Scientific Linux +* 405b3e9 Fix for a2mod +* 57b9048 Commit apache::vhost::redirect Manifest +* 8862d01 Commit apache::vhost::proxy Manifest +* d5c1fd0 Commit apache::mod::wsgi Manifest +* a825ac7 Commit apache::mod::python Manifest +* b77062f Commit Templates +* 9a51b4a Vhost File Declarations +* 6cf7312 Defaults for Parameters +* 6a5b11a Ensure installed +* f672e46 a2mod fix +* 8a56ee9 add pthon support to apache diff --git a/apache/Gemfile b/apache/Gemfile index dd87fe8cf..e960f7c4b 100644 --- a/apache/Gemfile +++ b/apache/Gemfile @@ -8,6 +8,8 @@ group :development, :test do gem 'puppet-lint', :require => false gem 'beaker', :require => false gem 'beaker-rspec', :require => false + gem 'pry', :require => false + gem 'simplecov', :require => false end if facterversion = ENV['FACTER_GEM_VERSION'] diff --git a/apache/Modulefile b/apache/Modulefile deleted file mode 100644 index 1a1db1427..000000000 --- a/apache/Modulefile +++ /dev/null @@ -1,12 +0,0 @@ -name 'puppetlabs-apache' -version '0.11.0' -source 'git://github.com/puppetlabs/puppetlabs-apache.git' -author 'puppetlabs' -license 'Apache 2.0' -summary 'Puppet module for Apache' -description 'Module for Apache configuration' -project_page 'https://github.com/puppetlabs/puppetlabs-apache' - -## Add dependencies, if any: -dependency 'puppetlabs/stdlib', '>= 2.4.0' -dependency 'puppetlabs/concat', '>= 1.0.0' diff --git a/apache/README.md b/apache/README.md index fd21507cd..b98749b58 100644 --- a/apache/README.md +++ b/apache/README.md @@ -12,14 +12,24 @@ 4. [Usage - The classes and defined types available for configuration](#usage) * [Classes and Defined Types](#classes-and-defined-types) * [Class: apache](#class-apache) + * [Defined Type: apache::custom_config](#defined-type-apachecustom_config) * [Class: apache::default_mods](#class-apachedefault_mods) * [Defined Type: apache::mod](#defined-type-apachemod) * [Classes: apache::mod::*](#classes-apachemodname) + * [Class: apache::mod::info](#class-apachemodinfo) + * [Class: apache::mod::pagespeed](#class-apachemodpagespeed) + * [Class: apache::mod::php](#class-apachemodphp) * [Class: apache::mod::ssl](#class-apachemodssl) * [Class: apache::mod::wsgi](#class-apachemodwsgi) + * [Class: apache::mod::fcgid](#class-apachemodfcgid) + * [Class: apache::mod::negotiation](#class-apachemodnegotiation) + * [Class: apache::mod::deflate](#class-apachemoddeflate) + * [Class: apache::mod::reqtimeout](#class-apachemodreqtimeout) + * [Class: apache::mod::version](#class-apachemodversion) * [Defined Type: apache::vhost](#defined-type-apachevhost) * [Parameter: `directories` for apache::vhost](#parameter-directories-for-apachevhost) * [SSL parameters for apache::vhost](#ssl-parameters-for-apachevhost) + * [Defined Type: apache::fastcgi::server](#defined-type-fastcgi-server) * [Virtual Host Examples - Demonstrations of some configuration options](#virtual-host-examples) * [Load Balancing](#load-balancing) * [Defined Type: apache::balancer](#defined-type-apachebalancer) @@ -56,7 +66,7 @@ Apache is a widely-used web server, and this module provides a simplified way of * Apache modules * virtual hosts * listened-to ports -* `/etc/make.conf` on FreeBSD +* `/etc/make.conf` on FreeBSD ###Beginning with Apache @@ -66,7 +76,7 @@ To install Apache with the default parameters class { 'apache': } ``` -The defaults are determined by your operating system (e.g. Debian systems have one set of defaults, and RedHat systems have another, as do FreeBSD systems). These defaults will work well in a testing environment, but are not suggested for production. To establish customized parameters +The defaults are determined by your operating system (e.g. Debian systems have one set of defaults, and RedHat systems have another, as do FreeBSD systems). These defaults work well in a testing environment, but are not suggested for production. To establish customized parameters ```puppet class { 'apache': @@ -77,7 +87,7 @@ The defaults are determined by your operating system (e.g. Debian systems have o ###Configure a virtual host -Declaring the `apache` class will create a default virtual host by setting up a vhost on port 80, listening on all interfaces and serving `$apache::docroot`. +Declaring the `apache` class creates a default virtual host by setting up a vhost on port 80, listening on all interfaces and serving `$apache::docroot`. ```puppet class { 'apache': } @@ -92,7 +102,7 @@ To configure a very basic, name-based virtual host } ``` -*Note:* The default priority is 15. If nothing matches this priority, the alphabetically first name-based vhost will be used. This is also true if you pass a higher priority and no names match anything else. +*Note:* The default priority is 15. If nothing matches this priority, the alphabetically first name-based vhost is used. This is also true if you pass a higher priority and no names match anything else. A slightly more complicated example, changes the docroot owner/group from the default 'root' @@ -143,7 +153,7 @@ To set up a virtual host with a wildcard alias for the subdomain mapped to a sam apache::vhost { 'subdomain.loc': vhost_name => '*', port => '80', - virtual_docroot' => '/var/www/%-2+', + virtual_docroot => '/var/www/%-2+', docroot => '/var/www', serveraliases => ['*.loc',], } @@ -172,9 +182,9 @@ To set up a virtual host with WSGI docroot => '/var/www/pythonapp', wsgi_application_group => '%{GLOBAL}', wsgi_daemon_process => 'wsgi', - wsgi_daemon_process_options => { - processes => '2', - threads => '15', + wsgi_daemon_process_options => { + processes => '2', + threads => '15', display-name => '%{GROUP}', }, wsgi_import_script => '/var/www/demo.wsgi', @@ -203,7 +213,7 @@ See a list of all [virtual host parameters](#defined-type-apachevhost). See an e ###Classes and Defined Types -This module modifies Apache configuration files and directories, and will purge any configuration not managed by Puppet. Configuration of Apache should be managed by Puppet, as non-Puppet configuration files can cause unexpected failures. +This module modifies Apache configuration files and directories and purges any configuration not managed by Puppet. Configuration of Apache should be managed by Puppet, as non-Puppet configuration files can cause unexpected failures. It is possible to temporarily disable full Puppet management by setting the [`purge_configs`](#purge_configs) parameter within the base `apache` class to 'false'. This option should only be used as a temporary means of saving and relocating customized configurations. See the [`purge_configs` parameter](#purge_configs) for more information. @@ -211,14 +221,22 @@ It is possible to temporarily disable full Puppet management by setting the [`pu The apache module's primary class, `apache`, guides the basic setup of Apache on your system. -You may establish a default vhost in this class, the `vhost` class, or both. You may add additional vhost configurations for specific virtual hosts using a declaration of the `vhost` type. +You can establish a default vhost in this class, the `vhost` class, or both. You can add additional vhost configurations for specific virtual hosts using a declaration of the `vhost` type. **Parameters within `apache`:** +#####`allow_encoded_slashes` + +This sets the server default for the [`AllowEncodedSlashes` declaration](http://httpd.apache.org/docs/current/mod/core.html#allowencodedslashes) which modifies the responses to URLs with `\` and `/` characters. The default is undefined, which omits the declaration from the server configuration and select the Apache default setting of `Off`. Allowed values are: `on`, `off` or `nodecode`. + #####`apache_version` Configures the behavior of the module templates, package names, and default mods by setting the Apache version. Default is determined by the class `apache::version` using the OS family and release. It should not be configured manually without special reason. +#####`conf_dir` + +Changes the location of the configuration directory the main configuration file is placed in. Defaults to '/etc/httpd/conf' on RedHat, '/etc/apache2' on Debian, and '/usr/local/etc/apache22' on FreeBSD. + #####`confd_dir` Changes the location of the configuration directory your custom configuration files are placed in. Defaults to '/etc/httpd/conf' on RedHat, '/etc/apache2' on Debian, and '/usr/local/etc/apache22' on FreeBSD. @@ -235,41 +253,45 @@ Generates default set of include-able Apache configuration files under `${apach #####`default_mods` -Sets up Apache with default settings based on your OS. Valid values are 'true', 'false', or an array of mod names. +Sets up Apache with default settings based on your OS. Valid values are 'true', 'false', or an array of mod names. -Defaults to 'true', which will include the default [HTTPD mods](https://github.com/puppetlabs/puppetlabs-apache/blob/master/manifests/default_mods.pp). +Defaults to 'true', which includes the default [HTTPD mods](https://github.com/puppetlabs/puppetlabs-apache/blob/master/manifests/default_mods.pp). -If false, it will only include the mods required to make HTTPD work, and any other mods can be declared on their own. +If false, it only includes the mods required to make HTTPD work, and any other mods can be declared on their own. -If an array, the apache module will include the array of mods listed. +If an array, the apache module includes the array of mods listed. #####`default_ssl_ca` -The default certificate authority, which is automatically set to 'undef'. This default will work out of the box but must be updated with your specific certificate information before being used in production. +The default certificate authority, which is automatically set to 'undef'. This default works out of the box but must be updated with your specific certificate information before being used in production. #####`default_ssl_cert` -The default SSL certification, which is automatically set based on your operating system ('/etc/pki/tls/certs/localhost.crt' for RedHat, '/etc/ssl/certs/ssl-cert-snakeoil.pem' for Debian, and '/usr/local/etc/apache22/server.crt' for FreeBSD). This default will work out of the box but must be updated with your specific certificate information before being used in production. +The default SSL certification, which is automatically set based on your operating system ('/etc/pki/tls/certs/localhost.crt' for RedHat, '/etc/ssl/certs/ssl-cert-snakeoil.pem' for Debian, and '/usr/local/etc/apache22/server.crt' for FreeBSD). This default works out of the box but must be updated with your specific certificate information before being used in production. #####`default_ssl_chain` -The default SSL chain, which is automatically set to 'undef'. This default will work out of the box but must be updated with your specific certificate information before being used in production. +The default SSL chain, which is automatically set to 'undef'. This default works out of the box but must be updated with your specific certificate information before being used in production. #####`default_ssl_crl` -The default certificate revocation list to use, which is automatically set to 'undef'. This default will work out of the box but must be updated with your specific certificate information before being used in production. +The default certificate revocation list to use, which is automatically set to 'undef'. This default works out of the box but must be updated with your specific certificate information before being used in production. #####`default_ssl_crl_path` -The default certificate revocation list path, which is automatically set to 'undef'. This default will work out of the box but must be updated with your specific certificate information before being used in production. +The default certificate revocation list path, which is automatically set to 'undef'. This default works out of the box but must be updated with your specific certificate information before being used in production. + +#####`default_ssl_crl_check` + +Sets the default certificate revocation check level via the [SSLCARevocationCheck directive](http://httpd.apache.org/docs/current/mod/mod_ssl.html#sslcarevocationcheck), which is automatically set to 'undef'. This default works out of the box but must be specified when using CRLs in production. Only applicable to Apache 2.4 or higher, the value is ignored on older versions. #####`default_ssl_key` -The default SSL key, which is automatically set based on your operating system ('/etc/pki/tls/private/localhost.key' for RedHat, '/etc/ssl/private/ssl-cert-snakeoil.key' for Debian, and '/usr/local/etc/apache22/server.key' for FreeBSD). This default will work out of the box but must be updated with your specific certificate information before being used in production. +The default SSL key, which is automatically set based on your operating system ('/etc/pki/tls/private/localhost.key' for RedHat, '/etc/ssl/private/ssl-cert-snakeoil.key' for Debian, and '/usr/local/etc/apache22/server.key' for FreeBSD). This default works out of the box but must be updated with your specific certificate information before being used in production. #####`default_ssl_vhost` -Sets up a default SSL virtual host. Defaults to 'false'. If set to 'true', will set up the following vhost: +Sets up a default SSL virtual host. Defaults to 'false'. If set to 'true', sets up the following vhost: ```puppet apache::vhost { 'default-ssl': @@ -288,13 +310,17 @@ SSL vhosts only respond to HTTPS queries. Sets up a default virtual host. Defaults to 'true', set to 'false' to set up [customized virtual hosts](#configure-a-virtual-host). +#####`docroot` + +Changes the location of the default [Documentroot](https://httpd.apache.org/docs/current/mod/core.html#documentroot). Defaults to '/var/www/html' on RedHat, '/var/www' on Debian, and '/usr/local/www/apache22/data' on FreeBSD. + #####`error_documents` Enables custom error documents. Defaults to 'false'. #####`httpd_dir` -Changes the base location of the configuration directories used for the apache service. This is useful for specially repackaged HTTPD builds, but may have unintended consequences when used in combination with the default distribution packages. Defaults to '/etc/httpd' on RedHat, '/etc/apache2' on Debian, and '/usr/local/etc/apache22' on FreeBSD. +Changes the base location of the configuration directories used for the apache service. This is useful for specially repackaged HTTPD builds, but might have unintended consequences when used in combination with the default distribution packages. Defaults to '/etc/httpd' on RedHat, '/etc/apache2' on Debian, and '/usr/local/etc/apache22' on FreeBSD. #####`keepalive` @@ -302,23 +328,44 @@ Enables persistent connections. #####`keepalive_timeout` -Sets the amount of time the server will wait for subsequent requests on a persistent connection. Defaults to '15'. +Sets the amount of time the server waits for subsequent requests on a persistent connection. Defaults to '15'. + +#####`max_keepalive_requests` + +Sets the limit of the number of requests allowed per connection when KeepAlive is on. Defaults to '100'. + +#####`loadfile_name` + +Sets the file name for the module loadfile. Should be in the format *.load. This can be used to set the module load order. #####`log_level` Changes the verbosity level of the error log. Defaults to 'warn'. Valid values are 'emerg', 'alert', 'crit', 'error', 'warn', 'notice', 'info', or 'debug'. +#####`log_formats` + +Define additional [LogFormats](https://httpd.apache.org/docs/current/mod/mod_log_config.html#logformat). This is done in a Hash: + +```puppet + $log_formats = { vhost_common => '%v %h %l %u %t \"%r\" %>s %b' } +``` + #####`logroot` Changes the directory where Apache log files for the virtual host are placed. Defaults to '/var/log/httpd' on RedHat, '/var/log/apache2' on Debian, and '/var/log/apache22' on FreeBSD. +#####`logroot_mode` + +Overrides the mode the default logroot directory is set to ($::apache::logroot). Defaults to undef. Do NOT give people write access to the directory the logs are stored +in without being aware of the consequences; see http://httpd.apache.org/docs/2.4/logs.html#security for details. + #####`manage_group` -Setting this to 'false' will stop the group resource from being created. This is for when you have a group, created from another Puppet module, you want to use to run Apache. Without this parameter, attempting to use a previously established group would result in a duplicate resource error. +Setting this to 'false' stops the group resource from being created. This is for when you have a group, created from another Puppet module, you want to use to run Apache. Without this parameter, attempting to use a previously established group would result in a duplicate resource error. #####`manage_user` -Setting this to 'false' will stop the user resource from being created. This is for instances when you have a user, created from another Puppet module, you want to use to run Apache. Without this parameter, attempting to use a previously established user would result in a duplicate resource error. +Setting this to 'false' stops the user resource from being created. This is for instances when you have a user, created from another Puppet module, you want to use to run Apache. Without this parameter, attempting to use a previously established user would result in a duplicate resource error. #####`mod_dir` @@ -332,9 +379,9 @@ Determines which MPM is loaded and configured for the HTTPD process. Valid value * `apache::mod::itk` * `apache::mod::peruser` * `apache::mod::prefork` -* `apache::mod::worker` +* `apache::mod::worker` -*Note:* Switching between different MPMs on FreeBSD is possible but quite difficult. Before changing `$mpm_module` you must uninstall all packages that depend on your currently-installed Apache. +*Note:* Switching between different MPMs on FreeBSD is possible but quite difficult. Before changing `$mpm_module` you must uninstall all packages that depend on your currently-installed Apache. #####`package_ensure` @@ -348,6 +395,10 @@ Changes the name of the file containing Apache ports configuration. Default is ` Removes all other Apache configs and vhosts, defaults to 'true'. Setting this to 'false' is a stopgap measure to allow the apache module to coexist with existing or otherwise-managed configuration. It is recommended that you move your configuration entirely to resources within this module. +#####`purge_vhost_configs` + +If `vhost_dir` != `confd_dir`, this controls the removal of any configurations that are not managed by Puppet within `vhost_dir`. It defaults to the value of `purge_configs`. Setting this to false is a stopgap measure to allow the apache module to coexist with existing or otherwise unmanaged configurations within `vhost_dir` + #####`sendfile` Makes Apache use the Linux kernel sendfile to serve static files. Defaults to 'On'. @@ -378,7 +429,7 @@ Determines whether the HTTPD service is enabled when the machine is booted. Defa #####`service_ensure` -Determines whether the service should be running. Can be set to 'undef', which is useful when you want to let the service be managed by some other application like Pacemaker. Defaults to 'running'. +Determines whether the service should be running. Valid values are 'true', 'false', 'running', or 'stopped' when Puppet should manage the service. Any other value sets ensure to 'false' for the Apache service, which is useful when you want to let the service be managed by some other application like Pacemaker. Defaults to 'running'. #####`service_name` @@ -392,6 +443,50 @@ Controls how TRACE requests per RFC 2616 are handled. More information about [Tr Changes the location of the configuration directory your virtual host configuration files are placed in. Defaults to 'etc/httpd/conf.d' on RedHat, '/etc/apache2/sites-available' on Debian, and '/usr/local/etc/apache22/Vhosts' on FreeBSD. +#####`apache_name` + +The name of the Apache package to install. This is automatically detected in `::apache::params`. You might need to override this if you are using a non-standard Apache package, such as those from Red Hat's software collections. + +####Defined Type: `apache::custom_config` + +Allows you to create custom configs for Apache. The configuration files are only added to the Apache confd dir if the file is valid. An error is raised during the Puppet run if the file is invalid and `$verify_config` is `true`. + +```puppet + apache::custom_config { 'test': + content => '# Test', + } +``` + +**Parameters within `apache::custom_config`:** + +#####`ensure` + +Specify whether the configuration file is present or absent. Defaults to 'present'. Valid values are 'present' and 'absent'. + +#####`confdir` + +The directory to place the configuration file in. Defaults to `$::apache::confd_dir`. + +#####`content` + +The content of the configuration file. Only one of `$content` and `$source` can be specified. + +#####`priority` + +The priority of the configuration file, used for ordering. Defaults to '25'. + +#####`source` + +The source of the configuration file. Only one of `$content` and `$source` can be specified. + +#####`verify_command` + +The command to use to verify the configuration file. It should use a fully qualified command. Defaults to '/usr/sbin/apachectl -t'. The `$verify_command` is only used if `$verify_config` is `true`. If the `$verify_command` fails, the configuration file is deleted, the Apache service is not notified, and an error is raised during the Puppet run. + +#####`verify_config` + +Boolean to specify whether the configuration file should be validated before the Apache service is notified. Defaults to `true`. + ####Class: `apache::default_mods` Installs default Apache modules based on what OS you are running. @@ -402,7 +497,7 @@ Installs default Apache modules based on what OS you are running. ####Defined Type: `apache::mod` -Used to enable arbitrary Apache HTTPD modules for which there is no specific `apache::mod::[name]` class. The `apache::mod` defined type will also install the required packages to enable the module, if any. +Used to enable arbitrary Apache HTTPD modules for which there is no specific `apache::mod::[name]` class. The `apache::mod` defined type also installs the required packages to enable the module, if any. ```puppet apache::mod { 'rewrite': } @@ -413,6 +508,7 @@ Used to enable arbitrary Apache HTTPD modules for which there is no specific `ap There are many `apache::mod::[name]` classes within this module that can be declared using `include`: +* `actions` * `alias` * `auth_basic` * `auth_kerb` @@ -423,7 +519,7 @@ There are many `apache::mod::[name]` classes within this module that can be decl * `cgid` * `dav` * `dav_fs` -* `dav_svn` +* `dav_svn`* * `deflate` * `dev` * `dir`* @@ -434,13 +530,14 @@ There are many `apache::mod::[name]` classes within this module that can be decl * `fcgid` * `headers` * `include` -* `info` +* `info`* * `itk` * `ldap` * `mime` * `mime_magic`* * `negotiation` * `nss`* +* `pagespeed` (see [`apache::mod::pagespeed`](#class-apachemodpagespeed) below) * `passenger`* * `perl` * `peruser` @@ -456,6 +553,8 @@ There are many `apache::mod::[name]` classes within this module that can be decl * `rewrite` * `rpaf`* * `setenvif` +* `shib`* (see [`apache::mod::shib`](#class-apachemodshib) below) +* `speling` * `ssl`* (see [`apache::mod::ssl`](#class-apachemodssl) below) * `status`* * `suphp` @@ -465,9 +564,148 @@ There are many `apache::mod::[name]` classes within this module that can be decl * `wsgi` (see [`apache::mod::wsgi`](#class-apachemodwsgi) below) * `xsendfile` -Modules noted with a * indicate that the module has settings and, thus, a template that includes parameters. These parameters control the module's configuration. Most of the time, these parameters will not require any configuration or attention. +Modules noted with a * indicate that the module has settings and, thus, a template that includes parameters. These parameters control the module's configuration. Most of the time, these parameters do not require any configuration or attention. + +The modules mentioned above, and other Apache modules that have templates, cause template files to be dropped along with the mod install. The module will not work without the template. Any module without a template installs the package but drops no files. + +####Class: `apache::mod::info` + +Installs and manages mod_info which provides a comprehensive overview of the server configuration. + +Full documentation for mod_info is available from [Apache](http://httpd.apache.org/docs/2.2/mod/mod_info.html). + +These are the default settings: + +```puppet + $allow_from = ['127.0.0.1','::1'], + $apache_version = $::apache::apache_version, + $restrict_access = true, +``` + +To set the addresses that are allowed to access /server-info add the following: + +```puppet + class {'apache::mod::info': + allow_from => [ + '10.10.36', + '10.10.38', + '127.0.0.1', + ], + } +``` + +To disable the access restrictions add the following: + +```puppet + class {'apache::mod::info': + restrict_access => false, + } +``` + +It is not recommended to leave this set to false though it can be very useful for testing. For this reason, you can insert this setting in your normal code to temporarily disable the restrictions like so: + +```puppet + class {'apache::mod::info': + restrict_access => false, # false disables the block below + allow_from => [ + '10.10.36', + '10.10.38', + '127.0.0.1', + ], + } +``` + +####Class: `apache::mod::pagespeed` + +Installs and manages mod_pagespeed, which is a Google module that rewrites web pages to reduce latency and bandwidth. + +This module does *not* manage the software repositories needed to automatically install the +mod-pagespeed-stable package. The module does however require that the package be installed, +or be installable using the system's default package provider. You should ensure that this +pre-requisite is met or declaring `apache::mod::pagespeed` causes the Puppet run to fail. + +These are the defaults: + +```puppet + class { 'apache::mod::pagespeed': + inherit_vhost_config => 'on', + filter_xhtml => false, + cache_path => '/var/cache/mod_pagespeed/', + log_dir => '/var/log/pagespeed', + memcache_servers => [], + rewrite_level => 'CoreFilters', + disable_filters => [], + enable_filters => [], + forbid_filters => [], + rewrite_deadline_per_flush_ms => 10, + additional_domains => undef, + file_cache_size_kb => 102400, + file_cache_clean_interval_ms => 3600000, + lru_cache_per_process => 1024, + lru_cache_byte_limit => 16384, + css_flatten_max_bytes => 2048, + css_inline_max_bytes => 2048, + css_image_inline_max_bytes => 2048, + image_inline_max_bytes => 2048, + js_inline_max_bytes => 2048, + css_outline_min_bytes => 3000, + js_outline_min_bytes => 3000, + inode_limit => 500000, + image_max_rewrites_at_once => 8, + num_rewrite_threads => 4, + num_expensive_rewrite_threads => 4, + collect_statistics => 'on', + statistics_logging => 'on', + allow_view_stats => [], + allow_pagespeed_console => [], + allow_pagespeed_message => [], + message_buffer_size => 100000, + additional_configuration => { } + } +``` + +Full documentation for mod_pagespeed is available from [Google](http://modpagespeed.com). -The modules mentioned above, and other Apache modules that have templates, will cause template files to be dropped along with the mod install and the module will not work without the template. Any module without a template will install the package but drop no files. +####Class: `apache::mod::php` + +Installs and configures mod_php. The defaults are OS-dependant. + +Overriding the package name: +```puppet + class {'::apache::mod::php': + package_name => "php54-php", + path => "${::apache::params::lib_path}/libphp54-php5.so", + } +``` + +Overriding the default configuartion: +```puppet + class {'::apache::mod::php': + source => 'puppet:///modules/apache/my_php.conf', + } +``` + +or +```puppet + class {'::apache::mod::php': + template => 'apache/php.conf.erb', + } +``` + +or + +```puppet + class {'::apache::mod::php': + content => ' +AddHandler php5-script .php +AddType text/html .php', + } +``` +####Class: `apache::mod::shib` + +Installs the [Shibboleth](http://shibboleth.net/) module for Apache which allows the use of SAML2 Single-Sign-On (SSO) authentication by Shibboleth Identity Providers and Shibboleth Federations. This class only installs and configures the Apache components of a Shibboleth Service Provider (a web application that consumes Shibboleth SSO identities). The Shibboleth configuration can be managed manually, with Puppet, or using a [Shibboleth Puppet Module](https://github.com/aethylred/puppet-shibboleth). + +Defining this class enables the Shibboleth specific parameters in `apache::vhost` instances. ####Class: `apache::mod::ssl` @@ -477,6 +715,7 @@ Installs Apache SSL capabilities and uses the ssl.conf.erb template. These are t class { 'apache::mod::ssl': ssl_compression => false, ssl_options => [ 'StdEnvVars' ], + ssl_protocol => [ 'all', '-SSLv2', '-SSLv3'], } ``` @@ -484,7 +723,7 @@ To *use* SSL with a virtual host, you must either set the`default_ssl_vhost` par ####Class: `apache::mod::wsgi` -Enables Python support in the WSGI module. To use, simply `include 'apache::mod::wsgi'`. +Enables Python support in the WSGI module. To use, simply `include 'apache::mod::wsgi'`. For customized parameters, which tell Apache how Python is currently configured on the operating system, @@ -498,12 +737,132 @@ For customized parameters, which tell Apache how Python is currently configured More information about [WSGI](http://modwsgi.readthedocs.org/en/latest/). +####Class: `apache::mod::fcgid` + +Installs and configures mod_fcgid. + +The class makes no effort to list all available options, but rather uses an options hash to allow for ultimate flexibility: + +```puppet + class { 'apache::mod::fcgid': + options => { + 'FcgidIPCDir' => '/var/run/fcgidsock', + 'SharememPath' => '/var/run/fcgid_shm', + 'AddHandler' => 'fcgid-script .fcgi', + }, + } +``` + +For a full list op options, see the [official mod_fcgid documentation](https://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html). + +It is also possible to set the FcgidWrapper per directory per vhost. You must ensure the fcgid module is loaded because there is no auto loading. + +```puppet + include apache::mod::fcgid + apache::vhost { 'example.org': + docroot => '/var/www/html', + directories => { + path => '/var/www/html', + fcgiwrapper => { + command => '/usr/local/bin/fcgiwrapper', + } + }, + } +``` + +See [FcgidWrapper documentation](https://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html#fcgidwrapper) for more information. + +####Class: `apache::mod::negotiation` + +Installs and configures mod_negotiation. If there are not provided any +parameter, default apache mod_negotiation configuration is done. + +```puppet + class { '::apache::mod::negotiation': + force_language_priority => 'Prefer', + language_priority => [ 'es', 'en', 'ca', 'cs', 'da', 'de', 'el', 'eo' ], + } +``` + +**Parameters within `apache::mod::negotiation`:** + +#####`force_language_priority` + +A string that sets the `ForceLanguagePriority` option. Defaults to `Prefer Fallback`. + +#####`language_priority` + +An array of languages to set the `LanguagePriority` option of the module. + +####Class: `apache::mod::deflate` + +Installs and configures mod_deflate. If no parameters are provided, a default configuration is applied. + +```puppet + class { '::apache::mod::deflate': + types => [ 'text/html', 'text/css' ], + notes => { + 'Input' => 'instream', + 'Ratio' => 'ratio', + }, + } +``` + +#####`types` + +An array of mime types to be deflated. + +#####`notes` + +A hash where the key represents the type and the value represents the note name. + + +####Class: `apache::mod::reqtimeout` + +Installs and configures mod_reqtimeout. Defaults to recommended apache +mod_reqtimeout configuration. + +```puppet + class { '::apache::mod::reqtimeout': + timeouts => ['header=20-40,MinRate=500', 'body=20,MinRate=500'], + } +``` + +####Class: `apache::mod::reqtimeout` + +This wrapper around mod_version warns on Debian and Ubuntu systems with Apache httpd 2.4 +about loading mod_version, as on these platforms it's already built-in. + +```puppet + include '::apache::mod::version' +``` + +#####`timeouts` + +A string or an array that sets the `RequestReadTimeout` option. Defaults to +`['header=20-40,MinRate=500', 'body=20,MinRate=500']`. + + ####Defined Type: `apache::vhost` -The Apache module allows a lot of flexibility in the setup and configuration of virtual hosts. This flexibility is due, in part, to `vhost`'s being a defined resource type, which allows it to be evaluated multiple times with different parameters. +The Apache module allows a lot of flexibility in the setup and configuration of virtual hosts. This flexibility is due, in part, to `vhost` being a defined resource type, which allows it to be evaluated multiple times with different parameters. The `vhost` defined type allows you to have specialized configurations for virtual hosts that have requirements outside the defaults. You can set up a default vhost within the base `::apache` class, as well as set a customized vhost as default. Your customized vhost (priority 10) will be privileged over the base class vhost (15). +The `vhost` defined type uses `concat::fragment` to build the configuration file, so if you want to inject custom fragments for pieces of the configuration not supported by default by the defined type, you can add a custom fragment. For the `order` parameter for the custom fragment, the `vhost` defined type uses multiples of 10, so any order that isn't a multiple of 10 should work. + +```puppet + apache::vhost { "example.com": + docroot => '/var/www/html', + priority => '25', + } + concat::fragment { "example.com-my_custom_fragment": + target => '25-example.com.conf', + order => 11, + content => '# my custom comment', + } +``` + If you have a series of specific configurations and do not want a base `::apache` class default vhost, make sure to set the base class `default_vhost` to 'false'. ```puppet @@ -516,7 +875,7 @@ If you have a series of specific configurations and do not want a base `::apache #####`access_log` -Specifies whether `*_access.log` directives (`*_file`,`*_pipe`, or `*_syslog`) should be configured. Setting the value to 'false' will choose none. Defaults to 'true'. +Specifies whether `*_access.log` directives (`*_file`,`*_pipe`, or `*_syslog`) should be configured. Setting the value to 'false' chooses none. Defaults to 'true'. #####`access_log_file` @@ -538,6 +897,10 @@ Specifies the use of either a LogFormat nickname or a custom format string for t Specifies that only requests with particular environment variables be logged. Defaults to 'undef'. +#####`add_default_charset` + +Sets [AddDefaultCharset](http://httpd.apache.org/docs/current/mod/core.html#adddefaultcharset), a default value for the media charset, which is added to text/plain and text/html responses. + #####`add_listen` Determines whether the vhost creates a Listen statement. The default value is 'true'. @@ -550,26 +913,41 @@ Specifies paths to additional static, vhost-specific Apache configuration files. #####`aliases` -Passes a list of hashes to the vhost to create Alias or AliasMatch directives as per the [mod_alias documentation](http://httpd.apache.org/docs/current/mod/mod_alias.html). These hashes are formatted as follows: +Passes a list of hashes to the vhost to create Alias, AliasMatch, ScriptAlias or ScriptAliasMatch directives as per the [mod_alias documentation](http://httpd.apache.org/docs/current/mod/mod_alias.html). These hashes are formatted as follows: ```puppet aliases => [ - { aliasmatch => '^/image/(.*)\.jpg$', - path => '/files/jpg.images/$1.jpg', + { aliasmatch => '^/image/(.*)\.jpg$', + path => '/files/jpg.images/$1.jpg', } - { alias => '/image', - path => '/ftp/pub/image', + { alias => '/image', + path => '/ftp/pub/image', + }, + { scriptaliasmatch => '^/cgi-bin(.*)', + path => '/usr/local/share/cgi-bin$1', + }, + { scriptalias => '/nagios/cgi-bin/', + path => '/usr/lib/nagios/cgi-bin/', + }, + { alias => '/nagios', + path => '/usr/share/nagios/html', }, ], ``` -For `alias` and `aliasmatch` to work, each will need a corresponding context, such as '< Directory /path/to/directory>' or ''. The Alias and AliasMatch directives are created in the order specified in the `aliases` parameter. As described in the [`mod_alias` documentation](http://httpd.apache.org/docs/current/mod/mod_alias.html), more specific `alias` or `aliasmatch` parameters should come before the more general ones to avoid shadowing. +For `alias`, `aliasmatch`, `scriptalias` and `scriptaliasmatch` to work, each needs a corresponding context, such as `` or ``. The directives are created in the order specified in the `aliases` parameter. As described in the [`mod_alias` documentation](http://httpd.apache.org/docs/current/mod/mod_alias.html), more specific `alias`, `aliasmatch`, `scriptalias` or `scriptaliasmatch` parameters should come before the more general ones to avoid shadowing. + +*Note*: Using the `aliases` parameter is preferred over the `scriptaliases` parameter since here the order of the various alias directives among each other can be controlled precisely. Defining ScriptAliases using the `scriptaliases` parameter means *all* ScriptAlias directives will come after *all* Alias directives, which can lead to Alias directives shadowing ScriptAlias directives. This is often problematic, for example in case of Nagios. + +*Note:* If `apache::mod::passenger` is loaded and `PassengerHighPerformance => true` is set, then Alias might have issues honoring the `PassengerEnabled => off` statement. See [this article](http://www.conandalton.net/2010/06/passengerenabled-off-not-working.html) for details. -*Note:* If `apache::mod::passenger` is loaded and `PassengerHighPerformance => true` is set, then Alias may have issues honoring the `PassengerEnabled => off` statement. See [this article](http://www.conandalton.net/2010/06/passengerenabled-off-not-working.html) for details. +#####`allow_encoded_slashes` + +This sets the [`AllowEncodedSlashes` declaration](http://httpd.apache.org/docs/current/mod/core.html#allowencodedslashes) for the vhost, overriding the server default. This modifies the vhost responses to URLs with `\` and `/` characters. The default is undefined, which omits the declaration from the server configuration and select the Apache default setting of `Off`. Allowed values are: `on`, `off` or `nodecode`. #####`block` -Specifies the list of things Apache will block access to. The default is an empty set, '[]'. Currently, the only option is 'scm', which blocks web access to .svn, .git and .bzr directories. +Specifies the list of things Apache blocks access to. The default is an empty set, '[]'. Currently, the only option is 'scm', which blocks web access to .svn, .git and .bzr directories. #####`custom_fragment` @@ -589,7 +967,9 @@ Sets the list of resources to look for when a client requests an index of the di #####`docroot` -Provides the [DocumentRoot](http://httpd.apache.org/docs/current/mod/core.html#documentroot) directive, which identifies the directory Apache serves files from. Required. +Provides the +[DocumentRoot](http://httpd.apache.org/docs/current/mod/core.html#documentroot) +directive, which identifies the directory Apache serves files from. Required. #####`docroot_group` @@ -599,6 +979,14 @@ Sets group access to the docroot directory. Defaults to 'root'. Sets individual user access to the docroot directory. Defaults to 'root'. +#####`docroot_mode` + +Sets access permissions of the docroot directory. Defaults to 'undef'. + +#####`manage_docroot` + +Whether to manage to docroot directory at all. Defaults to 'true'. + #####`error_log` Specifies whether `*_error.log` directives should be configured. Defaults to 'true'. @@ -646,11 +1034,11 @@ Sets the IP address the vhost listens on. Defaults to listen on all IPs. #####`ip_based` -Enables an [IP-based](httpd.apache.org/docs/current/vhosts/ip-based.html) vhost. This parameter inhibits the creation of a NameVirtualHost directive, since those are used to funnel requests to name-based vhosts. Defaults to 'false'. +Enables an [IP-based](http://httpd.apache.org/docs/current/vhosts/ip-based.html) vhost. This parameter inhibits the creation of a NameVirtualHost directive, since those are used to funnel requests to name-based vhosts. Defaults to 'false'. #####`itk` -Configures [ITK](http://mpm-itk.sesse.net/) in a hash. Keys may be: +Configures [ITK](http://mpm-itk.sesse.net/) in a hash. Keys can be: * user + group * `assignuseridexpr` @@ -660,7 +1048,7 @@ Configures [ITK](http://mpm-itk.sesse.net/) in a hash. Keys may be: * `limituidrange` (Linux 3.5.0 or newer) * `limitgidrange` (Linux 3.5.0 or newer) -Usage will typically look like: +Usage typically looks like: ```puppet apache::vhost { 'sample.example.net': @@ -676,6 +1064,15 @@ Usage will typically look like: Specifies the location of the virtual host's logfiles. Defaults to '/var/log//'. +#####`$logroot_ensure` + +Determines whether or not to remove the logroot directory for a virtual host. Valid values are 'directory', or 'absent'. + +#####`logroot_mode` + +Overrides the mode the logroot directory is set to. Defaults to undef. Do NOT give people write access to the directory the logs are stored +in without being aware of the consequences; see http://httpd.apache.org/docs/2.4/logs.html#security for details. + #####`log_level` Specifies the verbosity of the error log. Defaults to 'warn' for the global server configuration and can be overridden on a per-vhost basis. Valid values are 'emerg', 'alert', 'crit', 'error', 'warn', 'notice', 'info' or 'debug'. @@ -684,6 +1081,10 @@ Specifies the verbosity of the error log. Defaults to 'warn' for the global serv Specifies URLs you do not want to proxy. This parameter is meant to be used in combination with [`proxy_dest`](#proxy_dest). +#####`proxy_preserve_host` + +Sets the [ProxyPreserveHost Directive](http://httpd.apache.org/docs/2.2/mod/mod_proxy.html#proxypreservehost). true Enables the Host: line from an incoming request to be proxied to the host instead of hostname . false sets this option to off (default). + #####`options` Sets the [Options](http://httpd.apache.org/docs/current/mod/core.html#options) for the specified virtual host. Defaults to '['Indexes','FollowSymLinks','MultiViews']', as demonstrated below: @@ -701,19 +1102,39 @@ Sets the [Options](http://httpd.apache.org/docs/current/mod/core.html#options) f Sets the overrides for the specified virtual host. Accepts an array of [AllowOverride](http://httpd.apache.org/docs/current/mod/core.html#allowoverride) arguments. Defaults to '[none]'. +#####`passenger_app_root` + +Sets [PassengerRoot](https://www.phusionpassenger.com/documentation/Users%20guide%20Apache.html#PassengerAppRoot), the location of the Passenger application root if different from the DocumentRoot. + +#####`passenger_ruby` + +Sets [PassengerRuby](https://www.phusionpassenger.com/documentation/Users%20guide%20Apache.html#PassengerRuby) on this virtual host, the Ruby interpreter to use for the application. + +#####`passenger_min_instances` + +Sets [PassengerMinInstances](https://www.phusionpassenger.com/documentation/Users%20guide%20Apache.html#PassengerMinInstances), the minimum number of application processes to run. + +#####`passenger_start_timeout` + +Sets [PassengerStartTimeout](https://www.phusionpassenger.com/documentation/Users%20guide%20Apache.html#_passengerstarttimeout_lt_seconds_gt), the timeout for the application startup. + +#####`passenger_pre_start` + +Sets [PassengerPreStart](https://www.phusionpassenger.com/documentation/Users%20guide%20Apache.html#PassengerPreStart), the URL of the application if pre-starting is required. + #####`php_admin_flags & values` Allows per-vhost setting [`php_admin_value`s or `php_admin_flag`s](http://php.net/manual/en/configuration.changes.php). These flags or values cannot be overwritten by a user or an application. Defaults to '[]'. #####`port` -Sets the port the host is configured on. The module's defaults ensure the host listens on port 80 for non-SSL vhosts and port 443 for SSL vhosts. The host will only listen on the port set in this parameter. +Sets the port the host is configured on. The module's defaults ensure the host listens on port 80 for non-SSL vhosts and port 443 for SSL vhosts. The host only listens on the port set in this parameter. #####`priority` Sets the relative load-order for Apache HTTPD VirtualHost configuration files. Defaults to '25'. -If nothing matches the priority, the first name-based vhost will be used. Likewise, passing a higher priority will cause the alphabetically first name-based vhost to be used if no other names match. +If nothing matches the priority, the first name-based vhost is used. Likewise, passing a higher priority causes the alphabetically first name-based vhost to be used if no other names match. *Note:* You should not need to use this parameter. However, if you do use it, be aware that the `default_vhost` parameter for `apache::vhost` passes a priority of '15'. @@ -723,22 +1144,31 @@ Specifies the destination address of a [ProxyPass](http://httpd.apache.org/docs/ #####`proxy_pass` -Specifies an array of `path => URI` for a [ProxyPass](http://httpd.apache.org/docs/current/mod/mod_proxy.html#proxypass) configuration. Defaults to 'undef'. +Specifies an array of `path => URI` for a [ProxyPass](http://httpd.apache.org/docs/current/mod/mod_proxy.html#proxypass) configuration. Defaults to 'undef'. Optionally parameters can be added as an array. ```puppet apache::vhost { 'site.name.fdqn': - … + … proxy_pass => [ { 'path' => '/a', 'url' => 'http://backend-a/' }, { 'path' => '/b', 'url' => 'http://backend-b/' }, - { 'path' => '/c', 'url' => 'http://backend-a/c' }, + { 'path' => '/c', 'url' => 'http://backend-a/c', 'params' => 'max=20 ttl=120 retry=300' }, + { 'path' => '/l', 'url' => 'http://backend-xy', + 'reverse_urls' => ['http://backend-x', 'http://backend-y'] }, + { 'path' => '/d', 'url' => 'http://backend-a/d', + 'params' => { 'retry' => '0', 'timeout' => '5' }, }, + { 'path' => '/e', 'url' => 'http://backend-a/e', + 'keywords' => ['nocanon', 'interpolate'] }, ], } ``` +`reverse_urls` is optional and can be an array or a string. It is useful when used with `mod_proxy_balancer`. +`params` is an optional parameter. It allows to provide the ProxyPass key=value parameters (Connection settings). + #####`rack_base_uris` -Specifies the resource identifiers for a rack configuration. The file paths specified will be listed as rack application roots for [Phusion Passenger](http://www.modrails.com/documentation/Users%20guide%20Apache.html#_railsbaseuri_and_rackbaseuri) in the _rack.erb template. Defaults to 'undef'. +Specifies the resource identifiers for a rack configuration. The file paths specified are listed as rack application roots for [Phusion Passenger](http://www.modrails.com/documentation/Users%20guide%20Apache.html#_railsbaseuri_and_rackbaseuri) in the _rack.erb template. Defaults to 'undef'. #####`redirect_dest` @@ -746,7 +1176,7 @@ Specifies the address to redirect to. Defaults to 'undef'. #####`redirect_source` -Specifies the source URIs that will redirect to the destination specified in `redirect_dest`. If more than one item for redirect is supplied, the source and destination must be the same length and the items will be order-dependent. +Specifies the source URIs that redirect to the destination specified in `redirect_dest`. If more than one item for redirect is supplied, the source and destination must be the same length, and the items are order-dependent. ```puppet apache::vhost { 'site.name.fdqn': @@ -767,15 +1197,16 @@ Specifies the status to append to the redirect. Defaults to 'undef'. } ``` -#####`redirectmatch_regexp` & `redirectmatch_status` +#####`redirectmatch_regexp` & `redirectmatch_status` & `redirectmatch_dest` -Determines which server status should be raised for a given regular expression. Entered as an array. Defaults to 'undef'. +Determines which server status should be raised for a given regular expression and where to forward the user to. Entered as arrays. Defaults to 'undef'. ```puppet apache::vhost { 'site.name.fdqn': … redirectmatch_status => ['404','404'], redirectmatch_regexp => ['\.git(/.*|$)/','\.svn(/.*|$)'], + redirectmatch_dest => ['http://www.example.com/1','http://www.example.com/2'], } ``` @@ -795,9 +1226,9 @@ Modifies collected [request headers](http://httpd.apache.org/docs/current/mod/mo #####`rewrites` -Creates URL rewrite rules. Expects an array of hashes, and the hash keys can be any of 'comment', 'rewrite_base', 'rewrite_cond', or 'rewrite_rule'. Defaults to 'undef'. +Creates URL rewrite rules. Expects an array of hashes, and the hash keys can be any of 'comment', 'rewrite_base', 'rewrite_cond', or 'rewrite_rule'. Defaults to 'undef'. -For example, you can specify that anyone trying to access index.html will be served welcome.html +For example, you can specify that anyone trying to access index.html is served welcome.html ```puppet apache::vhost { 'site.name.fdqn': @@ -806,7 +1237,7 @@ For example, you can specify that anyone trying to access index.html will be ser } ``` -The parameter allows rewrite conditions that, when true, will execute the associated rule. For instance, if you wanted to rewrite URLs only if the visitor is using IE +The parameter allows rewrite conditions that, when true, execute the associated rule. For instance, if you wanted to rewrite URLs only if the visitor is using IE ```puppet apache::vhost { 'site.name.fdqn': @@ -852,11 +1283,11 @@ Multiple rewrites and conditions are also possible rewrite_cond => ['%{HTTP_USER_AGENT} ^MSIE'], rewrite_rule => ['^index\.html$ /index.IE.html [L]'], }, - } + { rewrite_base => /apps/, rewrite_rule => ['^index\.cgi$ index.php', '^index\.html$ index.php', '^index\.asp$ index.html'], }, - ], + ], } ``` @@ -868,6 +1299,8 @@ Defines a directory of CGI scripts to be aliased to the path '/cgi-bin', for exa #####`scriptaliases` +*Note*: This parameter is deprecated in favour of the `aliases` parameter. + Passes an array of hashes to the vhost to create either ScriptAlias or ScriptAliasMatch statements as per the [`mod_alias` documentation](http://httpd.apache.org/docs/current/mod/mod_alias.html). These hashes are formatted as follows: ```puppet @@ -895,7 +1328,7 @@ The ScriptAlias and ScriptAliasMatch directives are created in the order specifi #####`serveradmin` -Specifies the email address Apache will display when it renders one of its error pages. Defaults to 'undef'. +Specifies the email address Apache displays when it renders one of its error pages. Defaults to 'undef'. #####`serveraliases` @@ -915,7 +1348,7 @@ Used by HTTPD to conditionally set environment variables for vhosts. Defaults to #####`suphp_addhandler`, `suphp_configpath`, & `suphp_engine` -Set up a virtual host with [suPHP](http://suphp.org/DocumentationView.html?file=apache/CONFIG). +Set up a virtual host with [suPHP](http://suphp.org/DocumentationView.html?file=apache/CONFIG). `suphp_addhandler` defaults to 'php5-script' on RedHat and FreeBSD, and 'x-httpd-php' on Debian. @@ -940,11 +1373,11 @@ To set up a virtual host with suPHP #####`vhost_name` -Enables name-based virtual hosting. If no IP is passed to the virtual host but the vhost is assigned a port, then the vhost name will be 'vhost_name:port'. If the virtual host has no assigned IP or port, the vhost name will be set to the title of the resource. Defaults to '*'. +Enables name-based virtual hosting. If no IP is passed to the virtual host, but the vhost is assigned a port, then the vhost name is 'vhost_name:port'. If the virtual host has no assigned IP or port, the vhost name is set to the title of the resource. Defaults to '*'. -#####`virtual_docroot` +#####`virtual_docroot` -Sets up a virtual host with a wildcard alias subdomain mapped to a directory with the same name. For example, 'http://example.com' would map to '/var/www/example.com'. Defaults to 'false'. +Sets up a virtual host with a wildcard alias subdomain mapped to a directory with the same name. For example, 'http://example.com' would map to '/var/www/example.com'. Defaults to 'false'. ```puppet apache::vhost { 'subdomain.loc': @@ -956,7 +1389,7 @@ Sets up a virtual host with a wildcard alias subdomain mapped to a directory wit } ``` -#####`wsgi_daemon_process`, `wsgi_daemon_process_options`, `wsgi_process_group`, & `wsgi_script_aliases` +#####`wsgi_daemon_process`, `wsgi_daemon_process_options`, `wsgi_process_group`, `wsgi_script_aliases`, & `wsgi_pass_authorization` Set up a virtual host with [WSGI](https://code.google.com/p/modwsgi/). @@ -964,10 +1397,14 @@ Set up a virtual host with [WSGI](https://code.google.com/p/modwsgi/). `wsgi_daemon_process_options` is optional and defaults to 'undef'. -`wsgi_process_group` sets the group ID the virtual host will run under. Defaults to 'undef'. +`wsgi_process_group` sets the group ID the virtual host runs under. Defaults to 'undef'. `wsgi_script_aliases` requires a hash of web paths to filesystem .wsgi paths. Defaults to 'undef'. +`wsgi_pass_authorization` the WSGI application handles authorisation instead of Apache when set to 'On'. For more information see [here] (http://modwsgi.readthedocs.org/en/latest/configuration-directives/WSGIPassAuthorization.html). Defaults to 'undef' where apache sets the defaults setting to 'Off'. + +`wsgi_chunked_request` enables support for chunked requests. Defaults to 'undef'. + To set up a virtual host with WSGI ```puppet @@ -976,12 +1413,13 @@ To set up a virtual host with WSGI docroot => '/var/www/pythonapp', wsgi_daemon_process => 'wsgi', wsgi_daemon_process_options => - { processes => '2', - threads => '15', + { processes => '2', + threads => '15', display-name => '%{GROUP}', }, wsgi_process_group => 'wsgi', wsgi_script_aliases => { '/' => '/var/www/demo.wsgi' }, + wsgi_chunked_request => 'On', } ``` @@ -989,71 +1427,63 @@ To set up a virtual host with WSGI The `directories` parameter within the `apache::vhost` class passes an array of hashes to the vhost to create [Directory](http://httpd.apache.org/docs/current/mod/core.html#directory), [File](http://httpd.apache.org/docs/current/mod/core.html#files), and [Location](http://httpd.apache.org/docs/current/mod/core.html#location) directive blocks. These blocks take the form, '< Directory /path/to/directory>...< /Directory>'. -Each hash passed to `directories` must contain `path` as one of the keys. You may also pass in `provider` which, if missing, defaults to 'directory'. (A full list of acceptable keys is below.) General usage will look something like +The `path` key sets the path for the directory, files, and location blocks. Its value must be a path for the 'directory', 'files', and 'location' providers, or a regex for the 'directorymatch', 'filesmatch', or 'locationmatch' providers. Each hash passed to `directories` **must** contain `path` as one of the keys. -```puppet - apache::vhost { 'sample.example.net': - docroot => '/path/to/directory', - directories => [ - { path => '/path/to/directory', => }, - { path => '/path/to/another/directory', => }, - ], - } -``` - -*Note:* At least one directory should match the `docroot` parameter. Once you start declaring directories, `apache::vhost` assumes that all required Directory blocks will be declared. If not defined, a single default Directory block will be created that matches the `docroot` parameter. +The `provider` key is optional. If missing, this key defaults to 'directory'. Valid values for `provider` are 'directory', 'files', 'location', 'directorymatch', 'filesmatch', or 'locationmatch'. If you set `provider` to 'directorymatch', it uses the keyword 'DirectoryMatch' in the Apache config file. -The `provider` key can be set to 'directory', 'files', or 'location'. If the path starts with a [~](https://httpd.apache.org/docs/current/mod/core.html#files), HTTPD will interpret this as the equivalent of DirectoryMatch, FilesMatch, or LocationMatch. +General `directories` usage looks something like ```puppet apache::vhost { 'files.example.net': docroot => '/var/www/files', directories => [ - { 'path' => '/var/www/files', - 'provider' => 'files', - 'deny' => 'from all' + { 'path' => '/var/www/files', + 'provider' => 'files', + 'deny' => 'from all' }, ], } ``` -Available handlers, represented as keys, should be placed within the `directory`,`'files`, or `location` hashes. This looks like +*Note:* At least one directory should match the `docroot` parameter. After you start declaring directories, `apache::vhost` assumes that all required Directory blocks will be declared. If not defined, a single default Directory block is created that matches the `docroot` parameter. + +Available handlers, represented as keys, should be placed within the `directory`,`'files`, or `location` hashes. This looks like ```puppet - apache::vhost { 'sample.example.net': + apache::vhost { 'sample.example.net': docroot => '/path/to/directory', directories => [ { path => '/path/to/directory', handler => value } ], } ``` -Any handlers you do not set in these hashes will be considered 'undefined' within Puppet and will not be added to the virtual host, resulting in the module using their default values. Currently this is the list of supported handlers: +Any handlers you do not set in these hashes are considered 'undefined' within Puppet and are not added to the virtual host, resulting in the module using their default values. Supported handlers are: ######`addhandlers` -Sets [AddHandler](http://httpd.apache.org/docs/current/mod/mod_mime.html#addhandler) directives, which map filename extensions to the specified handler. Accepts a list of hashes, with `extensions` serving to list the extensions being managed by the handler, and takes the form: `{ handler => 'handler-name', extensions => ['extension']}`. +Sets [AddHandler](http://httpd.apache.org/docs/current/mod/mod_mime.html#addhandler) directives, which map filename extensions to the specified handler. Accepts a list of hashes, with `extensions` serving to list the extensions being managed by the handler, and takes the form: `{ handler => 'handler-name', extensions => ['extension']}`. ```puppet apache::vhost { 'sample.example.net': docroot => '/path/to/directory', - directories => [ + directories => [ { path => '/path/to/directory', addhandlers => [{ handler => 'cgi-script', extensions => ['.cgi']}], - }, + }, ], } ``` ######`allow` -Sets an [Allow](http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#allow) directive, which groups authorizations based on hostnames or IPs. **Deprecated:** This parameter is being deprecated due to a change in Apache. It will only work with Apache 2.2 and lower. +Sets an [Allow](http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#allow) directive, which groups authorizations based on hostnames or IPs. **Deprecated:** This parameter is being deprecated due to a change in Apache. It only works with Apache 2.2 and lower. You can use it as a single string for one rule or as an array for more than one. ```puppet apache::vhost { 'sample.example.net': docroot => '/path/to/directory', - directories => [ - { path => '/path/to/directory', - allow => 'from example.org', - }, + directories => [ + { path => '/path/to/directory', + allow => 'from example.org', + }, ], } ``` @@ -1065,10 +1495,10 @@ Sets the types of directives allowed in [.htaccess](http://httpd.apache.org/docs ```puppet apache::vhost { 'sample.example.net': docroot => '/path/to/directory', - directories => [ - { path => '/path/to/directory', - allow_override => ['AuthConfig', 'Indexes'], - }, + directories => [ + { path => '/path/to/directory', + allow_override => ['AuthConfig', 'Indexes'], + }, ], } ``` @@ -1079,35 +1509,35 @@ Sets the value for [AuthBasicAuthoritative](https://httpd.apache.org/docs/curren ######`auth_basic_fake` -Sets the value for [AuthBasicFake](httpd.apache.org/docs/current/mod/mod_auth_basic.html#authbasicfake), which statically configures authorization credentials for a given directive block. +Sets the value for [AuthBasicFake](http://httpd.apache.org/docs/current/mod/mod_auth_basic.html#authbasicfake), which statically configures authorization credentials for a given directive block. ######`auth_basic_provider` -Sets the value for [AuthBasicProvider] (httpd.apache.org/docs/current/mod/mod_auth_basic.html#authbasicprovider), which sets the authentication provider for a given location. +Sets the value for [AuthBasicProvider] (http://httpd.apache.org/docs/current/mod/mod_auth_basic.html#authbasicprovider), which sets the authentication provider for a given location. ######`auth_digest_algorithm` -Sets the value for [AuthDigestAlgorithm](httpd.apache.org/docs/current/mod/mod_auth_digest.html#authdigestalgorithm), which selects the algorithm used to calculate the challenge and response hashes. +Sets the value for [AuthDigestAlgorithm](http://httpd.apache.org/docs/current/mod/mod_auth_digest.html#authdigestalgorithm), which selects the algorithm used to calculate the challenge and response hashes. ######`auth_digest_domain` -Sets the value for [AuthDigestDomain](httpd.apache.org/docs/current/mod/mod_auth_digest.html#authdigestdomain), which allows you to specify one or more URIs in the same protection space for digest authentication. +Sets the value for [AuthDigestDomain](http://httpd.apache.org/docs/current/mod/mod_auth_digest.html#authdigestdomain), which allows you to specify one or more URIs in the same protection space for digest authentication. ######`auth_digest_nonce_lifetime` -Sets the value for [AuthDigestNonceLifetime](httpd.apache.org/docs/current/mod/mod_auth_digest.html#authdigestnoncelifetime), which controls how long the server nonce is valid. +Sets the value for [AuthDigestNonceLifetime](http://httpd.apache.org/docs/current/mod/mod_auth_digest.html#authdigestnoncelifetime), which controls how long the server nonce is valid. ######`auth_digest_provider` -Sets the value for [AuthDigestProvider](httpd.apache.org/docs/current/mod/mod_auth_digest.html#authdigestprovider), which sets the authentication provider for a given location. +Sets the value for [AuthDigestProvider](http://httpd.apache.org/docs/current/mod/mod_auth_digest.html#authdigestprovider), which sets the authentication provider for a given location. ######`auth_digest_qop` -Sets the value for [AuthDigestQop](httpd.apache.org/docs/current/mod/mod_auth_digest.html#authdigestqop), which determines the quality-of-protection to use in digest authentication. +Sets the value for [AuthDigestQop](http://httpd.apache.org/docs/current/mod/mod_auth_digest.html#authdigestqop), which determines the quality-of-protection to use in digest authentication. ######`auth_digest_shmem_size` -Sets the value for [AuthAuthDigestShmemSize](httpd.apache.org/docs/current/mod/mod_auth_digest.html#authdigestshmemsize), which defines the amount of shared memory allocated to the server for keeping track of clients. +Sets the value for [AuthAuthDigestShmemSize](http://httpd.apache.org/docs/current/mod/mod_auth_digest.html#authdigestshmemsize), which defines the amount of shared memory allocated to the server for keeping track of clients. ######`auth_group_file` @@ -1123,11 +1553,11 @@ Sets the entity name you're requiring to allow access. Read more about [Require] ######`auth_type` -Sets the value for [AuthType](httpd.apache.org/docs/current/mod/mod_authn_core.html#authtype), which guides the type of user authentication. +Sets the value for [AuthType](http://httpd.apache.org/docs/current/mod/mod_authn_core.html#authtype), which guides the type of user authentication. ######`auth_user_file` -Sets the value for [AuthUserFile](httpd.apache.org/docs/current/mod/mod_authn_file.html#authuserfile), which sets the name of the text file containing the users/passwords for authentication. +Sets the value for [AuthUserFile](http://httpd.apache.org/docs/current/mod/mod_authn_file.html#authuserfile), which sets the name of the text file containing the users/passwords for authentication. ######`custom_fragment` @@ -1135,7 +1565,7 @@ Pass a string of custom configuration directives to be placed at the end of the ```puppet apache::vhost { 'monitor': - … + … custom_fragment => ' SetHandler balancer-manager @@ -1153,29 +1583,29 @@ Pass a string of custom configuration directives to be placed at the end of the ######`deny` -Sets a [Deny](http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#deny) directive, specifying which hosts are denied access to the server. **Deprecated:** This parameter is being deprecated due to a change in Apache. It will only work with Apache 2.2 and lower. +Sets a [Deny](http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#deny) directive, specifying which hosts are denied access to the server. **Deprecated:** This parameter is being deprecated due to a change in Apache. It only works with Apache 2.2 and lower. ```puppet apache::vhost { 'sample.example.net': docroot => '/path/to/directory', - directories => [ - { path => '/path/to/directory', - deny => 'from example.org', - }, + directories => [ + { path => '/path/to/directory', + deny => 'from example.org', + }, ], } ``` ######`error_documents` -An array of hashes used to override the [ErrorDocument](https://httpd.apache.org/docs/current/mod/core.html#errordocument) settings for the directory. +An array of hashes used to override the [ErrorDocument](https://httpd.apache.org/docs/current/mod/core.html#errordocument) settings for the directory. ```puppet apache::vhost { 'sample.example.net': - directories => [ + directories => [ { path => '/srv/www', error_documents => [ - { 'error_code' => '503', + { 'error_code' => '503', 'document' => '/service-unavail', }, ], @@ -1200,14 +1630,14 @@ Adds lines for [Header](http://httpd.apache.org/docs/current/mod/mod_headers.htm ######`index_options` -Allows configuration settings for [directory indexing](httpd.apache.org/docs/current/mod/mod_autoindex.html#indexoptions). +Allows configuration settings for [directory indexing](http://httpd.apache.org/docs/current/mod/mod_autoindex.html#indexoptions). ```puppet apache::vhost { 'sample.example.net': docroot => '/path/to/directory', - directories => [ - { path => '/path/to/directory', - options => ['Indexes','FollowSymLinks','MultiViews'], + directories => [ + { path => '/path/to/directory', + options => ['Indexes','FollowSymLinks','MultiViews'], index_options => ['IgnoreCase', 'FancyIndexing', 'FoldersFirst', 'NameWidth=*', 'DescriptionWidth=*', 'SuppressHTMLPreamble'], }, ], @@ -1221,25 +1651,25 @@ Sets the [default ordering](http://httpd.apache.org/docs/current/mod/mod_autoind ```puppet apache::vhost { 'sample.example.net': docroot => '/path/to/directory', - directories => [ - { path => '/path/to/directory', - order => 'Allow,Deny', + directories => [ + { path => '/path/to/directory', + order => 'Allow,Deny', index_order_default => ['Descending', 'Date'], - }, + }, ], } ``` ######`options` -Lists the [Options](httpd.apache.org/docs/current/mod/core.html#options) for the given Directory block. +Lists the [Options](http://httpd.apache.org/docs/current/mod/core.html#options) for the given Directory block. ```puppet apache::vhost { 'sample.example.net': docroot => '/path/to/directory', - directories => [ - { path => '/path/to/directory', - options => ['Indexes','FollowSymLinks','MultiViews'], + directories => [ + { path => '/path/to/directory', + options => ['Indexes','FollowSymLinks','MultiViews'], }, ], } @@ -1247,14 +1677,14 @@ Lists the [Options](httpd.apache.org/docs/current/mod/core.html#options) for the ######`order` -Sets the order of processing Allow and Deny statements as per [Apache core documentation](httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order). **Deprecated:** This parameter is being deprecated due to a change in Apache. It will only work with Apache 2.2 and lower. +Sets the order of processing Allow and Deny statements as per [Apache core documentation](http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order). **Deprecated:** This parameter is being deprecated due to a change in Apache. It only works with Apache 2.2 and lower. ```puppet apache::vhost { 'sample.example.net': docroot => '/path/to/directory', - directories => [ - { path => '/path/to/directory', - order => 'Allow,Deny', + directories => [ + { path => '/path/to/directory', + order => 'Allow,Deny', }, ], } @@ -1281,6 +1711,84 @@ Sets the value for the [PassengerEnabled](http://www.modrails.com/documentation/ `php_admin_value` sets the value of the directory, and `php_admin_flag` uses a boolean to configure the directory. Further information can be found [here](http://php.net/manual/en/configuration.changes.php). + +######`satisfy` + +Sets a `Satisfy` directive as per the [Apache Core documentation](http://httpd.apache.org/docs/2.2/mod/core.html#satisfy). **Deprecated:** This parameter is being deprecated due to a change in Apache. It only works with Apache 2.2 and lower. + +```puppet + apache::vhost { 'sample.example.net': + docroot => '/path/to/directory', + directories => [ + { path => '/path/to/directory', + satisfy => 'Any', + } + ], + } +``` + +######`sethandler` + +Sets a `SetHandler` directive as per the [Apache Core documentation](http://httpd.apache.org/docs/2.2/mod/core.html#sethandler). An example: + +```puppet + apache::vhost { 'sample.example.net': + docroot => '/path/to/directory', + directories => [ + { path => '/path/to/directory', + sethandler => 'None', + } + ], + } +``` + +######`rewrites` + +Creates URL [`rewrites`](#rewrites) rules in vhost directories. Expects an array of hashes, and the hash keys can be any of 'comment', 'rewrite_base', 'rewrite_cond', or 'rewrite_rule'. + +```puppet + apache::vhost { 'secure.example.net': + docroot => '/path/to/directory', + directories => [ + { path => '/path/to/directory', + rewrites => [ { comment => 'Permalink Rewrites', + rewrite_base => '/' + }, + { rewrite_rule => [ '^index\.php$ - [L]' ] + }, + { rewrite_cond => [ '%{REQUEST_FILENAME} !-f', + '%{REQUEST_FILENAME} !-d', + ], + rewrite_rule => [ '. /index.php [L]' ], + } + ], + }, + ], + } +``` + +***Note*** If you include rewrites in your directories make sure you are also including `apache::mod::rewrite`. You may also want to consider setting the rewrites using the `rewrites` parameter in `apache::vhost` rather than setting the rewrites in the vhost directories. + +######`shib_request_setting` + +Allows an valid content setting to be set or altered for the application request. This command takes two parameters, the name of the content setting, and the value to set it to.Check the Shibboleth [content setting documentation](https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPContentSettings) for valid settings. This key is disabled if `apache::mod::shib` is not defined. Check the [`mod_shib` documentation](https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPApacheConfig#NativeSPApacheConfig-Server/VirtualHostOptions) for more details. + +```puppet + apache::vhost { 'secure.example.net': + docroot => '/path/to/directory', + directories => [ + { path => '/path/to/directory', + shib_require_setting => 'requiresession 1', + shib_use_headers => 'On', + }, + ], + } +``` + +######`shib_use_headers` + +When set to 'On' this turns on the use of request headers to publish attributes to applications. Valid values for this key is 'On' or 'Off', and the default value is 'Off'. This key is disabled if `apache::mod::shib` is not defined. Check the [`mod_shib` documentation](https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPApacheConfig#NativeSPApacheConfig-Server/VirtualHostOptions) for more details. + ######`ssl_options` String or list of [SSLOptions](https://httpd.apache.org/docs/current/mod/mod_ssl.html#ssloptions), which configure SSL engine run-time options. This handler takes precedence over SSLOptions set in the parent block of the vhost. @@ -1289,10 +1797,10 @@ String or list of [SSLOptions](https://httpd.apache.org/docs/current/mod/mod_ssl apache::vhost { 'secure.example.net': docroot => '/path/to/directory', directories => [ - { path => '/path/to/directory', - ssl_options => '+ExportCertData', + { path => '/path/to/directory', + ssl_options => '+ExportCertData', }, - { path => '/path/to/different/dir', + { path => '/path/to/different/dir', ssl_options => [ '-StdEnvVars', '+ExportCertData'], }, ], @@ -1301,16 +1809,16 @@ String or list of [SSLOptions](https://httpd.apache.org/docs/current/mod/mod_ssl ######`suphp` -A hash containing the 'user' and 'group' keys for the [suPHP_UserGroup](http://www.suphp.org/DocumentationView.html?file=apache/CONFIG) setting. It must be used with `suphp_engine => on` in the vhost declaration, and may only be passed within `directories`. +A hash containing the 'user' and 'group' keys for the [suPHP_UserGroup](http://www.suphp.org/DocumentationView.html?file=apache/CONFIG) setting. It must be used with `suphp_engine => on` in the vhost declaration, and can only be passed within `directories`. ```puppet apache::vhost { 'secure.example.net': docroot => '/path/to/directory', directories => [ - { path => '/path/to/directory', - suphp => - { user => 'myappuser', - group => 'myappgroup', + { path => '/path/to/directory', + suphp => + { user => 'myappuser', + group => 'myappgroup', }, }, ], @@ -1319,11 +1827,11 @@ A hash containing the 'user' and 'group' keys for the [suPHP_UserGroup](http://w ####SSL parameters for `apache::vhost` -All of the SSL parameters for `::vhost` will default to whatever is set in the base `apache` class. Use the below parameters to tweak individual SSL settings for specific vhosts. +All of the SSL parameters for `::vhost` default to whatever is set in the base `apache` class. Use the below parameters to tweak individual SSL settings for specific vhosts. #####`ssl` -Enables SSL for the virtual host. SSL vhosts only respond to HTTPS queries. Valid values are 'true' or 'false'. Defaults to 'false'. +Enables SSL for the virtual host. SSL vhosts only respond to HTTPS queries. Valid values are 'true' or 'false'. Defaults to 'false'. #####`ssl_ca` @@ -1335,15 +1843,15 @@ Specifies the SSL certification. Defaults are based on your OS: '/etc/pki/tls/ce #####`ssl_protocol` -Specifies [SSLProtocol](http://httpd.apache.org/docs/current/mod/mod_ssl.html#sslprotocol). Defaults to 'undef'. +Specifies [SSLProtocol](http://httpd.apache.org/docs/current/mod/mod_ssl.html#sslprotocol). Defaults to 'undef'. -If you do not use this parameter, it will use the HTTPD default from ssl.conf.erb, 'all -SSLv2'. +If you do not use this parameter, it uses the HTTPD default from ssl.conf.erb, 'all -SSLv2 -SSLv3'. #####`ssl_cipher` Specifies [SSLCipherSuite](http://httpd.apache.org/docs/current/mod/mod_ssl.html#sslciphersuite). Defaults to 'undef'. -If you do not use this parameter, it will use the HTTPD default from ssl.conf.erb, 'HIGH:MEDIUM:!aNULL:!MD5'. +If you do not use this parameter, it uses the HTTPD default from ssl.conf.erb, 'HIGH:MEDIUM:!aNULL:!MD5'. #####`ssl_honorcipherorder` @@ -1355,19 +1863,23 @@ Specifies the location of the SSL certification directory. Defaults to '/etc/ssl #####`ssl_chain` -Specifies the SSL chain. Defaults to 'undef'. (This default will work out of the box but must be updated in the base `apache` class with your specific certificate information before being used in production.) +Specifies the SSL chain. Defaults to 'undef'. (This default works out of the box, but it must be updated in the base `apache` class with your specific certificate information before being used in production.) #####`ssl_crl` -Specifies the certificate revocation list to use. Defaults to 'undef'. (This default will work out of the box but must be updated in the base `apache` class with your specific certificate information before being used in production.) +Specifies the certificate revocation list to use. Defaults to 'undef'. (This default works out of the box but must be updated in the base `apache` class with your specific certificate information before being used in production.) #####`ssl_crl_path` -Specifies the location of the certificate revocation list. Defaults to 'undef'. (This default will work out of the box but must be updated in the base `apache` class with your specific certificate information before being used in production.) +Specifies the location of the certificate revocation list. Defaults to 'undef'. (This default works out of the box but must be updated in the base `apache` class with your specific certificate information before being used in production.) + +#####`ssl_crl_check` + +Sets the certificate revocation check level via the [SSLCARevocationCheck directive](http://httpd.apache.org/docs/current/mod/mod_ssl.html#sslcarevocationcheck), defaults to 'undef'. This default works out of the box but must be specified when using CRLs in production. Only applicable to Apache 2.4 or higher; the value is ignored on older versions. #####`ssl_key` -Specifies the SSL key. Defaults are based on your operating system: '/etc/pki/tls/private/localhost.key' for RedHat, '/etc/ssl/private/ssl-cert-snakeoil.key' for Debian, and '/usr/local/etc/apache22/server.key' for FreeBSD. (This default will work out of the box but must be updated in the base `apache` class with your specific certificate information before being used in production.) +Specifies the SSL key. Defaults are based on your operating system: '/etc/pki/tls/private/localhost.key' for RedHat, '/etc/ssl/private/ssl-cert-snakeoil.key' for Debian, and '/usr/local/etc/apache22/server.key' for FreeBSD. (This default works out of the box but must be updated in the base `apache` class with your specific certificate information before being used in production.) #####`ssl_verify_client` @@ -1393,7 +1905,7 @@ Sets the [SSLVerifyDepth](http://httpd.apache.org/docs/current/mod/mod_ssl.html# #####`ssl_options` -Sets the [SSLOptions](http://httpd.apache.org/docs/current/mod/mod_ssl.html#ssloptions) directive, which configures various SSL engine run-time options. This is the global setting for the given vhost and can be a string or an array. Defaults to 'undef'. +Sets the [SSLOptions](http://httpd.apache.org/docs/current/mod/mod_ssl.html#ssloptions) directive, which configures various SSL engine run-time options. This is the global setting for the given vhost and can be a string or an array. Defaults to 'undef'. A string: @@ -1417,10 +1929,60 @@ An array: Specifies whether or not to use [SSLProxyEngine](http://httpd.apache.org/docs/current/mod/mod_ssl.html#sslproxyengine). Valid values are 'true' and 'false'. Defaults to 'false'. +####Defined Type: FastCGI Server + +This type is intended for use with mod_fastcgi. It allows you to define one or more external FastCGI servers to handle specific file types. + +Ex: + +```puppet +apache::fastcgi::server { 'php': + host => '127.0.0.1:9000', + timeout => 15, + flush => false, + faux_path => '/var/www/php.fcgi', + fcgi_alias => '/php.fcgi', + file_type => 'application/x-httpd-php' +} +``` + +Within your virtual host, you can then configure the specified file type to be handled by the fastcgi server specified above. + +```puppet +apache::vhost { 'www': + ... + custom_fragment = 'AddType application/x-httpd-php .php' + ... +} +``` + +#####`host` + +The hostname or IP address and TCP port number (1-65535) of the FastCGI server. + +#####`timeout` + +The number of seconds of FastCGI application inactivity allowed before the request is aborted and the event is logged (at the error LogLevel). The inactivity timer applies only as long as a connection is pending with the FastCGI application. If a request is queued to an application, but the application doesn't respond (by writing and flushing) within this period, the request is aborted. If communication is complete with the application but incomplete with the client (the response is buffered), the timeout does not apply. + +#####`flush` + +Force a write to the client as data is received from the application. By default, mod_fastcgi buffers data in order to free the application as quickly as possible. + +#####`faux_path` + +`faux_path` does not have to exist in the local filesystem. URIs that Apache resolves to this filename are handled by this external FastCGI application. + +#####`alias` + +A unique alias. This is used internally to link the action with the FastCGI server. + +#####`file_type` + +The MIME-type of the file to be processed by the FastCGI server. ###Virtual Host Examples -The apache module allows you to set up pretty much any configuration of virtual host you might need. This section will address some common configurations, but look at the [Tests section](https://github.com/puppetlabs/puppetlabs-apache/tree/master/tests) for even more examples. +The apache module allows you to set up pretty much any configuration of virtual host you might need. This section addresses some common configurations, but look at the [Tests section](https://github.com/puppetlabs/puppetlabs-apache/tree/master/tests) for even more examples. Configure a vhost with a server administrator @@ -1501,7 +2063,7 @@ Configure a vhost to redirect non-SSL connections to SSL servername => 'sixteenth.example.com', port => '80', docroot => '/var/www/sixteenth', - redirect_status => 'permanent' + redirect_status => 'permanent', redirect_dest => 'https://sixteenth.example.com/' } apache::vhost { 'sixteenth.example.com ssl': @@ -1514,14 +2076,14 @@ Configure a vhost to redirect non-SSL connections to SSL - - - -Set up IP-based vhosts on any listen port and have them respond to requests on specific IP addresses. In this example, we will set listening on ports 80 and 81. This is required because the example vhosts are not declared with a port parameter. +Set up IP-based vhosts on any listen port and have them respond to requests on specific IP addresses. In this example, we set listening on ports 80 and 81. This is required because the example vhosts are not declared with a port parameter. ```puppet apache::listen { '80': } apache::listen { '81': } ``` -Then we will set up the IP-based vhosts +Then we set up the IP-based vhosts ```puppet apache::vhost { 'first.example.com': @@ -1538,7 +2100,7 @@ Then we will set up the IP-based vhosts - - - -Configure a mix of name-based and IP-based vhosts. First, we will add two IP-based vhosts on 10.0.0.10, one SSL and one non-SSL +Configure a mix of name-based and IP-based vhosts. First, we add two IP-based vhosts on 10.0.0.10, one SSL and one non-SSL ```puppet apache::vhost { 'The first IP-based vhost, non-ssl': @@ -1558,7 +2120,7 @@ Configure a mix of name-based and IP-based vhosts. First, we will add two IP-bas } ``` -Then, we will add two name-based vhosts listening on 10.0.0.20 +Then, we add two name-based vhosts listening on 10.0.0.20 ```puppet apache::vhost { 'second.example.com': @@ -1573,7 +2135,7 @@ Then, we will add two name-based vhosts listening on 10.0.0.20 } ``` -If you want to add two name-based vhosts so that they will answer on either 10.0.0.10 or 10.0.0.20, you **MUST** declare `add_listen => 'false'` to disable the otherwise automatic 'Listen 80', as it will conflict with the preceding IP-based vhosts. +If you want to add two name-based vhosts so that they answer on either 10.0.0.10 or 10.0.0.20, you **MUST** declare `add_listen => 'false'` to disable the otherwise automatic 'Listen 80', as it conflicts with the preceding IP-based vhosts. ```puppet apache::vhost { 'fourth.example.com': @@ -1592,7 +2154,7 @@ If you want to add two name-based vhosts so that they will answer on either 10.0 ####Defined Type: `apache::balancer` -`apache::balancer` creates an Apache balancer cluster. Each balancer cluster needs one or more balancer members, which are declared with [`apache::balancermember`](#defined-type-apachebalancermember). +`apache::balancer` creates an Apache balancer cluster. Each balancer cluster needs one or more balancer members, which are declared with [`apache::balancermember`](#defined-type-apachebalancermember). One `apache::balancer` defined resource should be defined for each Apache load balanced set of servers. The `apache::balancermember` resources for all balancer members can be exported and collected on a single Apache load balancer server using exported resources. @@ -1600,7 +2162,7 @@ One `apache::balancer` defined resource should be defined for each Apache load b #####`name` -Sets the balancer cluster's title. This parameter will also set the title of the conf.d file. +Sets the balancer cluster's title. This parameter also sets the title of the conf.d file. #####`proxy_set` @@ -1608,23 +2170,23 @@ Configures key-value pairs as [ProxySet](http://httpd.apache.org/docs/current/mo #####`collect_exported` -Determines whether or not to use exported resources. Valid values 'true' and 'false', defaults to 'true'. +Determines whether or not to use exported resources. Valid values 'true' and 'false', defaults to 'true'. If you statically declare all of your backend servers, you should set this to 'false' to rely on existing declared balancer member resources. Also make sure to use `apache::balancermember` with array arguments. If you wish to dynamically declare your backend servers via [exported resources](http://docs.puppetlabs.com/guides/exported_resources.html) collected on a central node, you must set this parameter to 'true' in order to collect the exported balancer member resources that were exported by the balancer member nodes. -If you choose not to use exported resources, all balancer members will be configured in a single puppet run. If you are using exported resources, Puppet has to run on the balanced nodes, then run on the balancer. +If you choose not to use exported resources, all balancer members will be configured in a single Puppet run. If you are using exported resources, Puppet has to run on the balanced nodes, then run on the balancer. ####Defined Type: `apache::balancermember` -Defines members of [mod_proxy_balancer](http://httpd.apache.org/docs/current/mod/mod_proxy_balancer.html), which will set up a balancer member inside a listening service configuration block in etc/apache/apache.cfg on the load balancer. +Defines members of [mod_proxy_balancer](http://httpd.apache.org/docs/current/mod/mod_proxy_balancer.html), which sets up a balancer member inside a listening service configuration block in etc/apache/apache.cfg on the load balancer. **Parameters within `apache::balancermember`:** #####`name` -Sets the title of the resource. This name will also set the name of the concat fragment. +Sets the title of the resource. This name also sets the name of the concat fragment. #####`balancer_cluster` @@ -1686,7 +2248,7 @@ If you need to use ProxySet in the balancer config * [`apache`](#class-apache): Guides the basic setup of Apache. * `apache::dev`: Installs Apache development libraries. (*Note:* On FreeBSD, you must declare `apache::package` or `apache` before `apache::dev`.) * [`apache::mod::[name]`](#classes-apachemodname): Enables specific Apache HTTPD modules. - + ####Private Classes * `apache::confd::no_accf`: Creates the no-accf.conf configuration file in conf.d, required by FreeBSD's Apache 2.4. @@ -1705,7 +2267,7 @@ If you need to use ProxySet in the balancer config * `apache::listen`: Based on the title, controls which ports Apache binds to for listening. Adds [Listen](http://httpd.apache.org/docs/current/bind.html) directives to ports.conf in the Apache HTTPD configuration directory. Titles take the form '', ':', or ':'. * `apache::mod`: Used to enable arbitrary Apache HTTPD modules for which there is no specific `apache::mod::[name]` class. * `apache::namevirtualhost`: Enables name-based hosting of a virtual host. Adds all [NameVirtualHost](http://httpd.apache.org/docs/current/vhosts/name-based.html) directives to the `ports.conf` file in the Apache HTTPD configuration directory. Titles take the form '\*', '*:', '\_default_:, '', or ':'. -* `apache::vhost`: Allows specialized configurations for virtual hosts that have requirements outside the defaults. +* `apache::vhost`: Allows specialized configurations for virtual hosts that have requirements outside the defaults. ####Private Defined Types @@ -1718,9 +2280,54 @@ The Apache module relies heavily on templates to enable the `vhost` and `apache: ##Limitations -This module is CI tested on Centos 5 & 6, Ubuntu 12.04, Debian 7, and RHEL 5 & 6 platforms against both the OSS and Enterprise version of Puppet. +###Ubuntu 10.04 + +The `apache::vhost::WSGIImportScript` parameter creates a statement inside the VirtualHost which is unsupported on older versions of Apache, causing this to fail. This will be remedied in a future refactoring. + +###RHEL/CentOS 5 + +The `apache::mod::passenger` and `apache::mod::proxy_html` classes are untested since repositories are missing compatible packages. + +###RHEL/CentOS 7 + +The `apache::mod::passenger` class is untested as the repository does not have packages for EL7 yet. The fact that passenger packages aren't available also makes us unable to test the `rack_base_uri` parameter in `apache::vhost`. + +###General + +This module is CI tested on Centos 5 & 6, Ubuntu 12.04 & 14.04, Debian 7, and RHEL 5, 6 & 7 platforms against both the OSS and Enterprise version of Puppet. + +The module contains support for other distributions and operating systems, such as FreeBSD and Amazon Linux, but is not formally tested on those and regressions can occur. + +###SELinux and Custom Paths + +If you are running with SELinux in enforcing mode and want to use custom paths for your `logroot`, `mod_dir`, `vhost_dir`, and `docroot`, you need to manage the context for the files yourself. + +Something along the lines of: + +```puppet + exec { 'set_apache_defaults': + command => 'semanage fcontext -a -t httpd_sys_content_t "/custom/path(/.*)?"', + path => '/bin:/usr/bin/:/sbin:/usr/sbin', + require => Package['policycoreutils-python'], + } + package { 'policycoreutils-python': ensure => installed } + exec { 'restorecon_apache': + command => 'restorecon -Rv /apache_spec', + path => '/bin:/usr/bin/:/sbin:/usr/sbin', + before => Service['httpd'], + require => Class['apache'], + } + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + file { '/custom/path': ensure => directory, } + file { '/custom/path/include': ensure => present, content => '#additional_includes' } + apache::vhost { 'test.server': + docroot => '/custom/path', + additional_includes => '/custom/path/include', + } +``` -The module contains support for other distributions and operating systems, such as FreeBSD and Amazon Linux, but is not formally tested on those and regressions may occur. +You need to set the contexts using `semanage fcontext` not `chcon` because `file {...}` resources reset the context to the values in the database if the resource isn't specifying the context. ##Development diff --git a/apache/README.passenger.md b/apache/README.passenger.md index cecacccc4..4b4caa8c0 100644 --- a/apache/README.passenger.md +++ b/apache/README.passenger.md @@ -1,73 +1,258 @@ # Passenger -Just enabling the Passenger module is insufficient for the use of Passenger in production. Passenger should be tunable to better fit the environment in which it is run while being aware of the resources it required. +Just enabling the Passenger module is insufficient for the use of Passenger in +production. Passenger should be tunable to better fit the environment in which +it is run while being aware of the resources it required. -To this end the Apache passenger module has been modified to apply system wide Passenger tuning declarations to `passenger.conf`. Declarations specific to a virtual host should be passed through when defining a `vhost` (e.g. `rack_base_uris' parameter on the `apache::vhost` class, check `README.md`). +To this end the Apache passenger module has been modified to apply system wide +Passenger tuning declarations to `passenger.conf`. Declarations specific to a +virtual host should be passed through when defining a `vhost` (e.g. +`rack_base_uris` parameter on the `apache::vhost` type, check `README.md`). + +Also, general apache module loading parameters can be supplied to enable using +a customized passenger module in place of a default-package-based version of +the module. + +# Operating system support and Passenger versions + +The most important configuration directive for the Apache Passenger module is +`PassengerRoot`. Its value depends on the Passenger version used (2.x, 3.x or +4.x) and on the operating system package from which the Apache Passenger module +is installed. + +The following table summarises the current *default versions* and +`PassengerRoot` settings for the operating systems supported by +puppetlabs-apache: + +OS | Passenger version | `PassengerRoot` +---------------- | ------------------ | ---------------- +Debian 7 | 3.0.13 | /usr +Ubuntu 12.04 | 2.2.11 | /usr +Ubuntu 14.04 | 4.0.37 | /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini +RHEL with EPEL6 | 3.0.21 | /usr/lib/ruby/gems/1.8/gems/passenger-3.0.21 + +As mentioned in `README.md` there are no compatible packages available for +RHEL/CentOS 5 or RHEL/CentOS 7. + +## Configuration files and locations on RHEL/CentOS + +Notice two important points: + +1. The Passenger version packaged in the EPEL repositories may change over time. +2. The value of `PassengerRoot` depends on the Passenger version installed. + +To prevent the puppetlabs-apache module from having to keep up with these +package versions the Passenger configuration files installed by the +packages are left untouched by this module. All configuration is placed in an +extra configuration file managed by puppetlabs-apache. + +This means '/etc/httpd/conf.d/passenger.conf' is installed by the +`mod_passenger` package and contains correct values for `PassengerRoot` and +`PassengerRuby`. Puppet will ignore this file. Additional configuration +directives as described in the remainder of this document are placed in +'/etc/httpd/conf.d/passenger_extra.conf', managed by Puppet. + +This pertains *only* to RHEL/CentOS, *not* Debian and Ubuntu. + +## Third-party and custom Passenger packages and versions + +The Passenger version distributed by the default OS packages may be too old to +be useful. Newer versions may be installed via Gems, from source or from +third-party OS packages. + +Most notably the Passenger developers officially provide Debian packages for a +variety of Debian and Ubuntu releases in the [Passenger APT +repository](https://oss-binaries.phusionpassenger.com/apt/passenger). Read more +about [installing these packages in the offical user +guide](http://www.modrails.com/documentation/Users%20guide%20Apache.html#install_on_debian_ubuntu). + +If you install custom Passenger packages and newer version make sure to set the +directives `PassengerRoot`, `PassengerRuby` and/or `PassengerDefaultRuby` +correctly, or Passenger and Apache will fail to function properly. + +For Passenger 4.x packages on Debian and Ubuntu the `PassengerRoot` directive +should almost universally be set to +`/usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini`. # Parameters for `apache::mod::passenger` -The following declarations are supported and can be passed to `apache::mod::passenger` as parameters, for example: +The following class parameters configure Passenger in a global, server-wide +context. -``` -class {'apache::mod::passenger': - passenger_high_performance => 'on', - rails_autodetect => 'off', +Example: + +```puppet +class { 'apache::mod::passenger': + passenger_root => '/usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini', + passenger_default_ruby => '/usr/bin/ruby1.9.3', + passenger_high_performance => 'on', + rails_autodetect => 'off', + mod_lib_path => '/usr/lib/apache2/custom_modules', } ``` -The general form is using the all lower case version of the declaration. +The general form is using the all lower-case version of the configuration +directive, with underscores instead of CamelCase. + +## Parameters used with passenger.conf + +If you pass a default value to `apache::mod::passenger` it will be ignored and +not passed through to the configuration file. + +### passenger_root + +The location to the Phusion Passenger root directory. This configuration option +is essential to Phusion Passenger, and allows Phusion Passenger to locate its +own data files. + +The default depends on the Passenger version and the means of installation. See +the above section on operating system support, versions and packages for more +information. + +http://www.modrails.com/documentation/Users%20guide%20Apache.html#_passengerroot_lt_directory_gt + +### passenger_default_ruby -If you pass a default value to `apache::mod::passenger` it will be ignored and not passed through to the configuration file. +This option specifies the default Ruby interpreter to use for web apps as well +as for all sorts of internal Phusion Passenger helper scripts, e.g. the one +used by PassengerPreStart. -## passenger_high_performance +This directive was introduced in Passenger 4.0.0 and will not work in versions +< 4.x. Do not set this parameter if your Passenger version is older than 4.0.0. -Default is `off`, when turned `on` Passenger runs in a higher performance mode that can be less compatible with other Apache modules. +Defaults to `undef` for all operating systems except Ubuntu 14.04, where it is +set to '/usr/bin/ruby'. + +http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerDefaultRuby + +### passenger_ruby + +This directive is the same as `passenger_default_ruby` for Passenger versions +< 4.x and must be used instead of `passenger_default_ruby` for such versions. + +It makes no sense to set `PassengerRuby` for Passenger >= 4.x. That +directive should only be used to override the value of `PassengerDefaultRuby` +on a non-global context, i.e. in ``, ``, `` +and so on. + +Defaults to `/usr/bin/ruby` for all supported operating systems except Ubuntu +14.04, where it is set to `undef`. + +http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerRuby + +### passenger_high_performance + +Default is `off`. When turned `on` Passenger runs in a higher performance mode +that can be less compatible with other Apache modules. http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerHighPerformance -## passenger_max_pool_size +### passenger_max_pool_size -Set's the maximum number of Passenger application processes that may simultaneously run. The default value is 6. +Sets the maximum number of Passenger application processes that may +simultaneously run. The default value is 6. http://www.modrails.com/documentation/Users%20guide%20Apache.html#_passengermaxpoolsize_lt_integer_gt -## passenger_pool_idle_time +### passenger_pool_idle_time -The maximum number of seconds a Passenger Application process will be allowed to remain idle before being shut down. The default value is 300. +The maximum number of seconds a Passenger Application process will be allowed +to remain idle before being shut down. The default value is 300. http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerPoolIdleTime -## passenger_max_requests +### passenger_max_requests -The maximum number of request a Passenger application will process before being restarted. The default value is 0, which indicates that a process will only shut down if the Pool Idle Time (see above) expires. +The maximum number of request a Passenger application will process before being +restarted. The default value is 0, which indicates that a process will only +shut down if the Pool Idle Time (see above) expires. http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerMaxRequests -## passenger_stat_throttle_rate +### passenger_stat_throttle_rate -Sets how often Passenger performs file system checks, at most once every _x_ seconds. Default is 0, which means the checks are performed with every request. +Sets how often Passenger performs file system checks, at most once every _x_ +seconds. Default is 0, which means the checks are performed with every request. http://www.modrails.com/documentation/Users%20guide%20Apache.html#_passengerstatthrottlerate_lt_integer_gt -## rack_autodetect +### rack_autodetect -Should Passenger automatically detect if the document root of a virtual host is a Rack application. The default is `on` +Should Passenger automatically detect if the document root of a virtual host is +a Rack application. Not set by default (`undef`). Note that this directive has +been removed in Passenger 4.0.0 and `PassengerEnabled` should be used instead. +Use this directive only on Passenger < 4.x. http://www.modrails.com/documentation/Users%20guide%20Apache.html#_rackautodetect_lt_on_off_gt -## rails_autodetect +### rails_autodetect -Should Passenger automatically detect if the document root of a virtual host is a Rails application. The default is on. +Should Passenger automatically detect if the document root of a virtual host is +a Rails application. Not set by default (`undef`). Note that this directive +has been removed in Passenger 4.0.0 and `PassengerEnabled` should be used +instead. Use this directive only on Passenger < 4.x. http://www.modrails.com/documentation/Users%20guide%20Apache.html#_railsautodetect_lt_on_off_gt -## passenger_use_global_queue +### passenger_use_global_queue + +Allows toggling of PassengerUseGlobalQueue. NOTE: PassengerUseGlobalQueue is +the default in Passenger 4.x and the versions >= 4.x have disabled this +configuration option altogether. Use with caution. + +## Parameters used to load the module + +Unlike the tuning parameters specified above, the following parameters are only +used when loading customized passenger modules. + +### mod_package + +Allows overriding the default package name used for the passenger module +package. + +### mod_package_ensure + +Allows overriding the package installation setting used by puppet when +installing the passenger module. The default is 'present'. + +### mod_id + +Allows overriding the value used by apache to identify the passenger module. +The default is 'passenger_module'. + +### mod_lib_path + +Allows overriding the directory path used by apache when loading the passenger +module. The default is the value of `$apache::params::lib_path`. + +### mod_lib + +Allows overriding the library file name used by apache when loading the +passenger module. The default is 'mod_passenger.so'. + +### mod_path + +Allows overriding the full path to the library file used by apache when loading +the passenger module. The default is the concatenation of the `mod_lib_path` +and `mod_lib` parameters. + +# Dependencies + +RedHat-based systems will need to configure additional package repositories in +order to install Passenger, specifically: + +* [Extra Packages for Enterprise Linux](https://fedoraproject.org/wiki/EPEL) +* [Phusion Passenger](http://passenger.stealthymonkeys.com) -Allows toggling of PassengerUseGlobalQueue. NOTE: PassengerUseGlobalQueue is the default in Passenger 4.x and the versions >= 4.x have disabled this configuration option altogether. Use with caution. +Configuration of these repositories is beyond the scope of this module and is +left to the user. # Attribution -The Passenger tuning parameters for the `apache::mod::puppet` Puppet class was modified by Aaron Hicks (hicksa@landcareresearch.co.nz) for work on the NeSI Project and the Tuakiri New Zealand Access Federation as a fork from the PuppetLabs Apache module on GitHub. +The Passenger tuning parameters for the `apache::mod::passenger` Puppet class +was modified by Aaron Hicks (hicksa@landcareresearch.co.nz) for work on the +NeSI Project and the Tuakiri New Zealand Access Federation as a fork from the +PuppetLabs Apache module on GitHub. * https://github.com/puppetlabs/puppetlabs-apache * https://github.com/nesi/puppetlabs-apache diff --git a/apache/Rakefile b/apache/Rakefile index 5868545f2..01c85eb99 100644 --- a/apache/Rakefile +++ b/apache/Rakefile @@ -2,9 +2,10 @@ require 'puppetlabs_spec_helper/rake_tasks' require 'puppet-lint/tasks/puppet-lint' PuppetLint.configuration.fail_on_warnings +PuppetLint.configuration.send('relative') PuppetLint.configuration.send('disable_80chars') PuppetLint.configuration.send('disable_class_inherits_from_params_class') -PuppetLint.configuration.send('disable_class_parameter_defaults') PuppetLint.configuration.send('disable_documentation') PuppetLint.configuration.send('disable_single_quote_string_with_variables') +PuppetLint.configuration.send('disable_only_variable_string') PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "pkg/**/*.pp"] diff --git a/apache/lib/puppet/parser/functions/bool2httpd.rb b/apache/lib/puppet/parser/functions/bool2httpd.rb new file mode 100644 index 000000000..5fb79f6f5 --- /dev/null +++ b/apache/lib/puppet/parser/functions/bool2httpd.rb @@ -0,0 +1,30 @@ +Puppet::Parser::Functions::newfunction(:bool2httpd, :type => :rvalue, :doc => <<-EOS +Transform a supposed boolean to On or Off. Pass all other values through. +Given a nil value (undef), bool2httpd will return 'Off' + +Example: + + $trace_enable = false + $server_signature = 'mail' + + bool2httpd($trace_enable) + # => 'Off' + bool2httpd($server_signature) + # => 'mail' + bool2httpd(undef) + # => 'Off' + +EOS +) do |args| + raise(Puppet::ParseError, "bool2httpd() wrong number of arguments. Given: #{args.size} for 1)") if args.size != 1 + + arg = args[0] + + if arg.nil? or arg == false or arg =~ /false/i or arg == :undef + return 'Off' + elsif arg == true or arg =~ /true/i + return 'On' + end + + return arg.to_s +end diff --git a/apache/manifests/balancer.pp b/apache/manifests/balancer.pp index 30887823b..a59b6762b 100644 --- a/apache/manifests/balancer.pp +++ b/apache/manifests/balancer.pp @@ -42,7 +42,6 @@ $proxy_set = {}, $collect_exported = true, ) { - include concat::setup include ::apache::mod::proxy_balancer $target = "${::apache::params::confd_dir}/balancer_${name}.conf" @@ -55,6 +54,7 @@ } concat::fragment { "00-${name}-header": + ensure => present, target => $target, order => '01', content => "\n", @@ -67,12 +67,14 @@ # concat fragments. We don't have to do anything about them. concat::fragment { "01-${name}-proxyset": + ensure => present, target => $target, order => '19', - content => inline_template("<% proxy_set.each do |key, value| %> Proxyset <%= key %>=<%= value %>\n<% end %>"), + content => inline_template("<% @proxy_set.keys.sort.each do |key| %> Proxyset <%= key %>=<%= @proxy_set[key] %>\n<% end %>"), } concat::fragment { "01-${name}-footer": + ensure => present, target => $target, order => '20', content => "\n", diff --git a/apache/manifests/balancermember.pp b/apache/manifests/balancermember.pp index c48cb1ebb..459081a71 100644 --- a/apache/manifests/balancermember.pp +++ b/apache/manifests/balancermember.pp @@ -45,7 +45,8 @@ $options = [], ) { - concat::fragment { "BalancerMember ${url}": + concat::fragment { "BalancerMember ${name}": + ensure => present, target => "${::apache::params::confd_dir}/balancer_${balancer_cluster}.conf", content => inline_template(" BalancerMember ${url} <%= @options.join ' ' %>\n"), } diff --git a/apache/manifests/custom_config.pp b/apache/manifests/custom_config.pp new file mode 100644 index 000000000..cadc2354d --- /dev/null +++ b/apache/manifests/custom_config.pp @@ -0,0 +1,60 @@ +# See README.md for usage information +define apache::custom_config ( + $ensure = 'present', + $confdir = $::apache::confd_dir, + $content = undef, + $priority = '25', + $source = undef, + $verify_command = $::apache::params::verify_command, + $verify_config = true, +) { + + if $content and $source { + fail('Only one of $content and $source can be specified.') + } + + if $ensure == 'present' and ! $content and ! $source { + fail('One of $content and $source must be specified.') + } + + validate_re($ensure, '^(present|absent)$', + "${ensure} is not supported for ensure. + Allowed values are 'present' and 'absent'.") + + validate_bool($verify_config) + + ## Apache include does not always work with spaces in the filename + $filename = regsubst($name, ' ', '_', 'G') + + if ! $verify_config or $ensure == 'absent' { + $notifies = Service['httpd'] + } else { + $notifies = undef + } + + file { "apache_${name}": + ensure => $ensure, + path => "${confdir}/${priority}-${filename}.conf", + content => $content, + source => $source, + require => Package['httpd'], + notify => $notifies, + } + + if $ensure == 'present' and $verify_config { + exec { "service notify for ${name}": + command => $verify_command, + subscribe => File["apache_${name}"], + refreshonly => true, + notify => Service['httpd'], + before => Exec["remove ${name} if invalid"], + } + + exec { "remove ${name} if invalid": + command => "/bin/rm ${confdir}/${priority}-${filename}.conf", + unless => $verify_command, + subscribe => File["apache_${name}"], + refreshonly => true, + } + } +} diff --git a/apache/manifests/default_mods.pp b/apache/manifests/default_mods.pp index 139503e3c..09e360a49 100644 --- a/apache/manifests/default_mods.pp +++ b/apache/manifests/default_mods.pp @@ -9,6 +9,14 @@ case $::osfamily { 'redhat', 'freebsd': { ::apache::mod { 'log_config': } + if versioncmp($apache_version, '2.4') >= 0 { + # Lets fork it + # Do not try to load mod_systemd on RHEL/CentOS 6 SCL. + if ( !($::osfamily == 'redhat' and versioncmp($::operatingsystemrelease, '7.0') == -1) and !($::operatingsystem == 'Amazon' and versioncmp($::operatingsystemrelease, '2014.09') <= 0 ) ) { + ::apache::mod { 'systemd': } + } + ::apache::mod { 'unixd': } + } } default: {} } @@ -19,14 +27,20 @@ case $::osfamily { 'debian': { include ::apache::mod::reqtimeout + if versioncmp($apache_version, '2.4') >= 0 { + ::apache::mod { 'authn_core': } + } } 'redhat': { + include ::apache::mod::actions include ::apache::mod::cache include ::apache::mod::mime include ::apache::mod::mime_magic - include ::apache::mod::vhost_alias include ::apache::mod::rewrite - ::apache::mod { 'actions': } + include ::apache::mod::speling + include ::apache::mod::suexec + include ::apache::mod::version + include ::apache::mod::vhost_alias ::apache::mod { 'auth_digest': } ::apache::mod { 'authn_anon': } ::apache::mod { 'authn_dbm': } @@ -36,17 +50,10 @@ ::apache::mod { 'ext_filter': } ::apache::mod { 'include': } ::apache::mod { 'logio': } - ::apache::mod { 'speling': } ::apache::mod { 'substitute': } - ::apache::mod { 'suexec': } ::apache::mod { 'usertrack': } - ::apache::mod { 'version': } - if $apache_version >= 2.4 { - # Lets fork it - ::apache::mod { 'systemd': } - - ::apache::mod { 'unixd': } + if versioncmp($apache_version, '2.4') >= 0 { ::apache::mod { 'authn_core': } } else { @@ -55,6 +62,7 @@ } } 'freebsd': { + include ::apache::mod::actions include ::apache::mod::cache include ::apache::mod::disk_cache include ::apache::mod::headers @@ -63,9 +71,10 @@ include ::apache::mod::reqtimeout include ::apache::mod::rewrite include ::apache::mod::userdir + include ::apache::mod::version include ::apache::mod::vhost_alias + include ::apache::mod::speling - ::apache::mod { 'actions': } ::apache::mod { 'asis': } ::apache::mod { 'auth_digest': } ::apache::mod { 'authn_alias': } @@ -83,10 +92,8 @@ ::apache::mod { 'imagemap':} ::apache::mod { 'include': } ::apache::mod { 'logio': } - ::apache::mod { 'speling': } ::apache::mod { 'unique_id': } ::apache::mod { 'usertrack': } - ::apache::mod { 'version': } } default: {} } @@ -113,7 +120,7 @@ ::apache::mod { 'auth_basic': } ::apache::mod { 'authn_file': } - if $apache_version >= 2.4 { + if versioncmp($apache_version, '2.4') >= 0 { # authz_core is needed for 'Require' directive ::apache::mod { 'authz_core': id => 'authz_core_module', @@ -121,6 +128,9 @@ # filter is needed by mod_deflate ::apache::mod { 'filter': } + + # lots of stuff seems to break without access_compat + ::apache::mod { 'access_compat': } } else { ::apache::mod { 'authz_default': } } @@ -131,7 +141,7 @@ } elsif $mods { ::apache::default_mods::load { $mods: } - if $apache_version >= 2.4 { + if versioncmp($apache_version, '2.4') >= 0 { # authz_core is needed for 'Require' directive ::apache::mod { 'authz_core': id => 'authz_core_module', @@ -141,7 +151,7 @@ ::apache::mod { 'filter': } } } else { - if $apache_version >= 2.4 { + if versioncmp($apache_version, '2.4') >= 0 { # authz_core is needed for 'Require' directive ::apache::mod { 'authz_core': id => 'authz_core_module', diff --git a/apache/manifests/fastcgi/server.pp b/apache/manifests/fastcgi/server.pp new file mode 100644 index 000000000..afc7c8860 --- /dev/null +++ b/apache/manifests/fastcgi/server.pp @@ -0,0 +1,24 @@ +define apache::fastcgi::server ( + $host = '127.0.0.1:9000', + $timeout = 15, + $flush = false, + $faux_path = "/var/www/${name}.fcgi", + $fcgi_alias = "/${name}.fcgi", + $file_type = 'application/x-httpd-php' +) { + include apache::mod::fastcgi + + Apache::Mod['fastcgi'] -> Apache::Fastcgi::Server[$title] + + file { "fastcgi-pool-${name}.conf": + ensure => present, + path => "${::apache::confd_dir}/fastcgi-pool-${name}.conf", + owner => 'root', + group => $::apache::params::root_group, + mode => '0644', + content => template('apache/fastcgi/server.erb'), + require => Exec["mkdir ${::apache::confd_dir}"], + before => File[$::apache::confd_dir], + notify => Class['apache::service'], + } +} diff --git a/apache/manifests/init.pp b/apache/manifests/init.pp index 7f2565cf9..32966b86f 100644 --- a/apache/manifests/init.pp +++ b/apache/manifests/init.pp @@ -13,50 +13,59 @@ # Sample Usage: # class apache ( - $service_name = $::apache::params::service_name, - $default_mods = true, - $default_vhost = true, - $default_confd_files = true, - $default_ssl_vhost = false, - $default_ssl_cert = $::apache::params::default_ssl_cert, - $default_ssl_key = $::apache::params::default_ssl_key, - $default_ssl_chain = undef, - $default_ssl_ca = undef, - $default_ssl_crl_path = undef, - $default_ssl_crl = undef, - $ip = undef, - $service_enable = true, - $service_ensure = 'running', - $purge_configs = true, - $purge_vdir = false, - $serveradmin = 'root@localhost', - $sendfile = 'On', - $error_documents = false, - $timeout = '120', - $httpd_dir = $::apache::params::httpd_dir, - $server_root = $::apache::params::server_root, - $confd_dir = $::apache::params::confd_dir, - $vhost_dir = $::apache::params::vhost_dir, - $vhost_enable_dir = $::apache::params::vhost_enable_dir, - $mod_dir = $::apache::params::mod_dir, - $mod_enable_dir = $::apache::params::mod_enable_dir, - $mpm_module = $::apache::params::mpm_module, - $conf_template = $::apache::params::conf_template, - $servername = $::apache::params::servername, - $manage_user = true, - $manage_group = true, - $user = $::apache::params::user, - $group = $::apache::params::group, - $keepalive = $::apache::params::keepalive, - $keepalive_timeout = $::apache::params::keepalive_timeout, - $logroot = $::apache::params::logroot, - $log_level = $::apache::params::log_level, - $ports_file = $::apache::params::ports_file, - $apache_version = $::apache::version::default, - $server_tokens = 'OS', - $server_signature = 'On', - $trace_enable = 'On', - $package_ensure = 'installed', + $apache_name = $::apache::params::apache_name, + $service_name = $::apache::params::service_name, + $default_mods = true, + $default_vhost = true, + $default_confd_files = true, + $default_ssl_vhost = false, + $default_ssl_cert = $::apache::params::default_ssl_cert, + $default_ssl_key = $::apache::params::default_ssl_key, + $default_ssl_chain = undef, + $default_ssl_ca = undef, + $default_ssl_crl_path = undef, + $default_ssl_crl = undef, + $default_ssl_crl_check = undef, + $ip = undef, + $service_enable = true, + $service_ensure = 'running', + $purge_configs = true, + $purge_vhost_dir = undef, + $purge_vdir = false, + $serveradmin = 'root@localhost', + $sendfile = 'On', + $error_documents = false, + $timeout = '120', + $httpd_dir = $::apache::params::httpd_dir, + $server_root = $::apache::params::server_root, + $conf_dir = $::apache::params::conf_dir, + $confd_dir = $::apache::params::confd_dir, + $vhost_dir = $::apache::params::vhost_dir, + $vhost_enable_dir = $::apache::params::vhost_enable_dir, + $mod_dir = $::apache::params::mod_dir, + $mod_enable_dir = $::apache::params::mod_enable_dir, + $mpm_module = $::apache::params::mpm_module, + $conf_template = $::apache::params::conf_template, + $servername = $::apache::params::servername, + $manage_user = true, + $manage_group = true, + $user = $::apache::params::user, + $group = $::apache::params::group, + $keepalive = $::apache::params::keepalive, + $keepalive_timeout = $::apache::params::keepalive_timeout, + $max_keepalive_requests = $apache::params::max_keepalive_requests, + $logroot = $::apache::params::logroot, + $logroot_mode = $::apache::params::logroot_mode, + $log_level = $::apache::params::log_level, + $log_formats = {}, + $ports_file = $::apache::params::ports_file, + $docroot = $::apache::params::docroot, + $apache_version = $::apache::version::default, + $server_tokens = 'OS', + $server_signature = 'On', + $trace_enable = 'On', + $allow_encoded_slashes = undef, + $package_ensure = 'installed', ) inherits ::apache::params { validate_bool($default_vhost) validate_bool($default_ssl_vhost) @@ -65,7 +74,7 @@ validate_bool($service_enable) $valid_mpms_re = $apache_version ? { - 2.4 => '(event|itk|peruser|prefork|worker)', + '2.4' => '(event|itk|peruser|prefork|worker)', default => '(event|itk|prefork|worker)' } @@ -73,6 +82,10 @@ validate_re($mpm_module, $valid_mpms_re) } + if $allow_encoded_slashes { + validate_re($allow_encoded_slashes, '(^on$|^off$|^nodecode$)', "${allow_encoded_slashes} is not permitted for allow_encoded_slashes. Allowed values are 'on', 'off' or 'nodecode'.") + } + # NOTE: on FreeBSD it's mpm module's responsibility to install httpd package. # NOTE: the same strategy may be introduced for other OSes. For this, you # should delete the 'if' block below and modify all MPM modules' manifests @@ -81,7 +94,7 @@ if $::osfamily != 'FreeBSD' { package { 'httpd': ensure => $package_ensure, - name => $::apache::params::apache_name, + name => $apache_name, notify => Class['Apache::Service'], } } @@ -124,6 +137,13 @@ $purge_confd = $purge_configs } + # Set purge vhostd appropriately + if $purge_vhost_dir == undef { + $purge_vhostd = $purge_confd + } else { + $purge_vhostd = $purge_vhost_dir + } + Exec { path => '/bin:/sbin:/usr/bin:/usr/sbin', } @@ -181,7 +201,7 @@ file { $vhost_dir: ensure => directory, recurse => true, - purge => $purge_configs, + purge => $purge_vhostd, notify => Class['Apache::Service'], require => Package['httpd'], } @@ -196,7 +216,7 @@ file { $vhost_enable_dir: ensure => directory, recurse => true, - purge => $purge_configs, + purge => $purge_vhostd, notify => Class['Apache::Service'], require => Package['httpd'], } @@ -212,22 +232,21 @@ require => Package['httpd'], } concat::fragment { 'Apache ports header': + ensure => present, target => $ports_file, content => template('apache/ports_header.erb') } - if $::apache::params::conf_dir and $::apache::params::conf_file { + if $::apache::conf_dir and $::apache::params::conf_file { case $::osfamily { 'debian': { - $docroot = '/var/www' - $pidfile = '${APACHE_PID_FILE}' + $pidfile = "\${APACHE_PID_FILE}" $error_log = 'error.log' $error_documents_path = '/usr/share/apache2/error' $scriptalias = '/usr/lib/cgi-bin' $access_log_file = 'access.log' } 'redhat': { - $docroot = '/var/www/html' $pidfile = 'run/httpd.pid' $error_log = 'error_log' $error_documents_path = '/var/www/error' @@ -235,7 +254,6 @@ $access_log_file = 'access_log' } 'freebsd': { - $docroot = '/usr/local/www/apache22/data' $pidfile = '/var/run/httpd.pid' $error_log = 'httpd-error.log' $error_documents_path = '/usr/local/www/apache22/error' @@ -268,11 +286,12 @@ # - $apxs_workaround # - $keepalive # - $keepalive_timeout + # - $max_keepalive_requests # - $server_root # - $server_tokens # - $server_signature # - $trace_enable - file { "${::apache::params::conf_dir}/${::apache::params::conf_file}": + file { "${::apache::conf_dir}/${::apache::params::conf_file}": ensure => file, content => template($conf_template), notify => Class['Apache::Service'], @@ -316,6 +335,7 @@ access_log_file => $access_log_file, priority => '15', ip => $ip, + logroot_mode => $logroot_mode, } $ssl_access_log_file = $::osfamily ? { 'freebsd' => $access_log_file, @@ -331,6 +351,7 @@ access_log_file => $ssl_access_log_file, priority => '15', ip => $ip, + logroot_mode => $logroot_mode, } } } diff --git a/apache/manifests/listen.pp b/apache/manifests/listen.pp index 503ee8860..e6a8a3c76 100644 --- a/apache/manifests/listen.pp +++ b/apache/manifests/listen.pp @@ -3,6 +3,7 @@ # Template uses: $listen_addr_port concat::fragment { "Listen ${listen_addr_port}": + ensure => present, target => $::apache::ports_file, content => template('apache/listen.erb'), } diff --git a/apache/manifests/mod.pp b/apache/manifests/mod.pp index 8be99afd0..88cdcd6a5 100644 --- a/apache/manifests/mod.pp +++ b/apache/manifests/mod.pp @@ -1,10 +1,12 @@ define apache::mod ( - $package = undef, + $package = undef, $package_ensure = 'present', - $lib = undef, - $lib_path = $::apache::params::lib_path, - $id = undef, - $path = undef, + $lib = undef, + $lib_path = $::apache::params::lib_path, + $id = undef, + $path = undef, + $loadfile_name = undef, + $loadfiles = undef, ) { if ! defined(Class['apache']) { fail('You must include the apache base class before using any apache defined resources') @@ -16,11 +18,10 @@ # Determine if we have special lib $mod_libs = $::apache::params::mod_libs - $mod_lib = $mod_libs[$mod] # 2.6 compatibility hack if $lib { $_lib = $lib - } elsif $mod_lib { - $_lib = $mod_lib + } elsif has_key($mod_libs, $mod) { # 2.6 compatibility hack + $_lib = $mod_libs[$mod] } else { $_lib = "mod_${mod}.so" } @@ -38,13 +39,20 @@ $_id = "${mod}_module" } + if $loadfile_name { + $_loadfile_name = $loadfile_name + } else { + $_loadfile_name = "${mod}.load" + } + # Determine if we have a package $mod_packages = $::apache::params::mod_packages - $mod_package = $mod_packages[$mod] # 2.6 compatibility hack if $package { $_package = $package - } elsif $mod_package { - $_package = $mod_package + } elsif has_key($mod_packages, $mod) { # 2.6 compatibility hack + $_package = $mod_packages[$mod] + } else { + $_package = undef } if $_package and ! defined(Package[$_package]) { # note: FreeBSD/ports uses apxs tool to activate modules; apxs clutters @@ -53,11 +61,13 @@ # the module gets installed. $package_before = $::osfamily ? { 'freebsd' => [ - File["${mod_dir}/${mod}.load"], - File["${::apache::params::conf_dir}/${::apache::params::conf_file}"] + File[$_loadfile_name], + File["${::apache::conf_dir}/${::apache::params::conf_file}"] ], - default => File["${mod_dir}/${mod}.load"], + default => File[$_loadfile_name], } + # if there are any packages, they should be installed before the associated conf file + Package[$_package] -> File<| title == "${mod}.conf" |> # $_package may be an array package { $_package: ensure => $package_ensure, @@ -66,13 +76,13 @@ } } - file { "${mod}.load": + file { $_loadfile_name: ensure => file, - path => "${mod_dir}/${mod}.load", + path => "${mod_dir}/${_loadfile_name}", owner => 'root', group => $::apache::params::root_group, mode => '0644', - content => "LoadModule ${_id} ${_path}\n", + content => template('apache/mod/load.erb'), require => [ Package['httpd'], Exec["mkdir ${mod_dir}"], @@ -83,15 +93,15 @@ if $::osfamily == 'Debian' { $enable_dir = $::apache::mod_enable_dir - file{ "${mod}.load symlink": + file{ "${_loadfile_name} symlink": ensure => link, - path => "${enable_dir}/${mod}.load", - target => "${mod_dir}/${mod}.load", + path => "${enable_dir}/${_loadfile_name}", + target => "${mod_dir}/${_loadfile_name}", owner => 'root', group => $::apache::params::root_group, mode => '0644', require => [ - File["${mod}.load"], + File[$_loadfile_name], Exec["mkdir ${enable_dir}"], ], before => File[$enable_dir], diff --git a/apache/manifests/mod/actions.pp b/apache/manifests/mod/actions.pp new file mode 100644 index 000000000..3b60f297f --- /dev/null +++ b/apache/manifests/mod/actions.pp @@ -0,0 +1,3 @@ +class apache::mod::actions { + apache::mod { 'actions': } +} diff --git a/apache/manifests/mod/cgid.pp b/apache/manifests/mod/cgid.pp index 5c89251a1..2a0c178e0 100644 --- a/apache/manifests/mod/cgid.pp +++ b/apache/manifests/mod/cgid.pp @@ -4,7 +4,7 @@ # Debian specifies it's cgid sock path, but RedHat uses the default value # with no config file $cgisock_path = $::osfamily ? { - 'debian' => '${APACHE_RUN_DIR}/cgisock', + 'debian' => "\${APACHE_RUN_DIR}/cgisock", 'freebsd' => 'cgisock', default => undef, } diff --git a/apache/manifests/mod/dav_fs.pp b/apache/manifests/mod/dav_fs.pp index 482f31617..0cb663f5c 100644 --- a/apache/manifests/mod/dav_fs.pp +++ b/apache/manifests/mod/dav_fs.pp @@ -1,6 +1,6 @@ class apache::mod::dav_fs { $dav_lock = $::osfamily ? { - 'debian' => '${APACHE_LOCK_DIR}/DAVLock', + 'debian' => "\${APACHE_LOCK_DIR}/DAVLock", 'freebsd' => '/usr/local/var/DavLock', default => '/var/lib/dav/lockdb', } diff --git a/apache/manifests/mod/dav_svn.pp b/apache/manifests/mod/dav_svn.pp index 3ffa75911..6e70598d0 100644 --- a/apache/manifests/mod/dav_svn.pp +++ b/apache/manifests/mod/dav_svn.pp @@ -1,5 +1,20 @@ -class apache::mod::dav_svn { +class apache::mod::dav_svn ( + $authz_svn_enabled = false, +) { Class['::apache::mod::dav'] -> Class['::apache::mod::dav_svn'] include ::apache::mod::dav ::apache::mod { 'dav_svn': } + + if $::osfamily == 'Debian' and ($::operatingsystemmajrelease != '6' and $::operatingsystemmajrelease != '10.04' and $::operatingsystemrelease != '10.04') { + $loadfile_name = undef + } else { + $loadfile_name = 'dav_svn_authz_svn.load' + } + + if $authz_svn_enabled { + ::apache::mod { 'authz_svn': + loadfile_name => $loadfile_name, + require => Apache::Mod['dav_svn'], + } + } } diff --git a/apache/manifests/mod/deflate.pp b/apache/manifests/mod/deflate.pp index 9b597d946..561cbadbf 100644 --- a/apache/manifests/mod/deflate.pp +++ b/apache/manifests/mod/deflate.pp @@ -1,6 +1,18 @@ -class apache::mod::deflate { +class apache::mod::deflate ( + $types = [ + 'text/html text/plain text/xml', + 'text/css', + 'application/x-javascript application/javascript application/ecmascript', + 'application/rss+xml' + ], + $notes = { + 'Input' => 'instream', + 'Output' => 'outstream', + 'Ratio' => 'ratio' + } +) { ::apache::mod { 'deflate': } - # Template uses no variables + file { 'deflate.conf': ensure => file, path => "${::apache::mod_dir}/deflate.conf", diff --git a/apache/manifests/mod/event.pp b/apache/manifests/mod/event.pp index cad00774c..cb7ed96cd 100644 --- a/apache/manifests/mod/event.pp +++ b/apache/manifests/mod/event.pp @@ -44,7 +44,7 @@ case $::osfamily { 'redhat': { - if $apache_version >= 2.4 { + if versioncmp($apache_version, '2.4') >= 0 { apache::mpm{ 'event': apache_version => $apache_version, } diff --git a/apache/manifests/mod/fcgid.pp b/apache/manifests/mod/fcgid.pp index 9eb799742..70761e41d 100644 --- a/apache/manifests/mod/fcgid.pp +++ b/apache/manifests/mod/fcgid.pp @@ -1,3 +1,24 @@ -class apache::mod::fcgid { - ::apache::mod { 'fcgid': } +class apache::mod::fcgid( + $options = {}, +) { + if $::osfamily == 'RedHat' and $::operatingsystemmajrelease == '7' { + $loadfile_name = 'unixd_fcgid.load' + } else { + $loadfile_name = undef + } + + ::apache::mod { 'fcgid': + loadfile_name => $loadfile_name + } + + # Template uses: + # - $options + file { 'fcgid.conf': + ensure => file, + path => "${::apache::mod_dir}/fcgid.conf", + content => template('apache/mod/fcgid.conf.erb'), + require => Exec["mkdir ${::apache::mod_dir}"], + before => File[$::apache::mod_dir], + notify => Service['httpd'], + } } diff --git a/apache/manifests/mod/info.pp b/apache/manifests/mod/info.pp index 18f9ea1df..2c3d56ed8 100644 --- a/apache/manifests/mod/info.pp +++ b/apache/manifests/mod/info.pp @@ -1,6 +1,7 @@ class apache::mod::info ( - $allow_from = ['127.0.0.1','::1'], - $apache_version = $::apache::apache_version, + $allow_from = ['127.0.0.1','::1'], + $apache_version = $::apache::apache_version, + $restrict_access = true, ){ apache::mod { 'info': } # Template uses diff --git a/apache/manifests/mod/ldap.pp b/apache/manifests/mod/ldap.pp index f489291a2..d3b17ff5b 100644 --- a/apache/manifests/mod/ldap.pp +++ b/apache/manifests/mod/ldap.pp @@ -1,6 +1,8 @@ -class apache::mod::ldap { +class apache::mod::ldap ( + $apache_version = $::apache::apache_version, +){ ::apache::mod { 'ldap': } - # Template uses no variables + # Template uses $apache_version file { 'ldap.conf': ensure => file, path => "${::apache::mod_dir}/ldap.conf", diff --git a/apache/manifests/mod/mime.pp b/apache/manifests/mod/mime.pp index 8348a06ad..ccdb5d4b3 100644 --- a/apache/manifests/mod/mime.pp +++ b/apache/manifests/mod/mime.pp @@ -15,7 +15,7 @@ if $mime_support_package { package { $mime_support_package: ensure => 'installed', - before => File["${::apache::mod_dir}/mime.conf"], + before => File['mime.conf'], } } } diff --git a/apache/manifests/mod/mime_magic.pp b/apache/manifests/mod/mime_magic.pp index 9de8bc4bc..c276268e4 100644 --- a/apache/manifests/mod/mime_magic.pp +++ b/apache/manifests/mod/mime_magic.pp @@ -1,5 +1,5 @@ class apache::mod::mime_magic ( - $magic_file = "${::apache::params::conf_dir}/magic" + $magic_file = "${::apache::conf_dir}/magic" ) { apache::mod { 'mime_magic': } # Template uses $magic_file diff --git a/apache/manifests/mod/negotiation.pp b/apache/manifests/mod/negotiation.pp index eff685b15..0bdbfdc3c 100644 --- a/apache/manifests/mod/negotiation.pp +++ b/apache/manifests/mod/negotiation.pp @@ -1,4 +1,17 @@ -class apache::mod::negotiation { +class apache::mod::negotiation ( + $force_language_priority = 'Prefer Fallback', + $language_priority = [ 'en', 'ca', 'cs', 'da', 'de', 'el', 'eo', 'es', 'et', + 'fr', 'he', 'hr', 'it', 'ja', 'ko', 'ltz', 'nl', 'nn', + 'no', 'pl', 'pt', 'pt-BR', 'ru', 'sv', 'zh-CN', + 'zh-TW' ], +) { + if !is_array($force_language_priority) and !is_string($force_language_priority) { + fail('force_languague_priority must be a string or array of strings') + } + if !is_array($language_priority) and !is_string($language_priority) { + fail('force_languague_priority must be a string or array of strings') + } + ::apache::mod { 'negotiation': } # Template uses no variables file { 'negotiation.conf': diff --git a/apache/manifests/mod/pagespeed.pp b/apache/manifests/mod/pagespeed.pp new file mode 100644 index 000000000..efe100861 --- /dev/null +++ b/apache/manifests/mod/pagespeed.pp @@ -0,0 +1,55 @@ +class apache::mod::pagespeed ( + $inherit_vhost_config = 'on', + $filter_xhtml = false, + $cache_path = '/var/cache/mod_pagespeed/', + $log_dir = '/var/log/pagespeed', + $memcache_servers = [], + $rewrite_level = 'CoreFilters', + $disable_filters = [], + $enable_filters = [], + $forbid_filters = [], + $rewrite_deadline_per_flush_ms = 10, + $additional_domains = undef, + $file_cache_size_kb = 102400, + $file_cache_clean_interval_ms = 3600000, + $lru_cache_per_process = 1024, + $lru_cache_byte_limit = 16384, + $css_flatten_max_bytes = 2048, + $css_inline_max_bytes = 2048, + $css_image_inline_max_bytes = 2048, + $image_inline_max_bytes = 2048, + $js_inline_max_bytes = 2048, + $css_outline_min_bytes = 3000, + $js_outline_min_bytes = 3000, + $inode_limit = 500000, + $image_max_rewrites_at_once = 8, + $num_rewrite_threads = 4, + $num_expensive_rewrite_threads = 4, + $collect_statistics = 'on', + $statistics_logging = 'on', + $allow_view_stats = [], + $allow_pagespeed_console = [], + $allow_pagespeed_message = [], + $message_buffer_size = 100000, + $additional_configuration = {}, + $apache_version = $::apache::apache_version, +){ + + $_lib = $::apache::apache_version ? { + '2.4' => 'mod_pagespeed_ap24.so', + default => undef + } + + apache::mod { 'pagespeed': + lib => $_lib, + } + + file { 'pagespeed.conf': + ensure => file, + path => "${::apache::mod_dir}/pagespeed.conf", + content => template('apache/mod/pagespeed.conf.erb'), + require => Exec["mkdir ${::apache::mod_dir}"], + before => File[$::apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/passenger.pp b/apache/manifests/mod/passenger.pp index 6a7404daa..12139cb2b 100644 --- a/apache/manifests/mod/passenger.pp +++ b/apache/manifests/mod/passenger.pp @@ -9,27 +9,65 @@ $rails_autodetect = undef, $passenger_root = $::apache::params::passenger_root, $passenger_ruby = $::apache::params::passenger_ruby, + $passenger_default_ruby = $::apache::params::passenger_default_ruby, $passenger_max_pool_size = undef, $passenger_use_global_queue = undef, + $mod_package = undef, + $mod_package_ensure = undef, + $mod_lib = undef, + $mod_lib_path = undef, + $mod_id = undef, + $mod_path = undef, ) { - if $::osfamily == 'FreeBSD' { - ::apache::mod { 'passenger': - lib_path => "${passenger_root}/buildout/apache2" - } - } else { - ::apache::mod { 'passenger': } - } - # Managed by the package, but declare it to avoid purging if $passenger_conf_package_file { file { 'passenger_package.conf': path => "${::apache::mod_dir}/${passenger_conf_package_file}", } + } else { + # Remove passenger_extra.conf left over from before Passenger support was + # reworked for Debian. This is a temporary fix for users running this + # module from master after release 1.0.1 It will be removed in two + # releases from now. + $passenger_package_conf_ensure = $::osfamily ? { + 'Debian' => 'absent', + default => undef, + } + + file { 'passenger_package.conf': + ensure => $passenger_package_conf_ensure, + path => "${::apache::mod_dir}/passenger_extra.conf", + } + } + + $_package = $mod_package + $_package_ensure = $mod_package_ensure + $_lib = $mod_lib + if $::osfamily == 'FreeBSD' { + if $mod_lib_path { + $_lib_path = $mod_lib_path + } else { + $_lib_path = "${passenger_root}/buildout/apache2" + } + } else { + $_lib_path = $mod_lib_path + } + + $_id = $mod_id + $_path = $mod_path + ::apache::mod { 'passenger': + package => $_package, + package_ensure => $_package_ensure, + lib => $_lib, + lib_path => $_lib_path, + id => $_id, + path => $_path, } # Template uses: # - $passenger_root # - $passenger_ruby + # - $passenger_default_ruby # - $passenger_max_pool_size # - $passenger_high_performance # - $passenger_max_requests diff --git a/apache/manifests/mod/php.pp b/apache/manifests/mod/php.pp index ace596d42..c1f76065a 100644 --- a/apache/manifests/mod/php.pp +++ b/apache/manifests/mod/php.pp @@ -1,23 +1,57 @@ class apache::mod::php ( + $package_name = undef, $package_ensure = 'present', + $path = undef, + $extensions = ['.php'], + $content = undef, + $template = 'apache/mod/php5.conf.erb', + $source = undef, ) { - if ! defined(Class['apache::mod::prefork']) { - fail('apache::mod::php requires apache::mod::prefork; please enable mpm_module => \'prefork\' on Class[\'apache\']') + if defined(Class['::apache::mod::prefork']) { + Class['::apache::mod::prefork']->File['php5.conf'] } + elsif defined(Class['::apache::mod::itk']) { + Class['::apache::mod::itk']->File['php5.conf'] + } + else { + fail('apache::mod::php requires apache::mod::prefork or apache::mod::itk; please enable mpm_module => \'prefork\' or mpm_module => \'itk\' on Class[\'apache\']') + } + validate_array($extensions) + + if $source and ($content or $template != 'apache/mod/php5.conf.erb') { + warning('source and content or template parameters are provided. source parameter will be used') + } elsif $content and $template != 'apache/mod/php5.conf.erb' { + warning('content and template parameters are provided. content parameter will be used') + } + + $manage_content = $source ? { + undef => $content ? { + undef => template($template), + default => $content, + }, + default => undef, + } + ::apache::mod { 'php5': + package => $package_name, package_ensure => $package_ensure, + path => $path, } include ::apache::mod::mime include ::apache::mod::dir Class['::apache::mod::mime'] -> Class['::apache::mod::dir'] -> Class['::apache::mod::php'] + # Template uses $extensions file { 'php5.conf': ensure => file, path => "${::apache::mod_dir}/php5.conf", - content => template('apache/mod/php5.conf.erb'), + owner => 'root', + group => 'root', + mode => '0644', + content => $manage_content, + source => $source, require => [ - Class['::apache::mod::prefork'], Exec["mkdir ${::apache::mod_dir}"], ], before => File[$::apache::mod_dir], diff --git a/apache/manifests/mod/prefork.pp b/apache/manifests/mod/prefork.pp index d615acbdd..b3adeae8c 100644 --- a/apache/manifests/mod/prefork.pp +++ b/apache/manifests/mod/prefork.pp @@ -42,7 +42,7 @@ case $::osfamily { 'redhat': { - if $apache_version >= 2.4 { + if versioncmp($apache_version, '2.4') >= 0 { ::apache::mpm{ 'prefork': apache_version => $apache_version, } diff --git a/apache/manifests/mod/proxy.pp b/apache/manifests/mod/proxy.pp index b6c0d6df7..03c1e78c9 100644 --- a/apache/manifests/mod/proxy.pp +++ b/apache/manifests/mod/proxy.pp @@ -1,9 +1,10 @@ class apache::mod::proxy ( $proxy_requests = 'Off', $allow_from = undef, + $apache_version = $::apache::apache_version, ) { ::apache::mod { 'proxy': } - # Template uses $proxy_requests + # Template uses $proxy_requests, $apache_version file { 'proxy.conf': ensure => file, path => "${::apache::mod_dir}/proxy.conf", diff --git a/apache/manifests/mod/proxy_html.pp b/apache/manifests/mod/proxy_html.pp index 91d7bd3c8..549eb117f 100644 --- a/apache/manifests/mod/proxy_html.pp +++ b/apache/manifests/mod/proxy_html.pp @@ -1,21 +1,30 @@ class apache::mod::proxy_html { Class['::apache::mod::proxy'] -> Class['::apache::mod::proxy_html'] Class['::apache::mod::proxy_http'] -> Class['::apache::mod::proxy_html'] - ::apache::mod { 'proxy_html': } + + # Add libxml2 case $::osfamily { - 'RedHat': { + /RedHat|FreeBSD/: { ::apache::mod { 'xml2enc': } + $loadfiles = undef } 'Debian': { - $proxy_html_loadfiles = $::apache::params::distrelease ? { - '6' => '/usr/lib/libxml2.so.2', - default => "/usr/lib/${::hardwaremodel}-linux-gnu/libxml2.so.2", + $gnu_path = $::hardwaremodel ? { + 'i686' => 'i386', + default => $::hardwaremodel, + } + $loadfiles = $::apache::params::distrelease ? { + '6' => ['/usr/lib/libxml2.so.2'], + '10' => ['/usr/lib/libxml2.so.2'], + default => ["/usr/lib/${gnu_path}-linux-gnu/libxml2.so.2"], } } - 'FreeBSD': { - ::apache::mod { 'xml2enc': } - } } + + ::apache::mod { 'proxy_html': + loadfiles => $loadfiles, + } + # Template uses $icons_path file { 'proxy_html.conf': ensure => file, diff --git a/apache/manifests/mod/reqtimeout.pp b/apache/manifests/mod/reqtimeout.pp index 80b301830..62088873b 100644 --- a/apache/manifests/mod/reqtimeout.pp +++ b/apache/manifests/mod/reqtimeout.pp @@ -1,4 +1,6 @@ -class apache::mod::reqtimeout { +class apache::mod::reqtimeout ( + $timeouts = ['header=20-40,minrate=500', 'body=10,minrate=500'] +){ ::apache::mod { 'reqtimeout': } # Template uses no variables file { 'reqtimeout.conf': diff --git a/apache/manifests/mod/shib.pp b/apache/manifests/mod/shib.pp new file mode 100644 index 000000000..8ec4c6dd1 --- /dev/null +++ b/apache/manifests/mod/shib.pp @@ -0,0 +1,15 @@ +class apache::mod::shib ( + $suppress_warning = false, +) { + + if $::osfamily == 'RedHat' and ! $suppress_warning { + warning('RedHat distributions do not have Apache mod_shib in their default package repositories.') + } + + $mod_shib = 'shib2' + + apache::mod {$mod_shib: + id => 'mod_shib', + } + +} \ No newline at end of file diff --git a/apache/manifests/mod/speling.pp b/apache/manifests/mod/speling.pp new file mode 100644 index 000000000..eb46d78f0 --- /dev/null +++ b/apache/manifests/mod/speling.pp @@ -0,0 +1,3 @@ +class apache::mod::speling { + ::apache::mod { 'speling': } +} diff --git a/apache/manifests/mod/ssl.pp b/apache/manifests/mod/ssl.pp index d644ac5ef..293e9319c 100644 --- a/apache/manifests/mod/ssl.pp +++ b/apache/manifests/mod/ssl.pp @@ -1,22 +1,25 @@ class apache::mod::ssl ( $ssl_compression = false, $ssl_options = [ 'StdEnvVars' ], + $ssl_cipher = 'HIGH:MEDIUM:!aNULL:!MD5', + $ssl_protocol = [ 'all', '-SSLv2', '-SSLv3' ], $apache_version = $::apache::apache_version, + $package_name = undef, ) { $session_cache = $::osfamily ? { - 'debian' => '${APACHE_RUN_DIR}/ssl_scache(512000)', + 'debian' => "\${APACHE_RUN_DIR}/ssl_scache(512000)", 'redhat' => '/var/cache/mod_ssl/scache(512000)', 'freebsd' => '/var/run/ssl_scache(512000)', } case $::osfamily { 'debian': { - if $apache_version >= 2.4 and $::operatingsystem == 'Ubuntu' { + if versioncmp($apache_version, '2.4') >= 0 { $ssl_mutex = 'default' } elsif $::operatingsystem == 'Ubuntu' and $::operatingsystemrelease == '10.04' { $ssl_mutex = 'file:/var/run/apache2/ssl_mutex' } else { - $ssl_mutex = 'file:${APACHE_RUN_DIR}/ssl_mutex' + $ssl_mutex = "file:\${APACHE_RUN_DIR}/ssl_mutex" } } 'redhat': { @@ -30,9 +33,11 @@ } } - ::apache::mod { 'ssl': } + ::apache::mod { 'ssl': + package => $package_name, + } - if $apache_version >= 2.4 { + if versioncmp($apache_version, '2.4') >= 0 { ::apache::mod { 'socache_shmcb': } } diff --git a/apache/manifests/mod/status.pp b/apache/manifests/mod/status.pp index fdaba4b07..cfab5d58e 100644 --- a/apache/manifests/mod/status.pp +++ b/apache/manifests/mod/status.pp @@ -26,11 +26,12 @@ class apache::mod::status ( $allow_from = ['127.0.0.1','::1'], $extended_status = 'On', + $apache_version = $::apache::apache_version, ){ validate_array($allow_from) validate_re(downcase($extended_status), '^(on|off)$', "${extended_status} is not supported for extended_status. Allowed values are 'On' and 'Off'.") ::apache::mod { 'status': } - # Template uses $allow_from, $extended_status + # Template uses $allow_from, $extended_status, $apache_version file { 'status.conf': ensure => file, path => "${::apache::mod_dir}/status.conf", diff --git a/apache/manifests/mod/suexec.pp b/apache/manifests/mod/suexec.pp new file mode 100644 index 000000000..ded013d49 --- /dev/null +++ b/apache/manifests/mod/suexec.pp @@ -0,0 +1,3 @@ +class apache::mod::suexec { + ::apache::mod { 'suexec': } +} diff --git a/apache/manifests/mod/userdir.pp b/apache/manifests/mod/userdir.pp index 27af54c66..accfe64a7 100644 --- a/apache/manifests/mod/userdir.pp +++ b/apache/manifests/mod/userdir.pp @@ -2,10 +2,11 @@ $home = '/home', $dir = 'public_html', $disable_root = true, + $apache_version = $::apache::apache_version, ) { ::apache::mod { 'userdir': } - # Template uses $home, $dir, $disable_root + # Template uses $home, $dir, $disable_root, $apache_version file { 'userdir.conf': ensure => file, path => "${::apache::mod_dir}/userdir.conf", diff --git a/apache/manifests/mod/version.pp b/apache/manifests/mod/version.pp new file mode 100644 index 000000000..c0e405686 --- /dev/null +++ b/apache/manifests/mod/version.pp @@ -0,0 +1,8 @@ +class apache::mod::version { + + if ($::osfamily == 'debian' and versioncmp($apache_version, '2.4') >= 0) { + warning("${module_name}: module version_module is built-in and can't be loaded") + } else { + ::apache::mod { 'version': } + } +} diff --git a/apache/manifests/mod/worker.pp b/apache/manifests/mod/worker.pp index 8007953cf..0d2815964 100644 --- a/apache/manifests/mod/worker.pp +++ b/apache/manifests/mod/worker.pp @@ -6,6 +6,7 @@ $threadsperchild = '25', $maxrequestsperchild = '0', $serverlimit = '25', + $threadlimit = '64', $apache_version = $::apache::apache_version, ) { if defined(Class['apache::mod::event']) { @@ -34,6 +35,7 @@ # - $threadsperchild # - $maxrequestsperchild # - $serverlimit + # - $threadLimit file { "${::apache::mod_dir}/worker.conf": ensure => file, content => template('apache/mod/worker.conf.erb'), @@ -44,7 +46,7 @@ case $::osfamily { 'redhat': { - if $apache_version >= 2.4 { + if versioncmp($apache_version, '2.4') >= 0 { ::apache::mpm{ 'worker': apache_version => $apache_version, } diff --git a/apache/manifests/mod/wsgi.pp b/apache/manifests/mod/wsgi.pp index 244a3458b..2a47bb68e 100644 --- a/apache/manifests/mod/wsgi.pp +++ b/apache/manifests/mod/wsgi.pp @@ -1,5 +1,5 @@ class apache::mod::wsgi ( - $wsgi_socket_prefix = undef, + $wsgi_socket_prefix = $::apache::params::wsgi_socket_prefix, $wsgi_python_path = undef, $wsgi_python_home = undef, ){ diff --git a/apache/manifests/mpm.pp b/apache/manifests/mpm.pp index b6b2cfebe..6437016ba 100644 --- a/apache/manifests/mpm.pp +++ b/apache/manifests/mpm.pp @@ -13,7 +13,7 @@ $_path = "${lib_path}/${_lib}" $_id = "mpm_${mpm}_module" - if $apache_version >= 2.4 { + if versioncmp($apache_version, '2.4') >= 0 { file { "${mod_dir}/${mpm}.load": ensure => file, path => "${mod_dir}/${mpm}.load", @@ -37,7 +37,7 @@ notify => Service['httpd'], } - if $apache_version >= 2.4 { + if versioncmp($apache_version, '2.4') >= 0 { file { "${::apache::mod_enable_dir}/${mpm}.load": ensure => link, target => "${::apache::mod_dir}/${mpm}.load", @@ -47,7 +47,7 @@ } } - if $apache_version < 2.4 { + if versioncmp($apache_version, '2.4') < 0 { package { "apache2-mpm-${mpm}": ensure => present, } diff --git a/apache/manifests/namevirtualhost.pp b/apache/manifests/namevirtualhost.pp index 4fa879518..f8c3a80d8 100644 --- a/apache/manifests/namevirtualhost.pp +++ b/apache/manifests/namevirtualhost.pp @@ -3,6 +3,7 @@ # Template uses: $addr_port concat::fragment { "NameVirtualHost ${addr_port}": + ensure => present, target => $::apache::ports_file, content => template('apache/namevirtualhost.erb'), } diff --git a/apache/manifests/package.pp b/apache/manifests/package.pp index c5ef31536..a4e4015c5 100644 --- a/apache/manifests/package.pp +++ b/apache/manifests/package.pp @@ -1,7 +1,7 @@ class apache::package ( $ensure = 'present', $mpm_module = $::apache::params::mpm_module, -) { +) inherits ::apache::params { case $::osfamily { 'freebsd' : { $all_mpms = [ @@ -26,7 +26,7 @@ ensure => $ensure, path => '/etc/make.conf', line => "APACHE_PORT=${apache_package}", - match => '^\\s*#?\\s*APACHE_PORT\\s*=\\s*', + match => '^\s*#?\s*APACHE_PORT\s*=\s*', before => Package['httpd'], } # remove other packages diff --git a/apache/manifests/params.pp b/apache/manifests/params.pp index 04c3b65fe..6a221fd63 100644 --- a/apache/manifests/params.pp +++ b/apache/manifests/params.pp @@ -28,6 +28,11 @@ # The default error log level $log_level = 'warn' + if $::operatingsystem == 'Ubuntu' and $::lsbdistrelease == '10.04' { + $verify_command = '/usr/sbin/apache2ctl -t' + } else { + $verify_command = '/usr/sbin/apachectl -t' + } if $::osfamily == 'RedHat' or $::operatingsystem == 'amazon' { $user = 'apache' $group = 'apache' @@ -39,10 +44,13 @@ $conf_dir = "${httpd_dir}/conf" $confd_dir = "${httpd_dir}/conf.d" $mod_dir = "${httpd_dir}/conf.d" + $mod_enable_dir = undef $vhost_dir = "${httpd_dir}/conf.d" + $vhost_enable_dir = undef $conf_file = 'httpd.conf' $ports_file = "${conf_dir}/ports.conf" $logroot = '/var/log/httpd' + $logroot_mode = undef $lib_path = 'modules' $mpm_module = 'prefork' $dev_packages = 'httpd-devel' @@ -53,17 +61,23 @@ $passenger_conf_package_file = 'passenger.conf' $passenger_root = undef $passenger_ruby = undef + $passenger_default_ruby = undef $suphp_addhandler = 'php5-script' $suphp_engine = 'off' $suphp_configpath = undef + # NOTE: The module for Shibboleth is not available to RH/CentOS without an additional repository. http://wiki.aaf.edu.au/tech-info/sp-install-guide $mod_packages = { 'auth_kerb' => 'mod_auth_kerb', - 'authnz_ldap' => 'mod_authz_ldap', + 'authnz_ldap' => $::apache::version::distrelease ? { + '7' => 'mod_ldap', + default => 'mod_authz_ldap', + }, 'fastcgi' => 'mod_fastcgi', 'fcgid' => 'mod_fcgid', + 'pagespeed' => 'mod-pagespeed-stable', 'passenger' => 'mod_passenger', 'perl' => 'mod_perl', - 'php5' => $distrelease ? { + 'php5' => $::apache::version::distrelease ? { '5' => 'php53', default => 'php', }, @@ -76,6 +90,7 @@ 'suphp' => 'mod_suphp', 'xsendfile' => 'mod_xsendfile', 'nss' => 'mod_nss', + 'shib2' => 'shibboleth', } $mod_libs = { 'php5' => 'libphp5.so', @@ -84,45 +99,50 @@ $conf_template = 'apache/httpd.conf.erb' $keepalive = 'Off' $keepalive_timeout = 15 + $max_keepalive_requests = 100 $fastcgi_lib_path = undef $mime_support_package = 'mailcap' $mime_types_config = '/etc/mime.types' + $docroot = '/var/www/html' + if $::osfamily == "RedHat" { + $wsgi_socket_prefix = '/var/run/wsgi' + } else { + $wsgi_socket_prefix = undef + } } elsif $::osfamily == 'Debian' { - $user = 'www-data' - $group = 'www-data' - $root_group = 'root' - $apache_name = 'apache2' - $service_name = 'apache2' - $httpd_dir = '/etc/apache2' - $server_root = '/etc/apache2' - $conf_dir = $httpd_dir - $confd_dir = "${httpd_dir}/conf.d" - $mod_dir = "${httpd_dir}/mods-available" - $mod_enable_dir = "${httpd_dir}/mods-enabled" - $vhost_dir = "${httpd_dir}/sites-available" - $vhost_enable_dir = "${httpd_dir}/sites-enabled" - $conf_file = 'apache2.conf' - $ports_file = "${conf_dir}/ports.conf" - $logroot = '/var/log/apache2' - $lib_path = '/usr/lib/apache2/modules' - $mpm_module = 'worker' - $dev_packages = ['libaprutil1-dev', 'libapr1-dev', 'apache2-prefork-dev'] - $default_ssl_cert = '/etc/ssl/certs/ssl-cert-snakeoil.pem' - $default_ssl_key = '/etc/ssl/private/ssl-cert-snakeoil.key' - $ssl_certs_dir = '/etc/ssl/certs' - $passenger_conf_file = 'passenger.conf' - $passenger_conf_package_file = undef - $passenger_root = '/usr' - $passenger_ruby = '/usr/bin/ruby' - $suphp_addhandler = 'x-httpd-php' - $suphp_engine = 'off' - $suphp_configpath = '/etc/php5/apache2' - $mod_packages = { + $user = 'www-data' + $group = 'www-data' + $root_group = 'root' + $apache_name = 'apache2' + $service_name = 'apache2' + $httpd_dir = '/etc/apache2' + $server_root = '/etc/apache2' + $conf_dir = $httpd_dir + $confd_dir = "${httpd_dir}/conf.d" + $mod_dir = "${httpd_dir}/mods-available" + $mod_enable_dir = "${httpd_dir}/mods-enabled" + $vhost_dir = "${httpd_dir}/sites-available" + $vhost_enable_dir = "${httpd_dir}/sites-enabled" + $conf_file = 'apache2.conf' + $ports_file = "${conf_dir}/ports.conf" + $logroot = '/var/log/apache2' + $logroot_mode = undef + $lib_path = '/usr/lib/apache2/modules' + $mpm_module = 'worker' + $dev_packages = ['libaprutil1-dev', 'libapr1-dev', 'apache2-prefork-dev'] + $default_ssl_cert = '/etc/ssl/certs/ssl-cert-snakeoil.pem' + $default_ssl_key = '/etc/ssl/private/ssl-cert-snakeoil.key' + $ssl_certs_dir = '/etc/ssl/certs' + $suphp_addhandler = 'x-httpd-php' + $suphp_engine = 'off' + $suphp_configpath = '/etc/php5/apache2' + $mod_packages = { 'auth_kerb' => 'libapache2-mod-auth-kerb', 'dav_svn' => 'libapache2-svn', 'fastcgi' => 'libapache2-mod-fastcgi', 'fcgid' => 'libapache2-mod-fcgid', 'nss' => 'libapache2-mod-nss', + 'pagespeed' => 'mod-pagespeed-stable', 'passenger' => 'libapache2-mod-passenger', 'perl' => 'libapache2-mod-perl2', 'php5' => 'libapache2-mod-php5', @@ -132,16 +152,67 @@ 'suphp' => 'libapache2-mod-suphp', 'wsgi' => 'libapache2-mod-wsgi', 'xsendfile' => 'libapache2-mod-xsendfile', + 'shib2' => 'libapache2-mod-shib2', } - $mod_libs = { + $mod_libs = { 'php5' => 'libphp5.so', } - $conf_template = 'apache/httpd.conf.erb' - $keepalive = 'Off' - $keepalive_timeout = 15 - $fastcgi_lib_path = '/var/lib/apache2/fastcgi' + $conf_template = 'apache/httpd.conf.erb' + $keepalive = 'Off' + $keepalive_timeout = 15 + $max_keepalive_requests = 100 + $fastcgi_lib_path = '/var/lib/apache2/fastcgi' $mime_support_package = 'mime-support' - $mime_types_config = '/etc/mime.types' + $mime_types_config = '/etc/mime.types' + $docroot = '/var/www' + + # + # Passenger-specific settings + # + + $passenger_conf_file = 'passenger.conf' + $passenger_conf_package_file = undef + + case $::operatingsystem { + 'Ubuntu': { + case $::lsbdistrelease { + '12.04': { + $passenger_root = '/usr' + $passenger_ruby = '/usr/bin/ruby' + $passenger_default_ruby = undef + } + '14.04': { + $passenger_root = '/usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini' + $passenger_ruby = undef + $passenger_default_ruby = '/usr/bin/ruby' + } + default: { + # The following settings may or may not work on Ubuntu releases not + # supported by this module. + $passenger_root = '/usr' + $passenger_ruby = '/usr/bin/ruby' + $passenger_default_ruby = undef + } + } + } + 'Debian': { + case $::lsbdistcodename { + 'wheezy': { + $passenger_root = '/usr' + $passenger_ruby = '/usr/bin/ruby' + $passenger_default_ruby = undef + } + default: { + # The following settings may or may not work on Debian releases not + # supported by this module. + $passenger_root = '/usr' + $passenger_ruby = '/usr/bin/ruby' + $passenger_default_ruby = undef + } + } + } + } + $wsgi_socket_prefix = undef } elsif $::osfamily == 'FreeBSD' { $user = 'www' $group = 'www' @@ -159,6 +230,7 @@ $conf_file = 'httpd.conf' $ports_file = "${conf_dir}/ports.conf" $logroot = '/var/log/apache22' + $logroot_mode = undef $lib_path = '/usr/local/libexec/apache22' $mpm_module = 'prefork' $dev_packages = undef @@ -169,6 +241,7 @@ $passenger_conf_package_file = undef $passenger_root = '/usr/local/lib/ruby/gems/1.9/gems/passenger-4.0.10' $passenger_ruby = '/usr/bin/ruby' + $passenger_default_ruby = undef $suphp_addhandler = 'php5-script' $suphp_engine = 'off' $suphp_configpath = undef @@ -178,7 +251,6 @@ # NOTE: 'php' needs to enable APACHE option in make config # NOTE: 'dav_svn' needs to enable MOD_DAV_SVN make config # NOTE: not sure where the shibboleth should come from - # NOTE: don't know where the shibboleth module should come from 'auth_kerb' => 'www/mod_auth_kerb2', 'fcgid' => 'www/mod_fcgid', 'passenger' => 'www/rubygem-passenger', @@ -189,7 +261,8 @@ 'wsgi' => 'www/mod_wsgi', 'dav_svn' => 'devel/subversion', 'xsendfile' => 'www/mod_xsendfile', - 'rpaf' => 'www/mod_rpaf2' + 'rpaf' => 'www/mod_rpaf2', + 'shib2' => 'security/shibboleth2-sp', } $mod_libs = { 'php5' => 'libphp5.so', @@ -197,9 +270,12 @@ $conf_template = 'apache/httpd.conf.erb' $keepalive = 'Off' $keepalive_timeout = 15 + $max_keepalive_requests = 100 $fastcgi_lib_path = undef # TODO: revisit $mime_support_package = 'misc/mime-support' $mime_types_config = '/usr/local/etc/mime.types' + $wsgi_socket_prefix = undef + $docroot = '/usr/local/www/apache22/data' } else { fail("Class['apache::params']: Unsupported osfamily: ${::osfamily}") } diff --git a/apache/manifests/service.pp b/apache/manifests/service.pp index b21a25f4b..0c1f7b96a 100644 --- a/apache/manifests/service.pp +++ b/apache/manifests/service.pp @@ -12,7 +12,7 @@ # Sample Usage: # # sometype { 'foo': -# notify => Class['apache::service], +# notify => Class['apache::service'], # } # # @@ -27,8 +27,17 @@ } validate_bool($service_enable) + case $service_ensure { + true, false, 'running', 'stopped': { + $_service_ensure = $service_ensure + } + default: { + $_service_ensure = undef + } + } + service { 'httpd': - ensure => $service_ensure, + ensure => $_service_ensure, name => $service_name, enable => $service_enable, } diff --git a/apache/manifests/version.pp b/apache/manifests/version.pp index 581fdde6f..dc5288dca 100644 --- a/apache/manifests/version.pp +++ b/apache/manifests/version.pp @@ -7,26 +7,26 @@ $osr_array = split($::operatingsystemrelease,'[\/\.]') $distrelease = $osr_array[0] if ! $distrelease { - fail("Class['apache::params']: Unparsable \$::operatingsystemrelease: ${::operatingsystemrelease}") + fail("Class['apache::version']: Unparsable \$::operatingsystemrelease: ${::operatingsystemrelease}") } - + case $::osfamily { 'RedHat': { if ($::operatingsystem == 'Fedora' and $distrelease >= 18) or ($::operatingsystem != 'Fedora' and $distrelease >= 7) { - $default = 2.4 + $default = '2.4' } else { - $default = 2.2 + $default = '2.2' } } 'Debian': { - if $::operatingsystem == 'Ubuntu' and $distrelease >= 13.10 { - $default = 2.4 + if $::operatingsystem == 'Ubuntu' and $::operatingsystemrelease >= 13.10 { + $default = '2.4' } else { - $default = 2.2 + $default = '2.2' } } 'FreeBSD': { - $default = 2.2 + $default = '2.2' } default: { fail("Class['apache::version']: Unsupported osfamily: ${::osfamily}") diff --git a/apache/manifests/vhost.pp b/apache/manifests/vhost.pp index 8525235e9..0841dfff4 100644 --- a/apache/manifests/vhost.pp +++ b/apache/manifests/vhost.pp @@ -1,180 +1,112 @@ -# Definition: apache::vhost -# -# This class installs Apache Virtual Hosts -# -# Parameters: -# - The $port to configure the host on -# - The $docroot provides the DocumentRoot variable -# - The $virtual_docroot provides VirtualDocumentationRoot variable -# - The $serveradmin will specify an email address for Apache that it will -# display when it renders one of it's error pages -# - The $ssl option is set true or false to enable SSL for this Virtual Host -# - The $priority of the site -# - The $servername is the primary name of the virtual host -# - The $serveraliases of the site -# - The $ip to configure the host on, defaulting to * -# - The $options for the given vhost -# - The $override for the given vhost (list of AllowOverride arguments) -# - The $vhost_name for name based virtualhosting, defaulting to * -# - The $logroot specifies the location of the virtual hosts logfiles, default -# to /var/log// -# - The $log_level specifies the verbosity of the error log for this vhost. Not -# set by default for the vhost, instead the global server configuration default -# of 'warn' is used. -# - The $access_log specifies if *_access.log directives should be configured. -# - The $ensure specifies if vhost file is present or absent. -# - The $headers is a list of Header statement strings as per http://httpd.apache.org/docs/2.2/mod/mod_headers.html#header -# - The $request_headers is a list of RequestHeader statement strings as per http://httpd.apache.org/docs/2.2/mod/mod_headers.html#requestheader -# - $aliases is a list of Alias hashes for mod_alias as per http://httpd.apache.org/docs/current/mod/mod_alias.html -# each statement is a hash in the form of { alias => '/alias', path => '/real/path/to/directory' } -# - $directories is a lost of hashes for creating statements as per http://httpd.apache.org/docs/2.2/mod/core.html#directory -# each statement is a hash in the form of { path => '/path/to/directory', => } -# see README.md for list of supported directives. -# -# Actions: -# - Install Apache Virtual Hosts -# -# Requires: -# - The apache class -# -# Sample Usage: -# -# # Simple vhost definition: -# apache::vhost { 'site.name.fqdn': -# port => '80', -# docroot => '/path/to/docroot', -# } -# -# # Multiple Mod Rewrites: -# apache::vhost { 'site.name.fqdn': -# port => '80', -# docroot => '/path/to/docroot', -# rewrites => [ -# { -# comment => 'force www domain', -# rewrite_cond => ['%{HTTP_HOST} ^([a-z.]+)?example.com$ [NC]', '%{HTTP_HOST} !^www. [NC]'], -# rewrite_rule => ['.? http://www.%1example.com%{REQUEST_URI} [R=301,L]'] -# }, -# { -# comment => 'prevent image hotlinking', -# rewrite_cond => ['%{HTTP_REFERER} !^$', '%{HTTP_REFERER} !^http://(www.)?example.com/ [NC]'], -# rewrite_rule => ['.(gif|jpg|png)$ - [F]'] -# }, -# ] -# } -# -# # SSL vhost with non-SSL rewrite: -# apache::vhost { 'site.name.fqdn': -# port => '443', -# ssl => true, -# docroot => '/path/to/docroot', -# } -# apache::vhost { 'site.name.fqdn': -# port => '80', -# rewrites => [ -# { -# comment => "redirect non-SSL traffic to SSL site", -# rewrite_cond => ['%{HTTPS} off'], -# rewrite_rule => ['(.*) https://%{HTTPS_HOST}%{REQUEST_URI}'] -# } -# ] -# } -# apache::vhost { 'site.name.fqdn': -# port => '80', -# docroot => '/path/to/other_docroot', -# custom_fragment => template("${module_name}/my_fragment.erb"), -# } -# +# See README.md for usage information define apache::vhost( - $docroot, - $virtual_docroot = false, - $port = undef, - $ip = undef, - $ip_based = false, - $add_listen = true, - $docroot_owner = 'root', - $docroot_group = $::apache::params::root_group, - $serveradmin = undef, - $ssl = false, - $ssl_cert = $::apache::default_ssl_cert, - $ssl_key = $::apache::default_ssl_key, - $ssl_chain = $::apache::default_ssl_chain, - $ssl_ca = $::apache::default_ssl_ca, - $ssl_crl_path = $::apache::default_ssl_crl_path, - $ssl_crl = $::apache::default_ssl_crl, - $ssl_certs_dir = $::apache::params::ssl_certs_dir, - $ssl_protocol = undef, - $ssl_cipher = undef, - $ssl_honorcipherorder = undef, - $ssl_verify_client = undef, - $ssl_verify_depth = undef, - $ssl_options = undef, - $ssl_proxyengine = false, - $priority = undef, - $default_vhost = false, - $servername = $name, - $serveraliases = [], - $options = ['Indexes','FollowSymLinks','MultiViews'], - $override = ['None'], - $directoryindex = '', - $vhost_name = '*', - $logroot = $::apache::logroot, - $log_level = undef, - $access_log = true, - $access_log_file = undef, - $access_log_pipe = undef, - $access_log_syslog = undef, - $access_log_format = undef, - $access_log_env_var = undef, - $aliases = undef, - $directories = undef, - $error_log = true, - $error_log_file = undef, - $error_log_pipe = undef, - $error_log_syslog = undef, - $error_documents = [], - $fallbackresource = undef, - $scriptalias = undef, - $scriptaliases = [], - $proxy_dest = undef, - $proxy_pass = undef, - $suphp_addhandler = $::apache::params::suphp_addhandler, - $suphp_engine = $::apache::params::suphp_engine, - $suphp_configpath = $::apache::params::suphp_configpath, - $php_admin_flags = [], - $php_admin_values = [], - $no_proxy_uris = [], - $redirect_source = '/', - $redirect_dest = undef, - $redirect_status = undef, - $redirectmatch_status = undef, - $redirectmatch_regexp = undef, - $rack_base_uris = undef, - $headers = undef, - $request_headers = undef, - $rewrites = undef, - $rewrite_base = undef, - $rewrite_rule = undef, - $rewrite_cond = undef, - $setenv = [], - $setenvif = [], - $block = [], - $ensure = 'present', - $wsgi_application_group = undef, - $wsgi_daemon_process = undef, - $wsgi_daemon_process_options = undef, - $wsgi_import_script = undef, - $wsgi_import_script_options = undef, - $wsgi_process_group = undef, - $wsgi_script_aliases = undef, - $custom_fragment = undef, - $itk = undef, - $fastcgi_server = undef, - $fastcgi_socket = undef, - $fastcgi_dir = undef, - $additional_includes = [], - $apache_version = $::apache::apache_version - ) { + $docroot, + $manage_docroot = true, + $virtual_docroot = false, + $port = undef, + $ip = undef, + $ip_based = false, + $add_listen = true, + $docroot_owner = 'root', + $docroot_group = $::apache::params::root_group, + $docroot_mode = undef, + $serveradmin = undef, + $ssl = false, + $ssl_cert = $::apache::default_ssl_cert, + $ssl_key = $::apache::default_ssl_key, + $ssl_chain = $::apache::default_ssl_chain, + $ssl_ca = $::apache::default_ssl_ca, + $ssl_crl_path = $::apache::default_ssl_crl_path, + $ssl_crl = $::apache::default_ssl_crl, + $ssl_crl_check = $::apache::default_ssl_crl_check, + $ssl_certs_dir = $::apache::params::ssl_certs_dir, + $ssl_protocol = undef, + $ssl_cipher = undef, + $ssl_honorcipherorder = undef, + $ssl_verify_client = undef, + $ssl_verify_depth = undef, + $ssl_options = undef, + $ssl_proxyengine = false, + $priority = undef, + $default_vhost = false, + $servername = $name, + $serveraliases = [], + $options = ['Indexes','FollowSymLinks','MultiViews'], + $override = ['None'], + $directoryindex = '', + $vhost_name = '*', + $logroot = $::apache::logroot, + $logroot_ensure = 'directory', + $logroot_mode = undef, + $log_level = undef, + $access_log = true, + $access_log_file = undef, + $access_log_pipe = undef, + $access_log_syslog = undef, + $access_log_format = undef, + $access_log_env_var = undef, + $aliases = undef, + $directories = undef, + $error_log = true, + $error_log_file = undef, + $error_log_pipe = undef, + $error_log_syslog = undef, + $error_documents = [], + $fallbackresource = undef, + $scriptalias = undef, + $scriptaliases = [], + $proxy_dest = undef, + $proxy_pass = undef, + $suphp_addhandler = $::apache::params::suphp_addhandler, + $suphp_engine = $::apache::params::suphp_engine, + $suphp_configpath = $::apache::params::suphp_configpath, + $php_admin_flags = {}, + $php_admin_values = {}, + $no_proxy_uris = [], + $proxy_preserve_host = false, + $redirect_source = '/', + $redirect_dest = undef, + $redirect_status = undef, + $redirectmatch_status = undef, + $redirectmatch_regexp = undef, + $redirectmatch_dest = undef, + $rack_base_uris = undef, + $headers = undef, + $request_headers = undef, + $rewrites = undef, + $rewrite_base = undef, + $rewrite_rule = undef, + $rewrite_cond = undef, + $setenv = [], + $setenvif = [], + $block = [], + $ensure = 'present', + $wsgi_application_group = undef, + $wsgi_daemon_process = undef, + $wsgi_daemon_process_options = undef, + $wsgi_import_script = undef, + $wsgi_import_script_options = undef, + $wsgi_process_group = undef, + $wsgi_script_aliases = undef, + $wsgi_pass_authorization = undef, + $wsgi_chunked_request = undef, + $custom_fragment = undef, + $itk = undef, + $action = undef, + $fastcgi_server = undef, + $fastcgi_socket = undef, + $fastcgi_dir = undef, + $additional_includes = [], + $apache_version = $::apache::apache_version, + $allow_encoded_slashes = undef, + $suexec_user_group = undef, + $passenger_app_root = undef, + $passenger_ruby = undef, + $passenger_min_instances = undef, + $passenger_start_timeout = undef, + $passenger_pre_start = undef, + $add_default_charset = undef, +) { # The base class must be included first because it is used by parameter defaults if ! defined(Class['apache']) { fail('You must include the apache base class before using any apache defined resources') @@ -199,6 +131,19 @@ validate_hash($rewrites[0]) } + # Input validation begins + + if $suexec_user_group { + validate_re($suexec_user_group, '^\w+ \w+$', + "${suexec_user_group} is not supported for suexec_user_group. Must be 'user group'.") + } + + if $wsgi_pass_authorization { + validate_re(downcase($wsgi_pass_authorization), '^(on|off)$', + "${wsgi_pass_authorization} is not supported for wsgi_pass_authorization. + Allowed values are 'on' and 'off'.") + } + # Deprecated backwards-compatibility if $rewrite_base { warning('Apache::Vhost: parameter rewrite_base is deprecated in favor of rewrites') @@ -223,6 +168,10 @@ validate_hash($itk) } + validate_re($logroot_ensure, '^(directory|absent)$', + "${logroot_ensure} is not supported for logroot_ensure. + Allowed values are 'directory' and 'absent'.") + if $log_level { validate_re($log_level, '^(emerg|alert|crit|error|warn|notice|info|debug)$', "Log level '${log_level}' is not one of the supported Apache HTTP Server log levels.") @@ -240,6 +189,16 @@ validate_re($fallbackresource, '^/|disabled', 'Please make sure fallbackresource starts with a / (or is "disabled")') } + if $custom_fragment { + validate_string($custom_fragment) + } + + if $allow_encoded_slashes { + validate_re($allow_encoded_slashes, '(^on$|^off$|^nodecode$)', "${allow_encoded_slashes} is not permitted for allow_encoded_slashes. Allowed values are 'on', 'off' or 'nodecode'.") + } + + # Input validation ends + if $ssl and $ensure == 'present' { include ::apache::mod::ssl # Required for the AddType lines. @@ -250,22 +209,50 @@ include ::apache::mod::vhost_alias } + if $wsgi_daemon_process { + include ::apache::mod::wsgi + } + + if $suexec_user_group { + include ::apache::mod::suexec + } + + if $passenger_app_root or $passenger_ruby or $passenger_min_instances or $passenger_start_timeout or $passenger_pre_start { + include ::apache::mod::passenger + } + + # Configure the defaultness of a vhost + if $priority { + $priority_real = $priority + } elsif $default_vhost { + $priority_real = '10' + } else { + $priority_real = '25' + } + + ## Apache include does not always work with spaces in the filename + $filename = regsubst($name, ' ', '_', 'G') + # This ensures that the docroot exists # But enables it to be specified across multiple vhost resources - if ! defined(File[$docroot]) { + if ! defined(File[$docroot]) and $manage_docroot { file { $docroot: ensure => directory, owner => $docroot_owner, group => $docroot_group, + mode => $docroot_mode, require => Package['httpd'], + before => Concat["${priority_real}-${filename}.conf"], } } # Same as above, but for logroot if ! defined(File[$logroot]) { file { $logroot: - ensure => directory, + ensure => $logroot_ensure, + mode => $logroot_mode, require => Package['httpd'], + before => Concat["${priority_real}-${filename}.conf"], } } @@ -273,6 +260,9 @@ # Is apache::mod::passenger enabled (or apache::mod['passenger']) $passenger_enabled = defined(Apache::Mod['passenger']) + # Is apache::mod::shib enabled (or apache::mod['shib2']) + $shibboleth_enabled = defined(Apache::Mod['shib2']) + # Define log file names if $access_log_file { $access_log_destination = "${logroot}/${access_log_file}" @@ -318,6 +308,7 @@ $listen_addr_port = "${ip}:${port}" $nvh_addr_port = "${ip}:${port}" } else { + $listen_addr_port = undef $nvh_addr_port = $ip if ! $servername and ! $ip_based { fail("Apache::Vhost[${name}]: must pass 'ip' and/or 'port' parameters for name-based vhosts") @@ -328,6 +319,7 @@ $listen_addr_port = $port $nvh_addr_port = "${vhost_name}:${port}" } else { + $listen_addr_port = undef $nvh_addr_port = $name if ! $servername { fail("Apache::Vhost[${name}]: must pass 'ip' and/or 'port' parameters, and/or 'servername' parameter") @@ -335,23 +327,23 @@ } } if $add_listen { - if $ip and defined(Apache::Listen[$port]) { + if $ip and defined(Apache::Listen["${port}"]) { fail("Apache::Vhost[${name}]: Mixing IP and non-IP Listen directives is not possible; check the add_listen parameter of the apache::vhost define to disable this") } - if ! defined(Apache::Listen[$listen_addr_port]) and $listen_addr_port and $ensure == 'present' { - ::apache::listen { $listen_addr_port: } + if ! defined(Apache::Listen["${listen_addr_port}"]) and $listen_addr_port and $ensure == 'present' { + ::apache::listen { "${listen_addr_port}": } } } if ! $ip_based { - if ! defined(Apache::Namevirtualhost[$nvh_addr_port]) and $ensure == 'present' { + if ! defined(Apache::Namevirtualhost[$nvh_addr_port]) and $ensure == 'present' and (versioncmp($apache_version, '2.4') < 0) { ::apache::namevirtualhost { $nvh_addr_port: } } } # Load mod_rewrite if needed and not yet loaded if $rewrites or $rewrite_cond { - if ! defined(Apache::Mod['rewrite']) { - ::apache::mod { 'rewrite': } + if ! defined(Class['apache::mod::rewrite']) { + include ::apache::mod::rewrite } } @@ -386,15 +378,6 @@ } } - # Configure the defaultness of a vhost - if $priority { - $priority_real = $priority - } elsif $default_vhost { - $priority_real = '10' - } else { - $priority_real = '25' - } - # Check if mod_headers is required to process $headers/$request_headers if $headers or $request_headers { if ! defined(Class['apache::mod::headers']) { @@ -402,9 +385,6 @@ } } - ## Apache include does not always work with spaces in the filename - $filename = regsubst($name, ' ', '_', 'G') - ## Create a default directory list if none defined if $directories { if !is_hash($directories) and !(is_array($directories) and is_hash($directories[0])) { @@ -420,7 +400,7 @@ directoryindex => $directoryindex, } - if $apache_version == 2.4 { + if versioncmp($apache_version, '2.4') >= 0 { $_directory_version = { require => 'all granted', } @@ -434,98 +414,14 @@ $_directories = [ merge($_directory, $_directory_version) ] } - # Template uses: - # - $nvh_addr_port - # - $servername - # - $serveradmin - # - $docroot - # - $virtual_docroot - # - $options - # - $override - # - $logroot - # - $name - # - $aliases - # - $_directories - # - $log_level - # - $access_log - # - $access_log_destination - # - $_access_log_format - # - $_access_log_env_var - # - $error_log - # - $error_log_destination - # - $error_documents - # - $fallbackresource - # - $custom_fragment - # - $additional_includes - # block fragment: - # - $block - # directories fragment: - # - $passenger_enabled - # - $php_admin_flags - # - $php_admin_values - # - $directories (a list of key-value hashes is expected) - # fastcgi fragment: - # - $fastcgi_server - # - $fastcgi_socket - # - $fastcgi_dir - # proxy fragment: - # - $proxy_dest - # - $no_proxy_uris - # rack fragment: - # - $rack_base_uris - # redirect fragment: - # - $redirect_source - # - $redirect_dest - # - $redirect_status - # header fragment - # - $headers - # requestheader fragment: - # - $request_headers - # rewrite fragment: - # - $rewrites - # scriptalias fragment: - # - $scriptalias - # - $scriptaliases - # - $ssl - # serveralias fragment: - # - $serveraliases - # setenv fragment: - # - $setenv - # - $setenvif - # ssl fragment: - # - $ssl - # - $ssl_cert - # - $ssl_key - # - $ssl_chain - # - $ssl_certs_dir - # - $ssl_ca - # - $ssl_crl - # - $ssl_crl_path - # - $ssl_verify_client - # - $ssl_verify_depth - # - $ssl_options - # suphp fragment: - # - $suphp_addhandler - # - $suphp_engine - # - $suphp_configpath - # wsgi fragment: - # - $wsgi_application_group - # - $wsgi_daemon_process - # - $wsgi_import_script - # - $wsgi_process_group - # - $wsgi_script_aliases - file { "${priority_real}-${filename}.conf": + concat { "${priority_real}-${filename}.conf": ensure => $ensure, path => "${::apache::vhost_dir}/${priority_real}-${filename}.conf", - content => template('apache/vhost.conf.erb'), owner => 'root', group => $::apache::params::root_group, mode => '0644', - require => [ - Package['httpd'], - File[$docroot], - File[$logroot], - ], + order => 'numeric', + require => Package['httpd'], notify => Service['httpd'], } if $::osfamily == 'Debian' { @@ -541,8 +437,386 @@ owner => 'root', group => $::apache::params::root_group, mode => '0644', - require => File["${priority_real}-${filename}.conf"], + require => Concat["${priority_real}-${filename}.conf"], notify => Service['httpd'], } } + + # Template uses: + # - $nvh_addr_port + # - $servername + # - $serveradmin + concat::fragment { "${name}-apache-header": + target => "${priority_real}-${filename}.conf", + order => 0, + content => template('apache/vhost/_file_header.erb'), + } + + # Template uses: + # - $virtual_docroot + # - $docroot + concat::fragment { "${name}-docroot": + target => "${priority_real}-${filename}.conf", + order => 10, + content => template('apache/vhost/_docroot.erb'), + } + + # Template uses: + # - $aliases + if $aliases and ! empty($aliases) { + concat::fragment { "${name}-aliases": + target => "${priority_real}-${filename}.conf", + order => 20, + content => template('apache/vhost/_aliases.erb'), + } + } + + # Template uses: + # - $itk + # - $::kernelversion + if $itk and ! empty($itk) { + concat::fragment { "${name}-itk": + target => "${priority_real}-${filename}.conf", + order => 30, + content => template('apache/vhost/_itk.erb'), + } + } + + # Template uses: + # - $fallbackresource + if $fallbackresource { + concat::fragment { "${name}-fallbackresource": + target => "${priority_real}-${filename}.conf", + order => 40, + content => template('apache/vhost/_fallbackresource.erb'), + } + } + + # Template uses: + # - $_directories + # - $docroot + # - $apache_version + # - $suphp_engine + # - $shibboleth_enabled + if $_directories and ! empty($_directories) { + concat::fragment { "${name}-directories": + target => "${priority_real}-${filename}.conf", + order => 50, + content => template('apache/vhost/_directories.erb'), + } + } + + # Template uses: + # - $additional_includes + if $additional_includes and ! empty($additional_includes) { + concat::fragment { "${name}-additional_includes": + target => "${priority_real}-${filename}.conf", + order => 60, + content => template('apache/vhost/_additional_includes.erb'), + } + } + + # Template uses: + # - $error_log + # - $log_level + # - $error_log_destination + # - $log_level + if $error_log or $log_level { + concat::fragment { "${name}-logging": + target => "${priority_real}-${filename}.conf", + order => 70, + content => template('apache/vhost/_logging.erb'), + } + } + + # Template uses no variables + concat::fragment { "${name}-serversignature": + target => "${priority_real}-${filename}.conf", + order => 80, + content => template('apache/vhost/_serversignature.erb'), + } + + # Template uses: + # - $access_log + # - $_access_log_env_var + # - $access_log_destination + # - $_access_log_format + # - $_access_log_env_var + if $access_log { + concat::fragment { "${name}-access_log": + target => "${priority_real}-${filename}.conf", + order => 90, + content => template('apache/vhost/_access_log.erb'), + } + } + + # Template uses: + # - $action + if $action { + concat::fragment { "${name}-action": + target => "${priority_real}-${filename}.conf", + order => 100, + content => template('apache/vhost/_action.erb'), + } + } + + # Template uses: + # - $block + # - $apache_version + if $block and ! empty($block) { + concat::fragment { "${name}-block": + target => "${priority_real}-${filename}.conf", + order => 110, + content => template('apache/vhost/_block.erb'), + } + } + + # Template uses: + # - $error_documents + if $error_documents and ! empty($error_documents) { + concat::fragment { "${name}-error_document": + target => "${priority_real}-${filename}.conf", + order => 120, + content => template('apache/vhost/_error_document.erb'), + } + } + + # Template uses: + # - $proxy_dest + # - $proxy_pass + # - $proxy_preserve_host + # - $no_proxy_uris + if $proxy_dest or $proxy_pass { + concat::fragment { "${name}-proxy": + target => "${priority_real}-${filename}.conf", + order => 130, + content => template('apache/vhost/_proxy.erb'), + } + } + + # Template uses: + # - $rack_base_uris + if $rack_base_uris { + concat::fragment { "${name}-rack": + target => "${priority_real}-${filename}.conf", + order => 140, + content => template('apache/vhost/_rack.erb'), + } + } + + # Template uses: + # - $redirect_source + # - $redirect_dest + # - $redirect_status + # - $redirect_dest_a + # - $redirect_source_a + # - $redirect_status_a + # - $redirectmatch_status + # - $redirectmatch_regexp + # - $redirectmatch_dest + # - $redirectmatch_status_a + # - $redirectmatch_regexp_a + # - $redirectmatch_dest + if ($redirect_source and $redirect_dest) or ($redirectmatch_status and $redirectmatch_regexp and $redirectmatch_dest) { + concat::fragment { "${name}-redirect": + target => "${priority_real}-${filename}.conf", + order => 150, + content => template('apache/vhost/_redirect.erb'), + } + } + + # Template uses: + # - $rewrites + # - $rewrite_base + # - $rewrite_rule + # - $rewrite_cond + if $rewrites or $rewrite_rule { + concat::fragment { "${name}-rewrite": + target => "${priority_real}-${filename}.conf", + order => 160, + content => template('apache/vhost/_rewrite.erb'), + } + } + + # Template uses: + # - $scriptaliases + # - $scriptalias + if $scriptaliases and ! empty($scriptaliases) { + concat::fragment { "${name}-scriptalias": + target => "${priority_real}-${filename}.conf", + order => 170, + content => template('apache/vhost/_scriptalias.erb'), + } + } + + # Template uses: + # - $serveraliases + if $serveraliases and ! empty($serveraliases) { + concat::fragment { "${name}-serveralias": + target => "${priority_real}-${filename}.conf", + order => 180, + content => template('apache/vhost/_serveralias.erb'), + } + } + + # Template uses: + # - $setenv + # - $setenvif + if ($setenv and ! empty($setenv)) or ($setenvif and ! empty($setenvif)) { + concat::fragment { "${name}-setenv": + target => "${priority_real}-${filename}.conf", + order => 190, + content => template('apache/vhost/_setenv.erb'), + } + } + + # Template uses: + # - $ssl + # - $ssl_cert + # - $ssl_key + # - $ssl_chain + # - $ssl_certs_dir + # - $ssl_ca + # - $ssl_crl_path + # - $ssl_crl + # - $ssl_crl_check + # - $ssl_proxyengine + # - $ssl_protocol + # - $ssl_cipher + # - $ssl_honorcipherorder + # - $ssl_verify_client + # - $ssl_verify_depth + # - $ssl_options + # - $apache_version + if $ssl { + concat::fragment { "${name}-ssl": + target => "${priority_real}-${filename}.conf", + order => 200, + content => template('apache/vhost/_ssl.erb'), + } + } + + # Template uses: + # - $suphp_engine + # - $suphp_addhandler + # - $suphp_configpath + if $suphp_engine == 'on' { + concat::fragment { "${name}-suphp": + target => "${priority_real}-${filename}.conf", + order => 210, + content => template('apache/vhost/_suphp.erb'), + } + } + + # Template uses: + # - $php_admin_values + # - $php_admin_flags + if ($php_admin_values and ! empty($php_admin_values)) or ($php_admin_flags and ! empty($php_admin_flags)) { + concat::fragment { "${name}-php_admin": + target => "${priority_real}-${filename}.conf", + order => 220, + content => template('apache/vhost/_php_admin.erb'), + } + } + + # Template uses: + # - $headers + if $headers and ! empty($headers) { + concat::fragment { "${name}-header": + target => "${priority_real}-${filename}.conf", + order => 230, + content => template('apache/vhost/_header.erb'), + } + } + + # Template uses: + # - $request_headers + if $request_headers and ! empty($request_headers) { + concat::fragment { "${name}-requestheader": + target => "${priority_real}-${filename}.conf", + order => 240, + content => template('apache/vhost/_requestheader.erb'), + } + } + + # Template uses: + # - $wsgi_application_group + # - $wsgi_daemon_process + # - $wsgi_daemon_process_options + # - $wsgi_import_script + # - $wsgi_import_script_options + # - $wsgi_process_group + # - $wsgi_script_aliases + # - $wsgi_pass_authorization + if $wsgi_application_group or $wsgi_daemon_process or ($wsgi_import_script and $wsgi_import_script_options) or $wsgi_process_group or ($wsgi_script_aliases and ! empty($wsgi_script_aliases)) or $wsgi_pass_authorization { + concat::fragment { "${name}-wsgi": + target => "${priority_real}-${filename}.conf", + order => 250, + content => template('apache/vhost/_wsgi.erb'), + } + } + + # Template uses: + # - $custom_fragment + if $custom_fragment { + concat::fragment { "${name}-custom_fragment": + target => "${priority_real}-${filename}.conf", + order => 260, + content => template('apache/vhost/_custom_fragment.erb'), + } + } + + # Template uses: + # - $fastcgi_server + # - $fastcgi_socket + # - $fastcgi_dir + # - $apache_version + if $fastcgi_server or $fastcgi_dir { + concat::fragment { "${name}-fastcgi": + target => "${priority_real}-${filename}.conf", + order => 270, + content => template('apache/vhost/_fastcgi.erb'), + } + } + + # Template uses: + # - $suexec_user_group + if $suexec_user_group { + concat::fragment { "${name}-suexec": + target => "${priority_real}-${filename}.conf", + order => 280, + content => template('apache/vhost/_suexec.erb'), + } + } + + # Template uses: + # - $passenger_app_root + # - $passenger_ruby + # - $passenger_min_instances + # - $passenger_start_timeout + # - $passenger_pre_start + if $passenger_app_root or $passenger_ruby or $passenger_min_instances or $passenger_start_timeout or $passenger_pre_start { + concat::fragment { "${name}-passenger": + target => "${priority_real}-${filename}.conf", + order => 290, + content => template('apache/vhost/_passenger.erb'), + } + } + + # Template uses: + # - $add_default_charset + if $add_default_charset { + concat::fragment { "${name}-charsets": + target => "${priority_real}-${filename}.conf", + order => 300, + content => template('apache/vhost/_charsets.erb'), + } + } + + # Template uses no variables + concat::fragment { "${name}-file_footer": + target => "${priority_real}-${filename}.conf", + order => 999, + content => template('apache/vhost/_file_footer.erb'), + } } diff --git a/apache/metadata.json b/apache/metadata.json new file mode 100644 index 000000000..f6361a10e --- /dev/null +++ b/apache/metadata.json @@ -0,0 +1,73 @@ +{ + "name": "puppetlabs-apache", + "version": "1.2.0", + "author": "puppetlabs", + "summary": "Puppet module for Apache", + "license": "Apache 2.0", + "source": "git://github.com/puppetlabs/puppetlabs-apache.git", + "project_page": "https://github.com/puppetlabs/puppetlabs-apache", + "issues_url": "https://tickets.puppetlabs.com/browse/MODULES", + "operatingsystem_support": [ + { + "operatingsystem": "RedHat", + "operatingsystemrelease": [ + "5", + "6", + "7" + ] + }, + { + "operatingsystem": "CentOS", + "operatingsystemrelease": [ + "5", + "6", + "7" + ] + }, + { + "operatingsystem": "OracleLinux", + "operatingsystemrelease": [ + "6", + "7" + ] + }, + { + "operatingsystem": "Scientific", + "operatingsystemrelease": [ + "5", + "6", + "7" + ] + }, + { + "operatingsystem": "Debian", + "operatingsystemrelease": [ + "6", + "7" + ] + }, + { + "operatingsystem": "Ubuntu", + "operatingsystemrelease": [ + "10.04", + "12.04", + "14.04" + ] + } + ], + "requirements": [ + { + "name": "pe", + "version_requirement": "3.x" + }, + { + "name": "puppet", + "version_requirement": "3.x" + } + ], + "description": "Module for Apache configuration", + "dependencies": [ + {"name":"puppetlabs/stdlib","version_requirement":">= 2.4.0"}, + {"name":"puppetlabs/concat","version_requirement":">= 1.1.1"} + ] +} diff --git a/apache/spec/acceptance/apache_parameters_spec.rb b/apache/spec/acceptance/apache_parameters_spec.rb index eeae55d45..3a21ab0eb 100644 --- a/apache/spec/acceptance/apache_parameters_spec.rb +++ b/apache/spec/acceptance/apache_parameters_spec.rb @@ -11,8 +11,8 @@ end if fact('osfamily') == 'FreeBSD' - describe file("#{confd_dir}/no-accf.conf.erb") do - it { should_not be_file } + describe file("#{$confd_dir}/no-accf.conf.erb") do + it { is_expected.not_to be_file } end end end @@ -24,7 +24,7 @@ if fact('osfamily') == 'FreeBSD' describe file("#{$confd_dir}/no-accf.conf.erb") do - it { should be_file } + it { is_expected.to be_file } end end end @@ -36,8 +36,8 @@ end describe file($ports_file) do - it { should be_file } - it { should contain 'Listen 10.1.1.1' } + it { is_expected.to be_file } + it { is_expected.to contain 'Listen 10.1.1.1' } end end @@ -53,8 +53,8 @@ class { 'apache': end describe service($service_name) do - it { should be_running } - it { should be_enabled } + it { is_expected.to be_running } + it { is_expected.to be_enabled } end end @@ -70,8 +70,8 @@ class { 'apache': end describe service($service_name) do - it { should_not be_running } - it { should_not be_enabled } + it { is_expected.not_to be_running } + it { is_expected.not_to be_enabled } end end @@ -79,17 +79,22 @@ class { 'apache': it 'applies cleanly' do pp = <<-EOS class { 'apache': - purge_configs => false, - purge_vdir => false, + purge_configs => false, + purge_vhost_dir => false, + vhost_dir => "#{$confd_dir}.vhosts" } EOS shell("touch #{$confd_dir}/test.conf") + shell("mkdir -p #{$confd_dir}.vhosts && touch #{$confd_dir}.vhosts/test.conf") apply_manifest(pp, :catch_failures => true) end - # Ensure the file didn't disappear. + # Ensure the files didn't disappear. describe file("#{$confd_dir}/test.conf") do - it { should be_file } + it { is_expected.to be_file } + end + describe file("#{$confd_dir}.vhosts/test.conf") do + it { is_expected.to be_file } end end @@ -98,17 +103,22 @@ class { 'apache': it 'applies cleanly' do pp = <<-EOS class { 'apache': - purge_configs => true, - purge_vdir => true, + purge_configs => true, + purge_vhost_dir => true, + vhost_dir => "#{$confd_dir}.vhosts" } EOS shell("touch #{$confd_dir}/test.conf") + shell("mkdir -p #{$confd_dir}.vhosts && touch #{$confd_dir}.vhosts/test.conf") apply_manifest(pp, :catch_failures => true) end # File should be gone describe file("#{$confd_dir}/test.conf") do - it { should_not be_file } + it { is_expected.not_to be_file } + end + describe file("#{$confd_dir}.vhosts/test.conf") do + it { is_expected.not_to be_file } end end end @@ -120,8 +130,8 @@ class { 'apache': end describe file($vhost) do - it { should be_file } - it { should contain 'ServerAdmin test@example.com' } + it { is_expected.to be_file } + it { is_expected.to contain 'ServerAdmin test@example.com' } end end @@ -134,8 +144,8 @@ class { 'apache': end describe file($conf_file) do - it { should be_file } - it { should contain 'EnableSendfile On' } + it { is_expected.to be_file } + it { is_expected.to contain 'EnableSendfile On' } end describe 'setup' do @@ -146,8 +156,8 @@ class { 'apache': end describe file($conf_file) do - it { should be_file } - it { should contain 'Sendfile Off' } + it { is_expected.to be_file } + it { is_expected.to contain 'Sendfile Off' } end end @@ -160,8 +170,8 @@ class { 'apache': end describe file($conf_file) do - it { should be_file } - it { should contain 'Alias /error/' } + it { is_expected.to be_file } + it { is_expected.to contain 'Alias /error/' } end end @@ -174,8 +184,8 @@ class { 'apache': end describe file($conf_file) do - it { should be_file } - it { should contain 'Timeout 1234' } + it { is_expected.to be_file } + it { is_expected.to contain 'Timeout 1234' } end end @@ -190,9 +200,9 @@ class { 'apache': httpd_dir => '/tmp', service_ensure => stopped } end end - describe file("#{$confd_dir}/mime.conf") do - it { should be_file } - it { should contain 'AddLanguage eo .eo' } + describe file("#{$mod_dir}/mime.conf") do + it { is_expected.to be_file } + it { is_expected.to contain 'AddLanguage eo .eo' } end end @@ -205,8 +215,8 @@ class { 'apache': httpd_dir => '/tmp', service_ensure => stopped } end describe file($conf_file) do - it { should be_file } - it { should contain 'ServerRoot "/tmp/root"' } + it { is_expected.to be_file } + it { is_expected.to contain 'ServerRoot "/tmp/root"' } end end @@ -218,15 +228,15 @@ class { 'apache': httpd_dir => '/tmp', service_ensure => stopped } end end - if $apache_version >= 2.4 + if $apache_version == '2.4' describe file($conf_file) do - it { should be_file } - it { should contain 'IncludeOptional "/tmp/root/*.conf"' } + it { is_expected.to be_file } + it { is_expected.to contain 'IncludeOptional "/tmp/root/*.conf"' } end else describe file($conf_file) do - it { should be_file } - it { should contain 'Include "/tmp/root/*.conf"' } + it { is_expected.to be_file } + it { is_expected.to contain 'Include "/tmp/root/*.conf"' } end end end @@ -242,8 +252,8 @@ class { 'apache': httpd_dir => '/tmp', service_ensure => stopped } end describe file($conf_file) do - it { should be_file } - it { should contain 'testcontent' } + it { is_expected.to be_file } + it { is_expected.to contain 'testcontent' } end end @@ -256,8 +266,8 @@ class { 'apache': httpd_dir => '/tmp', service_ensure => stopped } end describe file($conf_file) do - it { should be_file } - it { should contain 'ServerName "test.server"' } + it { is_expected.to be_file } + it { is_expected.to contain 'ServerName "test.server"' } end end @@ -277,48 +287,95 @@ class { 'apache': end describe user('testweb') do - it { should exist } - it { should belong_to_group 'testweb' } + it { is_expected.to exist } + it { is_expected.to belong_to_group 'testweb' } end describe group('testweb') do - it { should exist } + it { is_expected.to exist } end end + describe 'logformats' do + describe 'setup' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': + log_formats => { + 'vhost_common' => '%v %h %l %u %t \\\"%r\\\" %>s %b', + 'vhost_combined' => '%v %h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-agent}i\\\"', + } + } + EOS + apply_manifest(pp, :catch_failures => true) + end + end + + describe file($conf_file) do + it { is_expected.to be_file } + it { is_expected.to contain 'LogFormat "%v %h %l %u %t \"%r\" %>s %b" vhost_common' } + it { is_expected.to contain 'LogFormat "%v %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" vhost_combined' } + end + end + + describe 'keepalive' do describe 'setup' do it 'applies cleanly' do - pp = "class { 'apache': keepalive => 'On', keepalive_timeout => '30' }" + pp = "class { 'apache': keepalive => 'On', keepalive_timeout => '30', max_keepalive_requests => '200' }" apply_manifest(pp, :catch_failures => true) end end describe file($conf_file) do - it { should be_file } - it { should contain 'KeepAlive On' } - it { should contain 'KeepAliveTimeout 30' } + it { is_expected.to be_file } + it { is_expected.to contain 'KeepAlive On' } + it { is_expected.to contain 'KeepAliveTimeout 30' } + it { is_expected.to contain 'MaxKeepAliveRequests 200' } end end describe 'logging' do describe 'setup' do it 'applies cleanly' do - pp = "class { 'apache': logroot => '/tmp' }" + pp = <<-EOS + if $::osfamily == 'RedHat' and $::selinux == 'true' { + $semanage_package = $::operatingsystemmajrelease ? { + '5' => 'policycoreutils', + default => 'policycoreutils-python', + } + + package { $semanage_package: ensure => installed } + exec { 'set_apache_defaults': + command => 'semanage fcontext -a -t httpd_log_t "/apache_spec(/.*)?"', + path => '/bin:/usr/bin/:/sbin:/usr/sbin', + require => Package[$semanage_package], + } + exec { 'restorecon_apache': + command => 'restorecon -Rv /apache_spec', + path => '/bin:/usr/bin/:/sbin:/usr/sbin', + before => Service['httpd'], + require => Class['apache'], + } + } + file { '/apache_spec': ensure => directory, } + class { 'apache': logroot => '/apache_spec' } + EOS apply_manifest(pp, :catch_failures => true) end end - describe file("/tmp/#{$error_log}") do - it { should be_file } + describe file("/apache_spec/#{$error_log}") do + it { is_expected.to be_file } end end describe 'ports_file' do it 'applys cleanly' do pp = <<-EOS + file { '/apache_spec': ensure => directory, } class { 'apache': - ports_file => '/tmp/ports_file', + ports_file => '/apache_spec/ports_file', ip => '10.1.1.1', service_ensure => stopped } @@ -326,9 +383,9 @@ class { 'apache': apply_manifest(pp, :catch_failures => true) end - describe file('/tmp/ports_file') do - it { should be_file } - it { should contain 'Listen 10.1.1.1' } + describe file('/apache_spec/ports_file') do + it { is_expected.to be_file } + it { is_expected.to contain 'Listen 10.1.1.1' } end end @@ -343,8 +400,8 @@ class { 'apache': end describe file($conf_file) do - it { should be_file } - it { should contain 'ServerTokens Minor' } + it { is_expected.to be_file } + it { is_expected.to contain 'ServerTokens Minor' } end end @@ -360,8 +417,8 @@ class { 'apache': end describe file($conf_file) do - it { should be_file } - it { should contain 'ServerSignature testsig' } + it { is_expected.to be_file } + it { is_expected.to contain 'ServerSignature testsig' } end end @@ -376,8 +433,8 @@ class { 'apache': end describe file($conf_file) do - it { should be_file } - it { should contain 'TraceEnable Off' } + it { is_expected.to be_file } + it { is_expected.to contain 'TraceEnable Off' } end end @@ -392,7 +449,7 @@ class { 'apache': end describe package($package_name) do - it { should be_installed } + it { is_expected.to be_installed } end end diff --git a/apache/spec/acceptance/apache_ssl_spec.rb b/apache/spec/acceptance/apache_ssl_spec.rb index 649c02d84..f8023fa24 100644 --- a/apache/spec/acceptance/apache_ssl_spec.rb +++ b/apache/spec/acceptance/apache_ssl_spec.rb @@ -1,4 +1,5 @@ require 'spec_helper_acceptance' +require_relative './version.rb' case fact('osfamily') when 'RedHat' @@ -13,27 +14,33 @@ it 'runs without error' do pp = <<-EOS class { 'apache': - service_ensure => stopped, - default_ssl_vhost => true, - default_ssl_cert => '/tmp/ssl_cert', - default_ssl_key => '/tmp/ssl_key', - default_ssl_chain => '/tmp/ssl_chain', - default_ssl_ca => '/tmp/ssl_ca', - default_ssl_crl_path => '/tmp/ssl_crl_path', - default_ssl_crl => '/tmp/ssl_crl', + service_ensure => stopped, + default_ssl_vhost => true, + default_ssl_cert => '/tmp/ssl_cert', + default_ssl_key => '/tmp/ssl_key', + default_ssl_chain => '/tmp/ssl_chain', + default_ssl_ca => '/tmp/ssl_ca', + default_ssl_crl_path => '/tmp/ssl_crl_path', + default_ssl_crl => '/tmp/ssl_crl', + default_ssl_crl_check => 'chain', } EOS apply_manifest(pp, :catch_failures => true) end describe file("#{vhostd}/15-default-ssl.conf") do - it { should be_file } - it { should contain 'SSLCertificateFile "/tmp/ssl_cert"' } - it { should contain 'SSLCertificateKeyFile "/tmp/ssl_key"' } - it { should contain 'SSLCertificateChainFile "/tmp/ssl_chain"' } - it { should contain 'SSLCACertificateFile "/tmp/ssl_ca"' } - it { should contain 'SSLCARevocationPath "/tmp/ssl_crl_path"' } - it { should contain 'SSLCARevocationFile "/tmp/ssl_crl"' } + it { is_expected.to be_file } + it { is_expected.to contain 'SSLCertificateFile "/tmp/ssl_cert"' } + it { is_expected.to contain 'SSLCertificateKeyFile "/tmp/ssl_key"' } + it { is_expected.to contain 'SSLCertificateChainFile "/tmp/ssl_chain"' } + it { is_expected.to contain 'SSLCACertificateFile "/tmp/ssl_ca"' } + it { is_expected.to contain 'SSLCARevocationPath "/tmp/ssl_crl_path"' } + it { is_expected.to contain 'SSLCARevocationFile "/tmp/ssl_crl"' } + if $apache_version == '2.4' + it { is_expected.to contain 'SSLCARevocationCheck "chain"' } + else + it { is_expected.not_to contain 'SSLCARevocationCheck' } + end end end @@ -53,6 +60,7 @@ class { 'apache': ssl_ca => '/tmp/ssl_ca', ssl_crl_path => '/tmp/ssl_crl_path', ssl_crl => '/tmp/ssl_crl', + ssl_crl_check => 'chain', ssl_certs_dir => '/tmp', ssl_protocol => 'test', ssl_cipher => 'test', @@ -67,20 +75,25 @@ class { 'apache': end describe file("#{vhostd}/25-test_ssl.conf") do - it { should be_file } - it { should contain 'SSLCertificateFile "/tmp/ssl_cert"' } - it { should contain 'SSLCertificateKeyFile "/tmp/ssl_key"' } - it { should contain 'SSLCertificateChainFile "/tmp/ssl_chain"' } - it { should contain 'SSLCACertificateFile "/tmp/ssl_ca"' } - it { should contain 'SSLCARevocationPath "/tmp/ssl_crl_path"' } - it { should contain 'SSLCARevocationFile "/tmp/ssl_crl"' } - it { should contain 'SSLProxyEngine On' } - it { should contain 'SSLProtocol test' } - it { should contain 'SSLCipherSuite test' } - it { should contain 'SSLHonorCipherOrder test' } - it { should contain 'SSLVerifyClient test' } - it { should contain 'SSLVerifyDepth test' } - it { should contain 'SSLOptions test test1' } + it { is_expected.to be_file } + it { is_expected.to contain 'SSLCertificateFile "/tmp/ssl_cert"' } + it { is_expected.to contain 'SSLCertificateKeyFile "/tmp/ssl_key"' } + it { is_expected.to contain 'SSLCertificateChainFile "/tmp/ssl_chain"' } + it { is_expected.to contain 'SSLCACertificateFile "/tmp/ssl_ca"' } + it { is_expected.to contain 'SSLCARevocationPath "/tmp/ssl_crl_path"' } + it { is_expected.to contain 'SSLCARevocationFile "/tmp/ssl_crl"' } + it { is_expected.to contain 'SSLProxyEngine On' } + it { is_expected.to contain 'SSLProtocol test' } + it { is_expected.to contain 'SSLCipherSuite test' } + it { is_expected.to contain 'SSLHonorCipherOrder test' } + it { is_expected.to contain 'SSLVerifyClient test' } + it { is_expected.to contain 'SSLVerifyDepth test' } + it { is_expected.to contain 'SSLOptions test test1' } + if $apache_version == '2.4' + it { is_expected.to contain 'SSLCARevocationCheck "chain"' } + else + it { is_expected.not_to contain 'SSLCARevocationCheck' } + end end end diff --git a/apache/spec/acceptance/class_spec.rb b/apache/spec/acceptance/class_spec.rb index 1f5921d59..e006251cf 100644 --- a/apache/spec/acceptance/class_spec.rb +++ b/apache/spec/acceptance/class_spec.rb @@ -25,12 +25,12 @@ class { 'apache': } end describe package(package_name) do - it { should be_installed } + it { is_expected.to be_installed } end describe service(service_name) do - it { should be_enabled } - it { should be_running } + it { is_expected.to be_enabled } + it { is_expected.to be_running } end end @@ -38,10 +38,33 @@ class { 'apache': } # Using puppet_apply as a helper it 'should work with no errors' do pp = <<-EOS - file { '/tmp/apache_custom': ensure => directory, } + if $::osfamily == 'RedHat' and $::selinux == 'true' { + $semanage_package = $::operatingsystemmajrelease ? { + '5' => 'policycoreutils', + default => 'policycoreutils-python', + } + + package { $semanage_package: ensure => installed } + exec { 'set_apache_defaults': + command => 'semanage fcontext -a -t httpd_sys_content_t "/apache_spec(/.*)?"', + path => '/bin:/usr/bin/:/sbin:/usr/sbin', + subscribe => Package[$semanage_package], + refreshonly => true, + } + exec { 'restorecon_apache': + command => 'restorecon -Rv /apache_spec', + path => '/bin:/usr/bin/:/sbin:/usr/sbin', + before => Service['httpd'], + require => Class['apache'], + subscribe => Exec['set_apache_defaults'], + refreshonly => true, + } + } + file { '/apache_spec': ensure => directory, } + file { '/apache_spec/apache_custom': ensure => directory, } class { 'apache': - mod_dir => '/tmp/apache_custom/mods', - vhost_dir => '/tmp/apache_custom/vhosts', + mod_dir => '/apache_spec/apache_custom/mods', + vhost_dir => '/apache_spec/apache_custom/vhosts', } EOS @@ -51,8 +74,8 @@ class { 'apache': end describe service(service_name) do - it { should be_enabled } - it { should be_running } + it { is_expected.to be_enabled } + it { is_expected.to be_running } end end end diff --git a/apache/spec/acceptance/custom_config_spec.rb b/apache/spec/acceptance/custom_config_spec.rb new file mode 100644 index 000000000..fce6bb306 --- /dev/null +++ b/apache/spec/acceptance/custom_config_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper_acceptance' +require_relative './version.rb' + +describe 'apache::custom_config define', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do + context 'invalid config' do + it 'should not add the config' do + pp = <<-EOS + class { 'apache': } + apache::custom_config { 'acceptance_test': + content => 'INVALID', + } + EOS + + apply_manifest(pp, :expect_failures => true) + end + + describe file("#{$confd_dir}/25-acceptance_test.conf") do + it { is_expected.not_to be_file } + end + end + + context 'valid config' do + it 'should add the config' do + pp = <<-EOS + class { 'apache': } + apache::custom_config { 'acceptance_test': + content => '# just a comment', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{$confd_dir}/25-acceptance_test.conf") do + it { is_expected.to contain '# just a comment' } + end + end +end diff --git a/apache/spec/acceptance/default_mods_spec.rb b/apache/spec/acceptance/default_mods_spec.rb index 03e144560..2565ce77b 100644 --- a/apache/spec/acceptance/default_mods_spec.rb +++ b/apache/spec/acceptance/default_mods_spec.rb @@ -2,13 +2,14 @@ case fact('osfamily') when 'RedHat' + mod_dir = '/etc/httpd/conf.d' servicename = 'httpd' when 'Debian' + mod_dir = '/etc/apache2/mods-available' servicename = 'apache2' when 'FreeBSD' + mod_dir = '/usr/local/etc/apache22/Modules' servicename = 'apache22' -else - raise "Unconfigured OS for apache service on #{fact('osfamily')}" end describe 'apache::default_mods class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do @@ -27,7 +28,7 @@ class { 'apache': end describe service(servicename) do - it { should be_running } + it { is_expected.to be_running } end end @@ -53,7 +54,7 @@ class { 'apache': # Are these the same? describe service(servicename) do - it { should_not be_running } + it { is_expected.not_to be_running } end describe "service #{servicename}" do it 'should not be running' do @@ -91,7 +92,29 @@ class { 'apache': end describe service(servicename) do - it { should be_running } + it { is_expected.to be_running } + end + end + + describe 'change loadfile name' do + it 'should apply with no errors' do + pp = <<-EOS + class { 'apache': default_mods => false } + ::apache::mod { 'auth_basic': + loadfile_name => 'zz_auth_basic.load', + } + EOS + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + describe service(servicename) do + it { is_expected.to be_running } + end + + describe file("#{mod_dir}/zz_auth_basic.load") do + it { is_expected.to be_file } end end end diff --git a/apache/spec/acceptance/itk_spec.rb b/apache/spec/acceptance/itk_spec.rb index 86fc2c01c..b810657ec 100644 --- a/apache/spec/acceptance/itk_spec.rb +++ b/apache/spec/acceptance/itk_spec.rb @@ -27,7 +27,7 @@ class { 'apache': end describe service(service_name) do - it { should be_running } - it { should be_enabled } + it { is_expected.to be_running } + it { is_expected.to be_enabled } end end diff --git a/apache/spec/acceptance/mod_dav_svn_spec.rb b/apache/spec/acceptance/mod_dav_svn_spec.rb new file mode 100644 index 000000000..7e5afed52 --- /dev/null +++ b/apache/spec/acceptance/mod_dav_svn_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper_acceptance' + +describe 'apache::mod::dav_svn class', :unless => (fact('operatingsystem') == 'OracleLinux' and fact('operatingsystemmajrelease') == '7') do + case fact('osfamily') + when 'Debian' + mod_dir = '/etc/apache2/mods-available' + service_name = 'apache2' + if fact('operatingsystemmajrelease') == '6' or fact('operatingsystemmajrelease') == '10.04' or fact('operatingsystemrelease') == '10.04' + authz_svn_load_file = 'dav_svn_authz_svn.load' + else + authz_svn_load_file = 'authz_svn.load' + end + when 'RedHat' + mod_dir = '/etc/httpd/conf.d' + service_name = 'httpd' + authz_svn_load_file = 'dav_svn_authz_svn.load' + when 'FreeBSD' + mod_dir = '/usr/local/etc/apache22/Modules' + service_name = 'apache22' + authz_svn_load_file = 'dav_svn_authz_svn.load' + end + + context "default dav_svn config" do + it 'succeeds in puppeting dav_svn' do + pp= <<-EOS + class { 'apache': } + include apache::mod::dav_svn + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service(service_name) do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + + describe file("#{mod_dir}/dav_svn.load") do + it { is_expected.to contain "LoadModule dav_svn_module" } + end + end + + context "dav_svn with enabled authz_svn config" do + it 'succeeds in puppeting dav_svn' do + pp= <<-EOS + class { 'apache': } + class { 'apache::mod::dav_svn': + authz_svn_enabled => true, + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service(service_name) do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + + describe file("#{mod_dir}/#{authz_svn_load_file}") do + it { is_expected.to contain "LoadModule authz_svn_module" } + end + end +end diff --git a/apache/spec/acceptance/mod_deflate_spec.rb b/apache/spec/acceptance/mod_deflate_spec.rb new file mode 100644 index 000000000..6052cc283 --- /dev/null +++ b/apache/spec/acceptance/mod_deflate_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper_acceptance' + +describe 'apache::mod::deflate class' do + case fact('osfamily') + when 'Debian' + mod_dir = '/etc/apache2/mods-available' + service_name = 'apache2' + when 'RedHat' + mod_dir = '/etc/httpd/conf.d' + service_name = 'httpd' + when 'FreeBSD' + mod_dir = '/usr/local/etc/apache22/Modules' + service_name = 'apache22' + end + + context "default deflate config" do + it 'succeeds in puppeting deflate' do + pp= <<-EOS + class { 'apache': } + include apache::mod::deflate + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service(service_name) do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + + describe file("#{mod_dir}/deflate.conf") do + it { is_expected.to contain "AddOutputFilterByType DEFLATE text/html text/plain text/xml" } + it { is_expected.to contain "AddOutputFilterByType DEFLATE text/css" } + it { is_expected.to contain "AddOutputFilterByType DEFLATE application/x-javascript application/javascript application/ecmascript" } + it { is_expected.to contain "AddOutputFilterByType DEFLATE application/rss+xml" } + it { is_expected.to contain "DeflateFilterNote Input instream" } + it { is_expected.to contain "DeflateFilterNote Output outstream" } + it { is_expected.to contain "DeflateFilterNote Ratio ratio" } + end + end +end diff --git a/apache/spec/acceptance/mod_fcgid_spec.rb b/apache/spec/acceptance/mod_fcgid_spec.rb new file mode 100644 index 000000000..e99a7f299 --- /dev/null +++ b/apache/spec/acceptance/mod_fcgid_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper_acceptance' + +describe 'apache::mod::fcgid class', :unless => (UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) or (fact('operatingsystem') == 'OracleLinux' and fact('operatingsystemmajrelease') == '7')) do + context "default fcgid config", :if => (fact('osfamily') == 'RedHat' and fact('operatingsystemmajrelease') != '5') do + it 'succeeds in puppeting fcgid' do + pp = <<-EOS + class { 'epel': } # mod_fcgid lives in epel + class { 'apache': } + class { 'apache::mod::php': } # For /usr/bin/php-cgi + class { 'apache::mod::fcgid': + options => { + 'FcgidIPCDir' => '/var/run/fcgidsock', + }, + } + apache::vhost { 'fcgid.example.com': + port => '80', + docroot => '/var/www/fcgid', + directories => { + path => '/var/www/fcgid', + options => '+ExecCGI', + addhandlers => { + handler => 'fcgid-script', + extensions => '.php', + }, + fcgiwrapper => { + command => '/usr/bin/php-cgi', + suffix => '.php', + } + }, + } + file { '/var/www/fcgid/index.php': + ensure => file, + owner => 'root', + group => 'root', + content => "\\n", + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service('httpd') do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + + it 'should answer to fcgid.example.com' do + shell("/usr/bin/curl -H 'Host: fcgid.example.com' 127.0.0.1:80") do |r| + expect(r.stdout).to match(/^Hello world$/) + expect(r.exit_code).to eq(0) + end + end + + it 'should run a php-cgi process' do + shell("pgrep -u apache php-cgi", :acceptable_exit_codes => [0]) + end + end +end diff --git a/apache/spec/acceptance/mod_mime_spec.rb b/apache/spec/acceptance/mod_mime_spec.rb new file mode 100644 index 000000000..ff93dbca6 --- /dev/null +++ b/apache/spec/acceptance/mod_mime_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper_acceptance' + +describe 'apache::mod::mime class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do + case fact('osfamily') + when 'Debian' + mod_dir = '/etc/apache2/mods-available' + service_name = 'apache2' + when 'RedHat' + mod_dir = '/etc/httpd/conf.d' + service_name = 'httpd' + when 'FreeBSD' + mod_dir = '/usr/local/etc/apache22/Modules' + service_name = 'apache22' + end + + context "default mime config" do + it 'succeeds in puppeting mime' do + pp= <<-EOS + class { 'apache': } + include apache::mod::mime + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service(service_name) do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + + describe file("#{mod_dir}/mime.conf") do + it { is_expected.to contain "AddType application/x-compress .Z" } + end + end +end diff --git a/apache/spec/acceptance/mod_negotiation_spec.rb b/apache/spec/acceptance/mod_negotiation_spec.rb new file mode 100644 index 000000000..25e4acbea --- /dev/null +++ b/apache/spec/acceptance/mod_negotiation_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper_acceptance' + +describe 'apache::mod::negotiation class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do + case fact('osfamily') + when 'Debian' + vhost_dir = '/etc/apache2/sites-enabled' + mod_dir = '/etc/apache2/mods-available' + service_name = 'apache2' + when 'RedHat' + vhost_dir = '/etc/httpd/conf.d' + mod_dir = '/etc/httpd/conf.d' + service_name = 'httpd' + when 'FreeBSD' + vhost_dir = '/usr/local/etc/apache22/Vhosts' + mod_dir = '/usr/local/etc/apache22/Modules' + service_name = 'apache22' + end + + context "default negotiation config" do + it 'succeeds in puppeting negotiation' do + pp= <<-EOS + class { '::apache': default_mods => false } + class { '::apache::mod::negotiation': } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{mod_dir}/negotiation.conf") do + it { should contain "LanguagePriority en ca cs da de el eo es et fr he hr it ja ko ltz nl nn no pl pt pt-BR ru sv zh-CN zh-TW +ForceLanguagePriority Prefer Fallback" } + end + + describe service(service_name) do + it { should be_enabled } + it { should be_running } + end + end + + context "with alternative force_language_priority" do + it 'succeeds in puppeting negotiation' do + pp= <<-EOS + class { '::apache': default_mods => false } + class { '::apache::mod::negotiation': + force_language_priority => 'Prefer', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{mod_dir}/negotiation.conf") do + it { should contain "ForceLanguagePriority Prefer" } + end + + describe service(service_name) do + it { should be_enabled } + it { should be_running } + end + end + + context "with alternative language_priority" do + it 'succeeds in puppeting negotiation' do + pp= <<-EOS + class { '::apache': default_mods => false } + class { '::apache::mod::negotiation': + language_priority => [ 'en', 'es' ], + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{mod_dir}/negotiation.conf") do + it { should contain "LanguagePriority en es" } + end + + describe service(service_name) do + it { should be_enabled } + it { should be_running } + end + end +end diff --git a/apache/spec/acceptance/mod_pagespeed_spec.rb b/apache/spec/acceptance/mod_pagespeed_spec.rb new file mode 100644 index 000000000..0bc07389d --- /dev/null +++ b/apache/spec/acceptance/mod_pagespeed_spec.rb @@ -0,0 +1,85 @@ +require 'spec_helper_acceptance' + +describe 'apache::mod::pagespeed class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do + case fact('osfamily') + when 'Debian' + vhost_dir = '/etc/apache2/sites-enabled' + mod_dir = '/etc/apache2/mods-available' + service_name = 'apache2' + when 'RedHat' + vhost_dir = '/etc/httpd/conf.d' + mod_dir = '/etc/httpd/conf.d' + service_name = 'httpd' + when 'FreeBSD' + vhost_dir = '/usr/local/etc/apache22/Vhosts' + mod_dir = '/usr/local/etc/apache22/Modules' + service_name = 'apache22' + end + + context "default pagespeed config" do + it 'succeeds in puppeting pagespeed' do + pp= <<-EOS + if $::osfamily == 'Debian' { + class { 'apt': } + + apt::source { 'mod-pagespeed': + key => '7FAC5991', + key_server => 'pgp.mit.edu', + location => 'http://dl.google.com/linux/mod-pagespeed/deb/', + release => 'stable', + repos => 'main', + include_src => false, + before => Class['apache'], + } + } elsif $::osfamily == 'RedHat' { + yumrepo { 'mod-pagespeed': + baseurl => "http://dl.google.com/linux/mod-pagespeed/rpm/stable/$::architecture", + enabled => 1, + gpgcheck => 1, + gpgkey => 'https://dl-ssl.google.com/linux/linux_signing_key.pub', + before => Class['apache'], + } + } + + class { 'apache': + mpm_module => 'prefork', + } + class { 'apache::mod::pagespeed': + enable_filters => ['remove_comments'], + disable_filters => ['extend_cache'], + forbid_filters => ['rewrite_javascript'], + } + apache::vhost { 'pagespeed.example.com': + port => '80', + docroot => '/var/www/pagespeed', + } + host { 'pagespeed.example.com': ip => '127.0.0.1', } + file { '/var/www/pagespeed/index.html': + ensure => file, + content => "\n\n\n

Hello World!

\n\n", + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service(service_name) do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + + describe file("#{mod_dir}/pagespeed.conf") do + it { is_expected.to contain "AddOutputFilterByType MOD_PAGESPEED_OUTPUT_FILTER text/html" } + it { is_expected.to contain "ModPagespeedEnableFilters remove_comments" } + it { is_expected.to contain "ModPagespeedDisableFilters extend_cache" } + it { is_expected.to contain "ModPagespeedForbidFilters rewrite_javascript" } + end + + it 'should answer to pagespeed.example.com and include and be stripped of comments by mod_pagespeed' do + shell("/usr/bin/curl pagespeed.example.com:80") do |r| + expect(r.stdout).to match(//) + expect(r.stdout).not_to match(//) + expect(r.exit_code).to eq(0) + end + end + end +end diff --git a/apache/spec/acceptance/mod_passenger_spec.rb b/apache/spec/acceptance/mod_passenger_spec.rb new file mode 100644 index 000000000..9a758a44e --- /dev/null +++ b/apache/spec/acceptance/mod_passenger_spec.rb @@ -0,0 +1,300 @@ +require 'spec_helper_acceptance' + +describe 'apache::mod::passenger class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do + case fact('osfamily') + when 'Debian' + service_name = 'apache2' + mod_dir = '/etc/apache2/mods-available/' + conf_file = "#{mod_dir}passenger.conf" + load_file = "#{mod_dir}passenger.load" + + case fact('operatingsystem') + when 'Ubuntu' + case fact('lsbdistrelease') + when '10.04' + passenger_root = '/usr' + passenger_ruby = '/usr/bin/ruby' + when '12.04' + passenger_root = '/usr' + passenger_ruby = '/usr/bin/ruby' + when '14.04' + passenger_root = '/usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini' + passenger_ruby = '/usr/bin/ruby' + passenger_default_ruby = '/usr/bin/ruby' + else + # This may or may not work on Ubuntu releases other than the above + passenger_root = '/usr' + passenger_ruby = '/usr/bin/ruby' + end + when 'Debian' + case fact('lsbdistcodename') + when 'wheezy' + passenger_root = '/usr' + passenger_ruby = '/usr/bin/ruby' + else + # This may or may not work on Debian releases other than the above + passenger_root = '/usr' + passenger_ruby = '/usr/bin/ruby' + end + end + + passenger_module_path = '/usr/lib/apache2/modules/mod_passenger.so' + rackapp_user = 'www-data' + rackapp_group = 'www-data' + when 'RedHat' + service_name = 'httpd' + mod_dir = '/etc/httpd/conf.d/' + conf_file = "#{mod_dir}passenger.conf" + load_file = "#{mod_dir}passenger.load" + # sometimes installs as 3.0.12, sometimes as 3.0.19 - so just check for the stable part + passenger_root = '/usr/lib/ruby/gems/1.8/gems/passenger-3.0.1' + passenger_ruby = '/usr/bin/ruby' + passenger_tempdir = '/var/run/rubygem-passenger' + passenger_module_path = 'modules/mod_passenger.so' + rackapp_user = 'apache' + rackapp_group = 'apache' + end + + pp_rackapp = <<-EOS + /* a simple ruby rack 'hellow world' app */ + file { '/var/www/passenger': + ensure => directory, + owner => '#{rackapp_user}', + group => '#{rackapp_group}', + require => Class['apache::mod::passenger'], + } + file { '/var/www/passenger/config.ru': + ensure => file, + owner => '#{rackapp_user}', + group => '#{rackapp_group}', + content => "app = proc { |env| [200, { \\"Content-Type\\" => \\"text/html\\" }, [\\"hello world\\"]] }\\nrun app", + require => File['/var/www/passenger'] , + } + apache::vhost { 'passenger.example.com': + port => '80', + docroot => '/var/www/passenger/public', + docroot_group => '#{rackapp_group}' , + docroot_owner => '#{rackapp_user}' , + custom_fragment => "PassengerRuby #{passenger_ruby}\\nRailsEnv development" , + require => File['/var/www/passenger/config.ru'] , + } + host { 'passenger.example.com': ip => '127.0.0.1', } + EOS + + case fact('osfamily') + when 'Debian' + context "default passenger config" do + it 'succeeds in puppeting passenger' do + pp = <<-EOS + /* stock apache and mod_passenger */ + class { 'apache': } + class { 'apache::mod::passenger': } + #{pp_rackapp} + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service(service_name) do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + + describe file(conf_file) do + it { is_expected.to contain "PassengerRoot \"#{passenger_root}\"" } + + case fact('operatingsystem') + when 'Ubuntu' + case fact('lsbdistrelease') + when '10.04' + it { is_expected.to contain "PassengerRuby \"#{passenger_ruby}\"" } + it { is_expected.not_to contain "/PassengerDefaultRuby/" } + when '12.04' + it { is_expected.to contain "PassengerRuby \"#{passenger_ruby}\"" } + it { is_expected.not_to contain "/PassengerDefaultRuby/" } + when '14.04' + it { is_expected.to contain "PassengerDefaultRuby \"#{passenger_ruby}\"" } + it { is_expected.not_to contain "/PassengerRuby/" } + else + # This may or may not work on Ubuntu releases other than the above + it { is_expected.to contain "PassengerRuby \"#{passenger_ruby}\"" } + it { is_expected.not_to contain "/PassengerDefaultRuby/" } + end + when 'Debian' + case fact('lsbdistcodename') + when 'wheezy' + it { is_expected.to contain "PassengerRuby \"#{passenger_ruby}\"" } + it { is_expected.not_to contain "/PassengerDefaultRuby/" } + else + # This may or may not work on Debian releases other than the above + it { is_expected.to contain "PassengerRuby \"#{passenger_ruby}\"" } + it { is_expected.not_to contain "/PassengerDefaultRuby/" } + end + end + end + + describe file(load_file) do + it { is_expected.to contain "LoadModule passenger_module #{passenger_module_path}" } + end + + it 'should output status via passenger-memory-stats' do + shell("/usr/sbin/passenger-memory-stats") do |r| + expect(r.stdout).to match(/Apache processes/) + expect(r.stdout).to match(/Nginx processes/) + expect(r.stdout).to match(/Passenger processes/) + + # passenger-memory-stats output on Ubuntu 14.04 does not contain + # these two lines + unless fact('operatingsystem') == 'Ubuntu' && fact('operatingsystemrelease') == '14.04' + expect(r.stdout).to match(/### Processes: [0-9]+/) + expect(r.stdout).to match(/### Total private dirty RSS: [0-9\.]+ MB/) + end + + expect(r.exit_code).to eq(0) + end + end + + # passenger-status fails under stock ubuntu-server-12042-x64 + mod_passenger, + # even when the passenger process is successfully installed and running + unless fact('operatingsystem') == 'Ubuntu' && fact('operatingsystemrelease') == '12.04' + it 'should output status via passenger-status' do + # xml output not available on ubunutu <= 10.04, so sticking with default pool output + shell("/usr/sbin/passenger-status") do |r| + # spacing may vary + expect(r.stdout).to match(/[\-]+ General information [\-]+/) + if fact('operatingsystem') == 'Ubuntu' && fact('operatingsystemrelease') == '14.04' + expect(r.stdout).to match(/Max pool size[ ]+: [0-9]+/) + expect(r.stdout).to match(/Processes[ ]+: [0-9]+/) + expect(r.stdout).to match(/Requests in top-level queue[ ]+: [0-9]+/) + else + expect(r.stdout).to match(/max[ ]+= [0-9]+/) + expect(r.stdout).to match(/count[ ]+= [0-9]+/) + expect(r.stdout).to match(/active[ ]+= [0-9]+/) + expect(r.stdout).to match(/inactive[ ]+= [0-9]+/) + expect(r.stdout).to match(/Waiting on global queue: [0-9]+/) + end + + expect(r.exit_code).to eq(0) + end + end + end + + it 'should answer to passenger.example.com' do + shell("/usr/bin/curl passenger.example.com:80") do |r| + expect(r.stdout).to match(/^hello world<\/b>$/) + expect(r.exit_code).to eq(0) + end + end + + end + + when 'RedHat' + # no fedora 18 passenger package yet, and rhel5 packages only exist for ruby 1.8.5 + unless (fact('operatingsystem') == 'Fedora' and fact('operatingsystemrelease').to_f >= 18) or (fact('osfamily') == 'RedHat' and fact('operatingsystemmajrelease') == '5' and fact('rubyversion') != '1.8.5') + + if fact('osfamily') == 'RedHat' and fact('operatingsystemmajrelease') == '7' + pending('test passenger - RHEL7 packages don\'t exist') + else + context "default passenger config" do + it 'succeeds in puppeting passenger' do + pp = <<-EOS + /* EPEL and passenger repositories */ + class { 'epel': } + exec { 'passenger.repo GPG key': + command => '/usr/bin/curl -o /etc/yum.repos.d/RPM-GPG-KEY-stealthymonkeys.asc http://passenger.stealthymonkeys.com/RPM-GPG-KEY-stealthymonkeys.asc', + creates => '/etc/yum.repos.d/RPM-GPG-KEY-stealthymonkeys.asc', + } + file { 'passenger.repo GPG key': + ensure => file, + path => '/etc/yum.repos.d/RPM-GPG-KEY-stealthymonkeys.asc', + require => Exec['passenger.repo GPG key'], + } + epel::rpm_gpg_key { 'passenger.stealthymonkeys.com': + path => '/etc/yum.repos.d/RPM-GPG-KEY-stealthymonkeys.asc', + require => [ + Class['epel'], + File['passenger.repo GPG key'], + ] + } + $releasever_string = $operatingsystem ? { + 'Scientific' => '6', + default => '$releasever', + } + yumrepo { 'passenger': + baseurl => "http://passenger.stealthymonkeys.com/rhel/${releasever_string}/\\$basearch" , + descr => "Red Hat Enterprise ${releasever_string} - Phusion Passenger", + enabled => 1, + gpgcheck => 1, + gpgkey => 'http://passenger.stealthymonkeys.com/RPM-GPG-KEY-stealthymonkeys.asc', + mirrorlist => 'http://passenger.stealthymonkeys.com/rhel/mirrors', + require => [ + Epel::Rpm_gpg_key['passenger.stealthymonkeys.com'], + ], + } + /* apache and mod_passenger */ + class { 'apache': + require => [ + Class['epel'], + ], + } + class { 'apache::mod::passenger': + require => [ + Yumrepo['passenger'] + ], + } + #{pp_rackapp} + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service(service_name) do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + + describe file(conf_file) do + it { is_expected.to contain "PassengerRoot #{passenger_root}" } + it { is_expected.to contain "PassengerRuby #{passenger_ruby}" } + it { is_expected.to contain "PassengerTempDir #{passenger_tempdir}" } + end + + describe file(load_file) do + it { is_expected.to contain "LoadModule passenger_module #{passenger_module_path}" } + end + + it 'should output status via passenger-memory-stats' do + shell("/usr/bin/passenger-memory-stats", :pty => true) do |r| + expect(r.stdout).to match(/Apache processes/) + expect(r.stdout).to match(/Nginx processes/) + expect(r.stdout).to match(/Passenger processes/) + expect(r.stdout).to match(/### Processes: [0-9]+/) + expect(r.stdout).to match(/### Total private dirty RSS: [0-9\.]+ MB/) + + expect(r.exit_code).to eq(0) + end + end + + it 'should output status via passenger-status' do + shell("PASSENGER_TMPDIR=/var/run/rubygem-passenger /usr/bin/passenger-status") do |r| + # spacing may vary + r.stdout.should =~ /[\-]+ General information [\-]+/ + r.stdout.should =~ /max[ ]+= [0-9]+/ + r.stdout.should =~ /count[ ]+= [0-9]+/ + r.stdout.should =~ /active[ ]+= [0-9]+/ + r.stdout.should =~ /inactive[ ]+= [0-9]+/ + r.stdout.should =~ /Waiting on global queue: [0-9]+/ + + r.exit_code.should == 0 + end + end + + it 'should answer to passenger.example.com' do + shell("/usr/bin/curl passenger.example.com:80") do |r| + r.stdout.should =~ /^hello world<\/b>$/ + r.exit_code.should == 0 + end + end + end + end + end + end +end diff --git a/apache/spec/acceptance/mod_php_spec.rb b/apache/spec/acceptance/mod_php_spec.rb index d1c991621..a0efe7fb8 100644 --- a/apache/spec/acceptance/mod_php_spec.rb +++ b/apache/spec/acceptance/mod_php_spec.rb @@ -37,29 +37,31 @@ class { 'apache::mod::php': } end describe service(service_name) do - it { should be_enabled } - it { should be_running } + it { is_expected.to be_enabled } + it { is_expected.to be_running } end describe file("#{mod_dir}/php5.conf") do - it { should contain "DirectoryIndex index.php" } + it { is_expected.to contain "DirectoryIndex index.php" } end it 'should answer to php.example.com' do shell("/usr/bin/curl php.example.com:80") do |r| - r.stdout.should =~ /PHP Version/ - r.exit_code.should == 0 + expect(r.stdout).to match(/PHP Version/) + expect(r.exit_code).to eq(0) end end end - context "custom php admin_flag and php_admin_value" do + context "custom extensions, php_admin_flag, and php_admin_value" do it 'succeeds in puppeting php' do pp= <<-EOS class { 'apache': mpm_module => 'prefork', } - class { 'apache::mod::php': } + class { 'apache::mod::php': + extensions => ['.php','.php5'], + } apache::vhost { 'php.example.com': port => '80', docroot => '/var/www/php', @@ -67,7 +69,7 @@ class { 'apache::mod::php': } php_admin_flags => { 'engine' => 'on', }, } host { 'php.example.com': ip => '127.0.0.1', } - file { '/var/www/php/index.php': + file { '/var/www/php/index.php5': ensure => file, content => "\\n", } @@ -76,20 +78,58 @@ class { 'apache::mod::php': } end describe service(service_name) do - it { should be_enabled } - it { should be_running } + it { is_expected.to be_enabled } + it { is_expected.to be_running } end describe file("#{vhost_dir}/25-php.example.com.conf") do - it { should contain " php_admin_flag engine on" } - it { should contain " php_admin_value open_basedir /var/www/php/:/usr/share/pear/" } + it { is_expected.to contain " php_admin_flag engine on" } + it { is_expected.to contain " php_admin_value open_basedir /var/www/php/:/usr/share/pear/" } end it 'should answer to php.example.com' do shell("/usr/bin/curl php.example.com:80") do |r| - r.stdout.should =~ /\/usr\/share\/pear\// - r.exit_code.should == 0 + expect(r.stdout).to match(/\/usr\/share\/pear\//) + expect(r.exit_code).to eq(0) end end end + + context "provide custom config file" do + it 'succeeds in puppeting php' do + pp= <<-EOS + class {'apache': + mpm_module => 'prefork', + } + class {'apache::mod::php': + content => '# somecontent', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{mod_dir}/php5.conf") do + it { should contain "# somecontent" } + end + end + + context "provide content and template config file" do + it 'succeeds in puppeting php' do + pp= <<-EOS + class {'apache': + mpm_module => 'prefork', + } + class {'apache::mod::php': + content => '# somecontent', + template => 'apache/mod/php5.conf.erb', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{mod_dir}/php5.conf") do + it { should contain "# somecontent" } + end + end + end diff --git a/apache/spec/acceptance/mod_proxy_html_spec.rb b/apache/spec/acceptance/mod_proxy_html_spec.rb new file mode 100644 index 000000000..eab162b1a --- /dev/null +++ b/apache/spec/acceptance/mod_proxy_html_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper_acceptance' + +describe 'apache::mod::proxy_html class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do + case fact('osfamily') + when 'Debian' + service_name = 'apache2' + when 'RedHat' + service_name = 'httpd' + when 'FreeBSD' + service_name = 'apache22' + end + + context "default proxy_html config" do + if fact('osfamily') == 'RedHat' and fact('operatingsystemmajrelease') =~ /(5|6)/ + it 'adds epel' do + pp = "class { 'epel': }" + apply_manifest(pp, :catch_failures => true) + end + end + + it 'succeeds in puppeting proxy_html' do + pp= <<-EOS + class { 'apache': } + class { 'apache::mod::proxy': } + class { 'apache::mod::proxy_http': } + # mod_proxy_html doesn't exist in RHEL5 + if $::osfamily == 'RedHat' and $::operatingsystemmajrelease != '5' { + class { 'apache::mod::proxy_html': } + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service(service_name) do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + end +end diff --git a/apache/spec/acceptance/mod_suphp_spec.rb b/apache/spec/acceptance/mod_suphp_spec.rb index 9e26731d6..1b9158144 100644 --- a/apache/spec/acceptance/mod_suphp_spec.rb +++ b/apache/spec/acceptance/mod_suphp_spec.rb @@ -27,14 +27,14 @@ class { 'apache::mod::suphp': } end describe service('apache2') do - it { should be_enabled } - it { should be_running } + it { is_expected.to be_enabled } + it { is_expected.to be_running } end it 'should answer to suphp.example.com' do shell("/usr/bin/curl suphp.example.com:80") do |r| - r.stdout.should =~ /^daemon$/ - r.exit_code.should == 0 + expect(r.stdout).to match(/^daemon$/) + expect(r.exit_code).to eq(0) end end end diff --git a/apache/spec/acceptance/nodesets/centos-59-x64.yml b/apache/spec/acceptance/nodesets/centos-59-x64.yml index cde1fe5a8..2ad90b86a 100644 --- a/apache/spec/acceptance/nodesets/centos-59-x64.yml +++ b/apache/spec/acceptance/nodesets/centos-59-x64.yml @@ -7,5 +7,4 @@ HOSTS: box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-59-x64-vbox4210-nocm.box hypervisor : vagrant CONFIG: - log_level: debug type: git diff --git a/apache/spec/acceptance/nodesets/centos-64-x64-pe.yml b/apache/spec/acceptance/nodesets/centos-64-x64-pe.yml index e408d1be7..7d9242f1b 100644 --- a/apache/spec/acceptance/nodesets/centos-64-x64-pe.yml +++ b/apache/spec/acceptance/nodesets/centos-64-x64-pe.yml @@ -9,5 +9,4 @@ HOSTS: box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box hypervisor : vagrant CONFIG: - log_level: debug type: pe diff --git a/mysql/spec/acceptance/nodesets/centos-65-x64.yml b/apache/spec/acceptance/nodesets/centos-65-x64.yml similarity index 100% rename from mysql/spec/acceptance/nodesets/centos-65-x64.yml rename to apache/spec/acceptance/nodesets/centos-65-x64.yml diff --git a/apache/spec/acceptance/nodesets/debian-73-i386.yml b/apache/spec/acceptance/nodesets/debian-73-i386.yml new file mode 100644 index 000000000..a38902d89 --- /dev/null +++ b/apache/spec/acceptance/nodesets/debian-73-i386.yml @@ -0,0 +1,11 @@ +HOSTS: + debian-73-i386: + roles: + - master + platform: debian-7-i386 + box : debian-73-i386-virtualbox-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-73-i386-virtualbox-nocm.box + hypervisor : vagrant +CONFIG: + log_level: debug + type: git diff --git a/apache/spec/acceptance/nodesets/debian-73-x64.yml b/apache/spec/acceptance/nodesets/debian-73-x64.yml new file mode 100644 index 000000000..f9cf0c9b8 --- /dev/null +++ b/apache/spec/acceptance/nodesets/debian-73-x64.yml @@ -0,0 +1,11 @@ +HOSTS: + debian-73-x64: + roles: + - master + platform: debian-7-amd64 + box : debian-73-x64-virtualbox-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-73-x64-virtualbox-nocm.box + hypervisor : vagrant +CONFIG: + log_level: debug + type: git diff --git a/apache/spec/acceptance/nodesets/default.yml b/apache/spec/acceptance/nodesets/default.yml deleted file mode 120000 index 2719644a6..000000000 --- a/apache/spec/acceptance/nodesets/default.yml +++ /dev/null @@ -1 +0,0 @@ -centos-64-x64.yml \ No newline at end of file diff --git a/apache/spec/acceptance/nodesets/default.yml b/apache/spec/acceptance/nodesets/default.yml new file mode 100644 index 000000000..ce47212a8 --- /dev/null +++ b/apache/spec/acceptance/nodesets/default.yml @@ -0,0 +1,11 @@ +HOSTS: + centos-64-x64: + roles: + - master + platform: el-6-x86_64 + box : centos-64-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + log_level: debug + type: git diff --git a/apache/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml b/apache/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml index c1b8bdf8f..5ca1514e4 100644 --- a/apache/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml +++ b/apache/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml @@ -7,5 +7,4 @@ HOSTS: box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-10044-x64-vbox4210-nocm.box hypervisor : vagrant CONFIG: - log_level: debug - type: git + type: foss diff --git a/apache/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml b/apache/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml index f7df2ccce..d065b304f 100644 --- a/apache/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml +++ b/apache/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml @@ -7,5 +7,4 @@ HOSTS: box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-12042-x64-vbox4210-nocm.box hypervisor : vagrant CONFIG: - log_level: debug - type: git + type: foss diff --git a/mysql/spec/acceptance/nodesets/ubuntu-server-1404-x64.yml b/apache/spec/acceptance/nodesets/ubuntu-server-1404-x64.yml similarity index 100% rename from mysql/spec/acceptance/nodesets/ubuntu-server-1404-x64.yml rename to apache/spec/acceptance/nodesets/ubuntu-server-1404-x64.yml diff --git a/apache/spec/acceptance/prefork_worker_spec.rb b/apache/spec/acceptance/prefork_worker_spec.rb index beffe0a01..562ff5323 100644 --- a/apache/spec/acceptance/prefork_worker_spec.rb +++ b/apache/spec/acceptance/prefork_worker_spec.rb @@ -28,8 +28,8 @@ class { 'apache': end describe service(servicename) do - it { should be_running } - it { should be_enabled } + it { is_expected.to be_running } + it { is_expected.to be_enabled } end end end @@ -51,8 +51,8 @@ class { 'apache': end describe service(servicename) do - it { should be_running } - it { should be_enabled } + it { is_expected.to be_running } + it { is_expected.to be_enabled } end end @@ -73,7 +73,7 @@ class { 'apache': end describe service(servicename) do - it { should be_running } - it { should be_enabled } + it { is_expected.to be_running } + it { is_expected.to be_enabled } end end diff --git a/apache/spec/acceptance/version.rb b/apache/spec/acceptance/version.rb index 169054ec2..b88412b42 100644 --- a/apache/spec/acceptance/version.rb +++ b/apache/spec/acceptance/version.rb @@ -5,6 +5,7 @@ case _osfamily when 'RedHat' $confd_dir = '/etc/httpd/conf.d' + $mod_dir = '/etc/httpd/conf.d' $conf_file = '/etc/httpd/conf/httpd.conf' $ports_file = '/etc/httpd/conf/ports.conf' $vhost_dir = '/etc/httpd/conf.d' @@ -17,12 +18,13 @@ $suphp_configpath = 'undef' if (_operatingsystem == 'Fedora' and _operatingsystemrelease >= 18) or (_operatingsystem != 'Fedora' and _operatingsystemrelease >= 7) - $apache_version = 2.4 + $apache_version = '2.4' else - $apache_version = 2.2 + $apache_version = '2.2' end when 'Debian' - $confd_dir = '/etc/apache2/mods-available' + $confd_dir = '/etc/apache2/conf.d' + $mod_dir = '/etc/apache2/mods-available' $conf_file = '/etc/apache2/apache2.conf' $ports_file = '/etc/apache2/ports.conf' $vhost = '/etc/apache2/sites-available/15-default.conf' @@ -35,12 +37,13 @@ $suphp_configpath = '/etc/php5/apache2' if _operatingsystem == 'Ubuntu' and _operatingsystemrelease >= 13.10 - $apache_version = 2.4 + $apache_version = '2.4' else - $apache_version = 2.2 + $apache_version = '2.2' end when 'FreeBSD' $confd_dir = '/usr/local/etc/apache22/Includes' + $mod_dir = '/usr/local/etc/apache22/Modules' $conf_file = '/usr/local/etc/apache22/httpd.conf' $ports_file = '/usr/local/etc/apache22/Includes/ports.conf' $vhost = '/usr/local/etc/apache22/Vhosts/15-default.conf' @@ -50,6 +53,8 @@ $package_name = 'apache22' $error_log = 'http-error.log' - $apache_version = 2.2 + $apache_version = '2.2' +else + $apache_version = '0' end diff --git a/apache/spec/acceptance/vhost_spec.rb b/apache/spec/acceptance/vhost_spec.rb index f6749d35a..483b74945 100644 --- a/apache/spec/acceptance/vhost_spec.rb +++ b/apache/spec/acceptance/vhost_spec.rb @@ -16,11 +16,11 @@ class { 'apache': end describe file("#{$vhost_dir}/15-default.conf") do - it { should_not be_file } + it { is_expected.not_to be_file } end describe file("#{$vhost_dir}/15-default-ssl.conf") do - it { should_not be_file } + it { is_expected.not_to be_file } end end @@ -34,11 +34,11 @@ class { 'apache': } end describe file("#{$vhost_dir}/15-default.conf") do - it { should contain '' } + it { is_expected.to contain '' } end describe file("#{$vhost_dir}/15-default-ssl.conf") do - it { should_not be_file } + it { is_expected.not_to be_file } end end @@ -59,12 +59,12 @@ class { 'apache': end describe file("#{$vhost_dir}/15-default.conf") do - it { should contain '' } + it { is_expected.to contain '' } end describe file("#{$vhost_dir}/15-default-ssl.conf") do - it { should contain '' } - it { should contain "SSLEngine on" } + it { is_expected.to contain '' } + it { is_expected.to contain "SSLEngine on" } end end @@ -87,8 +87,8 @@ class { 'apache': } end describe file("#{$vhost_dir}/25-first.example.com.conf") do - it { should contain '' } - it { should contain "ServerName first.example.com" } + it { is_expected.to contain '' } + it { is_expected.to contain "ServerName first.example.com" } end end @@ -102,16 +102,18 @@ class { 'apache': } proxy_pass => [ { 'path' => '/foo', 'url' => 'http://backend-foo/'}, ], + proxy_preserve_host => true, } EOS apply_manifest(pp, :catch_failures => true) end describe file("#{$vhost_dir}/25-proxy.example.com.conf") do - it { should contain '' } - it { should contain "ServerName proxy.example.com" } - it { should contain "ProxyPass" } - it { should_not contain "" } + it { is_expected.to contain '' } + it { is_expected.to contain "ServerName proxy.example.com" } + it { is_expected.to contain "ProxyPass" } + it { is_expected.to contain "ProxyPreserveHost On" } + it { is_expected.not_to contain "" } end end @@ -142,19 +144,19 @@ class { 'apache': } end describe service($service_name) do - it { should be_enabled } - it { should be_running } + it { is_expected.to be_enabled } + it { is_expected.to be_running } end it 'should answer to first.example.com' do shell("/usr/bin/curl first.example.com:80", {:acceptable_exit_codes => 0}) do |r| - r.stdout.should == "Hello from first\n" + expect(r.stdout).to eq("Hello from first\n") end end it 'should answer to second.example.com' do shell("/usr/bin/curl second.example.com:80", {:acceptable_exit_codes => 0}) do |r| - r.stdout.should == "Hello from second\n" + expect(r.stdout).to eq("Hello from second\n") end end end @@ -165,7 +167,7 @@ class { 'apache': } pp = <<-EOS class { 'apache': } - if $apache::apache_version >= 2.4 { + if versioncmp($apache::apache_version, '2.4') >= 0 { $_files_match_directory = { 'path' => '(\.swp|\.bak|~)$', 'provider' => 'filesmatch', 'require' => 'all denied', } } else { $_files_match_directory = { 'path' => '(\.swp|\.bak|~)$', 'provider' => 'filesmatch', 'deny' => 'from all', } @@ -194,13 +196,13 @@ class { 'apache': } end describe service($service_name) do - it { should be_enabled } - it { should be_running } + it { is_expected.to be_enabled } + it { is_expected.to be_running } end it 'should answer to files.example.net' do - shell("/usr/bin/curl -sSf files.example.net:80/index.html").stdout.should eq("Hello World\n") - shell("/usr/bin/curl -sSf files.example.net:80/index.html.bak", {:acceptable_exit_codes => 22}).stderr.should match(/curl: \(22\) The requested URL returned error: 403/) + expect(shell("/usr/bin/curl -sSf files.example.net:80/index.html").stdout).to eq("Hello World\n") + expect(shell("/usr/bin/curl -sSf files.example.net:80/index.html.bak", {:acceptable_exit_codes => 22}).stderr).to match(/curl: \(22\) The requested URL returned error: 403/) end end @@ -209,10 +211,13 @@ class { 'apache': } pp = <<-EOS class { 'apache': } - if $apache::apache_version >= 2.4 { + if versioncmp($apache::apache_version, '2.4') >= 0 { $_files_match_directory = { 'path' => 'private.html$', 'provider' => 'filesmatch', 'require' => 'all denied' } } else { - $_files_match_directory = { 'path' => 'private.html$', 'provider' => 'filesmatch', 'deny' => 'from all' } + $_files_match_directory = [ + { 'path' => 'private.html$', 'provider' => 'filesmatch', 'deny' => 'from all' }, + { 'path' => '/bar/bar.html', 'provider' => 'location', allow => [ 'from 127.0.0.1', ] }, + ] } $_directories = [ @@ -236,7 +241,122 @@ class { 'apache': } ensure => file, content => "Hello World\\n", } + file { '/var/www/files/bar': + ensure => directory, + } + file { '/var/www/files/bar/bar.html': + ensure => file, + content => "Hello Bar\\n", + } + host { 'files.example.net': ip => '127.0.0.1', } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service($service_name) do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + + it 'should answer to files.example.net' do + expect(shell("/usr/bin/curl -sSf files.example.net:80/").stdout).to eq("Hello World\n") + expect(shell("/usr/bin/curl -sSf files.example.net:80/foo/").stdout).to eq("Hello Foo\n") + expect(shell("/usr/bin/curl -sSf files.example.net:80/private.html", {:acceptable_exit_codes => 22}).stderr).to match(/curl: \(22\) The requested URL returned error: 403/) + expect(shell("/usr/bin/curl -sSf files.example.net:80/bar/bar.html").stdout).to eq("Hello Bar\n") + end + end + + describe 'SetHandler directive' do + it 'should configure a vhost with a SetHandler directive' do + pp = <<-EOS + class { 'apache': } + apache::mod { 'status': } + host { 'files.example.net': ip => '127.0.0.1', } + apache::vhost { 'files.example.net': + docroot => '/var/www/files', + directories => [ + { path => '/var/www/files', }, + { path => '/server-status', provider => 'location', sethandler => 'server-status', }, + ], + } + file { '/var/www/files/index.html': + ensure => file, + content => "Hello World\\n", + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service($service_name) do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + + it 'should answer to files.example.net' do + expect(shell("/usr/bin/curl -sSf files.example.net:80/index.html").stdout).to eq("Hello World\n") + expect(shell("/usr/bin/curl -sSf files.example.net:80/server-status?auto").stdout).to match(/Scoreboard: /) + end + end + + describe 'Satisfy and Auth directive', :unless => $apache_version == '2.4' do + it 'should configure a vhost with Satisfy and Auth directive' do + pp = <<-EOS + class { 'apache': } host { 'files.example.net': ip => '127.0.0.1', } + apache::vhost { 'files.example.net': + docroot => '/var/www/files', + directories => [ + { + path => '/var/www/files/foo', + auth_type => 'Basic', + auth_name => 'Basic Auth', + auth_user_file => '/var/www/htpasswd', + auth_require => "valid-user", + }, + { + path => '/var/www/files/bar', + auth_type => 'Basic', + auth_name => 'Basic Auth', + auth_user_file => '/var/www/htpasswd', + auth_require => 'valid-user', + satisfy => 'Any', + }, + { + path => '/var/www/files/baz', + allow => 'from 10.10.10.10', + auth_type => 'Basic', + auth_name => 'Basic Auth', + auth_user_file => '/var/www/htpasswd', + auth_require => 'valid-user', + satisfy => 'Any', + }, + ], + } + file { '/var/www/files/foo': + ensure => directory, + } + file { '/var/www/files/bar': + ensure => directory, + } + file { '/var/www/files/baz': + ensure => directory, + } + file { '/var/www/files/foo/index.html': + ensure => file, + content => "Hello World\\n", + } + file { '/var/www/files/bar/index.html': + ensure => file, + content => "Hello World\\n", + } + file { '/var/www/files/baz/index.html': + ensure => file, + content => "Hello World\\n", + } + file { '/var/www/htpasswd': + ensure => file, + content => "login:IZ7jMcLSx0oQk", # "password" as password + } EOS apply_manifest(pp, :catch_failures => true) end @@ -247,9 +367,12 @@ class { 'apache': } end it 'should answer to files.example.net' do - shell("/usr/bin/curl -sSf files.example.net:80/").stdout.should eq("Hello World\n") - shell("/usr/bin/curl -sSf files.example.net:80/foo/").stdout.should eq("Hello Foo\n") - shell("/usr/bin/curl -sSf files.example.net:80/private.html", {:acceptable_exit_codes => 22}).stderr.should match(/curl: \(22\) The requested URL returned error: 403/) + shell("/usr/bin/curl -sSf files.example.net:80/foo/index.html", {:acceptable_exit_codes => 22}).stderr.should match(/curl: \(22\) The requested URL returned error: 401/) + shell("/usr/bin/curl -sSf -u login:password files.example.net:80/foo/index.html").stdout.should eq("Hello World\n") + shell("/usr/bin/curl -sSf files.example.net:80/bar/index.html").stdout.should eq("Hello World\n") + shell("/usr/bin/curl -sSf -u login:password files.example.net:80/bar/index.html").stdout.should eq("Hello World\n") + shell("/usr/bin/curl -sSf files.example.net:80/baz/index.html", {:acceptable_exit_codes => 22}).stderr.should match(/curl: \(22\) The requested URL returned error: 401/) + shell("/usr/bin/curl -sSf -u login:password files.example.net:80/baz/index.html").stdout.should eq("Hello World\n") end end end @@ -274,13 +397,13 @@ class { 'apache': } end describe service($service_name) do - it { should be_enabled } - it { should be_running } + it { is_expected.to be_enabled } + it { is_expected.to be_running } end it 'should answer to fallback.example.net' do shell("/usr/bin/curl fallback.example.net:80/Does/Not/Exist") do |r| - r.stdout.should == "Hello World\n" + expect(r.stdout).to eq("Hello World\n") end end @@ -315,19 +438,19 @@ class { 'apache': } end describe service($service_name) do - it { should be_enabled } - it { should be_running } + it { is_expected.to be_enabled } + it { is_expected.to be_running } end it 'should answer to a.virt.example.com' do shell("/usr/bin/curl a.virt.example.com:80", {:acceptable_exit_codes => 0}) do |r| - r.stdout.should == "Hello from a.virt\n" + expect(r.stdout).to eq("Hello from a.virt\n") end end it 'should answer to b.virt.example.com' do shell("/usr/bin/curl b.virt.example.com:80", {:acceptable_exit_codes => 0}) do |r| - r.stdout.should == "Hello from b.virt\n" + expect(r.stdout).to eq("Hello from b.virt\n") end end end @@ -357,18 +480,18 @@ class { 'apache': default_vhost => false, } ensure => file, content => "Hello from localhost\\n", } - }, :catch_failures => true) + }, :catch_failures => true) end describe service($service_name) do - it { should be_enabled } - it { should be_running } + it { is_expected.to be_enabled } + it { is_expected.to be_running } end it 'should get a response from the back end' do shell("/usr/bin/curl --max-redirs 0 proxy.example.com:80") do |r| - r.stdout.should == "Hello from localhost\n" - r.exit_code.should == 0 + expect(r.stdout).to eq("Hello from localhost\n") + expect(r.exit_code).to eq(0) end end end @@ -388,8 +511,8 @@ class { 'apache': } end describe file($ports_file) do - it { should be_file } - it { should_not contain 'NameVirtualHost test.server' } + it { is_expected.to be_file } + it { is_expected.not_to contain 'NameVirtualHost test.server' } end end @@ -410,9 +533,9 @@ class { 'apache': default_vhost => false } end describe file($ports_file) do - it { should be_file } - it { should_not contain 'Listen 80' } - it { should contain 'Listen 81' } + it { is_expected.to be_file } + it { is_expected.not_to contain 'Listen 80' } + it { is_expected.to contain 'Listen 81' } end end @@ -427,15 +550,17 @@ class { 'apache': } docroot => '/tmp/test', docroot_owner => 'test_owner', docroot_group => 'test_group', + docroot_mode => '0750', } EOS apply_manifest(pp, :catch_failures => true) end describe file('/tmp/test') do - it { should be_directory } - it { should be_owned_by 'test_owner' } - it { should be_grouped_into 'test_group' } + it { is_expected.to be_directory } + it { is_expected.to be_owned_by 'test_owner' } + it { is_expected.to be_grouped_into 'test_group' } + it { is_expected.to be_mode 750 } end end @@ -452,8 +577,19 @@ class { 'apache': } apply_manifest(pp, :catch_failures => true) end + describe file($ports_file) do + it { is_expected.to be_file } + if fact('osfamily') == 'RedHat' and fact('operatingsystemmajrelease') == '7' + it { is_expected.not_to contain 'NameVirtualHost test.server' } + elsif fact('operatingsystem') == 'Ubuntu' and fact('operatingsystemrelease') =~ /(14\.04|13\.10)/ + it { is_expected.not_to contain 'NameVirtualHost test.server' } + else + it { is_expected.to contain 'NameVirtualHost test.server' } + end + end + describe file("#{$vhost_dir}/10-test.server.conf") do - it { should be_file } + it { is_expected.to be_file } end end @@ -471,8 +607,8 @@ class { 'apache': } end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain 'Options Indexes FollowSymLinks ExecCGI' } + it { is_expected.to be_file } + it { is_expected.to contain 'Options Indexes FollowSymLinks ExecCGI' } end end @@ -490,8 +626,8 @@ class { 'apache': } end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain 'AllowOverride All' } + it { is_expected.to be_file } + it { is_expected.to contain 'AllowOverride All' } end end @@ -509,8 +645,8 @@ class { 'apache': } end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain ' CustomLog "/tmp' } + it { is_expected.to be_file } + it { is_expected.to contain ' CustomLog "/tmp' } end end @@ -530,15 +666,15 @@ class { 'apache': } apache::vhost { 'test.server': docroot => '/tmp', logroot => '/tmp', - #{logtype}_log => false, + #{logtype}_log => false, } EOS apply_manifest(pp, :catch_failures => true) end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should_not contain " #{logname} \"/tmp" } + it { is_expected.to be_file } + it { is_expected.not_to contain " #{logname} \"/tmp" } end end @@ -550,15 +686,15 @@ class { 'apache': } apache::vhost { 'test.server': docroot => '/tmp', logroot => '/tmp', - #{logtype}_log_pipe => '|/bin/sh', + #{logtype}_log_pipe => '|/bin/sh', } EOS apply_manifest(pp, :catch_failures => true) end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain " #{logname} \"|/bin/sh" } + it { is_expected.to be_file } + it { is_expected.to contain " #{logname} \"|/bin/sh" } end end @@ -570,15 +706,15 @@ class { 'apache': } apache::vhost { 'test.server': docroot => '/tmp', logroot => '/tmp', - #{logtype}_log_syslog => 'syslog', + #{logtype}_log_syslog => 'syslog', } EOS apply_manifest(pp, :catch_failures => true) end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain " #{logname} \"syslog\"" } + it { is_expected.to be_file } + it { is_expected.to contain " #{logname} \"syslog\"" } end end end @@ -599,8 +735,8 @@ class { 'apache': } end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain 'CustomLog "syslog" "%h %l"' } + it { is_expected.to be_file } + it { is_expected.to contain 'CustomLog "syslog" "%h %l"' } end end @@ -620,8 +756,8 @@ class { 'apache': } end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain 'CustomLog "syslog" combined env=admin' } + it { is_expected.to be_file } + it { is_expected.to contain 'CustomLog "syslog" combined env=admin' } end end @@ -632,15 +768,19 @@ class { 'apache': } host { 'test.server': ip => '127.0.0.1' } apache::vhost { 'test.server': docroot => '/tmp', - aliases => [{ alias => '/image', path => '/ftp/pub/image' }], + aliases => [ + { alias => '/image' , path => '/ftp/pub/image' } , + { scriptalias => '/myscript' , path => '/usr/share/myscript' } + ], } EOS apply_manifest(pp, :catch_failures => true) end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain 'Alias /image "/ftp/pub/image"' } + it { is_expected.to be_file } + it { is_expected.to contain 'Alias /image "/ftp/pub/image"' } + it { is_expected.to contain 'ScriptAlias /myscript "/usr/share/myscript"' } end end @@ -658,8 +798,8 @@ class { 'apache': } end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain 'ScriptAlias /myscript "/usr/share/myscript"' } + it { is_expected.to be_file } + it { is_expected.to contain 'ScriptAlias /myscript "/usr/share/myscript"' } end end @@ -677,8 +817,28 @@ class { 'apache': service_ensure => stopped, } end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain 'ProxyPass / test2/' } + it { is_expected.to be_file } + it { is_expected.to contain 'ProxyPass / test2/' } + end + end + + describe 'actions' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + action => 'php-fastcgi', + } + EOS + pp = pp + "\nclass { 'apache::mod::actions': }" if fact('osfamily') == 'Debian' + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{$vhost_dir}/25-test.server.conf") do + it { is_expected.to be_file } + it { is_expected.to contain 'Action php-fastcgi /cgi-bin virtual' } end end @@ -698,10 +858,10 @@ class { 'apache': service_ensure => stopped, } end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain "suPHP_AddHandler #{$suphp_handler}" } - it { should contain 'suPHP_Engine on' } - it { should contain "suPHP_ConfigPath \"#{$suphp_configpath}\"" } + it { is_expected.to be_file } + it { is_expected.to contain "suPHP_AddHandler #{$suphp_handler}" } + it { is_expected.to contain 'suPHP_Engine on' } + it { is_expected.to contain "suPHP_ConfigPath \"#{$suphp_configpath}\"" } end end @@ -720,9 +880,9 @@ class { 'apache': service_ensure => stopped, } end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain 'ProxyPass / http://test2/' } - it { should contain 'ProxyPass http://test2/test !' } + it { is_expected.to be_file } + it { is_expected.to contain 'ProxyPass / http://test2/' } + it { is_expected.to contain 'ProxyPass http://test2/test !' } end end @@ -742,36 +902,40 @@ class { 'apache': } end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain 'Redirect permanent /images http://test.server/' } + it { is_expected.to be_file } + it { is_expected.to contain 'Redirect permanent /images http://test.server/' } end end # Passenger isn't even in EPEL on el-5 if default['platform'] !~ /^el-5/ - describe 'rack_base_uris' do - if fact('osfamily') == 'RedHat' - it 'adds epel' do - pp = "class { 'epel': }" - apply_manifest(pp, :catch_failures => true) + if fact('osfamily') == 'RedHat' and fact('operatingsystemmajrelease') == '7' + pending('Since we don\'t have passenger on RHEL7 rack_base_uris tests will fail') + else + describe 'rack_base_uris' do + if fact('osfamily') == 'RedHat' + it 'adds epel' do + pp = "class { 'epel': }" + apply_manifest(pp, :catch_failures => true) + end end - end - it 'applies cleanly' do - pp = <<-EOS - class { 'apache': } - host { 'test.server': ip => '127.0.0.1' } - apache::vhost { 'test.server': - docroot => '/tmp', - rack_base_uris => ['/test'], - } - EOS - apply_manifest(pp, :catch_failures => true) - end + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + rack_base_uris => ['/test'], + } + EOS + apply_manifest(pp, :catch_failures => true) + end - describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain 'RackBaseURI /test' } + describe file("#{$vhost_dir}/25-test.server.conf") do + it { is_expected.to be_file } + it { is_expected.to contain 'RackBaseURI /test' } + end end end end @@ -791,8 +955,8 @@ class { 'apache': } end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain 'append MirrorID "mirror 12"' } + it { is_expected.to be_file } + it { is_expected.to contain 'append MirrorID "mirror 12"' } end end @@ -814,11 +978,53 @@ class { 'apache': } apply_manifest(pp, :catch_failures => true) end + describe file("#{$vhost_dir}/25-test.server.conf") do + it { is_expected.to be_file } + it { is_expected.to contain '#test' } + it { is_expected.to contain 'RewriteCond %{HTTP_USER_AGENT} ^Lynx/ [OR]' } + it { is_expected.to contain 'RewriteRule ^index.html$ welcome.html' } + end + end + + describe 'directory rewrite rules' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + if ! defined(Class['apache::mod::rewrite']) { + include ::apache::mod::rewrite + } + apache::vhost { 'test.server': + docroot => '/tmp', + directories => [ + { + path => '/tmp', + rewrites => [ + { + comment => 'Permalink Rewrites', + rewrite_base => '/', + }, + { rewrite_rule => [ '^index\\.php$ - [L]' ] }, + { rewrite_cond => [ + '%{REQUEST_FILENAME} !-f', + '%{REQUEST_FILENAME} !-d', ], rewrite_rule => [ '. /index.php [L]' ], } + ], + }, + ], + } + EOS + apply_manifest(pp, :catch_failures => true) + end + describe file("#{$vhost_dir}/25-test.server.conf") do it { should be_file } - it { should contain '#test' } - it { should contain 'RewriteCond %{HTTP_USER_AGENT} ^Lynx/ [OR]' } - it { should contain 'RewriteRule ^index.html$ welcome.html' } + it { should contain '#Permalink Rewrites' } + it { should contain 'RewriteEngine On' } + it { should contain 'RewriteBase /' } + it { should contain 'RewriteRule ^index\.php$ - [L]' } + it { should contain 'RewriteCond %{REQUEST_FILENAME} !-f' } + it { should contain 'RewriteCond %{REQUEST_FILENAME} !-d' } + it { should contain 'RewriteRule . /index.php [L]' } end end @@ -837,9 +1043,9 @@ class { 'apache': } end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain 'SetEnv TEST /test' } - it { should contain 'SetEnvIf Request_URI "\.gif$" object_is_image=gif' } + it { is_expected.to be_file } + it { is_expected.to contain 'SetEnv TEST /test' } + it { is_expected.to contain 'SetEnvIf Request_URI "\.gif$" object_is_image=gif' } end end @@ -857,8 +1063,8 @@ class { 'apache': } end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain '' } + it { is_expected.to be_file } + it { is_expected.to contain '' } end end @@ -875,12 +1081,13 @@ class { 'apache::mod::wsgi': } wsgi_daemon_process_options => {processes => '2'}, wsgi_process_group => 'nobody', wsgi_script_aliases => { '/test' => '/test1' }, + wsgi_pass_authorization => 'On', } EOS apply_manifest(pp, :catch_failures => true) end - it 'import_script applies cleanly', :unless => fact('lsbcodename') == 'lucid' do + it 'import_script applies cleanly', :unless => (fact('lsbdistcodename') == 'lucid' or UNSUPPORTED_PLATFORMS.include?(fact('osfamily'))) do pp = <<-EOS class { 'apache': } class { 'apache::mod::wsgi': } @@ -894,18 +1101,22 @@ class { 'apache::mod::wsgi': } wsgi_import_script_options => { application-group => '%{GLOBAL}', process-group => 'wsgi' }, wsgi_process_group => 'nobody', wsgi_script_aliases => { '/test' => '/test1' }, + wsgi_pass_authorization => 'On', + wsgi_chunked_request => 'On', } EOS apply_manifest(pp, :catch_failures => true) end - describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain 'WSGIApplicationGroup %{GLOBAL}' } - it { should contain 'WSGIDaemonProcess wsgi processes=2' } - it { should contain 'WSGIImportScript /test1 application-group=%{GLOBAL} process-group=wsgi' } - it { should contain 'WSGIProcessGroup nobody' } - it { should contain 'WSGIScriptAlias /test "/test1"' } + describe file("#{$vhost_dir}/25-test.server.conf"), :unless => (fact('lsbdistcodename') == 'lucid' or UNSUPPORTED_PLATFORMS.include?(fact('osfamily'))) do + it { is_expected.to be_file } + it { is_expected.to contain 'WSGIApplicationGroup %{GLOBAL}' } + it { is_expected.to contain 'WSGIDaemonProcess wsgi processes=2' } + it { is_expected.to contain 'WSGIImportScript /test1 application-group=%{GLOBAL} process-group=wsgi' } + it { is_expected.to contain 'WSGIProcessGroup nobody' } + it { is_expected.to contain 'WSGIScriptAlias /test "/test1"' } + it { is_expected.to contain 'WSGIPassAuthorization On' } + it { is_expected.to contain 'WSGIChunkedRequest On' } end end @@ -923,8 +1134,8 @@ class { 'apache': } end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain '#weird test string' } + it { is_expected.to be_file } + it { is_expected.to contain '#weird test string' } end end @@ -942,13 +1153,13 @@ class { 'apache': } end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain 'AssignUserId nobody nobody' } + it { is_expected.to be_file } + it { is_expected.to contain 'AssignUserId nobody nobody' } end end # So what does this work on? - if default['platform'] !~ /^(debian-(6|7)|el-(5|6))/ + if default['platform'] !~ /^(debian-(6|7)|el-(5|6|7))/ describe 'fastcgi' do it 'applies cleanly' do pp = <<-EOS @@ -966,9 +1177,9 @@ class { 'apache::mod::fastcgi': } end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain 'FastCgiExternalServer localhost -socket /tmp/fast/1234' } - it { should contain '' } + it { is_expected.to be_file } + it { is_expected.to contain 'FastCgiExternalServer localhost -socket /tmp/fast/1234' } + it { is_expected.to contain '' } end end end @@ -976,20 +1187,39 @@ class { 'apache::mod::fastcgi': } describe 'additional_includes' do it 'applies cleanly' do pp = <<-EOS + if $::osfamily == 'RedHat' and $::selinux == 'true' { + $semanage_package = $::operatingsystemmajrelease ? { + '5' => 'policycoreutils', + default => 'policycoreutils-python', + } + exec { 'set_apache_defaults': + command => 'semanage fcontext -a -t httpd_sys_content_t "/apache_spec(/.*)?"', + path => '/bin:/usr/bin/:/sbin:/usr/sbin', + require => Package[$semanage_package], + } + package { $semanage_package: ensure => installed } + exec { 'restorecon_apache': + command => 'restorecon -Rv /apache_spec', + path => '/bin:/usr/bin/:/sbin:/usr/sbin', + before => Service['httpd'], + require => Class['apache'], + } + } class { 'apache': } host { 'test.server': ip => '127.0.0.1' } - file { '/tmp/include': ensure => present, content => '#additional_includes' } + file { '/apache_spec': ensure => directory, } + file { '/apache_spec/include': ensure => present, content => '#additional_includes' } apache::vhost { 'test.server': - docroot => '/tmp', - additional_includes => '/tmp/include', + docroot => '/apache_spec', + additional_includes => '/apache_spec/include', } EOS apply_manifest(pp, :catch_failures => true) end describe file("#{$vhost_dir}/25-test.server.conf") do - it { should be_file } - it { should contain 'Include "/tmp/include"' } + it { is_expected.to be_file } + it { is_expected.to contain 'Include "/apache_spec/include"' } end end diff --git a/apache/spec/classes/apache_spec.rb b/apache/spec/classes/apache_spec.rb index 1a9a58d1b..fe61a9796 100644 --- a/apache/spec/classes/apache_spec.rb +++ b/apache/spec/classes/apache_spec.rb @@ -4,21 +4,30 @@ context "on a Debian OS" do let :facts do { + :id => 'root', + :kernel => 'Linux', + :lsbdistcodename => 'squeeze', :osfamily => 'Debian', + :operatingsystem => 'Debian', :operatingsystemrelease => '6', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', :concat_basedir => '/dne', } end - it { should contain_class("apache::params") } - it { should contain_package("httpd").with( + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_package("httpd").with( 'notify' => 'Class[Apache::Service]', 'ensure' => 'installed' ) } - it { should contain_user("www-data") } - it { should contain_group("www-data") } - it { should contain_class("apache::service") } - it { should contain_file("/etc/apache2/sites-enabled").with( + it { is_expected.to contain_user("www-data") } + it { is_expected.to contain_group("www-data") } + it { is_expected.to contain_class("apache::service") } + it { is_expected.to contain_file("/var/www").with( + 'ensure' => 'directory' + ) + } + it { is_expected.to contain_file("/etc/apache2/sites-enabled").with( 'ensure' => 'directory', 'recurse' => 'true', 'purge' => 'true', @@ -26,7 +35,7 @@ 'require' => 'Package[httpd]' ) } - it { should contain_file("/etc/apache2/mods-enabled").with( + it { is_expected.to contain_file("/etc/apache2/mods-enabled").with( 'ensure' => 'directory', 'recurse' => 'true', 'purge' => 'true', @@ -34,7 +43,7 @@ 'require' => 'Package[httpd]' ) } - it { should contain_file("/etc/apache2/mods-available").with( + it { is_expected.to contain_file("/etc/apache2/mods-available").with( 'ensure' => 'directory', 'recurse' => 'true', 'purge' => 'false', @@ -42,7 +51,7 @@ 'require' => 'Package[httpd]' ) } - it { should contain_concat("/etc/apache2/ports.conf").with( + it { is_expected.to contain_concat("/etc/apache2/ports.conf").with( 'owner' => 'root', 'group' => 'root', 'mode' => '0644', @@ -60,33 +69,41 @@ 'dav', 'env' ].each do |modname| - it { should contain_file("#{modname}.load").with( + it { is_expected.to contain_file("#{modname}.load").with( 'path' => "/etc/apache2/mods-available/#{modname}.load", 'ensure' => 'file' ) } - it { should contain_file("#{modname}.load symlink").with( + it { is_expected.to contain_file("#{modname}.load symlink").with( 'path' => "/etc/apache2/mods-enabled/#{modname}.load", 'ensure' => 'link', 'target' => "/etc/apache2/mods-available/#{modname}.load" ) } - it { should_not contain_file("#{modname}.conf") } - it { should_not contain_file("#{modname}.conf symlink") } + it { is_expected.not_to contain_file("#{modname}.conf") } + it { is_expected.not_to contain_file("#{modname}.conf symlink") } end context "with Apache version < 2.4" do let :params do - { :apache_version => 2.2 } + { :apache_version => '2.2' } end - it { should contain_file("/etc/apache2/apache2.conf").with_content %r{^Include "/etc/apache2/conf\.d/\*\.conf"$} } + it { is_expected.to contain_file("/etc/apache2/apache2.conf").with_content %r{^Include "/etc/apache2/conf\.d/\*\.conf"$} } end context "with Apache version >= 2.4" do let :params do - { :apache_version => 2.4 } + { :apache_version => '2.4' } + end + + it { is_expected.to contain_file("/etc/apache2/apache2.conf").with_content %r{^IncludeOptional "/etc/apache2/conf\.d/\*\.conf"$} } + end + + context "when specifying slash encoding behaviour" do + let :params do + { :allow_encoded_slashes => 'nodecode' } end - it { should contain_file("/etc/apache2/apache2.conf").with_content %r{^IncludeOptional "/etc/apache2/conf\.d/\*\.conf"$} } + it { is_expected.to contain_file("/etc/apache2/apache2.conf").with_content %r{^AllowEncodedSlashes nodecode$} } end # Assert that both load files and conf files are placed and symlinked for these mods @@ -100,20 +117,20 @@ 'negotiation', 'setenvif', ].each do |modname| - it { should contain_file("#{modname}.load").with( + it { is_expected.to contain_file("#{modname}.load").with( 'path' => "/etc/apache2/mods-available/#{modname}.load", 'ensure' => 'file' ) } - it { should contain_file("#{modname}.load symlink").with( + it { is_expected.to contain_file("#{modname}.load symlink").with( 'path' => "/etc/apache2/mods-enabled/#{modname}.load", 'ensure' => 'link', 'target' => "/etc/apache2/mods-available/#{modname}.load" ) } - it { should contain_file("#{modname}.conf").with( + it { is_expected.to contain_file("#{modname}.conf").with( 'path' => "/etc/apache2/mods-available/#{modname}.conf", 'ensure' => 'file' ) } - it { should contain_file("#{modname}.conf symlink").with( + it { is_expected.to contain_file("#{modname}.conf symlink").with( 'path' => "/etc/apache2/mods-enabled/#{modname}.conf", 'ensure' => 'link', 'target' => "/etc/apache2/mods-available/#{modname}.conf" @@ -126,8 +143,8 @@ { :manage_user => false } end - it { should_not contain_user('www-data') } - it { should contain_file("/etc/apache2/apache2.conf").with_content %r{^User www-data\n} } + it { is_expected.not_to contain_user('www-data') } + it { is_expected.to contain_file("/etc/apache2/apache2.conf").with_content %r{^User www-data\n} } end end describe "Don't create group resource" do @@ -136,29 +153,87 @@ { :manage_group => false } end - it { should_not contain_group('www-data') } - it { should contain_file("/etc/apache2/apache2.conf").with_content %r{^Group www-data\n} } + it { is_expected.not_to contain_group('www-data') } + it { is_expected.to contain_file("/etc/apache2/apache2.conf").with_content %r{^Group www-data\n} } + end + end + + describe "Add extra LogFormats" do + context "When parameter log_formats is a hash" do + let :params do + { :log_formats => { + 'vhost_common' => "%v %h %l %u %t \"%r\" %>s %b", + 'vhost_combined' => "%v %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" + } } + end + + it { is_expected.to contain_file("/etc/apache2/apache2.conf").with_content %r{^LogFormat "%v %h %l %u %t \"%r\" %>s %b" vhost_common\n} } + it { is_expected.to contain_file("/etc/apache2/apache2.conf").with_content %r{^LogFormat "%v %h %l %u %t \"%r\" %>s %b \"%\{Referer\}i\" \"%\{User-agent\}i\"" vhost_combined\n} } + end + end + + context "on Ubuntu" do + let :facts do + super().merge({ + :operatingsystem => 'Ubuntu' + }) + end + + context "13.10" do + let :facts do + super().merge({ + :lsbdistrelease => '13.10', + :operatingsystemrelease => '13.10' + }) + end + it { is_expected.to contain_class('apache').with_apache_version('2.4') } + end + context "12.04" do + let :facts do + super().merge({ + :lsbdistrelease => '12.04', + :operatingsystemrelease => '12.04' + }) + end + it { is_expected.to contain_class('apache').with_apache_version('2.2') } + end + context "13.04" do + let :facts do + super().merge({ + :lsbdistrelease => '13.04', + :operatingsystemrelease => '13.04' + }) + end + it { is_expected.to contain_class('apache').with_apache_version('2.2') } end end end context "on a RedHat 5 OS" do let :facts do { + :id => 'root', + :kernel => 'Linux', :osfamily => 'RedHat', + :operatingsystem => 'RedHat', :operatingsystemrelease => '5', :concat_basedir => '/dne', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_package("httpd").with( + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_package("httpd").with( 'notify' => 'Class[Apache::Service]', 'ensure' => 'installed' ) } - it { should contain_user("apache") } - it { should contain_group("apache") } - it { should contain_class("apache::service") } - it { should contain_file("/etc/httpd/conf.d").with( + it { is_expected.to contain_user("apache") } + it { is_expected.to contain_group("apache") } + it { is_expected.to contain_class("apache::service") } + it { is_expected.to contain_file("/var/www/html").with( + 'ensure' => 'directory' + ) + } + it { is_expected.to contain_file("/etc/httpd/conf.d").with( 'ensure' => 'directory', 'recurse' => 'true', 'purge' => 'true', @@ -166,7 +241,7 @@ 'require' => 'Package[httpd]' ) } - it { should contain_concat("/etc/httpd/conf/ports.conf").with( + it { is_expected.to contain_concat("/etc/httpd/conf/ports.conf").with( 'owner' => 'root', 'group' => 'root', 'mode' => '0644', @@ -183,7 +258,7 @@ end ['mod.d','site.d','conf.d'].each do |dir| - it { should contain_file("/etc/httpd/#{dir}").with( + it { is_expected.to contain_file("/etc/httpd/#{dir}").with( 'ensure' => 'directory', 'recurse' => 'true', 'purge' => 'true', @@ -203,10 +278,10 @@ 'dav', 'env', ].each do |modname| - it { should contain_file("#{modname}.load").with_path( + it { is_expected.to contain_file("#{modname}.load").with_path( "/etc/httpd/mod.d/#{modname}.load" ) } - it { should_not contain_file("#{modname}.conf").with_path( + it { is_expected.not_to contain_file("#{modname}.conf").with_path( "/etc/httpd/mod.d/#{modname}.conf" ) } end @@ -222,33 +297,53 @@ 'negotiation', 'setenvif', ].each do |modname| - it { should contain_file("#{modname}.load").with_path( + it { is_expected.to contain_file("#{modname}.load").with_path( "/etc/httpd/mod.d/#{modname}.load" ) } - it { should contain_file("#{modname}.conf").with_path( + it { is_expected.to contain_file("#{modname}.conf").with_path( "/etc/httpd/mod.d/#{modname}.conf" ) } end context "with Apache version < 2.4" do let :params do - { :apache_version => 2.2 } + { :apache_version => '2.2' } end - it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^Include "/etc/httpd/conf\.d/\*\.conf"$} } + it { is_expected.to contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^Include "/etc/httpd/conf\.d/\*\.conf"$} } end context "with Apache version >= 2.4" do let :params do - { :apache_version => 2.4 } + { :apache_version => '2.4' } + end + + it { is_expected.to contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^IncludeOptional "/etc/httpd/conf\.d/\*\.conf"$} } + end + + context "when specifying slash encoding behaviour" do + let :params do + { :allow_encoded_slashes => 'nodecode' } end - it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^IncludeOptional "/etc/httpd/conf\.d/\*\.conf"$} } + it { is_expected.to contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^AllowEncodedSlashes nodecode$} } end - it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^Include "/etc/httpd/site\.d/\*\.conf"$} } - it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^Include "/etc/httpd/mod\.d/\*\.conf"$} } - it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^Include "/etc/httpd/mod\.d/\*\.load"$} } + it { is_expected.to contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^Include "/etc/httpd/site\.d/\*"$} } + it { is_expected.to contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^Include "/etc/httpd/mod\.d/\*\.conf"$} } + it { is_expected.to contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^Include "/etc/httpd/mod\.d/\*\.load"$} } + end + + describe "Alternate conf directory" do + let :params do + { :conf_dir => '/opt/rh/root/etc/httpd/conf' } + end + + it { is_expected.to contain_file("/opt/rh/root/etc/httpd/conf/httpd.conf").with( + 'ensure' => 'file', + 'notify' => 'Class[Apache::Service]', + 'require' => 'Package[httpd]' + ) } end describe "Alternate conf.d directory" do @@ -256,7 +351,7 @@ { :confd_dir => '/etc/httpd/special_conf.d' } end - it { should contain_file("/etc/httpd/special_conf.d").with( + it { is_expected.to contain_file("/etc/httpd/special_conf.d").with( 'ensure' => 'directory', 'recurse' => 'true', 'purge' => 'true', @@ -271,32 +366,32 @@ { :mpm_module => false } end it 'should not declare mpm modules' do - should_not contain_class('apache::mod::event') - should_not contain_class('apache::mod::itk') - should_not contain_class('apache::mod::peruser') - should_not contain_class('apache::mod::prefork') - should_not contain_class('apache::mod::worker') + is_expected.not_to contain_class('apache::mod::event') + is_expected.not_to contain_class('apache::mod::itk') + is_expected.not_to contain_class('apache::mod::peruser') + is_expected.not_to contain_class('apache::mod::prefork') + is_expected.not_to contain_class('apache::mod::worker') end end context "when declaring mpm_module => prefork" do let :params do { :mpm_module => 'prefork' } end - it { should contain_class('apache::mod::prefork') } - it { should_not contain_class('apache::mod::event') } - it { should_not contain_class('apache::mod::itk') } - it { should_not contain_class('apache::mod::peruser') } - it { should_not contain_class('apache::mod::worker') } + it { is_expected.to contain_class('apache::mod::prefork') } + it { is_expected.not_to contain_class('apache::mod::event') } + it { is_expected.not_to contain_class('apache::mod::itk') } + it { is_expected.not_to contain_class('apache::mod::peruser') } + it { is_expected.not_to contain_class('apache::mod::worker') } end context "when declaring mpm_module => worker" do let :params do { :mpm_module => 'worker' } end - it { should contain_class('apache::mod::worker') } - it { should_not contain_class('apache::mod::event') } - it { should_not contain_class('apache::mod::itk') } - it { should_not contain_class('apache::mod::peruser') } - it { should_not contain_class('apache::mod::prefork') } + it { is_expected.to contain_class('apache::mod::worker') } + it { is_expected.not_to contain_class('apache::mod::event') } + it { is_expected.not_to contain_class('apache::mod::itk') } + it { is_expected.not_to contain_class('apache::mod::peruser') } + it { is_expected.not_to contain_class('apache::mod::prefork') } end context "when declaring mpm_module => breakme" do let :params do @@ -311,13 +406,13 @@ let :params do { :conf_template => 'apache/httpd.conf.erb' } end - it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^# Security\n} } + it { is_expected.to contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^# Security\n} } end context "with non-default" do let :params do { :conf_template => 'site_apache/fake.conf.erb' } end - it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^Fake template for rspec.$} } + it { is_expected.to contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^Fake template for rspec.$} } end end @@ -327,8 +422,8 @@ { :default_mods => false } end - it { should contain_apache__mod('authz_host') } - it { should_not contain_apache__mod('env') } + it { is_expected.to contain_apache__mod('authz_host') } + it { is_expected.not_to contain_apache__mod('env') } end context "custom" do let :params do @@ -342,10 +437,10 @@ ]} end - it { should contain_apache__mod('authz_host') } - it { should contain_apache__mod('env') } - it { should contain_class('apache::mod::info') } - it { should contain_class('apache::mod::mime') } + it { is_expected.to contain_apache__mod('authz_host') } + it { is_expected.to contain_apache__mod('env') } + it { is_expected.to contain_class('apache::mod::info') } + it { is_expected.to contain_class('apache::mod::mime') } end end describe "Don't create user resource" do @@ -354,8 +449,8 @@ { :manage_user => false } end - it { should_not contain_user('apache') } - it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^User apache\n} } + it { is_expected.not_to contain_user('apache') } + it { is_expected.to contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^User apache\n} } end end describe "Don't create group resource" do @@ -364,8 +459,8 @@ { :manage_group => false } end - it { should_not contain_group('apache') } - it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^Group apache\n} } + it { is_expected.not_to contain_group('apache') } + it { is_expected.to contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^Group apache\n} } end end @@ -384,44 +479,52 @@ let :params do { :sendfile => 'On' } end - it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^EnableSendfile On\n} } + it { is_expected.to contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^EnableSendfile On\n} } end context "Off" do let :params do { :sendfile => 'Off' } end - it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^EnableSendfile Off\n} } + it { is_expected.to contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^EnableSendfile Off\n} } end end end context "on a FreeBSD OS" do let :facts do { + :id => 'root', + :kernel => 'FreeBSD', :osfamily => 'FreeBSD', + :operatingsystem => 'FreeBSD', :operatingsystemrelease => '9', :concat_basedir => '/dne', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_class("apache::package").with({'ensure' => 'present'}) } - it { should contain_user("www") } - it { should contain_group("www") } - it { should contain_class("apache::service") } - it { should contain_file("/usr/local/etc/apache22/Vhosts").with( + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_class("apache::package").with({'ensure' => 'present'}) } + it { is_expected.to contain_user("www") } + it { is_expected.to contain_group("www") } + it { is_expected.to contain_class("apache::service") } + it { is_expected.to contain_file("/usr/local/www/apache22/data").with( + 'ensure' => 'directory' + ) + } + it { is_expected.to contain_file("/usr/local/etc/apache22/Vhosts").with( 'ensure' => 'directory', 'recurse' => 'true', 'purge' => 'true', 'notify' => 'Class[Apache::Service]', 'require' => 'Package[httpd]' ) } - it { should contain_file("/usr/local/etc/apache22/Modules").with( + it { is_expected.to contain_file("/usr/local/etc/apache22/Modules").with( 'ensure' => 'directory', 'recurse' => 'true', 'purge' => 'true', 'notify' => 'Class[Apache::Service]', 'require' => 'Package[httpd]' ) } - it { should contain_concat("/usr/local/etc/apache22/ports.conf").with( + it { is_expected.to contain_concat("/usr/local/etc/apache22/ports.conf").with( 'owner' => 'root', 'group' => 'wheel', 'mode' => '0644', @@ -438,11 +541,11 @@ 'dav', 'env' ].each do |modname| - it { should contain_file("#{modname}.load").with( + it { is_expected.to contain_file("#{modname}.load").with( 'path' => "/usr/local/etc/apache22/Modules/#{modname}.load", 'ensure' => 'file' ) } - it { should_not contain_file("#{modname}.conf") } + it { is_expected.not_to contain_file("#{modname}.conf") } end # Assert that both load files and conf files are placed for these mods @@ -456,11 +559,11 @@ 'negotiation', 'setenvif', ].each do |modname| - it { should contain_file("#{modname}.load").with( + it { is_expected.to contain_file("#{modname}.load").with( 'path' => "/usr/local/etc/apache22/Modules/#{modname}.load", 'ensure' => 'file' ) } - it { should contain_file("#{modname}.conf").with( + it { is_expected.to contain_file("#{modname}.conf").with( 'path' => "/usr/local/etc/apache22/Modules/#{modname}.conf", 'ensure' => 'file' ) } @@ -469,28 +572,58 @@ context 'on all OSes' do let :facts do { + :id => 'root', + :kernel => 'Linux', :osfamily => 'RedHat', + :operatingsystem => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + context 'with a custom apache_name parameter' do + let :params do { + :apache_name => 'httpd24-httpd' + } + end + it { is_expected.to contain_package("httpd").with( + 'notify' => 'Class[Apache::Service]', + 'ensure' => 'installed', + 'name' => 'httpd24-httpd' + ) } end context 'default vhost defaults' do - it { should contain_apache__vhost('default').with_ensure('present') } - it { should contain_apache__vhost('default-ssl').with_ensure('absent') } + it { is_expected.to contain_apache__vhost('default').with_ensure('present') } + it { is_expected.to contain_apache__vhost('default-ssl').with_ensure('absent') } end context 'without default non-ssl vhost' do let :params do { :default_vhost => false } end - it { should contain_apache__vhost('default').with_ensure('absent') } + it { is_expected.to contain_apache__vhost('default').with_ensure('absent') } end context 'with default ssl vhost' do let :params do { :default_ssl_vhost => true } end - it { should contain_apache__vhost('default-ssl').with_ensure('present') } + it { is_expected.to contain_apache__vhost('default-ssl').with_ensure('present') } + end + end + context 'with unsupported osfamily' do + let :facts do + { :osfamily => 'Darwin', + :operatingsystemrelease => '13.1.0', + :concat_basedir => '/dne', + } + end + + it do + expect { + should compile + }.to raise_error(Puppet::Error, /Unsupported osfamily/) end end end diff --git a/apache/spec/classes/dev_spec.rb b/apache/spec/classes/dev_spec.rb index e3d7dee39..df342d40e 100644 --- a/apache/spec/classes/dev_spec.rb +++ b/apache/spec/classes/dev_spec.rb @@ -4,24 +4,27 @@ context "on a Debian OS" do let :facts do { + :lsbdistcodename => 'squeeze', :osfamily => 'Debian', + :operatingsystem => 'Debian', :operatingsystemrelease => '6', } end - it { should contain_class("apache::params") } - it { should contain_package("libaprutil1-dev") } - it { should contain_package("libapr1-dev") } - it { should contain_package("apache2-prefork-dev") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_package("libaprutil1-dev") } + it { is_expected.to contain_package("libapr1-dev") } + it { is_expected.to contain_package("apache2-prefork-dev") } end context "on a RedHat OS" do let :facts do { :osfamily => 'RedHat', + :operatingsystem => 'RedHat', :operatingsystemrelease => '6', } end - it { should contain_class("apache::params") } - it { should contain_package("httpd-devel") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_package("httpd-devel") } end context "on a FreeBSD OS" do let :pre_condition do @@ -30,9 +33,10 @@ let :facts do { :osfamily => 'FreeBSD', + :operatingsystem => 'FreeBSD', :operatingsystemrelease => '9', } end - it { should contain_class("apache::params") } + it { is_expected.to contain_class("apache::params") } end end diff --git a/apache/spec/classes/mod/auth_kerb_spec.rb b/apache/spec/classes/mod/auth_kerb_spec.rb index 71c2349c3..1706bfb8d 100644 --- a/apache/spec/classes/mod/auth_kerb_spec.rb +++ b/apache/spec/classes/mod/auth_kerb_spec.rb @@ -1,41 +1,56 @@ +require 'spec_helper' + describe 'apache::mod::auth_kerb', :type => :class do let :pre_condition do 'include apache' end - context "on a Debian OS" do + context "on a Debian OS", :compile do let :facts do { + :id => 'root', + :kernel => 'Linux', + :lsbdistcodename => 'squeeze', :osfamily => 'Debian', + :operatingsystem => 'Debian', :operatingsystemrelease => '6', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', :concat_basedir => '/dne', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod("auth_kerb") } - it { should contain_package("libapache2-mod-auth-kerb") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod("auth_kerb") } + it { is_expected.to contain_package("libapache2-mod-auth-kerb") } end - context "on a RedHat OS" do + context "on a RedHat OS", :compile do let :facts do { + :id => 'root', + :kernel => 'Linux', :osfamily => 'RedHat', + :operatingsystem => 'RedHat', :operatingsystemrelease => '6', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', :concat_basedir => '/dne', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod("auth_kerb") } - it { should contain_package("mod_auth_kerb") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod("auth_kerb") } + it { is_expected.to contain_package("mod_auth_kerb") } end - context "on a FreeBSD OS" do + context "on a FreeBSD OS", :compile do let :facts do { + :id => 'root', + :kernel => 'FreeBSD', :osfamily => 'FreeBSD', + :operatingsystem => 'FreeBSD', :operatingsystemrelease => '9', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', :concat_basedir => '/dne', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod("auth_kerb") } - it { should contain_package("www/mod_auth_kerb2") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod("auth_kerb") } + it { is_expected.to contain_package("www/mod_auth_kerb2") } end end diff --git a/apache/spec/classes/mod/authnz_ldap_spec.rb b/apache/spec/classes/mod/authnz_ldap_spec.rb index c8e832d95..a0a913a6e 100644 --- a/apache/spec/classes/mod/authnz_ldap_spec.rb +++ b/apache/spec/classes/mod/authnz_ldap_spec.rb @@ -1,3 +1,5 @@ +require 'spec_helper' + describe 'apache::mod::authnz_ldap', :type => :class do let :pre_condition do 'include apache' @@ -6,28 +8,33 @@ context "on a Debian OS" do let :facts do { + :lsbdistcodename => 'squeeze', :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :id => 'root', + :kernel => 'Linux', + :operatingsystem => 'Debian', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_class("apache::mod::ldap") } - it { should contain_apache__mod('authnz_ldap') } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_class("apache::mod::ldap") } + it { is_expected.to contain_apache__mod('authnz_ldap') } context 'default verifyServerCert' do - it { should contain_file('authnz_ldap.conf').with_content(/^LDAPVerifyServerCert On$/) } + it { is_expected.to contain_file('authnz_ldap.conf').with_content(/^LDAPVerifyServerCert On$/) } end context 'verifyServerCert = false' do let(:params) { { :verifyServerCert => false } } - it { should contain_file('authnz_ldap.conf').with_content(/^LDAPVerifyServerCert Off$/) } + it { is_expected.to contain_file('authnz_ldap.conf').with_content(/^LDAPVerifyServerCert Off$/) } end context 'verifyServerCert = wrong' do let(:params) { { :verifyServerCert => 'wrong' } } it 'should raise an error' do - expect { should raise_error Puppet::Error } + expect { is_expected.to raise_error Puppet::Error } end end end #Debian @@ -38,25 +45,29 @@ :osfamily => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :id => 'root', + :kernel => 'Linux', + :operatingsystem => 'RedHat', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_class("apache::mod::ldap") } - it { should contain_apache__mod('authnz_ldap') } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_class("apache::mod::ldap") } + it { is_expected.to contain_apache__mod('authnz_ldap') } context 'default verifyServerCert' do - it { should contain_file('authnz_ldap.conf').with_content(/^LDAPVerifyServerCert On$/) } + it { is_expected.to contain_file('authnz_ldap.conf').with_content(/^LDAPVerifyServerCert On$/) } end context 'verifyServerCert = false' do let(:params) { { :verifyServerCert => false } } - it { should contain_file('authnz_ldap.conf').with_content(/^LDAPVerifyServerCert Off$/) } + it { is_expected.to contain_file('authnz_ldap.conf').with_content(/^LDAPVerifyServerCert Off$/) } end context 'verifyServerCert = wrong' do let(:params) { { :verifyServerCert => 'wrong' } } it 'should raise an error' do - expect { should raise_error Puppet::Error } + expect { is_expected.to raise_error Puppet::Error } end end end # Redhat diff --git a/apache/spec/classes/mod/dav_svn_spec.rb b/apache/spec/classes/mod/dav_svn_spec.rb index fe11bb8cb..95abef994 100644 --- a/apache/spec/classes/mod/dav_svn_spec.rb +++ b/apache/spec/classes/mod/dav_svn_spec.rb @@ -1,3 +1,5 @@ +require 'spec_helper' + describe 'apache::mod::dav_svn', :type => :class do let :pre_condition do 'include apache' @@ -5,37 +7,53 @@ context "on a Debian OS" do let :facts do { - :osfamily => 'Debian', - :operatingsystemrelease => '6', - :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :operatingsystemmajrelease => '6', + :concat_basedir => '/dne', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('dav_svn') } - it { should contain_package("libapache2-svn") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('dav_svn') } + it { is_expected.to contain_package("libapache2-svn") } end context "on a RedHat OS" do let :facts do { - :osfamily => 'RedHat', - :operatingsystemrelease => '6', - :concat_basedir => '/dne', + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :operatingsystemmajrelease => '6', + :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('dav_svn') } - it { should contain_package("mod_dav_svn") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('dav_svn') } + it { is_expected.to contain_package("mod_dav_svn") } end context "on a FreeBSD OS" do let :facts do { - :osfamily => 'FreeBSD', - :operatingsystemrelease => '9', - :concat_basedir => '/dne', + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :operatingsystemmajrelease => '9', + :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('dav_svn') } - it { should contain_package("devel/subversion") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('dav_svn') } + it { is_expected.to contain_package("devel/subversion") } end end diff --git a/apache/spec/classes/mod/deflate_spec.rb b/apache/spec/classes/mod/deflate_spec.rb new file mode 100644 index 000000000..c61010f28 --- /dev/null +++ b/apache/spec/classes/mod/deflate_spec.rb @@ -0,0 +1,100 @@ +require 'spec_helper' + +# This function is called inside the OS specific contexts +def general_deflate_specs + it { is_expected.to contain_apache__mod("deflate") } + + it do + is_expected.to contain_file("deflate.conf").with_content( + "AddOutputFilterByType DEFLATE text/css\n"\ + "AddOutputFilterByType DEFLATE text/html\n"\ + "\n"\ + "DeflateFilterNote Input instream\n"\ + "DeflateFilterNote Ratio ratio\n" + ) + end +end + +describe 'apache::mod::deflate', :type => :class do + let :pre_condition do + 'class { "apache": + default_mods => false, + } + class { "apache::mod::deflate": + types => [ "text/html", "text/css" ], + notes => { + "Input" => "instream", + "Ratio" => "ratio", + } + } + ' + end + + context "On a Debian OS with default params" do + let :facts do + { + :id => 'root', + :lsbdistcodename => 'squeeze', + :kernel => 'Linux', + :osfamily => 'Debian', + :operatingsystem => 'Debian', + :operatingsystemrelease => '6', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + :concat_basedir => '/dne', + } + end + + # Load the more generic tests for this context + general_deflate_specs() + + it { is_expected.to contain_file("deflate.conf").with({ + :ensure => 'file', + :path => '/etc/apache2/mods-available/deflate.conf', + } ) } + it { is_expected.to contain_file("deflate.conf symlink").with({ + :ensure => 'link', + :path => '/etc/apache2/mods-enabled/deflate.conf', + } ) } + end + + context "on a RedHat OS with default params" do + let :facts do + { + :id => 'root', + :kernel => 'Linux', + :osfamily => 'RedHat', + :operatingsystem => 'RedHat', + :operatingsystemrelease => '6', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + :concat_basedir => '/dne', + } + end + + # Load the more generic tests for this context + general_deflate_specs() + + it { is_expected.to contain_file("deflate.conf").with_path("/etc/httpd/conf.d/deflate.conf") } + end + + context "On a FreeBSD OS with default params" do + let :facts do + { + :id => 'root', + :kernel => 'FreeBSD', + :osfamily => 'FreeBSD', + :operatingsystem => 'FreeBSD', + :operatingsystemrelease => '9', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + :concat_basedir => '/dne', + } + end + + # Load the more generic tests for this context + general_deflate_specs() + + it { is_expected.to contain_file("deflate.conf").with({ + :ensure => 'file', + :path => '/usr/local/etc/apache22/Modules/deflate.conf', + } ) } + end +end diff --git a/apache/spec/classes/mod/dev_spec.rb b/apache/spec/classes/mod/dev_spec.rb index b72217aad..84d80e344 100644 --- a/apache/spec/classes/mod/dev_spec.rb +++ b/apache/spec/classes/mod/dev_spec.rb @@ -1,10 +1,11 @@ require 'spec_helper' + describe 'apache::mod::dev', :type => :class do [ - ['RedHat', '6'], - ['Debian', '6'], - ['FreeBSD', '9'], - ].each do |osfamily, operatingsystemrelease| + ['RedHat', '6', 'Santiago'], + ['Debian', '6', 'squeeze'], + ['FreeBSD', '9', 'FreeBSD'], + ].each do |osfamily, operatingsystemrelease, lsbdistcodename| if osfamily == 'FreeBSD' let :pre_condition do 'include apache::package' @@ -13,11 +14,13 @@ context "on a #{osfamily} OS" do let :facts do { + :lsbdistcodename => lsbdistcodename, :osfamily => osfamily, + :operatingsystem => osfamily, :operatingsystemrelease => operatingsystemrelease, } end - it { should contain_class('apache::dev') } + it { is_expected.to contain_class('apache::dev') } end end end diff --git a/apache/spec/classes/mod/dir_spec.rb b/apache/spec/classes/mod/dir_spec.rb index b195eda0f..1efed2fe7 100644 --- a/apache/spec/classes/mod/dir_spec.rb +++ b/apache/spec/classes/mod/dir_spec.rb @@ -1,3 +1,5 @@ +require 'spec_helper' + describe 'apache::mod::dir', :type => :class do let :pre_condition do 'class { "apache": @@ -10,25 +12,30 @@ :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + :lsbdistcodename => 'squeeze', } end context "passing no parameters" do - it { should contain_class("apache::params") } - it { should contain_apache__mod('dir') } - it { should contain_file('dir.conf').with_content(/^DirectoryIndex /) } - it { should contain_file('dir.conf').with_content(/ index\.html /) } - it { should contain_file('dir.conf').with_content(/ index\.html\.var /) } - it { should contain_file('dir.conf').with_content(/ index\.cgi /) } - it { should contain_file('dir.conf').with_content(/ index\.pl /) } - it { should contain_file('dir.conf').with_content(/ index\.php /) } - it { should contain_file('dir.conf').with_content(/ index\.xhtml$/) } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('dir') } + it { is_expected.to contain_file('dir.conf').with_content(/^DirectoryIndex /) } + it { is_expected.to contain_file('dir.conf').with_content(/ index\.html /) } + it { is_expected.to contain_file('dir.conf').with_content(/ index\.html\.var /) } + it { is_expected.to contain_file('dir.conf').with_content(/ index\.cgi /) } + it { is_expected.to contain_file('dir.conf').with_content(/ index\.pl /) } + it { is_expected.to contain_file('dir.conf').with_content(/ index\.php /) } + it { is_expected.to contain_file('dir.conf').with_content(/ index\.xhtml$/) } end context "passing indexes => ['example.txt','fearsome.aspx']" do let :params do {:indexes => ['example.txt','fearsome.aspx']} end - it { should contain_file('dir.conf').with_content(/ example\.txt /) } - it { should contain_file('dir.conf').with_content(/ fearsome\.aspx$/) } + it { is_expected.to contain_file('dir.conf').with_content(/ example\.txt /) } + it { is_expected.to contain_file('dir.conf').with_content(/ fearsome\.aspx$/) } end end context "on a RedHat OS" do @@ -37,25 +44,29 @@ :osfamily => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'Redhat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end context "passing no parameters" do - it { should contain_class("apache::params") } - it { should contain_apache__mod('dir') } - it { should contain_file('dir.conf').with_content(/^DirectoryIndex /) } - it { should contain_file('dir.conf').with_content(/ index\.html /) } - it { should contain_file('dir.conf').with_content(/ index\.html\.var /) } - it { should contain_file('dir.conf').with_content(/ index\.cgi /) } - it { should contain_file('dir.conf').with_content(/ index\.pl /) } - it { should contain_file('dir.conf').with_content(/ index\.php /) } - it { should contain_file('dir.conf').with_content(/ index\.xhtml$/) } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('dir') } + it { is_expected.to contain_file('dir.conf').with_content(/^DirectoryIndex /) } + it { is_expected.to contain_file('dir.conf').with_content(/ index\.html /) } + it { is_expected.to contain_file('dir.conf').with_content(/ index\.html\.var /) } + it { is_expected.to contain_file('dir.conf').with_content(/ index\.cgi /) } + it { is_expected.to contain_file('dir.conf').with_content(/ index\.pl /) } + it { is_expected.to contain_file('dir.conf').with_content(/ index\.php /) } + it { is_expected.to contain_file('dir.conf').with_content(/ index\.xhtml$/) } end context "passing indexes => ['example.txt','fearsome.aspx']" do let :params do {:indexes => ['example.txt','fearsome.aspx']} end - it { should contain_file('dir.conf').with_content(/ example\.txt /) } - it { should contain_file('dir.conf').with_content(/ fearsome\.aspx$/) } + it { is_expected.to contain_file('dir.conf').with_content(/ example\.txt /) } + it { is_expected.to contain_file('dir.conf').with_content(/ fearsome\.aspx$/) } end end context "on a FreeBSD OS" do @@ -64,25 +75,29 @@ :osfamily => 'FreeBSD', :operatingsystemrelease => '9', :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end context "passing no parameters" do - it { should contain_class("apache::params") } - it { should contain_apache__mod('dir') } - it { should contain_file('dir.conf').with_content(/^DirectoryIndex /) } - it { should contain_file('dir.conf').with_content(/ index\.html /) } - it { should contain_file('dir.conf').with_content(/ index\.html\.var /) } - it { should contain_file('dir.conf').with_content(/ index\.cgi /) } - it { should contain_file('dir.conf').with_content(/ index\.pl /) } - it { should contain_file('dir.conf').with_content(/ index\.php /) } - it { should contain_file('dir.conf').with_content(/ index\.xhtml$/) } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('dir') } + it { is_expected.to contain_file('dir.conf').with_content(/^DirectoryIndex /) } + it { is_expected.to contain_file('dir.conf').with_content(/ index\.html /) } + it { is_expected.to contain_file('dir.conf').with_content(/ index\.html\.var /) } + it { is_expected.to contain_file('dir.conf').with_content(/ index\.cgi /) } + it { is_expected.to contain_file('dir.conf').with_content(/ index\.pl /) } + it { is_expected.to contain_file('dir.conf').with_content(/ index\.php /) } + it { is_expected.to contain_file('dir.conf').with_content(/ index\.xhtml$/) } end context "passing indexes => ['example.txt','fearsome.aspx']" do let :params do {:indexes => ['example.txt','fearsome.aspx']} end - it { should contain_file('dir.conf').with_content(/ example\.txt /) } - it { should contain_file('dir.conf').with_content(/ fearsome\.aspx$/) } + it { is_expected.to contain_file('dir.conf').with_content(/ example\.txt /) } + it { is_expected.to contain_file('dir.conf').with_content(/ fearsome\.aspx$/) } end end end diff --git a/apache/spec/classes/mod/event_spec.rb b/apache/spec/classes/mod/event_spec.rb index 320374a00..3061ca9b1 100644 --- a/apache/spec/classes/mod/event_spec.rb +++ b/apache/spec/classes/mod/event_spec.rb @@ -1,3 +1,5 @@ +require 'spec_helper' + describe 'apache::mod::event', :type => :class do let :pre_condition do 'class { "apache": mpm_module => false, }' @@ -8,52 +10,61 @@ :osfamily => 'FreeBSD', :operatingsystemrelease => '9', :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should_not contain_apache__mod('event') } - it { should contain_file("/usr/local/etc/apache22/Modules/event.conf").with_ensure('file') } + it { is_expected.to contain_class("apache::params") } + it { is_expected.not_to contain_apache__mod('event') } + it { is_expected.to contain_file("/usr/local/etc/apache22/Modules/event.conf").with_ensure('file') } end context "on a Debian OS" do let :facts do { + :lsbdistcodename => 'squeeze', :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should_not contain_apache__mod('event') } - it { should contain_file("/etc/apache2/mods-available/event.conf").with_ensure('file') } - it { should contain_file("/etc/apache2/mods-enabled/event.conf").with_ensure('link') } + it { is_expected.to contain_class("apache::params") } + it { is_expected.not_to contain_apache__mod('event') } + it { is_expected.to contain_file("/etc/apache2/mods-available/event.conf").with_ensure('file') } + it { is_expected.to contain_file("/etc/apache2/mods-enabled/event.conf").with_ensure('link') } context "with Apache version < 2.4" do let :params do { - :apache_version => 2.2, + :apache_version => '2.2', } end - it { should_not contain_file("/etc/apache2/mods-available/event.load") } - it { should_not contain_file("/etc/apache2/mods-enabled/event.load") } + it { is_expected.not_to contain_file("/etc/apache2/mods-available/event.load") } + it { is_expected.not_to contain_file("/etc/apache2/mods-enabled/event.load") } - it { should contain_package("apache2-mpm-event") } + it { is_expected.to contain_package("apache2-mpm-event") } end context "with Apache version >= 2.4" do let :params do { - :apache_version => 2.4, + :apache_version => '2.4', } end - it { should contain_file("/etc/apache2/mods-available/event.load").with({ + it { is_expected.to contain_file("/etc/apache2/mods-available/event.load").with({ 'ensure' => 'file', 'content' => "LoadModule mpm_event_module /usr/lib/apache2/modules/mod_mpm_event.so\n" }) } - it { should contain_file("/etc/apache2/mods-enabled/event.load").with_ensure('link') } + it { is_expected.to contain_file("/etc/apache2/mods-enabled/event.load").with_ensure('link') } end end context "on a RedHat OS" do @@ -62,23 +73,27 @@ :osfamily => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end context "with Apache version >= 2.4" do let :params do { - :apache_version => 2.4, + :apache_version => '2.4', } end - it { should contain_class("apache::params") } - it { should_not contain_apache__mod('worker') } - it { should_not contain_apache__mod('prefork') } + it { is_expected.to contain_class("apache::params") } + it { is_expected.not_to contain_apache__mod('worker') } + it { is_expected.not_to contain_apache__mod('prefork') } - it { should contain_file("/etc/httpd/conf.d/event.conf").with_ensure('file') } + it { is_expected.to contain_file("/etc/httpd/conf.d/event.conf").with_ensure('file') } - it { should contain_file("/etc/httpd/conf.d/event.load").with({ + it { is_expected.to contain_file("/etc/httpd/conf.d/event.load").with({ 'ensure' => 'file', 'content' => "LoadModule mpm_event_module modules/mod_mpm_event.so\n", }) diff --git a/apache/spec/classes/mod/fastcgi_spec.rb b/apache/spec/classes/mod/fastcgi_spec.rb index 8138bbab7..126c5cc3e 100644 --- a/apache/spec/classes/mod/fastcgi_spec.rb +++ b/apache/spec/classes/mod/fastcgi_spec.rb @@ -1,3 +1,5 @@ +require 'spec_helper' + describe 'apache::mod::fastcgi', :type => :class do let :pre_condition do 'include apache' @@ -8,12 +10,17 @@ :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('fastcgi') } - it { should contain_package("libapache2-mod-fastcgi") } - it { should contain_file('fastcgi.conf') } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('fastcgi') } + it { is_expected.to contain_package("libapache2-mod-fastcgi") } + it { is_expected.to contain_file('fastcgi.conf') } end context "on a RedHat OS" do @@ -22,11 +29,15 @@ :osfamily => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('fastcgi') } - it { should contain_package("mod_fastcgi") } - it { should_not contain_file('fastcgi.conf') } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('fastcgi') } + it { is_expected.to contain_package("mod_fastcgi") } + it { is_expected.not_to contain_file('fastcgi.conf') } end end diff --git a/apache/spec/classes/mod/fcgid_spec.rb b/apache/spec/classes/mod/fcgid_spec.rb index 5cc337291..a342e5813 100644 --- a/apache/spec/classes/mod/fcgid_spec.rb +++ b/apache/spec/classes/mod/fcgid_spec.rb @@ -1,41 +1,113 @@ +require 'spec_helper' + describe 'apache::mod::fcgid', :type => :class do let :pre_condition do 'include apache' end + context "on a Debian OS" do let :facts do { - :osfamily => 'Debian', - :operatingsystemrelease => '6', - :concat_basedir => '/dne', + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :operatingsystemmajrelease => '6', + :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('fcgid') } - it { should contain_package("libapache2-mod-fcgid") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('fcgid') } + it { is_expected.to contain_package("libapache2-mod-fcgid") } end + context "on a RedHat OS" do let :facts do { - :osfamily => 'RedHat', - :operatingsystemrelease => '6', - :concat_basedir => '/dne', + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :operatingsystemmajrelease => '6', + :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('fcgid') } - it { should contain_package("mod_fcgid") } + + describe 'without parameters' do + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('fcgid') } + it { is_expected.to contain_package("mod_fcgid") } + end + + describe 'with parameters' do + let :params do { + :options => { + 'FcgidIPCDir' => '/var/run/fcgidsock', + 'SharememPath' => '/var/run/fcgid_shm', + 'FcgidMinProcessesPerClass' => '0', + 'AddHandler' => 'fcgid-script .fcgi', + } + } end + + it 'should contain the correct config' do + content = subject.resource('file', 'fcgid.conf').send(:parameters)[:content] + expect(content.split("\n").reject { |c| c =~ /(^#|^$)/ }).to eq([ + '', + ' AddHandler fcgid-script .fcgi', + ' FcgidIPCDir /var/run/fcgidsock', + ' FcgidMinProcessesPerClass 0', + ' SharememPath /var/run/fcgid_shm', + '', + ]) + end + end + end + + context "on RHEL7" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '7', + :operatingsystemmajrelease => '7', + :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + + describe 'without parameters' do + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('fcgid').with({ + 'loadfile_name' => 'unixd_fcgid.load' + }) + } + it { is_expected.to contain_package("mod_fcgid") } + end end + context "on a FreeBSD OS" do let :facts do { - :osfamily => 'FreeBSD', - :operatingsystemrelease => '9', - :concat_basedir => '/dne', + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :operatingsystemmajrelease => '9', + :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('fcgid') } - it { should contain_package("www/mod_fcgid") } + + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('fcgid') } + it { is_expected.to contain_package("www/mod_fcgid") } end end diff --git a/apache/spec/classes/mod/info_spec.rb b/apache/spec/classes/mod/info_spec.rb index 21d253e98..20ed127dc 100644 --- a/apache/spec/classes/mod/info_spec.rb +++ b/apache/spec/classes/mod/info_spec.rb @@ -1,99 +1,141 @@ # This function is called inside the OS specific contexts def general_info_specs - it { should contain_apache__mod("info") } + it { is_expected.to contain_apache__mod('info') } - it do - should contain_file("info.conf").with_content( - "\n"\ - " SetHandler server-info\n"\ - " Order deny,allow\n"\ - " Deny from all\n"\ - " Allow from 127.0.0.1 ::1\n"\ - "\n" - ) + context 'passing no parameters' do + it { + is_expected.to contain_file('info.conf').with_content( + "\n"\ + " SetHandler server-info\n"\ + " Order deny,allow\n"\ + " Deny from all\n"\ + " Allow from 127.0.0.1\n"\ + " Allow from ::1\n"\ + "\n" + ) + } + end + context 'passing restrict_access => false' do + let :params do { + :restrict_access => false + } + end + it { + is_expected.to contain_file('info.conf').with_content( + "\n"\ + " SetHandler server-info\n"\ + "\n" + ) + } + end + context "passing allow_from => ['10.10.1.2', '192.168.1.2', '127.0.0.1']" do + let :params do + {:allow_from => ['10.10.1.2', '192.168.1.2', '127.0.0.1']} + end + it { + is_expected.to contain_file('info.conf').with_content( + "\n"\ + " SetHandler server-info\n"\ + " Order deny,allow\n"\ + " Deny from all\n"\ + " Allow from 10.10.1.2\n"\ + " Allow from 192.168.1.2\n"\ + " Allow from 127.0.0.1\n"\ + "\n" + ) + } + end + context 'passing both restrict_access and allow_from' do + let :params do + { + :restrict_access => false, + :allow_from => ['10.10.1.2', '192.168.1.2', '127.0.0.1'] + } + end + it { + is_expected.to contain_file('info.conf').with_content( + "\n"\ + " SetHandler server-info\n"\ + "\n" + ) + } end end describe 'apache::mod::info', :type => :class do let :pre_condition do - 'include apache' + "class { 'apache': default_mods => false, }" end - context "On a Debian OS with default params" do + context 'On a Debian OS' do let :facts do { :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end # Load the more generic tests for this context general_info_specs() - it { should contain_file("info.conf").with({ + it { is_expected.to contain_file('info.conf').with({ :ensure => 'file', :path => '/etc/apache2/mods-available/info.conf', } ) } - it { should contain_file("info.conf symlink").with({ + it { is_expected.to contain_file('info.conf symlink').with({ :ensure => 'link', :path => '/etc/apache2/mods-enabled/info.conf', } ) } end - context "on a RedHat OS with default params" do + context 'on a RedHat OS' do let :facts do { :osfamily => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end # Load the more generic tests for this context general_info_specs() - it { should contain_file("info.conf").with_path("/etc/httpd/conf.d/info.conf") } + it { is_expected.to contain_file('info.conf').with({ + :ensure => 'file', + :path => '/etc/httpd/conf.d/info.conf', + } ) } end - context "On a FreeBSD OS with default params" do + context 'on a FreeBSD OS' do let :facts do { :osfamily => 'FreeBSD', :operatingsystemrelease => '9', :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end # Load the more generic tests for this context general_info_specs() - it { should contain_file("info.conf").with({ + it { is_expected.to contain_file('info.conf').with({ :ensure => 'file', :path => '/usr/local/etc/apache22/Modules/info.conf', } ) } end - context "with $allow_from => ['10.10.10.10','11.11.11.11']" do - let :facts do - { - :osfamily => 'Debian', - :operatingsystemrelease => '6', - :concat_basedir => '/dne', - } - end - let :params do - { :allow_from => ['10.10.10.10','11.11.11.11'] } - end - it do - should contain_file("info.conf").with_content( - "\n"\ - " SetHandler server-info\n"\ - " Order deny,allow\n"\ - " Deny from all\n"\ - " Allow from 10.10.10.10 11.11.11.11\n"\ - "\n" - ) - end - end end diff --git a/apache/spec/classes/mod/itk_spec.rb b/apache/spec/classes/mod/itk_spec.rb index 032e122d4..b5d50a18a 100644 --- a/apache/spec/classes/mod/itk_spec.rb +++ b/apache/spec/classes/mod/itk_spec.rb @@ -1,3 +1,5 @@ +require 'spec_helper' + describe 'apache::mod::itk', :type => :class do let :pre_condition do 'class { "apache": mpm_module => false, }' @@ -8,39 +10,44 @@ :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should_not contain_apache__mod('itk') } - it { should contain_file("/etc/apache2/mods-available/itk.conf").with_ensure('file') } - it { should contain_file("/etc/apache2/mods-enabled/itk.conf").with_ensure('link') } + it { is_expected.to contain_class("apache::params") } + it { is_expected.not_to contain_apache__mod('itk') } + it { is_expected.to contain_file("/etc/apache2/mods-available/itk.conf").with_ensure('file') } + it { is_expected.to contain_file("/etc/apache2/mods-enabled/itk.conf").with_ensure('link') } context "with Apache version < 2.4" do let :params do { - :apache_version => 2.2, + :apache_version => '2.2', } end - it { should_not contain_file("/etc/apache2/mods-available/itk.load") } - it { should_not contain_file("/etc/apache2/mods-enabled/itk.load") } + it { is_expected.not_to contain_file("/etc/apache2/mods-available/itk.load") } + it { is_expected.not_to contain_file("/etc/apache2/mods-enabled/itk.load") } - it { should contain_package("apache2-mpm-itk") } + it { is_expected.to contain_package("apache2-mpm-itk") } end context "with Apache version >= 2.4" do let :params do { - :apache_version => 2.4, + :apache_version => '2.4', } end - it { should contain_file("/etc/apache2/mods-available/itk.load").with({ + it { is_expected.to contain_file("/etc/apache2/mods-available/itk.load").with({ 'ensure' => 'file', 'content' => "LoadModule mpm_itk_module /usr/lib/apache2/modules/mod_mpm_itk.so\n" }) } - it { should contain_file("/etc/apache2/mods-enabled/itk.load").with_ensure('link') } + it { is_expected.to contain_file("/etc/apache2/mods-enabled/itk.load").with_ensure('link') } end end context "on a FreeBSD OS" do @@ -49,10 +56,14 @@ :osfamily => 'FreeBSD', :operatingsystemrelease => '9', :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should_not contain_apache__mod('itk') } - it { should contain_file("/usr/local/etc/apache22/Modules/itk.conf").with_ensure('file') } + it { is_expected.to contain_class("apache::params") } + it { is_expected.not_to contain_apache__mod('itk') } + it { is_expected.to contain_file("/usr/local/etc/apache22/Modules/itk.conf").with_ensure('file') } end end diff --git a/apache/spec/classes/mod/mime_magic_spec.rb b/apache/spec/classes/mod/mime_magic_spec.rb index d10d8345b..5e78230e2 100644 --- a/apache/spec/classes/mod/mime_magic_spec.rb +++ b/apache/spec/classes/mod/mime_magic_spec.rb @@ -1,6 +1,8 @@ +require 'spec_helper' + # This function is called inside the OS specific contexts def general_mime_magic_specs - it { should contain_apache__mod("mime_magic") } + it { is_expected.to contain_apache__mod("mime_magic") } end describe 'apache::mod::mime_magic', :type => :class do @@ -14,22 +16,27 @@ def general_mime_magic_specs :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end general_mime_magic_specs() it do - should contain_file("mime_magic.conf").with_content( + is_expected.to contain_file("mime_magic.conf").with_content( "MIMEMagicFile \"/etc/apache2/magic\"\n" ) end - it { should contain_file("mime_magic.conf").with({ + it { is_expected.to contain_file("mime_magic.conf").with({ :ensure => 'file', :path => '/etc/apache2/mods-available/mime_magic.conf', } ) } - it { should contain_file("mime_magic.conf symlink").with({ + it { is_expected.to contain_file("mime_magic.conf symlink").with({ :ensure => 'link', :path => '/etc/apache2/mods-enabled/mime_magic.conf', } ) } @@ -40,7 +47,7 @@ def general_mime_magic_specs end it do - should contain_file("mime_magic.conf").with_content( + is_expected.to contain_file("mime_magic.conf").with_content( "MIMEMagicFile \"/tmp/Debian_magic\"\n" ) end @@ -54,18 +61,22 @@ def general_mime_magic_specs :osfamily => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end general_mime_magic_specs() it do - should contain_file("mime_magic.conf").with_content( + is_expected.to contain_file("mime_magic.conf").with_content( "MIMEMagicFile \"/etc/httpd/conf/magic\"\n" ) end - it { should contain_file("mime_magic.conf").with_path("/etc/httpd/conf.d/mime_magic.conf") } + it { is_expected.to contain_file("mime_magic.conf").with_path("/etc/httpd/conf.d/mime_magic.conf") } end @@ -75,6 +86,11 @@ def general_mime_magic_specs :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end @@ -83,7 +99,7 @@ def general_mime_magic_specs end it do - should contain_file("mime_magic.conf").with_content( + is_expected.to contain_file("mime_magic.conf").with_content( "MIMEMagicFile \"/tmp/magic\"\n" ) end diff --git a/apache/spec/classes/mod/mime_spec.rb b/apache/spec/classes/mod/mime_spec.rb new file mode 100644 index 000000000..32edbc4b0 --- /dev/null +++ b/apache/spec/classes/mod/mime_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' + +# This function is called inside the OS specific conte, :compilexts +def general_mime_specs + it { is_expected.to contain_apache__mod("mime") } +end + +describe 'apache::mod::mime', :type => :class do + let :pre_condition do + 'include apache' + end + + context "On a Debian OS with default params", :compile do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + + general_mime_specs() + + it { is_expected.to contain_file("mime.conf").with_path('/etc/apache2/mods-available/mime.conf') } + + end + + context "on a RedHat OS with default params", :compile do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + + general_mime_specs() + + it { is_expected.to contain_file("mime.conf").with_path("/etc/httpd/conf.d/mime.conf") } + + end + +end diff --git a/apache/spec/classes/mod/negotiation_spec.rb b/apache/spec/classes/mod/negotiation_spec.rb new file mode 100644 index 000000000..d01442cb9 --- /dev/null +++ b/apache/spec/classes/mod/negotiation_spec.rb @@ -0,0 +1,64 @@ +require 'spec_helper' + +describe 'apache::mod::negotiation', :type => :class do + describe "OS independent tests" do + + let :facts do + { + :osfamily => 'Debian', + :operatingsystem => 'Debian', + :kernel => 'Linux', + :lsbdistcodename => 'squeeze', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + :id => 'root', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + + context "default params" do + let :pre_condition do + 'class {"::apache": }' + end + it { should contain_class("apache") } + it do + should contain_file('negotiation.conf').with( { + :ensure => 'file', + :content => 'LanguagePriority en ca cs da de el eo es et fr he hr it ja ko ltz nl nn no pl pt pt-BR ru sv zh-CN zh-TW +ForceLanguagePriority Prefer Fallback +', + } ) + end + end + + context 'with force_language_priority parameter' do + let :pre_condition do + 'class {"::apache": default_mods => ["negotiation"]}' + end + let :params do + { :force_language_priority => 'Prefer' } + end + it do + should contain_file('negotiation.conf').with( { + :ensure => 'file', + :content => /^ForceLanguagePriority Prefer$/, + } ) + end + end + + context 'with language_priority parameter' do + let :pre_condition do + 'class {"::apache": default_mods => ["negotiation"]}' + end + let :params do + { :language_priority => [ 'en', 'es' ] } + end + it do + should contain_file('negotiation.conf').with( { + :ensure => 'file', + :content => /^LanguagePriority en es$/, + } ) + end + end + end +end diff --git a/apache/spec/classes/mod/pagespeed_spec.rb b/apache/spec/classes/mod/pagespeed_spec.rb new file mode 100644 index 000000000..c4abd3e10 --- /dev/null +++ b/apache/spec/classes/mod/pagespeed_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe 'apache::mod::pagespeed', :type => :class do + let :pre_condition do + 'include apache' + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('pagespeed') } + it { is_expected.to contain_package("mod-pagespeed-stable") } + it { is_expected.to contain_file('pagespeed.conf') } + end + + context "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('pagespeed') } + it { is_expected.to contain_package("mod-pagespeed-stable") } + it { is_expected.to contain_file('pagespeed.conf') } + end +end diff --git a/apache/spec/classes/mod/passenger_spec.rb b/apache/spec/classes/mod/passenger_spec.rb index 37177f477..23154014a 100644 --- a/apache/spec/classes/mod/passenger_spec.rb +++ b/apache/spec/classes/mod/passenger_spec.rb @@ -1,3 +1,5 @@ +require 'spec_helper' + describe 'apache::mod::passenger', :type => :class do let :pre_condition do 'include apache' @@ -7,111 +9,210 @@ { :osfamily => 'Debian', :operatingsystemrelease => '6', + :kernel => 'Linux', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('passenger') } - it { should contain_package("libapache2-mod-passenger") } - it { should contain_file('passenger.conf').with({ + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('passenger') } + it { is_expected.to contain_package("libapache2-mod-passenger") } + it { is_expected.to contain_file('passenger.load').with({ + 'path' => '/etc/apache2/mods-available/passenger.load', + }) } + it { is_expected.to contain_file('passenger.conf').with({ 'path' => '/etc/apache2/mods-available/passenger.conf', }) } - it { should contain_file('passenger.conf').with_content(/^ PassengerRoot "\/usr"$/) } - it { should contain_file('passenger.conf').with_content(/^ PassengerRuby "\/usr\/bin\/ruby"$/) } - describe "with passenger_high_performance => true" do + it { is_expected.to contain_file('passenger_package.conf').with_ensure('absent') } + describe "with passenger_root => '/usr/lib/example'" do let :params do - { :passenger_high_performance => 'true' } + { :passenger_root => '/usr/lib/example' } end - it { should contain_file('passenger.conf').with_content(/^ PassengerHighPerformance true$/) } + it { is_expected.to contain_file('passenger.conf').with_content(%r{PassengerRoot "/usr/lib/example"}) } + end + describe "with passenger_ruby => /usr/lib/example/ruby" do + let :params do + { :passenger_ruby => '/usr/lib/example/ruby' } + end + it { is_expected.to contain_file('passenger.conf').with_content(%r{PassengerRuby "/usr/lib/example/ruby"}) } + end + describe "with passenger_default_ruby => /usr/lib/example/ruby1.9.3" do + let :params do + { :passenger_ruby => '/usr/lib/example/ruby1.9.3' } + end + it { is_expected.to contain_file('passenger.conf').with_content(%r{PassengerRuby "/usr/lib/example/ruby1.9.3"}) } + end + describe "with passenger_high_performance => on" do + let :params do + { :passenger_high_performance => 'on' } + end + it { is_expected.to contain_file('passenger.conf').with_content(/^ PassengerHighPerformance on$/) } end describe "with passenger_pool_idle_time => 1200" do let :params do { :passenger_pool_idle_time => 1200 } end - it { should contain_file('passenger.conf').with_content(/^ PassengerPoolIdleTime 1200$/) } + it { is_expected.to contain_file('passenger.conf').with_content(/^ PassengerPoolIdleTime 1200$/) } end describe "with passenger_max_requests => 20" do let :params do { :passenger_max_requests => 20 } end - it { should contain_file('passenger.conf').with_content(/^ PassengerMaxRequests 20$/) } + it { is_expected.to contain_file('passenger.conf').with_content(/^ PassengerMaxRequests 20$/) } end describe "with passenger_stat_throttle_rate => 10" do let :params do { :passenger_stat_throttle_rate => 10 } end - it { should contain_file('passenger.conf').with_content(/^ PassengerStatThrottleRate 10$/) } + it { is_expected.to contain_file('passenger.conf').with_content(/^ PassengerStatThrottleRate 10$/) } end describe "with passenger_max_pool_size => 16" do let :params do { :passenger_max_pool_size => 16 } end - it { should contain_file('passenger.conf').with_content(/^ PassengerMaxPoolSize 16$/) } + it { is_expected.to contain_file('passenger.conf').with_content(/^ PassengerMaxPoolSize 16$/) } end - describe "with rack_autodetect => true" do + describe "with rack_autodetect => on" do let :params do - { :rack_autodetect => true } + { :rack_autodetect => 'on' } end - it { should contain_file('passenger.conf').with_content(/^ RackAutoDetect true$/) } + it { is_expected.to contain_file('passenger.conf').with_content(/^ RackAutoDetect on$/) } end - describe "with rails_autodetect => true" do + describe "with rails_autodetect => on" do let :params do - { :rails_autodetect => true } + { :rails_autodetect => 'on' } end - it { should contain_file('passenger.conf').with_content(/^ RailsAutoDetect true$/) } + it { is_expected.to contain_file('passenger.conf').with_content(/^ RailsAutoDetect on$/) } end - describe "with passenger_root => '/usr/lib/example'" do + describe "with passenger_use_global_queue => on" do let :params do - { :passenger_root => '/usr/lib/example' } + { :passenger_use_global_queue => 'on' } end - it { should contain_file('passenger.conf').with_content(/^ PassengerRoot "\/usr\/lib\/example"$/) } + it { is_expected.to contain_file('passenger.conf').with_content(/^ PassengerUseGlobalQueue on$/) } end - describe "with passenger_ruby => /user/lib/example/ruby" do + describe "with mod_path => '/usr/lib/foo/mod_foo.so'" do let :params do - { :passenger_ruby => '/user/lib/example/ruby' } + { :mod_path => '/usr/lib/foo/mod_foo.so' } end - it { should contain_file('passenger.conf').with_content(/^ PassengerRuby "\/user\/lib\/example\/ruby"$/) } + it { is_expected.to contain_file('passenger.load').with_content(/^LoadModule passenger_module \/usr\/lib\/foo\/mod_foo\.so$/) } end - describe "with passenger_use_global_queue => true" do + describe "with mod_lib_path => '/usr/lib/foo'" do let :params do - { :passenger_use_global_queue => 'true' } + { :mod_lib_path => '/usr/lib/foo' } end - it { should contain_file('passenger.conf').with_content(/^ PassengerUseGlobalQueue true$/) } + it { is_expected.to contain_file('passenger.load').with_content(/^LoadModule passenger_module \/usr\/lib\/foo\/mod_passenger\.so$/) } + end + describe "with mod_lib => 'mod_foo.so'" do + let :params do + { :mod_lib => 'mod_foo.so' } + end + it { is_expected.to contain_file('passenger.load').with_content(/^LoadModule passenger_module \/usr\/lib\/apache2\/modules\/mod_foo\.so$/) } + end + describe "with mod_id => 'mod_foo'" do + let :params do + { :mod_id => 'mod_foo' } + end + it { is_expected.to contain_file('passenger.load').with_content(/^LoadModule mod_foo \/usr\/lib\/apache2\/modules\/mod_passenger\.so$/) } + end + + context "with Ubuntu 12.04 defaults" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '12.04', + :kernel => 'Linux', + :operatingsystem => 'Ubuntu', + :lsbdistrelease => '12.04', + :concat_basedir => '/dne', + :id => 'root', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + + it { is_expected.to contain_file('passenger.conf').with_content(%r{PassengerRoot "/usr"}) } + it { is_expected.to contain_file('passenger.conf').with_content(%r{PassengerRuby "/usr/bin/ruby"}) } + it { is_expected.to contain_file('passenger.conf').without_content(/PassengerDefaultRuby/) } end + context "with Ubuntu 14.04 defaults" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '14.04', + :operatingsystem => 'Ubuntu', + :kernel => 'Linux', + :lsbdistrelease => '14.04', + :concat_basedir => '/dne', + :id => 'root', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + + it { is_expected.to contain_file('passenger.conf').with_content(%r{PassengerRoot "/usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini"}) } + it { is_expected.to contain_file('passenger.conf').without_content(/PassengerRuby/) } + it { is_expected.to contain_file('passenger.conf').with_content(%r{PassengerDefaultRuby "/usr/bin/ruby"}) } + end + + context "with Debian 7 defaults" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '7.3', + :operatingsystem => 'Debian', + :kernel => 'Linux', + :lsbdistcodename => 'wheezy', + :concat_basedir => '/dne', + :id => 'root', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + + it { is_expected.to contain_file('passenger.conf').with_content(%r{PassengerRoot "/usr"}) } + it { is_expected.to contain_file('passenger.conf').with_content(%r{PassengerRuby "/usr/bin/ruby"}) } + it { is_expected.to contain_file('passenger.conf').without_content(/PassengerDefaultRuby/) } + end end + context "on a RedHat OS" do let :facts do { :osfamily => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('passenger') } - it { should contain_package("mod_passenger") } - it { should contain_file('passenger_package.conf').with({ + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('passenger') } + it { is_expected.to contain_package("mod_passenger") } + it { is_expected.to contain_file('passenger_package.conf').with({ 'path' => '/etc/httpd/conf.d/passenger.conf', }) } - it { should contain_file('passenger_package.conf').without_content } - it { should contain_file('passenger_package.conf').without_source } - it { should contain_file('passenger.conf').with({ - 'path' => '/etc/httpd/conf.d/passenger_extra.conf', + it { is_expected.to contain_file('passenger_package.conf').without_content } + it { is_expected.to contain_file('passenger_package.conf').without_source } + it { is_expected.to contain_file('passenger.load').with({ + 'path' => '/etc/httpd/conf.d/passenger.load', }) } - it { should contain_file('passenger.conf').without_content(/PassengerRoot/) } - it { should contain_file('passenger.conf').without_content(/PassengerRuby/) } + it { is_expected.to contain_file('passenger.conf').without_content(/PassengerRoot/) } + it { is_expected.to contain_file('passenger.conf').without_content(/PassengerRuby/) } describe "with passenger_root => '/usr/lib/example'" do let :params do { :passenger_root => '/usr/lib/example' } end - it { should contain_file('passenger.conf').with_content(/^ PassengerRoot "\/usr\/lib\/example"$/) } + it { is_expected.to contain_file('passenger.conf').with_content(/^ PassengerRoot "\/usr\/lib\/example"$/) } end - describe "with passenger_ruby => /user/lib/example/ruby" do + describe "with passenger_ruby => /usr/lib/example/ruby" do let :params do - { :passenger_ruby => '/user/lib/example/ruby' } + { :passenger_ruby => '/usr/lib/example/ruby' } end - it { should contain_file('passenger.conf').with_content(/^ PassengerRuby "\/user\/lib\/example\/ruby"$/) } + it { is_expected.to contain_file('passenger.conf').with_content(/^ PassengerRuby "\/usr\/lib\/example\/ruby"$/) } end end context "on a FreeBSD OS" do @@ -120,10 +221,14 @@ :osfamily => 'FreeBSD', :operatingsystemrelease => '9', :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('passenger') } - it { should contain_package("www/rubygem-passenger") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('passenger') } + it { is_expected.to contain_package("www/rubygem-passenger") } end end diff --git a/apache/spec/classes/mod/perl_spec.rb b/apache/spec/classes/mod/perl_spec.rb index 3cb7a3e67..2c14c31f0 100644 --- a/apache/spec/classes/mod/perl_spec.rb +++ b/apache/spec/classes/mod/perl_spec.rb @@ -1,3 +1,5 @@ +require 'spec_helper' + describe 'apache::mod::perl', :type => :class do let :pre_condition do 'include apache' @@ -8,11 +10,16 @@ :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('perl') } - it { should contain_package("libapache2-mod-perl2") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('perl') } + it { is_expected.to contain_package("libapache2-mod-perl2") } end context "on a RedHat OS" do let :facts do @@ -20,11 +27,15 @@ :osfamily => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('perl') } - it { should contain_package("mod_perl") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('perl') } + it { is_expected.to contain_package("mod_perl") } end context "on a FreeBSD OS" do let :facts do @@ -32,10 +43,14 @@ :osfamily => 'FreeBSD', :operatingsystemrelease => '9', :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('perl') } - it { should contain_package("www/mod_perl2") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('perl') } + it { is_expected.to contain_package("www/mod_perl2") } end end diff --git a/apache/spec/classes/mod/peruser_spec.rb b/apache/spec/classes/mod/peruser_spec.rb index 062905ccc..c0dfc96f5 100644 --- a/apache/spec/classes/mod/peruser_spec.rb +++ b/apache/spec/classes/mod/peruser_spec.rb @@ -1,3 +1,5 @@ +require 'spec_helper' + describe 'apache::mod::peruser', :type => :class do let :pre_condition do 'class { "apache": mpm_module => false, }' @@ -8,10 +10,14 @@ :osfamily => 'FreeBSD', :operatingsystemrelease => '9', :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should_not contain_apache__mod('peruser') } - it { should contain_file("/usr/local/etc/apache22/Modules/peruser.conf").with_ensure('file') } + it { is_expected.to contain_class("apache::params") } + it { is_expected.not_to contain_apache__mod('peruser') } + it { is_expected.to contain_file("/usr/local/etc/apache22/Modules/peruser.conf").with_ensure('file') } end end diff --git a/apache/spec/classes/mod/php_spec.rb b/apache/spec/classes/mod/php_spec.rb index 57708a398..cf6131879 100644 --- a/apache/spec/classes/mod/php_spec.rb +++ b/apache/spec/classes/mod/php_spec.rb @@ -1,3 +1,5 @@ +require 'spec_helper' + describe 'apache::mod::php', :type => :class do describe "on a Debian OS" do let :facts do @@ -5,26 +7,36 @@ :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end context "with mpm_module => prefork" do let :pre_condition do 'class { "apache": mpm_module => prefork, }' end - it { should contain_class("apache::params") } - it { should contain_apache__mod('php5') } - it { should contain_package("libapache2-mod-php5") } - it { should contain_file("php5.load").with( + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_class("apache::mod::prefork") } + it { is_expected.to contain_apache__mod('php5') } + it { is_expected.to contain_package("libapache2-mod-php5") } + it { is_expected.to contain_file("php5.load").with( :content => "LoadModule php5_module /usr/lib/apache2/modules/libphp5.so\n" ) } end - context 'with mpm_module => worker' do + context "with mpm_module => itk" do let :pre_condition do - 'class { "apache": mpm_module => worker, }' - end - it 'should raise an error' do - expect { subject }.to raise_error Puppet::Error, /mpm_module => 'prefork'/ + 'class { "apache": mpm_module => itk, }' end + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_class("apache::mod::itk") } + it { is_expected.to contain_apache__mod('php5') } + it { is_expected.to contain_package("libapache2-mod-php5") } + it { is_expected.to contain_file("php5.load").with( + :content => "LoadModule php5_module /usr/lib/apache2/modules/libphp5.so\n" + ) } end end describe "on a RedHat OS" do @@ -33,19 +45,49 @@ :osfamily => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end context "with default params" do let :pre_condition do 'class { "apache": }' end - it { should contain_class("apache::params") } - it { should contain_apache__mod('php5') } - it { should contain_package("php") } - it { should contain_file("php5.load").with( + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('php5') } + it { is_expected.to contain_package("php") } + it { is_expected.to contain_file("php5.load").with( :content => "LoadModule php5_module modules/libphp5.so\n" ) } end + context "with alternative package name" do let :pre_condition do + 'class { "apache": }' + end + let :params do + { :package_name => 'php54'} + end + it { is_expected.to contain_package("php54") } + end + context "with alternative path" do let :pre_condition do + 'class { "apache": }' + end + let :params do + { :path => 'alternative-path'} + end + it { is_expected.to contain_file("php5.load").with( + :content => "LoadModule php5_module alternative-path\n" + ) } + end + context "with alternative extensions" do let :pre_condition do + 'class { "apache": }' + end + let :params do + { :extensions => ['.php','.php5']} + end + it { is_expected.to contain_file("php5.conf").with_content(/AddHandler php5-script .php .php5\n/) } + end context "with specific version" do let :pre_condition do 'class { "apache": }' @@ -53,7 +95,7 @@ let :params do { :package_ensure => '5.3.13'} end - it { should contain_package("php").with( + it { is_expected.to contain_package("php").with( :ensure => '5.3.13' ) } end @@ -61,13 +103,22 @@ let :pre_condition do 'class { "apache": mpm_module => prefork, }' end - it { should contain_class("apache::params") } - it { should contain_apache__mod('php5') } - it { should contain_package("php") } - it { should contain_file("php5.load").with( + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_class("apache::mod::prefork") } + it { is_expected.to contain_apache__mod('php5') } + it { is_expected.to contain_package("php") } + it { is_expected.to contain_file("php5.load").with( :content => "LoadModule php5_module modules/libphp5.so\n" ) } end + context "with mpm_module => itk" do + let :pre_condition do + 'class { "apache": mpm_module => itk, }' + end + it 'should raise an error' do + expect { expect(subject).to contain_class("apache::mod::itk") }.to raise_error Puppet::Error, /Unsupported osfamily RedHat/ + end + end end describe "on a FreeBSD OS" do let :facts do @@ -75,24 +126,126 @@ :osfamily => 'FreeBSD', :operatingsystemrelease => '9', :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end context "with mpm_module => prefork" do let :pre_condition do 'class { "apache": mpm_module => prefork, }' end - it { should contain_class('apache::params') } - it { should contain_apache__mod('php5') } - it { should contain_package("lang/php5") } - it { should contain_file('php5.load') } + it { is_expected.to contain_class('apache::params') } + it { is_expected.to contain_apache__mod('php5') } + it { is_expected.to contain_package("lang/php5") } + it { is_expected.to contain_file('php5.load') } + end + context "with mpm_module => itk" do + let :pre_condition do + 'class { "apache": mpm_module => itk, }' + end + it { is_expected.to contain_class('apache::params') } + it { is_expected.to contain_class('apache::mod::itk') } + it { is_expected.to contain_apache__mod('php5') } + it { is_expected.to contain_package("lang/php5") } + it { is_expected.to contain_file('php5.load') } + end + end + describe "OS independent tests" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystem => 'Debian', + :operatingsystemrelease => '6', + :kernel => 'Linux', + :lsbdistcodename => 'squeeze', + :concat_basedir => '/dne', + :id => 'root', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + context 'with content param' do + let :pre_condition do + 'class { "apache": mpm_module => prefork, }' + end + let :params do + { :content => 'somecontent' } + end + it { should contain_file('php5.conf').with( + :content => 'somecontent' + ) } + end + context 'with template param' do + let :pre_condition do + 'class { "apache": mpm_module => prefork, }' + end + let :params do + { :template => 'apache/mod/php5.conf.erb' } + end + it { should contain_file('php5.conf').with( + :content => /^# PHP is an HTML-embedded scripting language which attempts to make it/ + ) } + end + context 'with source param' do + let :pre_condition do + 'class { "apache": mpm_module => prefork, }' + end + let :params do + { :source => 'some-path' } + end + it { should contain_file('php5.conf').with( + :source => 'some-path' + ) } + end + context 'content has priority over template' do + let :pre_condition do + 'class { "apache": mpm_module => prefork, }' + end + let :params do + { + :template => 'apache/mod/php5.conf.erb', + :content => 'somecontent' + } + end + it { should contain_file('php5.conf').with( + :content => 'somecontent' + ) } + end + context 'source has priority over template' do + let :pre_condition do + 'class { "apache": mpm_module => prefork, }' + end + let :params do + { + :template => 'apache/mod/php5.conf.erb', + :source => 'some-path' + } + end + it { should contain_file('php5.conf').with( + :source => 'some-path' + ) } + end + context 'source has priority over content' do + let :pre_condition do + 'class { "apache": mpm_module => prefork, }' + end + let :params do + { + :content => 'somecontent', + :source => 'some-path' + } + end + it { should contain_file('php5.conf').with( + :source => 'some-path' + ) } end - # FIXME: not sure about the following context context 'with mpm_module => worker' do let :pre_condition do 'class { "apache": mpm_module => worker, }' end it 'should raise an error' do - expect { subject.should contain_apache__mod('php5') }.to raise_error Puppet::Error, /mpm_module => 'prefork'/ + expect { expect(subject).to contain_apache__mod('php5') }.to raise_error Puppet::Error, /mpm_module => 'prefork' or mpm_module => 'itk'/ end end end diff --git a/apache/spec/classes/mod/prefork_spec.rb b/apache/spec/classes/mod/prefork_spec.rb index 8eff78e4a..34bca08dc 100644 --- a/apache/spec/classes/mod/prefork_spec.rb +++ b/apache/spec/classes/mod/prefork_spec.rb @@ -1,3 +1,5 @@ +require 'spec_helper' + describe 'apache::mod::prefork', :type => :class do let :pre_condition do 'class { "apache": mpm_module => false, }' @@ -8,39 +10,44 @@ :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should_not contain_apache__mod('prefork') } - it { should contain_file("/etc/apache2/mods-available/prefork.conf").with_ensure('file') } - it { should contain_file("/etc/apache2/mods-enabled/prefork.conf").with_ensure('link') } + it { is_expected.to contain_class("apache::params") } + it { is_expected.not_to contain_apache__mod('prefork') } + it { is_expected.to contain_file("/etc/apache2/mods-available/prefork.conf").with_ensure('file') } + it { is_expected.to contain_file("/etc/apache2/mods-enabled/prefork.conf").with_ensure('link') } context "with Apache version < 2.4" do let :params do { - :apache_version => 2.2, + :apache_version => '2.2', } end - it { should_not contain_file("/etc/apache2/mods-available/prefork.load") } - it { should_not contain_file("/etc/apache2/mods-enabled/prefork.load") } + it { is_expected.not_to contain_file("/etc/apache2/mods-available/prefork.load") } + it { is_expected.not_to contain_file("/etc/apache2/mods-enabled/prefork.load") } - it { should contain_package("apache2-mpm-prefork") } + it { is_expected.to contain_package("apache2-mpm-prefork") } end context "with Apache version >= 2.4" do let :params do { - :apache_version => 2.4, + :apache_version => '2.4', } end - it { should contain_file("/etc/apache2/mods-available/prefork.load").with({ + it { is_expected.to contain_file("/etc/apache2/mods-available/prefork.load").with({ 'ensure' => 'file', 'content' => "LoadModule mpm_prefork_module /usr/lib/apache2/modules/mod_mpm_prefork.so\n" }) } - it { should contain_file("/etc/apache2/mods-enabled/prefork.load").with_ensure('link') } + it { is_expected.to contain_file("/etc/apache2/mods-enabled/prefork.load").with_ensure('link') } end end context "on a RedHat OS" do @@ -49,20 +56,24 @@ :osfamily => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should_not contain_apache__mod('prefork') } - it { should contain_file("/etc/httpd/conf.d/prefork.conf").with_ensure('file') } + it { is_expected.to contain_class("apache::params") } + it { is_expected.not_to contain_apache__mod('prefork') } + it { is_expected.to contain_file("/etc/httpd/conf.d/prefork.conf").with_ensure('file') } context "with Apache version < 2.4" do let :params do { - :apache_version => 2.2, + :apache_version => '2.2', } end - it { should contain_file_line("/etc/sysconfig/httpd prefork enable").with({ + it { is_expected.to contain_file_line("/etc/sysconfig/httpd prefork enable").with({ 'require' => 'Package[httpd]', }) } @@ -71,13 +82,13 @@ context "with Apache version >= 2.4" do let :params do { - :apache_version => 2.4, + :apache_version => '2.4', } end - it { should_not contain_apache__mod('event') } + it { is_expected.not_to contain_apache__mod('event') } - it { should contain_file("/etc/httpd/conf.d/prefork.load").with({ + it { is_expected.to contain_file("/etc/httpd/conf.d/prefork.load").with({ 'ensure' => 'file', 'content' => "LoadModule mpm_prefork_module modules/mod_mpm_prefork.so\n", }) @@ -90,10 +101,14 @@ :osfamily => 'FreeBSD', :operatingsystemrelease => '9', :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should_not contain_apache__mod('prefork') } - it { should contain_file("/usr/local/etc/apache22/Modules/prefork.conf").with_ensure('file') } + it { is_expected.to contain_class("apache::params") } + it { is_expected.not_to contain_apache__mod('prefork') } + it { is_expected.to contain_file("/usr/local/etc/apache22/Modules/prefork.conf").with_ensure('file') } end end diff --git a/apache/spec/classes/mod/proxy_html_spec.rb b/apache/spec/classes/mod/proxy_html_spec.rb index 90be60b0f..81a2bb537 100644 --- a/apache/spec/classes/mod/proxy_html_spec.rb +++ b/apache/spec/classes/mod/proxy_html_spec.rb @@ -1,3 +1,5 @@ +require 'spec_helper' + describe 'apache::mod::proxy_html', :type => :class do let :pre_condition do [ @@ -7,39 +9,77 @@ ] end context "on a Debian OS" do + shared_examples "debian" do |loadfiles| + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('proxy_html').with(:loadfiles => loadfiles) } + it { is_expected.to contain_package("libapache2-mod-proxy-html") } + end let :facts do { - :osfamily => 'Debian', - :operatingsystemrelease => '6', - :concat_basedir => '/dne', + :osfamily => 'Debian', + :concat_basedir => '/dne', + :architecture => 'i386', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + :hardwaremodel => 'i386', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('proxy_html') } - it { should contain_package("libapache2-mod-proxy-html") } + + context "on squeeze" do + let(:facts) { super().merge({ :operatingsystemrelease => '6' }) } + it_behaves_like "debian", ['/usr/lib/libxml2.so.2'] + end + context "on wheezy" do + let(:facts) { super().merge({ :operatingsystemrelease => '7' }) } + context "i386" do + let(:facts) { super().merge({ + :hardwaremodel => 'i686', + :architecture => 'i386' + })} + it_behaves_like "debian", ["/usr/lib/i386-linux-gnu/libxml2.so.2"] + end + context "x64" do + let(:facts) { super().merge({ + :hardwaremodel => 'x86_64', + :architecture => 'amd64' + })} + it_behaves_like "debian", ["/usr/lib/x86_64-linux-gnu/libxml2.so.2"] + end + end end - context "on a RedHat OS" do + context "on a RedHat OS", :compile do let :facts do { :osfamily => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('proxy_html') } - it { should contain_package("mod_proxy_html") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('proxy_html').with(:loadfiles => nil) } + it { is_expected.to contain_package("mod_proxy_html") } end - context "on a FreeBSD OS" do + context "on a FreeBSD OS", :compile do let :facts do { :osfamily => 'FreeBSD', :operatingsystemrelease => '9', :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('proxy_html') } - it { should contain_package("www/mod_proxy_html") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('proxy_html').with(:loadfiles => nil) } + it { is_expected.to contain_package("www/mod_proxy_html") } end end diff --git a/apache/spec/classes/mod/python_spec.rb b/apache/spec/classes/mod/python_spec.rb index 9042d0f1b..17b62d43b 100644 --- a/apache/spec/classes/mod/python_spec.rb +++ b/apache/spec/classes/mod/python_spec.rb @@ -1,3 +1,5 @@ +require 'spec_helper' + describe 'apache::mod::python', :type => :class do let :pre_condition do 'include apache' @@ -8,11 +10,16 @@ :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod("python") } - it { should contain_package("libapache2-mod-python") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod("python") } + it { is_expected.to contain_package("libapache2-mod-python") } end context "on a RedHat OS" do let :facts do @@ -20,11 +27,15 @@ :osfamily => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod("python") } - it { should contain_package("mod_python") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod("python") } + it { is_expected.to contain_package("mod_python") } end context "on a FreeBSD OS" do let :facts do @@ -32,10 +43,14 @@ :osfamily => 'FreeBSD', :operatingsystemrelease => '9', :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod("python") } - it { should contain_package("www/mod_python3") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod("python") } + it { is_expected.to contain_package("www/mod_python3") } end end diff --git a/apache/spec/classes/mod/reqtimeout_spec.rb b/apache/spec/classes/mod/reqtimeout_spec.rb new file mode 100644 index 000000000..07c09b094 --- /dev/null +++ b/apache/spec/classes/mod/reqtimeout_spec.rb @@ -0,0 +1,112 @@ +require 'spec_helper' + +describe 'apache::mod::reqtimeout', :type => :class do + let :pre_condition do + 'class { "apache": + default_mods => false, + }' + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + :lsbdistcodename => 'squeeze', + } + end + context "passing no parameters" do + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('reqtimeout') } + it { is_expected.to contain_file('reqtimeout.conf').with_content(/^RequestReadTimeout header=20-40,minrate=500\nRequestReadTimeout body=10,minrate=500$/) } + end + context "passing timeouts => ['header=20-60,minrate=600', 'body=60,minrate=600']" do + let :params do + {:timeouts => ['header=20-60,minrate=600', 'body=60,minrate=600']} + end + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('reqtimeout') } + it { is_expected.to contain_file('reqtimeout.conf').with_content(/^RequestReadTimeout header=20-60,minrate=600\nRequestReadTimeout body=60,minrate=600$/) } + end + context "passing timeouts => 'header=20-60,minrate=600'" do + let :params do + {:timeouts => 'header=20-60,minrate=600'} + end + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('reqtimeout') } + it { is_expected.to contain_file('reqtimeout.conf').with_content(/^RequestReadTimeout header=20-60,minrate=600$/) } + end + end + context "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + :operatingsystem => 'Redhat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + context "passing no parameters" do + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('reqtimeout') } + it { is_expected.to contain_file('reqtimeout.conf').with_content(/^RequestReadTimeout header=20-40,minrate=500\nRequestReadTimeout body=10,minrate=500$/) } + end + context "passing timeouts => ['header=20-60,minrate=600', 'body=60,minrate=600']" do + let :params do + {:timeouts => ['header=20-60,minrate=600', 'body=60,minrate=600']} + end + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('reqtimeout') } + it { is_expected.to contain_file('reqtimeout.conf').with_content(/^RequestReadTimeout header=20-60,minrate=600\nRequestReadTimeout body=60,minrate=600$/) } + end + context "passing timeouts => 'header=20-60,minrate=600'" do + let :params do + {:timeouts => 'header=20-60,minrate=600'} + end + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('reqtimeout') } + it { is_expected.to contain_file('reqtimeout.conf').with_content(/^RequestReadTimeout header=20-60,minrate=600$/) } + end + end + context "on a FreeBSD OS" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + context "passing no parameters" do + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('reqtimeout') } + it { is_expected.to contain_file('reqtimeout.conf').with_content(/^RequestReadTimeout header=20-40,minrate=500\nRequestReadTimeout body=10,minrate=500$/) } + end + context "passing timeouts => ['header=20-60,minrate=600', 'body=60,minrate=600']" do + let :params do + {:timeouts => ['header=20-60,minrate=600', 'body=60,minrate=600']} + end + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('reqtimeout') } + it { is_expected.to contain_file('reqtimeout.conf').with_content(/^RequestReadTimeout header=20-60,minrate=600\nRequestReadTimeout body=60,minrate=600$/) } + end + context "passing timeouts => 'header=20-60,minrate=600'" do + let :params do + {:timeouts => 'header=20-60,minrate=600'} + end + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('reqtimeout') } + it { is_expected.to contain_file('reqtimeout.conf').with_content(/^RequestReadTimeout header=20-60,minrate=600$/) } + end + end +end diff --git a/apache/spec/classes/mod/rpaf_spec.rb b/apache/spec/classes/mod/rpaf_spec.rb index d9c9015ab..ca3a59484 100644 --- a/apache/spec/classes/mod/rpaf_spec.rb +++ b/apache/spec/classes/mod/rpaf_spec.rb @@ -1,3 +1,5 @@ +require 'spec_helper' + describe 'apache::mod::rpaf', :type => :class do let :pre_condition do [ @@ -10,33 +12,38 @@ :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('rpaf') } - it { should contain_package("libapache2-mod-rpaf") } - it { should contain_file('rpaf.conf').with({ + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('rpaf') } + it { is_expected.to contain_package("libapache2-mod-rpaf") } + it { is_expected.to contain_file('rpaf.conf').with({ 'path' => '/etc/apache2/mods-available/rpaf.conf', }) } - it { should contain_file('rpaf.conf').with_content(/^RPAFenable On$/) } + it { is_expected.to contain_file('rpaf.conf').with_content(/^RPAFenable On$/) } describe "with sethostname => true" do let :params do { :sethostname => 'true' } end - it { should contain_file('rpaf.conf').with_content(/^RPAFsethostname On$/) } + it { is_expected.to contain_file('rpaf.conf').with_content(/^RPAFsethostname On$/) } end describe "with proxy_ips => [ 10.42.17.8, 10.42.18.99 ]" do let :params do { :proxy_ips => [ '10.42.17.8', '10.42.18.99' ] } end - it { should contain_file('rpaf.conf').with_content(/^RPAFproxy_ips 10.42.17.8 10.42.18.99$/) } + it { is_expected.to contain_file('rpaf.conf').with_content(/^RPAFproxy_ips 10.42.17.8 10.42.18.99$/) } end describe "with header => X-Real-IP" do let :params do { :header => 'X-Real-IP' } end - it { should contain_file('rpaf.conf').with_content(/^RPAFheader X-Real-IP$/) } + it { is_expected.to contain_file('rpaf.conf').with_content(/^RPAFheader X-Real-IP$/) } end end context "on a FreeBSD OS" do @@ -45,33 +52,37 @@ :osfamily => 'FreeBSD', :operatingsystemrelease => '9', :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('rpaf') } - it { should contain_package("www/mod_rpaf2") } - it { should contain_file('rpaf.conf').with({ + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__mod('rpaf') } + it { is_expected.to contain_package("www/mod_rpaf2") } + it { is_expected.to contain_file('rpaf.conf').with({ 'path' => '/usr/local/etc/apache22/Modules/rpaf.conf', }) } - it { should contain_file('rpaf.conf').with_content(/^RPAFenable On$/) } + it { is_expected.to contain_file('rpaf.conf').with_content(/^RPAFenable On$/) } describe "with sethostname => true" do let :params do { :sethostname => 'true' } end - it { should contain_file('rpaf.conf').with_content(/^RPAFsethostname On$/) } + it { is_expected.to contain_file('rpaf.conf').with_content(/^RPAFsethostname On$/) } end describe "with proxy_ips => [ 10.42.17.8, 10.42.18.99 ]" do let :params do { :proxy_ips => [ '10.42.17.8', '10.42.18.99' ] } end - it { should contain_file('rpaf.conf').with_content(/^RPAFproxy_ips 10.42.17.8 10.42.18.99$/) } + it { is_expected.to contain_file('rpaf.conf').with_content(/^RPAFproxy_ips 10.42.17.8 10.42.18.99$/) } end describe "with header => X-Real-IP" do let :params do { :header => 'X-Real-IP' } end - it { should contain_file('rpaf.conf').with_content(/^RPAFheader X-Real-IP$/) } + it { is_expected.to contain_file('rpaf.conf').with_content(/^RPAFheader X-Real-IP$/) } end end end diff --git a/apache/spec/classes/mod/shib_spec.rb b/apache/spec/classes/mod/shib_spec.rb new file mode 100644 index 000000000..e515db96d --- /dev/null +++ b/apache/spec/classes/mod/shib_spec.rb @@ -0,0 +1,40 @@ +describe 'apache::mod::shib', :type => :class do + let :pre_condition do + 'include apache' + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + :fqdn => 'test.example.com', + } + end + describe 'with no parameters' do + it { should contain_apache__mod('shib2').with_id('mod_shib') } + end + end + context "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + :fqdn => 'test.example.com', + } + end + describe 'with no parameters' do + it { should contain_apache__mod('shib2').with_id('mod_shib') } + end + end +end \ No newline at end of file diff --git a/apache/spec/classes/mod/speling_spec.rb b/apache/spec/classes/mod/speling_spec.rb new file mode 100644 index 000000000..814e0d672 --- /dev/null +++ b/apache/spec/classes/mod/speling_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe 'apache::mod::speling', :type => :class do + let :pre_condition do + 'include apache' + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + it { is_expected.to contain_apache__mod('speling') } + end + + context "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + it { is_expected.to contain_apache__mod('speling') } + end +end diff --git a/apache/spec/classes/mod/ssl_spec.rb b/apache/spec/classes/mod/ssl_spec.rb index 45005a191..93f04e3ca 100644 --- a/apache/spec/classes/mod/ssl_spec.rb +++ b/apache/spec/classes/mod/ssl_spec.rb @@ -1,3 +1,5 @@ +require 'spec_helper' + describe 'apache::mod::ssl', :type => :class do let :pre_condition do 'include apache' @@ -8,6 +10,10 @@ :osfamily => 'Magic', :operatingsystemrelease => '0', :concat_basedir => '/dne', + :operatingsystem => 'Magic', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end it { expect { subject }.to raise_error(Puppet::Error, /Unsupported osfamily:/) } @@ -19,11 +25,24 @@ :osfamily => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class('apache::params') } - it { should contain_apache__mod('ssl') } - it { should contain_package('mod_ssl') } + it { is_expected.to contain_class('apache::params') } + it { is_expected.to contain_apache__mod('ssl') } + it { is_expected.to contain_package('mod_ssl') } + context 'with a custom package_name parameter' do + let :params do + { :package_name => 'httpd24-mod_ssl' } + end + it { is_expected.to contain_class('apache::params') } + it { is_expected.to contain_apache__mod('ssl') } + it { is_expected.to contain_package('httpd24-mod_ssl') } + it { is_expected.not_to contain_package('mod_ssl') } + end end context 'on a Debian OS' do @@ -32,11 +51,16 @@ :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class('apache::params') } - it { should contain_apache__mod('ssl') } - it { should_not contain_package('libapache2-mod-ssl') } + it { is_expected.to contain_class('apache::params') } + it { is_expected.to contain_apache__mod('ssl') } + it { is_expected.not_to contain_package('libapache2-mod-ssl') } end context 'on a FreeBSD OS' do @@ -45,9 +69,13 @@ :osfamily => 'FreeBSD', :operatingsystemrelease => '9', :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class('apache::params') } - it { should contain_apache__mod('ssl') } + it { is_expected.to contain_class('apache::params') } + it { is_expected.to contain_apache__mod('ssl') } end end diff --git a/apache/spec/classes/mod/status_spec.rb b/apache/spec/classes/mod/status_spec.rb index 0a0658879..adb60861b 100644 --- a/apache/spec/classes/mod/status_spec.rb +++ b/apache/spec/classes/mod/status_spec.rb @@ -3,7 +3,7 @@ # Helper function for testing the contents of `status.conf` def status_conf_spec(allow_from, extended_status) it do - should contain_file("status.conf").with_content( + is_expected.to contain_file("status.conf").with_content( "\n"\ " SetHandler server-status\n"\ " Order deny,allow\n"\ @@ -31,19 +31,24 @@ def status_conf_spec(allow_from, extended_status) :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_apache__mod("status") } + it { is_expected.to contain_apache__mod("status") } status_conf_spec(["127.0.0.1", "::1"], "On") - it { should contain_file("status.conf").with({ + it { is_expected.to contain_file("status.conf").with({ :ensure => 'file', :path => '/etc/apache2/mods-available/status.conf', } ) } - it { should contain_file("status.conf symlink").with({ + it { is_expected.to contain_file("status.conf symlink").with({ :ensure => 'link', :path => '/etc/apache2/mods-enabled/status.conf', } ) } @@ -56,14 +61,18 @@ def status_conf_spec(allow_from, extended_status) :osfamily => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_apache__mod("status") } + it { is_expected.to contain_apache__mod("status") } status_conf_spec(["127.0.0.1", "::1"], "On") - it { should contain_file("status.conf").with_path("/etc/httpd/conf.d/status.conf") } + it { is_expected.to contain_file("status.conf").with_path("/etc/httpd/conf.d/status.conf") } end @@ -73,6 +82,11 @@ def status_conf_spec(allow_from, extended_status) :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end let :params do @@ -92,6 +106,11 @@ def status_conf_spec(allow_from, extended_status) :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end let :params do @@ -99,7 +118,7 @@ def status_conf_spec(allow_from, extended_status) end it 'should expect to succeed array validation' do expect { - should contain_file("status.conf") + is_expected.to contain_file("status.conf") }.not_to raise_error() end end @@ -110,6 +129,10 @@ def status_conf_spec(allow_from, extended_status) :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end let :params do @@ -117,7 +140,7 @@ def status_conf_spec(allow_from, extended_status) end it 'should expect to fail array validation' do expect { - should contain_file("status.conf") + is_expected.to contain_file("status.conf") }.to raise_error(Puppet::Error) end end @@ -130,6 +153,11 @@ def status_conf_spec(allow_from, extended_status) :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end let :params do @@ -137,7 +165,7 @@ def status_conf_spec(allow_from, extended_status) end it 'should expect to succeed regular expression validation' do expect { - should contain_file("status.conf") + is_expected.to contain_file("status.conf") }.not_to raise_error() end end @@ -150,6 +178,10 @@ def status_conf_spec(allow_from, extended_status) :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end let :params do @@ -157,7 +189,7 @@ def status_conf_spec(allow_from, extended_status) end it 'should expect to fail regular expression validation' do expect { - should contain_file("status.conf") + is_expected.to contain_file("status.conf") }.to raise_error(Puppet::Error) end end diff --git a/apache/spec/classes/mod/suphp_spec.rb b/apache/spec/classes/mod/suphp_spec.rb index 382314d9a..b74b4c864 100644 --- a/apache/spec/classes/mod/suphp_spec.rb +++ b/apache/spec/classes/mod/suphp_spec.rb @@ -1,3 +1,5 @@ +require 'spec_helper' + describe 'apache::mod::suphp', :type => :class do let :pre_condition do 'include apache' @@ -8,10 +10,15 @@ :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_package("libapache2-mod-suphp") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_package("libapache2-mod-suphp") } end context "on a RedHat OS" do let :facts do @@ -19,9 +26,13 @@ :osfamily => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_package("mod_suphp") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_package("mod_suphp") } end end diff --git a/apache/spec/classes/mod/worker_spec.rb b/apache/spec/classes/mod/worker_spec.rb index 504018e68..5902c2c7e 100644 --- a/apache/spec/classes/mod/worker_spec.rb +++ b/apache/spec/classes/mod/worker_spec.rb @@ -1,3 +1,5 @@ +require 'spec_helper' + describe 'apache::mod::worker', :type => :class do let :pre_condition do 'class { "apache": mpm_module => false, }' @@ -8,39 +10,44 @@ :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should_not contain_apache__mod('worker') } - it { should contain_file("/etc/apache2/mods-available/worker.conf").with_ensure('file') } - it { should contain_file("/etc/apache2/mods-enabled/worker.conf").with_ensure('link') } + it { is_expected.to contain_class("apache::params") } + it { is_expected.not_to contain_apache__mod('worker') } + it { is_expected.to contain_file("/etc/apache2/mods-available/worker.conf").with_ensure('file') } + it { is_expected.to contain_file("/etc/apache2/mods-enabled/worker.conf").with_ensure('link') } context "with Apache version < 2.4" do let :params do { - :apache_version => 2.2, + :apache_version => '2.2', } end - it { should_not contain_file("/etc/apache2/mods-available/worker.load") } - it { should_not contain_file("/etc/apache2/mods-enabled/worker.load") } + it { is_expected.not_to contain_file("/etc/apache2/mods-available/worker.load") } + it { is_expected.not_to contain_file("/etc/apache2/mods-enabled/worker.load") } - it { should contain_package("apache2-mpm-worker") } + it { is_expected.to contain_package("apache2-mpm-worker") } end context "with Apache version >= 2.4" do let :params do { - :apache_version => 2.4, + :apache_version => '2.4', } end - it { should contain_file("/etc/apache2/mods-available/worker.load").with({ + it { is_expected.to contain_file("/etc/apache2/mods-available/worker.load").with({ 'ensure' => 'file', 'content' => "LoadModule mpm_worker_module /usr/lib/apache2/modules/mod_mpm_worker.so\n" }) } - it { should contain_file("/etc/apache2/mods-enabled/worker.load").with_ensure('link') } + it { is_expected.to contain_file("/etc/apache2/mods-enabled/worker.load").with_ensure('link') } end end context "on a RedHat OS" do @@ -49,20 +56,24 @@ :osfamily => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should_not contain_apache__mod('worker') } - it { should contain_file("/etc/httpd/conf.d/worker.conf").with_ensure('file') } + it { is_expected.to contain_class("apache::params") } + it { is_expected.not_to contain_apache__mod('worker') } + it { is_expected.to contain_file("/etc/httpd/conf.d/worker.conf").with_ensure('file') } context "with Apache version < 2.4" do let :params do { - :apache_version => 2.2, + :apache_version => '2.2', } end - it { should contain_file_line("/etc/sysconfig/httpd worker enable").with({ + it { is_expected.to contain_file_line("/etc/sysconfig/httpd worker enable").with({ 'require' => 'Package[httpd]', }) } @@ -71,13 +82,13 @@ context "with Apache version >= 2.4" do let :params do { - :apache_version => 2.4, + :apache_version => '2.4', } end - it { should_not contain_apache__mod('event') } + it { is_expected.not_to contain_apache__mod('event') } - it { should contain_file("/etc/httpd/conf.d/worker.load").with({ + it { is_expected.to contain_file("/etc/httpd/conf.d/worker.load").with({ 'ensure' => 'file', 'content' => "LoadModule mpm_worker_module modules/mod_mpm_worker.so\n", }) @@ -90,10 +101,65 @@ :osfamily => 'FreeBSD', :operatingsystemrelease => '9', :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + it { is_expected.to contain_class("apache::params") } + it { is_expected.not_to contain_apache__mod('worker') } + it { is_expected.to contain_file("/usr/local/etc/apache22/Modules/worker.conf").with_ensure('file') } + end + + # Template config doesn't vary by distro + context "on all distros" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystem => 'CentOS', + :operatingsystemrelease => '6', + :kernel => 'Linux', + :id => 'root', + :concat_basedir => '/dne', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should_not contain_apache__mod('worker') } - it { should contain_file("/usr/local/etc/apache22/Modules/worker.conf").with_ensure('file') } + + context 'defaults' do + it { should contain_file('/etc/httpd/conf.d/worker.conf').with(:content => /^$/) } + it { should contain_file('/etc/httpd/conf.d/worker.conf').with(:content => /^\s+ServerLimit\s+25$/) } + it { should contain_file('/etc/httpd/conf.d/worker.conf').with(:content => /^\s+StartServers\s+2$/) } + it { should contain_file('/etc/httpd/conf.d/worker.conf').with(:content => /^\s+MaxClients\s+150$/) } + it { should contain_file('/etc/httpd/conf.d/worker.conf').with(:content => /^\s+MinSpareThreads\s+25$/) } + it { should contain_file('/etc/httpd/conf.d/worker.conf').with(:content => /^\s+MaxSpareThreads\s+75$/) } + it { should contain_file('/etc/httpd/conf.d/worker.conf').with(:content => /^\s+ThreadsPerChild\s+25$/) } + it { should contain_file('/etc/httpd/conf.d/worker.conf').with(:content => /^\s+MaxRequestsPerChild\s+0$/) } + it { should contain_file('/etc/httpd/conf.d/worker.conf').with(:content => /^\s+ThreadLimit\s+64$/) } + end + + context 'setting params' do + let :params do + { + :serverlimit => 10, + :startservers => 11, + :maxclients => 12, + :minsparethreads => 13, + :maxsparethreads => 14, + :threadsperchild => 15, + :maxrequestsperchild => 16, + :threadlimit => 17 + } + end + it { should contain_file('/etc/httpd/conf.d/worker.conf').with(:content => /^$/) } + it { should contain_file('/etc/httpd/conf.d/worker.conf').with(:content => /^\s+ServerLimit\s+10$/) } + it { should contain_file('/etc/httpd/conf.d/worker.conf').with(:content => /^\s+StartServers\s+11$/) } + it { should contain_file('/etc/httpd/conf.d/worker.conf').with(:content => /^\s+MaxClients\s+12$/) } + it { should contain_file('/etc/httpd/conf.d/worker.conf').with(:content => /^\s+MinSpareThreads\s+13$/) } + it { should contain_file('/etc/httpd/conf.d/worker.conf').with(:content => /^\s+MaxSpareThreads\s+14$/) } + it { should contain_file('/etc/httpd/conf.d/worker.conf').with(:content => /^\s+ThreadsPerChild\s+15$/) } + it { should contain_file('/etc/httpd/conf.d/worker.conf').with(:content => /^\s+MaxRequestsPerChild\s+16$/) } + it { should contain_file('/etc/httpd/conf.d/worker.conf').with(:content => /^\s+ThreadLimit\s+17$/) } + end end end diff --git a/apache/spec/classes/mod/wsgi_spec.rb b/apache/spec/classes/mod/wsgi_spec.rb index 44917cb88..3875d3fd0 100644 --- a/apache/spec/classes/mod/wsgi_spec.rb +++ b/apache/spec/classes/mod/wsgi_spec.rb @@ -1,3 +1,5 @@ +require 'spec_helper' + describe 'apache::mod::wsgi', :type => :class do let :pre_condition do 'include apache' @@ -8,11 +10,19 @@ :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('wsgi') } - it { should contain_package("libapache2-mod-wsgi") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_class('apache::mod::wsgi').with( + 'wsgi_socket_prefix' => nil + ) + } + it { is_expected.to contain_package("libapache2-mod-wsgi") } end context "on a RedHat OS" do let :facts do @@ -20,23 +30,30 @@ :osfamily => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('wsgi') } - it { should contain_package("mod_wsgi") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_class('apache::mod::wsgi').with( + 'wsgi_socket_prefix' => '/var/run/wsgi' + ) + } + it { is_expected.to contain_package("mod_wsgi") } describe "with custom WSGISocketPrefix" do let :params do { :wsgi_socket_prefix => 'run/wsgi' } end - it {should contain_file('wsgi.conf').with_content(/^ WSGISocketPrefix run\/wsgi$/)} + it {is_expected.to contain_file('wsgi.conf').with_content(/^ WSGISocketPrefix run\/wsgi$/)} end describe "with custom WSGIPythonHome" do let :params do { :wsgi_python_home => '/path/to/virtenv' } end - it {should contain_file('wsgi.conf').with_content(/^ WSGIPythonHome "\/path\/to\/virtenv"$/)} + it {is_expected.to contain_file('wsgi.conf').with_content(/^ WSGIPythonHome "\/path\/to\/virtenv"$/)} end end context "on a FreeBSD OS" do @@ -45,10 +62,17 @@ :osfamily => 'FreeBSD', :operatingsystemrelease => '9', :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_class("apache::params") } - it { should contain_apache__mod('wsgi') } - it { should contain_package("www/mod_wsgi") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_class('apache::mod::wsgi').with( + 'wsgi_socket_prefix' => nil + ) + } + it { is_expected.to contain_package("www/mod_wsgi") } end end diff --git a/apache/spec/classes/params_spec.rb b/apache/spec/classes/params_spec.rb index de1108af0..6f63758a3 100644 --- a/apache/spec/classes/params_spec.rb +++ b/apache/spec/classes/params_spec.rb @@ -7,15 +7,20 @@ :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_apache__params } + it { is_expected.to contain_apache__params } # There are 4 resources in this class currently # there should not be any more resources because it is a params class # The resources are class[apache::version], class[apache::params], class[main], class[settings], stage[main] it "Should not contain any resources" do - subject.resources.size.should == 5 + expect(subject.resources.size).to eq(5) end end end diff --git a/apache/spec/classes/service_spec.rb b/apache/spec/classes/service_spec.rb index accc54946..4d6efbe3f 100644 --- a/apache/spec/classes/service_spec.rb +++ b/apache/spec/classes/service_spec.rb @@ -10,9 +10,14 @@ :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_service("httpd").with( + it { is_expected.to contain_service("httpd").with( 'name' => 'apache2', 'ensure' => 'running', 'enable' => 'true' @@ -21,7 +26,7 @@ context "with $service_name => 'foo'" do let (:params) {{ :service_name => 'foo' }} - it { should contain_service("httpd").with( + it { is_expected.to contain_service("httpd").with( 'name' => 'foo' ) } @@ -29,7 +34,7 @@ context "with $service_enable => true" do let (:params) {{ :service_enable => true }} - it { should contain_service("httpd").with( + it { is_expected.to contain_service("httpd").with( 'name' => 'apache2', 'ensure' => 'running', 'enable' => 'true' @@ -39,7 +44,7 @@ context "with $service_enable => false" do let (:params) {{ :service_enable => false }} - it { should contain_service("httpd").with( + it { is_expected.to contain_service("httpd").with( 'name' => 'apache2', 'ensure' => 'running', 'enable' => 'false' @@ -57,7 +62,7 @@ context "with $service_ensure => 'running'" do let (:params) {{ :service_ensure => 'running', }} - it { should contain_service("httpd").with( + it { is_expected.to contain_service("httpd").with( 'ensure' => 'running', 'enable' => 'true' ) @@ -66,12 +71,17 @@ context "with $service_ensure => 'stopped'" do let (:params) {{ :service_ensure => 'stopped', }} - it { should contain_service("httpd").with( + it { is_expected.to contain_service("httpd").with( 'ensure' => 'stopped', 'enable' => 'true' ) } end + + context "with $service_ensure => 'UNDEF'" do + let (:params) {{ :service_ensure => 'UNDEF' }} + it { is_expected.to contain_service("httpd").without_ensure } + end end @@ -81,9 +91,13 @@ :osfamily => 'RedHat', :operatingsystemrelease => '5', :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_service("httpd").with( + it { is_expected.to contain_service("httpd").with( 'name' => 'httpd', 'ensure' => 'running', 'enable' => 'true' @@ -97,9 +111,13 @@ :osfamily => 'FreeBSD', :operatingsystemrelease => '9', :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end - it { should contain_service("httpd").with( + it { is_expected.to contain_service("httpd").with( 'name' => 'apache22', 'ensure' => 'running', 'enable' => 'true' diff --git a/apache/spec/defines/balancermember_spec.rb b/apache/spec/defines/balancermember_spec.rb new file mode 100644 index 000000000..b7293b390 --- /dev/null +++ b/apache/spec/defines/balancermember_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe 'apache::balancermember', :type => :define do + let :pre_condition do + 'include apache + apache::balancer {"balancer":} + apache::balancer {"balancer-external":} + apache::balancermember {"http://127.0.0.1:8080-external": url => "http://127.0.0.1:8080/", balancer_cluster => "balancer-external"} + ' + end + let :title do + 'http://127.0.0.1:8080/' + end + let :params do + { + :options => [], + :url => 'http://127.0.0.1:8080/', + :balancer_cluster => 'balancer-internal' + } + end + let :facts do + { + :osfamily => 'Debian', + :operatingsystem => 'Debian', + :operatingsystemrelease => '6', + :lsbdistcodename => 'squeeze', + :id => 'root', + :concat_basedir => '/dne', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + :kernel => 'Linux', + } + end + describe "allows multiple balancermembers with the same url" do + it { should contain_concat__fragment('BalancerMember http://127.0.0.1:8080/') } + end +end diff --git a/apache/spec/defines/custom_config_spec.rb b/apache/spec/defines/custom_config_spec.rb new file mode 100644 index 000000000..187b8a7b5 --- /dev/null +++ b/apache/spec/defines/custom_config_spec.rb @@ -0,0 +1,137 @@ +require 'spec_helper' + +describe 'apache::custom_config', :type => :define do + let :pre_condition do + 'class { "apache": }' + end + let :title do + 'rspec' + end + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + context 'defaults with content' do + let :params do + { + 'content' => '# Test', + } + end + it { is_expected.to contain_exec("service notify for rspec").with({ + 'refreshonly' => 'true', + 'subscribe' => 'File[apache_rspec]', + 'command' => '/usr/sbin/apachectl -t', + 'notify' => 'Service[httpd]', + 'before' => 'Exec[remove rspec if invalid]', + }) + } + it { is_expected.to contain_exec("remove rspec if invalid").with({ + 'unless' => '/usr/sbin/apachectl -t', + 'subscribe' => 'File[apache_rspec]', + 'refreshonly' => 'true', + }) + } + it { is_expected.to contain_file("apache_rspec").with({ + 'ensure' => 'present', + 'content' => '# Test', + 'require' => 'Package[httpd]', + }) + } + end + context 'set everything with source' do + let :params do + { + 'confdir' => '/dne', + 'priority' => '30', + 'source' => 'puppet:///modules/apache/test', + 'verify_command' => '/bin/true', + } + end + it { is_expected.to contain_exec("service notify for rspec").with({ + 'command' => '/bin/true', + }) + } + it { is_expected.to contain_exec("remove rspec if invalid").with({ + 'command' => '/bin/rm /dne/30-rspec.conf', + 'unless' => '/bin/true', + }) + } + it { is_expected.to contain_file("apache_rspec").with({ + 'path' => '/dne/30-rspec.conf', + 'ensure' => 'present', + 'source' => 'puppet:///modules/apache/test', + 'require' => 'Package[httpd]', + }) + } + end + context 'verify_config => false' do + let :params do + { + 'content' => '# test', + 'verify_config' => false, + } + end + it { is_expected.to_not contain_exec('service notify for rspec') } + it { is_expected.to_not contain_exec('remove rspec if invalid') } + it { is_expected.to contain_file('apache_rspec').with({ + 'notify' => 'Service[httpd]' + }) + } + end + context 'ensure => absent' do + let :params do + { + 'ensure' => 'absent' + } + end + it { is_expected.to_not contain_exec('service notify for rspec') } + it { is_expected.to_not contain_exec('remove rspec if invalid') } + it { is_expected.to contain_file('apache_rspec').with({ + 'ensure' => 'absent', + }) + } + end + describe 'validation' do + context 'both content and source' do + let :params do + { + 'content' => 'foo', + 'source' => 'bar', + } + end + it do + expect { + should compile + }.to raise_error(Puppet::Error, /Only one of \$content and \$source can be specified\./) + end + end + context 'neither content nor source' do + it do + expect { + should compile + }.to raise_error(Puppet::Error, /One of \$content and \$source must be specified\./) + end + end + context 'bad ensure' do + let :params do + { + 'content' => 'foo', + 'ensure' => 'foo', + } + end + it do + expect { + should compile + }.to raise_error(Puppet::Error, /is not supported for ensure/) + end + end + end +end diff --git a/apache/spec/defines/fastcgi_server_spec.rb b/apache/spec/defines/fastcgi_server_spec.rb new file mode 100644 index 000000000..1dc8fd444 --- /dev/null +++ b/apache/spec/defines/fastcgi_server_spec.rb @@ -0,0 +1,108 @@ +require 'spec_helper' + +describe 'apache::fastcgi::server', :type => :define do + let :pre_condition do + 'include apache' + end + let :title do + 'www' + end + describe 'os-dependent items' do + context "on RedHat based systems" do + let :default_facts do + { + :osfamily => 'RedHat', + :operatingsystem => 'CentOS', + :operatingsystemrelease => '6', + :kernel => 'Linux', + :id => 'root', + :concat_basedir => '/dne', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + let :facts do default_facts end + it { should contain_class("apache") } + it { should contain_class("apache::mod::fastcgi") } + it { should contain_file("fastcgi-pool-#{title}.conf").with( + :ensure => 'present', + :path => "/etc/httpd/conf.d/fastcgi-pool-#{title}.conf" + ) } + end + context "on Debian based systems" do + let :default_facts do + { + :osfamily => 'Debian', + :operatingsystem => 'Debian', + :operatingsystemrelease => '6', + :lsbdistcodename => 'squeeze', + :kernel => 'Linux', + :id => 'root', + :concat_basedir => '/dne', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + let :facts do default_facts end + it { should contain_class("apache") } + it { should contain_class("apache::mod::fastcgi") } + it { should contain_file("fastcgi-pool-#{title}.conf").with( + :ensure => 'present', + :path => "/etc/apache2/conf.d/fastcgi-pool-#{title}.conf" + ) } + end + context "on FreeBSD systems" do + let :default_facts do + { + :osfamily => 'FreeBSD', + :operatingsystem => 'FreeBSD', + :operatingsystemrelease => '9', + :kernel => 'FreeBSD', + :id => 'root', + :concat_basedir => '/dne', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + let :facts do default_facts end + it { should contain_class("apache") } + it { should contain_class("apache::mod::fastcgi") } + it { should contain_file("fastcgi-pool-#{title}.conf").with( + :ensure => 'present', + :path => "/usr/local/etc/apache22/Includes/fastcgi-pool-#{title}.conf" + ) } + end + end + describe 'os-independent items' do + let :facts do + { + :osfamily => 'Debian', + :operatingsystem => 'Debian', + :operatingsystemrelease => '6', + :lsbdistcodename => 'squeeze', + :kernel => 'Linux', + :id => 'root', + :concat_basedir => '/dne', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + describe ".conf content" do + let :params do + { + :host => '127.0.0.1:9001', + :timeout => 30, + :flush => true, + :faux_path => '/var/www/php-www.fcgi', + :fcgi_alias => '/php-www.fcgi', + :file_type => 'application/x-httpd-php' + } + end + let :expected do +'FastCGIExternalServer /var/www/php-www.fcgi -idle-timeout 30 -flush -host 127.0.0.1:9001 +Alias /php-www.fcgi /var/www/php-www.fcgi +Action application/x-httpd-php /php-www.fcgi +' + end + it do + should contain_file("fastcgi-pool-www.conf").with_content(expected) + end + end + end +end diff --git a/apache/spec/defines/mod_spec.rb b/apache/spec/defines/mod_spec.rb index bbc5f0bdc..377c87792 100644 --- a/apache/spec/defines/mod_spec.rb +++ b/apache/spec/defines/mod_spec.rb @@ -10,6 +10,10 @@ :osfamily => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end @@ -17,9 +21,9 @@ let :title do 'spec_m' end - it { should contain_class("apache::params") } + it { is_expected.to contain_class("apache::params") } it "should manage the module load file" do - should contain_file('spec_m.load').with({ + is_expected.to contain_file('spec_m.load').with({ :path => '/etc/httpd/conf.d/spec_m.load', :content => "LoadModule spec_m_module modules/mod_spec_m.so\n", :owner => 'root', @@ -37,8 +41,8 @@ # parameters let(:params) { {:package => 'mod_xsendfile'} } - it { should contain_class("apache::params") } - it { should contain_package('mod_xsendfile') } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_package('mod_xsendfile') } end end @@ -48,6 +52,11 @@ :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end @@ -55,9 +64,9 @@ let :title do 'spec_m' end - it { should contain_class("apache::params") } + it { is_expected.to contain_class("apache::params") } it "should manage the module load file" do - should contain_file('spec_m.load').with({ + is_expected.to contain_file('spec_m.load').with({ :path => '/etc/apache2/mods-available/spec_m.load', :content => "LoadModule spec_m_module /usr/lib/apache2/modules/mod_spec_m.so\n", :owner => 'root', @@ -66,7 +75,7 @@ } ) end it "should link the module load file" do - should contain_file('spec_m.load symlink').with({ + is_expected.to contain_file('spec_m.load symlink').with({ :path => '/etc/apache2/mods-enabled/spec_m.load', :target => '/etc/apache2/mods-available/spec_m.load', :owner => 'root', @@ -83,6 +92,10 @@ :osfamily => 'FreeBSD', :operatingsystemrelease => '9', :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end @@ -90,9 +103,9 @@ let :title do 'spec_m' end - it { should contain_class("apache::params") } + it { is_expected.to contain_class("apache::params") } it "should manage the module load file" do - should contain_file('spec_m.load').with({ + is_expected.to contain_file('spec_m.load').with({ :path => '/usr/local/etc/apache22/Modules/spec_m.load', :content => "LoadModule spec_m_module /usr/local/libexec/apache22/mod_spec_m.so\n", :owner => 'root', diff --git a/apache/spec/defines/vhost_spec.rb b/apache/spec/defines/vhost_spec.rb index 46e6fd395..84a95bf44 100644 --- a/apache/spec/defines/vhost_spec.rb +++ b/apache/spec/defines/vhost_spec.rb @@ -2,7 +2,7 @@ describe 'apache::vhost', :type => :define do let :pre_condition do - 'class { "apache": default_vhost => false, }' + 'class { "apache": default_vhost => false, default_mods => false, }' end let :title do 'rspec.example.com' @@ -20,12 +20,16 @@ :osfamily => 'RedHat', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end let :params do default_params end let :facts do default_facts end - it { should contain_class("apache") } - it { should contain_class("apache::params") } + it { is_expected.to contain_class("apache") } + it { is_expected.to contain_class("apache::params") } end context "on Debian based systems" do let :default_facts do @@ -33,17 +37,22 @@ :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end let :params do default_params end let :facts do default_facts end - it { should contain_class("apache") } - it { should contain_class("apache::params") } - it { should contain_file("25-rspec.example.com.conf").with( + it { is_expected.to contain_class("apache") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_file("25-rspec.example.com.conf").with( :ensure => 'present', :path => '/etc/apache2/sites-available/25-rspec.example.com.conf' ) } - it { should contain_file("25-rspec.example.com.conf symlink").with( + it { is_expected.to contain_file("25-rspec.example.com.conf symlink").with( :ensure => 'link', :path => '/etc/apache2/sites-enabled/25-rspec.example.com.conf', :target => '/etc/apache2/sites-available/25-rspec.example.com.conf' @@ -55,13 +64,17 @@ :osfamily => 'FreeBSD', :operatingsystemrelease => '9', :concat_basedir => '/dne', + :operatingsystem => 'FreeBSD', + :id => 'root', + :kernel => 'FreeBSD', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end let :params do default_params end let :facts do default_facts end - it { should contain_class("apache") } - it { should contain_class("apache::params") } - it { should contain_file("25-rspec.example.com.conf").with( + it { is_expected.to contain_class("apache") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_file("25-rspec.example.com.conf").with( :ensure => 'present', :path => '/usr/local/etc/apache22/Vhosts/25-rspec.example.com.conf' ) } @@ -73,1306 +86,538 @@ :osfamily => 'Debian', :operatingsystemrelease => '6', :concat_basedir => '/dne', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', } end describe 'basic assumptions' do let :params do default_params end - it { should contain_class("apache") } - it { should contain_class("apache::params") } - it { should contain_apache__listen(params[:port]) } - it { should contain_apache__namevirtualhost("*:#{params[:port]}") } + it { is_expected.to contain_class("apache") } + it { is_expected.to contain_class("apache::params") } + it { is_expected.to contain_apache__listen(params[:port]) } + it { is_expected.to contain_apache__namevirtualhost("*:#{params[:port]}") } end - - # All match and notmatch should be a list of regexs and exact match strings - context ".conf content" do - [ - { - :title => 'should contain docroot', - :attr => 'docroot', - :value => '/not/default', - :match => [/^ DocumentRoot "\/not\/default"$/,/ /], - }, - { - :title => 'should set a port', - :attr => 'port', - :value => '8080', - :match => [/^$/], - }, - { - :title => 'should set an ip', - :attr => 'ip', - :value => '10.0.0.1', - :match => [/^$/], - }, - { - :title => 'should set a serveradmin', - :attr => 'serveradmin', - :value => 'test@test.com', - :match => [/^ ServerAdmin test@test.com$/], - }, - { - :title => 'should enable ssl', - :attr => 'ssl', - :value => true, - :match => [/^ SSLEngine on$/], - }, - { - :title => 'should set a servername', - :attr => 'servername', - :value => 'param.test', - :match => [/^ ServerName param.test$/], - }, - { - :title => 'should accept server aliases', - :attr => 'serveraliases', - :value => ['one.com','two.com'], - :match => [ - /^ ServerAlias one\.com$/, - /^ ServerAlias two\.com$/ - ], - }, - { - :title => 'should accept setenv', - :attr => 'setenv', - :value => ['TEST1 one','TEST2 two'], - :match => [ - /^ SetEnv TEST1 one$/, - /^ SetEnv TEST2 two$/ - ], - }, - { - :title => 'should accept setenvif', - :attr => 'setenvif', - ## These are bugged in rspec-puppet; the $1 is droped - #:value => ['Host "^([^\.]*)\.website\.com$" CLIENT_NAME=$1'], - #:match => [' SetEnvIf Host "^([^\.]*)\.website\.com$" CLIENT_NAME=$1'], - :value => ['Host "^test\.com$" VHOST_ACCESS=test'], - :match => [/^ SetEnvIf Host "\^test\\.com\$" VHOST_ACCESS=test$/], - }, - { - :title => 'should accept options', - :attr => 'options', - :value => ['Fake','Options'], - :match => [/^ Options Fake Options$/], - }, - { - :title => 'should accept overrides', - :attr => 'override', - :value => ['Fake', 'Override'], - :match => [/^ AllowOverride Fake Override$/], - }, - { - :title => 'should accept logroot', - :attr => 'logroot', - :value => '/fake/log', - :match => [/CustomLog "\/fake\/log\//,/ErrorLog "\/fake\/log\//], - }, - { - :title => 'should accept log_level', - :attr => 'log_level', - :value => 'info', - :match => [/LogLevel info/], - }, - { - :title => 'should accept pipe destination for access log', - :attr => 'access_log_pipe', - :value => '| /bin/fake/logging', - :match => [/CustomLog "| \/bin\/fake\/logging" combined$/], - }, - { - :title => 'should accept pipe destination for error log', - :attr => 'error_log_pipe', - :value => '| /bin/fake/logging', - :match => [/ErrorLog "| \/bin\/fake\/logging" combined$/], - }, - { - :title => 'should accept syslog destination for access log', - :attr => 'access_log_syslog', - :value => 'syslog:local1', - :match => [/CustomLog "syslog:local1" combined$/], - }, - { - :title => 'should accept syslog destination for error log', - :attr => 'error_log_syslog', - :value => 'syslog', - :match => [/ErrorLog "syslog"$/], - }, - { - :title => 'should accept custom format for access logs', - :attr => 'access_log_format', - :value => '%h %{X-Forwarded-For}i %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-agent}i\" \"Host: %{Host}i\" %T %D', - :match => [/CustomLog "\/var\/log\/.+_access\.log" "%h %\{X-Forwarded-For\}i %l %u %t \\"%r\\" %s %b \\"%\{Referer\}i\\" \\"%\{User-agent\}i\\" \\"Host: %\{Host\}i\\" %T %D"$/], - }, - { - :title => 'should contain access logs', - :attr => 'access_log', - :value => true, - :match => [/CustomLog "\/var\/log\/.+_access\.log" combined$/], - }, - { - :title => 'should not contain access logs', - :attr => 'access_log', - :value => false, - :notmatch => [/CustomLog "\/var\/log\/.+_access\.log" combined$/], - }, - { - :title => 'should contain error logs', - :attr => 'error_log', - :value => true, - :match => [/ErrorLog.+$/], - }, - { - :title => 'should not contain error logs', - :attr => 'error_log', - :value => false, - :notmatch => [/ErrorLog.+$/], - }, - { - :title => 'should set ErrorDocument 503', - :attr => 'error_documents', - :value => [ { 'error_code' => '503', 'document' => '"Go away, the backend is broken."'}], - :match => [/^ ErrorDocument 503 "Go away, the backend is broken."$/], - }, - { - :title => 'should set ErrorDocuments 503 407', - :attr => 'error_documents', - :value => [ - { 'error_code' => '503', 'document' => '/service-unavail'}, - { 'error_code' => '407', 'document' => 'https://example.com/proxy/login'}, - ], - :match => [ - /^ ErrorDocument 503 \/service-unavail$/, - /^ ErrorDocument 407 https:\/\/example\.com\/proxy\/login$/, - ], - }, - { - :title => 'should set ErrorDocument 503 in directory', - :attr => 'directories', - :value => { 'path' => '/srv/www', 'error_documents' => [{ 'error_code' => '503', 'document' => '"Go away, the backend is broken."'}] }, - :match => [/^ ErrorDocument 503 "Go away, the backend is broken."$/], - }, - { - :title => 'should set ErrorDocuments 503 407 in directory', - :attr => 'directories', - :value => { 'path' => '/srv/www', 'error_documents' => - [ - { 'error_code' => '503', 'document' => '/service-unavail'}, - { 'error_code' => '407', 'document' => 'https://example.com/proxy/login'}, - ]}, - :match => [ - /^ ErrorDocument 503 \/service-unavail$/, - /^ ErrorDocument 407 https:\/\/example\.com\/proxy\/login$/, - ], - }, - { - :title => 'should accept a scriptalias', - :attr => 'scriptalias', - :value => '/usr/scripts', - :match => [ - /^ ScriptAlias \/cgi-bin\/ "\/usr\/scripts"$/, - ], - }, - { - :title => 'should accept a single scriptaliases', - :attr => 'scriptaliases', - :value => { 'alias' => '/blah/', 'path' => '/usr/scripts' }, - :match => [ - /^ ScriptAlias \/blah\/ "\/usr\/scripts"$/, - ], - :nomatch => [/ScriptAlias \/cgi\-bin\//], - }, - { - :title => 'should accept multiple scriptaliases', - :attr => 'scriptaliases', - :value => [ { 'alias' => '/blah', 'path' => '/usr/scripts' }, { 'alias' => '/blah2', 'path' => '/usr/scripts' } ], - :match => [ - /^ ScriptAlias \/blah "\/usr\/scripts"$/, - /^ ScriptAlias \/blah2 "\/usr\/scripts"$/, - ], - :nomatch => [/ScriptAlias \/cgi\-bin\//], - }, - { - :title => 'should accept multiple scriptaliases with and without trailing slashes', - :attr => 'scriptaliases', - :value => [ { 'alias' => '/blah', 'path' => '/usr/scripts' }, { 'alias' => '/blah2/', 'path' => '/usr/scripts2/' } ], - :match => [ - /^ ScriptAlias \/blah "\/usr\/scripts"$/, - /^ ScriptAlias \/blah2\/ "\/usr\/scripts2\/"$/, - ], - :nomatch => [/ScriptAlias \/cgi\-bin\//], - }, - { - :title => 'should accept a ScriptAliasMatch directive', - :attr => 'scriptaliases', - ## XXX As mentioned above, rspec-puppet drops constructs like $1. - ## Thus, these tests don't work as they should. As a workaround we - ## use FOO instead of $1 here. - :value => [ { 'aliasmatch' => '^/cgi-bin(.*)', 'path' => '/usr/local/apache/cgi-binFOO' } ], - :match => [ - /^ ScriptAliasMatch \^\/cgi-bin\(\.\*\) "\/usr\/local\/apache\/cgi-binFOO"$/ - ], - }, - { - :title => 'should accept multiple ScriptAliasMatch directives', - :attr => 'scriptaliases', - ## XXX As mentioned above, rspec-puppet drops constructs like $1. - ## Thus, these tests don't work as they should. As a workaround we - ## use FOO instead of $1 here. - :value => [ - { 'aliasmatch' => '^/cgi-bin(.*)', 'path' => '/usr/local/apache/cgi-binFOO' }, - { 'aliasmatch' => '"(?x)^/git/(.*/(HEAD|info/refs|objects/(info/[^/]+|[0-9a-f]{2}/[0-9a-f]{38}|pack/pack-[0-9a-f]{40}\.(pack|idx))|git-(upload|receive)-pack))"', 'path' => '/var/www/bin/gitolite-suexec-wrapper/FOO' }, - ], - :match => [ - /^ ScriptAliasMatch \^\/cgi-bin\(\.\*\) "\/usr\/local\/apache\/cgi-binFOO"$/, - /^ ScriptAliasMatch "\(\?x\)\^\/git\/\(\.\*\/\(HEAD\|info\/refs\|objects\/\(info\/\[\^\/\]\+\|\[0-9a-f\]\{2\}\/\[0-9a-f\]\{38\}\|pack\/pack-\[0-9a-f\]\{40\}\\\.\(pack\|idx\)\)\|git-\(upload\|receive\)-pack\)\)" "\/var\/www\/bin\/gitolite-suexec-wrapper\/FOO"$/, - ], - }, - { - :title => 'should accept mixed ScriptAlias and ScriptAliasMatch directives', - :attr => 'scriptaliases', - ## XXX As mentioned above, rspec-puppet drops constructs like $1. - ## Thus, these tests don't work as they should. As a workaround we - ## use FOO instead of $1 here. - :value => [ - { 'aliasmatch' => '"(?x)^/git/(.*/(HEAD|info/refs|objects/(info/[^/]+|[0-9a-f]{2}/[0-9a-f]{38}|pack/pack-[0-9a-f]{40}\.(pack|idx))|git-(upload|receive)-pack))"', 'path' => '/var/www/bin/gitolite-suexec-wrapper/FOO' }, - { 'alias' => '/git', 'path' => '/var/www/gitweb/index.cgi' }, - { 'aliasmatch' => '^/cgi-bin(.*)', 'path' => '/usr/local/apache/cgi-binFOO' }, - { 'alias' => '/trac', 'path' => '/etc/apache2/trac.fcgi' }, - ], - :match => [ - /^ ScriptAliasMatch "\(\?x\)\^\/git\/\(\.\*\/\(HEAD\|info\/refs\|objects\/\(info\/\[\^\/\]\+\|\[0-9a-f\]\{2\}\/\[0-9a-f\]\{38\}\|pack\/pack-\[0-9a-f\]\{40\}\\\.\(pack\|idx\)\)\|git-\(upload\|receive\)-pack\)\)" "\/var\/www\/bin\/gitolite-suexec-wrapper\/FOO"$/, - /^ ScriptAlias \/git "\/var\/www\/gitweb\/index\.cgi"$/, - /^ ScriptAliasMatch \^\/cgi-bin\(\.\*\) "\/usr\/local\/apache\/cgi-binFOO"$/, - /^ ScriptAlias \/trac "\/etc\/apache2\/trac.fcgi"$/, - ], - }, - { - :title => 'should accept proxy destinations', - :attr => 'proxy_dest', - :value => 'http://fake.com', - :match => [ - /^ ProxyPass \/ http:\/\/fake.com\/$/, - /^ $/, - /^ ProxyPassReverse http:\/\/fake.com\/$/, - /^ <\/Location>$/, - ], - :notmatch => [/ProxyPass .+!$/], - }, - { - :title => 'should accept proxy_pass hash', - :attr => 'proxy_pass', - :value => { 'path' => '/path-a', 'url' => 'http://fake.com/a' }, - :match => [ - /^ ProxyPass \/path-a http:\/\/fake.com\/a$/, - /^ $/, - /^ ProxyPassReverse http:\/\/fake.com\/a$/, - /^ <\/Location>$/, - - ], - :notmatch => [/ProxyPass .+!$/], - }, - { - :title => 'should accept proxy_pass array of hash', - :attr => 'proxy_pass', - :value => [ - { 'path' => '/path-a/', 'url' => 'http://fake.com/a/' }, - { 'path' => '/path-b', 'url' => 'http://fake.com/b' }, - ], - :match => [ - /^ ProxyPass \/path-a\/ http:\/\/fake.com\/a\/$/, - /^ $/, - /^ ProxyPassReverse http:\/\/fake.com\/a\/$/, - /^ <\/Location>$/, - /^ ProxyPass \/path-b http:\/\/fake.com\/b$/, - /^ $/, - /^ ProxyPassReverse http:\/\/fake.com\/b$/, - /^ <\/Location>$/, - ], - :notmatch => [/ProxyPass .+!$/], - }, - { - :title => 'should enable rack', - :attr => 'rack_base_uris', - :value => ['/rack1','/rack2'], - :match => [ - /^ RackBaseURI \/rack1$/, - /^ RackBaseURI \/rack2$/, - ], - }, - { - :title => 'should accept headers', - :attr => 'headers', - :value => ['add something', 'merge something_else'], - :match => [ - /^ Header add something$/, - /^ Header merge something_else$/, - ], - }, - { - :title => 'should accept request headers', - :attr => 'request_headers', - :value => ['append something', 'unset something_else'], - :match => [ - /^ RequestHeader append something$/, - /^ RequestHeader unset something_else$/, - ], - }, - { - :title => 'should accept rewrite rules', - :attr => 'rewrite_rule', - :value => 'not a real rule', - :match => [/^ RewriteRule not a real rule$/], - }, - { - :title => 'should accept rewrite rules', - :attr => 'rewrites', - :value => [{'rewrite_rule' => ['not a real rule']}], - :match => [/^ RewriteRule not a real rule$/], - }, - { - :title => 'should accept rewrite comment', - :attr => 'rewrites', - :value => [{'comment' => 'rewrite comment', 'rewrite_rule' => ['not a real rule']}], - :match => [/^ #rewrite comment/], - }, - { - :title => 'should accept rewrite conditions', - :attr => 'rewrites', - :value => [{'comment' => 'redirect IE', 'rewrite_cond' => ['%{HTTP_USER_AGENT} ^MSIE'], 'rewrite_rule' => ['^index\.html$ welcome.html'],}], - :match => [ - /^ #redirect IE$/, - /^ RewriteCond %{HTTP_USER_AGENT} \^MSIE$/, - /^ RewriteRule \^index\\\.html\$ welcome.html$/, - ], - }, - { - :title => 'should accept multiple rewrites', - :attr => 'rewrites', - :value => [ - {'rewrite_rule' => ['not a real rule']}, - {'rewrite_rule' => ['not a real rule two']}, - ], - :match => [ - /^ RewriteRule not a real rule$/, - /^ RewriteRule not a real rule two$/, - ], - }, - { - :title => 'should block scm', - :attr => 'block', - :value => 'scm', - :match => [/^ $/], - }, - { - :title => 'should accept a custom fragment', - :attr => 'custom_fragment', - :value => " Some custom fragment line\n That spans multiple lines", - :match => [ - /^ Some custom fragment line$/, - /^ That spans multiple lines$/, - /^<\/VirtualHost>$/, - ], - }, - { - :title => 'should accept an array of alias hashes', - :attr => 'aliases', - :value => [ { 'alias' => '/', 'path' => '/var/www'} ], - :match => [/^ Alias \/ "\/var\/www"$/], - }, - { - :title => 'should accept an alias hash', - :attr => 'aliases', - :value => { 'alias' => '/', 'path' => '/var/www'}, - :match => [/^ Alias \/ "\/var\/www"$/], - }, - { - :title => 'should accept multiple aliases', - :attr => 'aliases', - :value => [ - { 'alias' => '/', 'path' => '/var/www'}, - { 'alias' => '/cgi-bin', 'path' => '/var/www/cgi-bin'}, - { 'alias' => '/css', 'path' => '/opt/someapp/css'}, - ], - :match => [ - /^ Alias \/ "\/var\/www"$/, - /^ Alias \/cgi-bin "\/var\/www\/cgi-bin"$/, - /^ Alias \/css "\/opt\/someapp\/css"$/, - ], - }, - { - :title => 'should accept an aliasmatch hash', - :attr => 'aliases', - ## XXX As mentioned above, rspec-puppet drops the $1. Thus, these - # tests don't work. - #:value => { 'aliasmatch' => '^/image/(.*).gif', 'path' => '/files/gifs/$1.gif' }, - #:match => [/^ AliasMatch \^\/image\/\(\.\*\)\.gif \/files\/gifs\/\$1\.gif$/], - }, - { - :title => 'should accept a array of alias and aliasmatch hashes mixed', - :attr => 'aliases', - ## XXX As mentioned above, rspec-puppet drops the $1. Thus, these - # tests don't work. - #:value => [ - # { 'alias' => '/css', 'path' => '/files/css' }, - # { 'aliasmatch' => '^/image/(.*).gif', 'path' => '/files/gifs/$1.gif' }, - # { 'aliasmatch' => '^/image/(.*).jpg', 'path' => '/files/jpgs/$1.jpg' }, - # { 'alias' => '/image', 'path' => '/files/images' }, - #], - #:match => [ - # /^ Alias \/css \/files\/css$/, - # /^ AliasMatch \^\/image\/\(.\*\)\.gif \/files\/gifs\/\$1\.gif$/, - # /^ AliasMatch \^\/image\/\(.\*\)\.jpg \/files\/jpgs\/\$1\.jpg$/, - # /^ Alias \/image \/files\/images$/ - #], - }, - { - :title => 'should accept multiple additional includes', - :attr => 'additional_includes', - :value => [ - '/tmp/proxy_group_a', - '/tmp/proxy_group_b', - '/tmp/proxy_group_c', - ], - :match => [ - /^ Include "\/tmp\/proxy_group_a"$/, - /^ Include "\/tmp\/proxy_group_b"$/, - /^ Include "\/tmp\/proxy_group_c"$/, - ], - }, - { - :title => 'should accept a suPHP_Engine', - :attr => 'suphp_engine', - :value => 'on', - :match => [/^ suPHP_Engine on$/], - }, - { - :title => 'should accept a php_admin_flags', - :attr => 'php_admin_flags', - :value => { 'engine' => 'on' }, - :match => [/^ php_admin_flag engine on$/], - }, - { - :title => 'should accept php_admin_values', - :attr => 'php_admin_values', - :value => { 'open_basedir' => '/srv/web/www.com/:/usr/share/pear/' }, - :match => [/^ php_admin_value open_basedir \/srv\/web\/www.com\/:\/usr\/share\/pear\/$/], - }, - { - :title => 'should accept php_admin_flags in directories', - :attr => 'directories', - :value => { - 'path' => '/srv/www', - 'php_admin_flags' => { 'php_engine' => 'on' } - }, - :match => [/^ php_admin_flag php_engine on$/], - }, - { - :title => 'should accept php_admin_values', - :attr => 'php_admin_values', - :value => { 'open_basedir' => '/srv/web/www.com/:/usr/share/pear/' }, - :match => [/^ php_admin_value open_basedir \/srv\/web\/www.com\/:\/usr\/share\/pear\/$/], - }, - { - :title => 'should accept php_admin_values in directories', - :attr => 'directories', - :value => { - 'path' => '/srv/www', - 'php_admin_values' => { 'open_basedir' => '/srv/web/www.com/:/usr/share/pear/' } - }, - :match => [/^ php_admin_value open_basedir \/srv\/web\/www.com\/:\/usr\/share\/pear\/$/], - }, - { - :title => 'should accept a wsgi script alias', - :attr => 'wsgi_script_aliases', - :value => { '/' => '/var/www/myapp.wsgi'}, - :match => [/^ WSGIScriptAlias \/ "\/var\/www\/myapp.wsgi"$/], - }, - { - :title => 'should accept multiple wsgi aliases', - :attr => 'wsgi_script_aliases', - :value => { - '/wiki' => '/usr/local/wsgi/scripts/mywiki.wsgi', - '/blog' => '/usr/local/wsgi/scripts/myblog.wsgi', - '/' => '/usr/local/wsgi/scripts/myapp.wsgi', - }, - :match => [ - /^ WSGIScriptAlias \/wiki "\/usr\/local\/wsgi\/scripts\/mywiki.wsgi"$/, - /^ WSGIScriptAlias \/blog "\/usr\/local\/wsgi\/scripts\/myblog.wsgi"$/, - /^ WSGIScriptAlias \/ "\/usr\/local\/wsgi\/scripts\/myapp.wsgi"$/, - ], - }, - { - :title => 'should accept a wsgi application group', - :attr => 'wsgi_application_group', - :value => '%{GLOBAL}', - :match => [/^ WSGIApplicationGroup %{GLOBAL}$/], - }, - { - :title => 'should contain environment variables', - :attr => 'access_log_env_var', - :value => 'admin', - :match => [/CustomLog "\/var\/log\/.+_access\.log" combined env=admin$/] - }, - { - :title => 'should contain virtual_docroot', - :attr => 'virtual_docroot', - :value => '/not/default', - :match => [ - /^ VirtualDocumentRoot "\/not\/default"$/, - ], - }, - { - :title => 'should accept multiple directories', - :attr => 'directories', - :value => [ - { 'path' => '/opt/app' }, - { 'path' => '/var/www' }, - { 'path' => '/rspec/docroot'} - ], - :match => [ - /^ $/, - /^ $/, - /^ $/, - ], - }, - ].each do |param| - describe "when #{param[:attr]} is #{param[:value]}" do - let :params do default_params.merge({ param[:attr].to_sym => param[:value] }) end - - it { should contain_file("25-#{title}.conf").with_mode('0644') } - if param[:match] - it "#{param[:title]}: matches" do - param[:match].each do |match| - should contain_file("25-#{title}.conf").with_content( match ) - end - end - end - if param[:notmatch] - it "#{param[:title]}: notmatches" do - param[:notmatch].each do |notmatch| - should_not contain_file("25-#{title}.conf").with_content( notmatch ) - end - end - end - end - end - end - - # Apache below 2.4 (Default Version). All match and notmatch should be a list of regexs and exact match strings - context ".conf content with $apache_version < 2.4" do - [ - { - :title => 'should accept a directory', - :attr => 'directories', - :value => { 'path' => '/opt/app' }, - :notmatch => [' '], - :match => [ - /^ $/, - /^ AllowOverride None$/, - /^ Order allow,deny$/, - /^ Allow from all$/, - /^ <\/Directory>$/, - ], - }, - { - :title => 'should accept directory directives hash', - :attr => 'directories', - :value => { - 'path' => '/opt/app', - 'headers' => 'Set X-Robots-Tag "noindex, noarchive, nosnippet"', - 'allow' => 'from rspec.org', - 'allow_override' => 'Lol', - 'deny' => 'from google.com', - 'options' => '-MultiViews', - 'order' => 'deny,yned', - 'passenger_enabled' => 'onf', + context 'set everything!' do + let :params do + { + 'docroot' => '/var/www/foo', + 'manage_docroot' => false, + 'virtual_docroot' => true, + 'port' => '8080', + 'ip' => '127.0.0.1', + 'ip_based' => true, + 'add_listen' => false, + 'docroot_owner' => 'user', + 'docroot_group' => 'wheel', + 'docroot_mode' => '0664', + 'serveradmin' => 'foo@localhost', + 'ssl' => true, + 'ssl_cert' => '/ssl/cert', + 'ssl_key' => '/ssl/key', + 'ssl_chain' => '/ssl/chain', + 'ssl_crl_path' => '/ssl/crl', + 'ssl_crl' => 'foo.crl', + 'ssl_certs_dir' => '/ssl/certs', + 'ssl_protocol' => 'SSLv2', + 'ssl_cipher' => 'HIGH', + 'ssl_honorcipherorder' => 'Off', + 'ssl_verify_client' => 'optional', + 'ssl_verify_depth' => '3', + 'ssl_options' => '+ExportCertData', + 'ssl_proxyengine' => true, + 'priority' => '30', + 'default_vhost' => true, + 'servername' => 'example.com', + 'serveraliases' => ['test-example.com'], + 'options' => ['MultiView'], + 'override' => ['All'], + 'directoryindex' => 'index.html', + 'vhost_name' => 'test', + 'logroot' => '/var/www/logs', + 'logroot_ensure' => 'directory', + 'logroot_mode' => '0600', + 'log_level' => 'crit', + 'access_log' => false, + 'access_log_file' => 'httpd_access_log', + 'access_log_pipe' => '', + 'access_log_syslog' => true, + 'access_log_format' => '%h %l %u %t \"%r\" %>s %b', + 'access_log_env_var' => '', + 'aliases' => '/image', + 'directories' => { + 'path' => '/var/www/files', + 'provider' => 'files', + 'deny' => 'from all' }, - :match => [ - /^ $/, - /^ Header Set X-Robots-Tag "noindex, noarchive, nosnippet"$/, - /^ Allow from rspec.org$/, - /^ AllowOverride Lol$/, - /^ Deny from google.com$/, - /^ Options -MultiViews$/, - /^ Order deny,yned$/, - /^ PassengerEnabled onf$/, - /^ <\/Directory>$/, - ], - }, - { - :title => 'should accept directory directives with arrays and hashes', - :attr => 'directories', - :value => [ + 'error_log' => false, + 'error_log_file' => 'httpd_error_log', + 'error_log_pipe' => '', + 'error_log_syslog' => true, + 'error_documents' => 'true', + 'fallbackresource' => '/index.php', + 'scriptalias' => '/usr/lib/cgi-bin', + 'scriptaliases' => [ { - 'path' => '/opt/app1', - 'allow' => 'from rspec.org', - 'allow_override' => ['AuthConfig','Indexes'], - 'deny' => 'from google.com', - 'options' => ['-MultiViews','+MultiViews'], - 'order' => ['deny','yned'], - 'passenger_enabled' => 'onf', + 'alias' => '/myscript', + 'path' => '/usr/share/myscript', }, { - 'path' => '/opt/app2', - 'addhandlers' => { - 'handler' => 'cgi-script', - 'extensions' => '.cgi', - }, + 'aliasmatch' => '^/foo(.*)', + 'path' => '/usr/share/fooscripts$1', }, ], - :match => [ - /^ $/, - /^ Allow from rspec.org$/, - /^ AllowOverride AuthConfig Indexes$/, - /^ Deny from google.com$/, - /^ Options -MultiViews \+MultiViews$/, - /^ Order deny,yned$/, - /^ PassengerEnabled onf$/, - /^ <\/Directory>$/, - /^ $/, - /^ AllowOverride None$/, - /^ Order allow,deny$/, - /^ Allow from all$/, - /^ AddHandler cgi-script .cgi$/, - /^ <\/Directory>$/, + 'proxy_dest' => '/', + 'proxy_pass' => [ + { + 'path' => '/a', + 'url' => 'http://backend-a/', + 'keywords' => ['noquery', 'interpolate'], + 'params' => { + 'retry' => '0', + 'timeout' => '5' + } + } ], - }, - { - :title => 'should accept location for provider', - :attr => 'directories', - :value => { - 'path' => '/', - 'provider' => 'location', - }, - :notmatch => [' AllowOverride None'], - :match => [ - /^ $/, - /^ Order allow,deny$/, - /^ Allow from all$/, - /^ <\/Location>$/, + 'suphp_addhandler' => 'foo', + 'suphp_engine' => 'on', + 'suphp_configpath' => '/var/www/html', + 'php_admin_flags' => ['foo', 'bar'], + 'php_admin_values' => ['true', 'false'], + 'no_proxy_uris' => '/foo', + 'proxy_preserve_host' => true, + 'redirect_source' => '/bar', + 'redirect_dest' => '/', + 'redirect_status' => 'temp', + 'redirectmatch_status' => ['404'], + 'redirectmatch_regexp' => ['\.git$'], + 'redirectmatch_dest' => ['http://www.example.com'], + 'rack_base_uris' => ['/rackapp1'], + 'headers' => 'Set X-Robots-Tag "noindex, noarchive, nosnippet"', + 'request_headers' => ['append MirrorID "mirror 12"'], + 'rewrites' => [ + { + 'rewrite_rule' => ['^index\.html$ welcome.html'] + } ], - }, - { - :title => 'should accept files for provider', - :attr => 'directories', - :value => { - 'path' => 'index.html', - 'provider' => 'files', + 'rewrite_base' => '/', + 'rewrite_rule' => '^index\.html$ welcome.html', + 'rewrite_cond' => '%{HTTP_USER_AGENT} ^MSIE', + 'setenv' => ['FOO=/bin/true'], + 'setenvif' => 'Request_URI "\.gif$" object_is_image=gif', + 'block' => 'scm', + 'wsgi_application_group' => '%{GLOBAL}', + 'wsgi_daemon_process' => 'wsgi', + 'wsgi_daemon_process_options' => { + 'processes' => '2', + 'threads' => '15', + 'display-name' => '%{GROUP}', }, - :notmatch => [' AllowOverride None'], - :match => [ - /^ $/, - /^ Order allow,deny$/, - /^ Allow from all$/, - /^ <\/Files>$/, - ], - }, - { - :title => 'should accept files match for provider', - :attr => 'directories', - :value => { - 'path' => 'index.html', - 'provider' => 'filesmatch', + 'wsgi_import_script' => '/var/www/demo.wsgi', + 'wsgi_import_script_options' => { + 'process-group' => 'wsgi', + 'application-group' => '%{GLOBAL}' }, - :notmatch => [' AllowOverride None'], - :match => [ - /^ $/, - /^ Order allow,deny$/, - /^ Allow from all$/, - /^ <\/FilesMatch>$/, - ], - }, - ].each do |param| - describe "when #{param[:attr]} is #{param[:value]}" do - let :params do default_params.merge({ - param[:attr].to_sym => param[:value], - :apache_version => 2.2, - }) end - - it { should contain_file("25-#{title}.conf").with_mode('0644') } - if param[:match] - it "#{param[:title]}: matches" do - param[:match].each do |match| - should contain_file("25-#{title}.conf").with_content( match ) - end - end - end - if param[:notmatch] - it "#{param[:title]}: notmatches" do - param[:notmatch].each do |notmatch| - should_not contain_file("25-#{title}.conf").with_content( notmatch ) - end - end - end - end - end - end - - # Apache equals or above 2.4. All match and notmatch should be a list of regexs and exact match strings - context ".conf content with $apache_version >= 2.4" do - [ - { - :title => 'should accept a directory', - :attr => 'directories', - :value => { 'path' => '/opt/app' }, - :notmatch => [' '], - :match => [ - /^ $/, - /^ AllowOverride None$/, - /^ Require all granted$/, - /^ <\/Directory>$/, - ], - }, - { - :title => 'should accept directory directives hash', - :attr => 'directories', - :value => { - 'path' => '/opt/app', - 'headers' => 'Set X-Robots-Tag "noindex, noarchive, nosnippet"', - 'allow_override' => 'Lol', - 'options' => '-MultiViews', - 'require' => 'something denied', - 'passenger_enabled' => 'onf', + 'wsgi_process_group' => 'wsgi', + 'wsgi_script_aliases' => { + '/' => '/var/www/demo.wsgi' }, - :match => [ - /^ $/, - /^ Header Set X-Robots-Tag "noindex, noarchive, nosnippet"$/, - /^ AllowOverride Lol$/, - /^ Options -MultiViews$/, - /^ Require something denied$/, - /^ PassengerEnabled onf$/, - /^ <\/Directory>$/, - ], - }, - { - :title => 'should accept directory directives with arrays and hashes', - :attr => 'directories', - :value => [ - { - 'path' => '/opt/app1', - 'allow_override' => ['AuthConfig','Indexes'], - 'options' => ['-MultiViews','+MultiViews'], - 'require' => ['host','example.org'], - 'passenger_enabled' => 'onf', - }, - { - 'path' => '/opt/app2', - 'addhandlers' => { - 'handler' => 'cgi-script', - 'extensions' => '.cgi', - }, - }, - ], - :match => [ - /^ $/, - /^ AllowOverride AuthConfig Indexes$/, - /^ Options -MultiViews \+MultiViews$/, - /^ Require host example.org$/, - /^ PassengerEnabled onf$/, - /^ <\/Directory>$/, - /^ $/, - /^ AllowOverride None$/, - /^ Require all granted$/, - /^ AddHandler cgi-script .cgi$/, - /^ <\/Directory>$/, - ], - }, - { - :title => 'should accept location for provider', - :attr => 'directories', - :value => { - 'path' => '/', - 'provider' => 'location', - }, - :notmatch => [' AllowOverride None'], - :match => [ - /^ $/, - /^ Require all granted$/, - /^ <\/Location>$/, - ], - }, - { - :title => 'should accept files for provider', - :attr => 'directories', - :value => { - 'path' => 'index.html', - 'provider' => 'files', + 'wsgi_pass_authorization' => 'On', + 'custom_fragment' => '#custom string', + 'itk' => { + 'user' => 'someuser', + 'group' => 'somegroup' }, - :notmatch => [' AllowOverride None'], - :match => [ - /^ $/, - /^ Require all granted$/, - /^ <\/Files>$/, - ], - }, + 'wsgi_chunked_request' => 'On', + 'action' => 'foo', + 'fastcgi_server' => 'localhost', + 'fastcgi_socket' => '/tmp/fastcgi.socket', + 'fastcgi_dir' => '/tmp', + 'additional_includes' => '/custom/path/includes', + 'apache_version' => '2.4', + 'suexec_user_group' => 'root root', + 'allow_encoded_slashes' => 'nodecode', + 'passenger_app_root' => '/usr/share/myapp', + 'passenger_ruby' => '/usr/bin/ruby1.9.1', + 'passenger_min_instances' => '1', + 'passenger_start_timeout' => '600', + 'passenger_pre_start' => 'http://localhost/myapp', + 'add_default_charset' => 'UTF-8', + } + end + let :facts do { - :title => 'should accept files match for provider', - :attr => 'directories', - :value => { - 'path' => 'index.html', - 'provider' => 'filesmatch', - }, - :notmatch => [' AllowOverride None'], - :match => [ - /^ $/, - /^ Require all granted$/, - /^ <\/FilesMatch>$/, - ], - }, - ].each do |param| - describe "when #{param[:attr]} is #{param[:value]}" do - let :params do default_params.merge({ - param[:attr].to_sym => param[:value], - :apache_version => 2.4, - }) end - - it { should contain_file("25-#{title}.conf").with_mode('0644') } - if param[:match] - it "#{param[:title]}: matches" do - param[:match].each do |match| - should contain_file("25-#{title}.conf").with_content( match ) - end - end - end - if param[:notmatch] - it "#{param[:title]}: notmatches" do - param[:notmatch].each do |notmatch| - should_not contain_file("25-#{title}.conf").with_content( notmatch ) - end - end - end - end + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + :kernelversion => '3.6.2', + } end - end - # All match and notmatch should be a list of regexs and exact match strings - context ".conf content with SSL" do - [ - { - :title => 'should accept setting SSLCertificateFile', - :attr => 'ssl_cert', - :value => '/path/to/cert.pem', - :match => [/^ SSLCertificateFile "\/path\/to\/cert\.pem"$/], - }, - { - :title => 'should accept setting SSLCertificateKeyFile', - :attr => 'ssl_key', - :value => '/path/to/cert.pem', - :match => [/^ SSLCertificateKeyFile "\/path\/to\/cert\.pem"$/], - }, - { - :title => 'should accept setting SSLCertificateChainFile', - :attr => 'ssl_chain', - :value => '/path/to/cert.pem', - :match => [/^ SSLCertificateChainFile "\/path\/to\/cert\.pem"$/], - }, - { - :title => 'should accept setting SSLCertificatePath', - :attr => 'ssl_certs_dir', - :value => '/path/to/certs', - :match => [/^ SSLCACertificatePath "\/path\/to\/certs"$/], - }, - { - :title => 'should accept setting SSLCertificateFile', - :attr => 'ssl_ca', - :value => '/path/to/ca.pem', - :match => [/^ SSLCACertificateFile "\/path\/to\/ca\.pem"$/], - }, - { - :title => 'should accept setting SSLRevocationPath', - :attr => 'ssl_crl_path', - :value => '/path/to/crl', - :match => [/^ SSLCARevocationPath "\/path\/to\/crl"$/], - }, - { - :title => 'should accept setting SSLRevocationFile', - :attr => 'ssl_crl', - :value => '/path/to/crl.pem', - :match => [/^ SSLCARevocationFile "\/path\/to\/crl\.pem"$/], - }, - { - :title => 'should accept setting SSLProxyEngine', - :attr => 'ssl_proxyengine', - :value => true, - :match => [/^ SSLProxyEngine On$/], - }, + it { is_expected.to compile } + it { is_expected.to_not contain_file('/var/www/foo') } + it { is_expected.to contain_class('apache::mod::ssl') } + it { is_expected.to contain_class('apache::mod::mime') } + it { is_expected.to contain_class('apache::mod::vhost_alias') } + it { is_expected.to contain_class('apache::mod::wsgi') } + it { is_expected.to contain_class('apache::mod::suexec') } + it { is_expected.to contain_class('apache::mod::passenger') } + it { is_expected.to contain_file('/var/www/logs').with({ + 'ensure' => 'directory', + 'mode' => '0600', + }) + } + it { is_expected.to contain_class('apache::mod::rewrite') } + it { is_expected.to contain_class('apache::mod::alias') } + it { is_expected.to contain_class('apache::mod::proxy') } + it { is_expected.to contain_class('apache::mod::proxy_http') } + it { is_expected.to contain_class('apache::mod::passenger') } + it { is_expected.to contain_class('apache::mod::passenger') } + it { is_expected.to contain_class('apache::mod::fastcgi') } + it { is_expected.to contain_class('apache::mod::headers') } + it { is_expected.to contain_concat('30-rspec.example.com.conf').with({ + 'owner' => 'root', + 'mode' => '0644', + 'require' => 'Package[httpd]', + 'notify' => 'Service[httpd]', + }) + } + it { is_expected.to contain_concat__fragment('rspec.example.com-apache-header') } + it { is_expected.to contain_concat__fragment('rspec.example.com-docroot') } + it { is_expected.to contain_concat__fragment('rspec.example.com-aliases') } + it { is_expected.to contain_concat__fragment('rspec.example.com-itk') } + it { is_expected.to contain_concat__fragment('rspec.example.com-fallbackresource') } + it { is_expected.to contain_concat__fragment('rspec.example.com-directories') } + it { is_expected.to contain_concat__fragment('rspec.example.com-additional_includes') } + it { is_expected.to contain_concat__fragment('rspec.example.com-logging') } + it { is_expected.to contain_concat__fragment('rspec.example.com-serversignature') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-access_log') } + it { is_expected.to contain_concat__fragment('rspec.example.com-action') } + it { is_expected.to contain_concat__fragment('rspec.example.com-block') } + it { is_expected.to contain_concat__fragment('rspec.example.com-error_document') } + it { is_expected.to contain_concat__fragment('rspec.example.com-proxy').with_content( + /retry=0/) } + it { is_expected.to contain_concat__fragment('rspec.example.com-proxy').with_content( + /timeout=5/) } + it { is_expected.to contain_concat__fragment('rspec.example.com-proxy').with_content( + /noquery interpolate/) } + it { is_expected.to contain_concat__fragment('rspec.example.com-rack') } + it { is_expected.to contain_concat__fragment('rspec.example.com-redirect') } + it { is_expected.to contain_concat__fragment('rspec.example.com-rewrite') } + it { is_expected.to contain_concat__fragment('rspec.example.com-scriptalias') } + it { is_expected.to contain_concat__fragment('rspec.example.com-serveralias') } + it { is_expected.to contain_concat__fragment('rspec.example.com-setenv') } + it { is_expected.to contain_concat__fragment('rspec.example.com-ssl') } + it { is_expected.to contain_concat__fragment('rspec.example.com-suphp') } + it { is_expected.to contain_concat__fragment('rspec.example.com-php_admin') } + it { is_expected.to contain_concat__fragment('rspec.example.com-header') } + it { is_expected.to contain_concat__fragment('rspec.example.com-requestheader') } + it { is_expected.to contain_concat__fragment('rspec.example.com-wsgi') } + it { is_expected.to contain_concat__fragment('rspec.example.com-custom_fragment') } + it { is_expected.to contain_concat__fragment('rspec.example.com-fastcgi') } + it { is_expected.to contain_concat__fragment('rspec.example.com-suexec') } + it { is_expected.to contain_concat__fragment('rspec.example.com-passenger') } + it { is_expected.to contain_concat__fragment('rspec.example.com-charsets') } + it { is_expected.to contain_concat__fragment('rspec.example.com-file_footer') } + end + context 'not everything can be set together...' do + let :params do + { + 'access_log_pipe' => '/dev/null', + 'error_log_pipe' => '/dev/null', + 'docroot' => '/var/www/foo', + 'ensure' => 'absent', + 'manage_docroot' => true, + 'logroot' => '/tmp/logroot', + 'logroot_ensure' => 'absent', + } + end + let :facts do { - :title => 'should accept setting SSLProtocol', - :attr => 'ssl_protocol', - :value => 'all -SSLv2', - :match => [/^ SSLProtocol all -SSLv2$/], - }, + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + :operatingsystem => 'RedHat', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + :kernelversion => '3.6.2', + } + end + + it { is_expected.to compile } + it { is_expected.to_not contain_class('apache::mod::ssl') } + it { is_expected.to_not contain_class('apache::mod::mime') } + it { is_expected.to_not contain_class('apache::mod::vhost_alias') } + it { is_expected.to_not contain_class('apache::mod::wsgi') } + it { is_expected.to_not contain_class('apache::mod::passenger') } + it { is_expected.to_not contain_class('apache::mod::suexec') } + it { is_expected.to_not contain_class('apache::mod::rewrite') } + it { is_expected.to contain_class('apache::mod::alias') } + it { is_expected.to_not contain_class('apache::mod::proxy') } + it { is_expected.to_not contain_class('apache::mod::proxy_http') } + it { is_expected.to_not contain_class('apache::mod::passenger') } + it { is_expected.to_not contain_class('apache::mod::headers') } + it { is_expected.to contain_file('/var/www/foo') } + it { is_expected.to contain_file('/tmp/logroot').with({ + 'ensure' => 'absent', + }) + } + it { is_expected.to contain_concat('25-rspec.example.com.conf').with({ + 'ensure' => 'absent', + }) + } + it { is_expected.to contain_concat__fragment('rspec.example.com-apache-header') } + it { is_expected.to contain_concat__fragment('rspec.example.com-docroot') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-aliases') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-itk') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-fallbackresource') } + it { is_expected.to contain_concat__fragment('rspec.example.com-directories') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-additional_includes') } + it { is_expected.to contain_concat__fragment('rspec.example.com-logging') } + it { is_expected.to contain_concat__fragment('rspec.example.com-serversignature') } + it { is_expected.to contain_concat__fragment('rspec.example.com-access_log') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-action') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-block') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-error_document') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-proxy') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-rack') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-redirect') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-rewrite') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-scriptalias') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-serveralias') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-setenv') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-ssl') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-suphp') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-php_admin') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-header') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-requestheader') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-wsgi') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-custom_fragment') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-fastcgi') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-suexec') } + it { is_expected.to_not contain_concat__fragment('rspec.example.com-charsets') } + it { is_expected.to contain_concat__fragment('rspec.example.com-file_footer') } + end + end + describe 'validation' do + context 'bad ensure' do + let :params do { - :title => 'should accept setting SSLCipherSuite', - :attr => 'ssl_cipher', - :value => 'RC4-SHA:HIGH:!ADH:!SSLv2', - :match => [/^ SSLCipherSuite RC4-SHA:HIGH:!ADH:!SSLv2$/], - }, + 'docroot' => '/rspec/docroot', + 'ensure' => 'bogus', + } + end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } + end + context 'bad suphp_engine' do + let :params do { - :title => 'should accept setting SSLHonorCipherOrder', - :attr => 'ssl_honorcipherorder', - :value => 'On', - :match => [/^ SSLHonorCipherOrder On$/], - }, + 'docroot' => '/rspec/docroot', + 'suphp_engine' => 'bogus', + } + end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } + end + context 'bad ip_based' do + let :params do { - :title => 'should accept setting SSLVerifyClient', - :attr => 'ssl_verify_client', - :value => 'optional', - :match => [/^ SSLVerifyClient optional$/], - }, + 'docroot' => '/rspec/docroot', + 'ip_based' => 'bogus', + } + end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } + end + context 'bad access_log' do + let :params do { - :title => 'should accept setting SSLVerifyDepth', - :attr => 'ssl_verify_depth', - :value => '1', - :match => [/^ SSLVerifyDepth 1$/], - }, + 'docroot' => '/rspec/docroot', + 'access_log' => 'bogus', + } + end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } + end + context 'bad error_log' do + let :params do { - :title => 'should accept setting SSLOptions with a string', - :attr => 'ssl_options', - :value => '+ExportCertData', - :match => [/^ SSLOptions \+ExportCertData$/], - }, + 'docroot' => '/rspec/docroot', + 'error_log' => 'bogus', + } + end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } + end + context 'bad_ssl' do + let :params do { - :title => 'should accept setting SSLOptions with an array', - :attr => 'ssl_options', - :value => ['+StrictRequire','+ExportCertData'], - :match => [/^ SSLOptions \+StrictRequire \+ExportCertData/], - }, + 'docroot' => '/rspec/docroot', + 'ssl' => 'bogus', + } + end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } + end + context 'bad default_vhost' do + let :params do { - :title => 'should accept setting SSLOptions with a string in directories', - :attr => 'directories', - :value => { 'path' => '/srv/www', 'ssl_options' => '+ExportCertData'}, - :match => [/^ SSLOptions \+ExportCertData$/], - }, + 'docroot' => '/rspec/docroot', + 'default_vhost' => 'bogus', + } + end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } + end + context 'bad ssl_proxyengine' do + let :params do { - :title => 'should accept setting SSLOptions with an array in directories', - :attr => 'directories', - :value => { 'path' => '/srv/www', 'ssl_options' => ['-StdEnvVars','+ExportCertData']}, - :match => [/^ SSLOptions -StdEnvVars \+ExportCertData/], - }, - ].each do |param| - describe "when #{param[:attr]} is #{param[:value]} with SSL" do - let :params do - default_params.merge( { - param[:attr].to_sym => param[:value], - :ssl => true, - } ) - end - it { should contain_file("25-#{title}.conf").with_mode('0644') } - if param[:match] - it "#{param[:title]}: matches" do - param[:match].each do |match| - should contain_file("25-#{title}.conf").with_content( match ) - end - end - end - if param[:notmatch] - it "#{param[:title]}: notmatches" do - param[:notmatch].each do |notmatch| - should_not contain_file("25-#{title}.conf").with_content( notmatch ) - end - end - end - end + 'docroot' => '/rspec/docroot', + 'ssl_proxyengine' => 'bogus', + } end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } end - - context 'attribute resources' do - describe 'when access_log_file and access_log_pipe are specified' do - let :params do default_params.merge({ - :access_log_file => 'fake.log', - :access_log_pipe => '| /bin/fake', - }) end - it 'should cause a failure' do - expect { subject }.to raise_error(Puppet::Error, /'access_log_file' and 'access_log_pipe' cannot be defined at the same time/) - end + context 'bad rewrites' do + let :params do + { + 'docroot' => '/rspec/docroot', + 'rewrites' => 'bogus', + } end - describe 'when error_log_file and error_log_pipe are specified' do - let :params do default_params.merge({ - :error_log_file => 'fake.log', - :error_log_pipe => '| /bin/fake', - }) end - it 'should cause a failure' do - expect { subject }.to raise_error(Puppet::Error, /'error_log_file' and 'error_log_pipe' cannot be defined at the same time/) - end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } + end + context 'bad rewrites 2' do + let :params do + { + 'docroot' => '/rspec/docroot', + 'rewrites' => ['bogus'], + } end - describe 'when docroot owner is specified' do - let :params do default_params.merge({ - :docroot_owner => 'testuser', - :docroot_group => 'testgroup', - }) end - it 'should set vhost ownership' do - should contain_file(params[:docroot]).with({ - :ensure => :directory, - :owner => 'testuser', - :group => 'testgroup', - }) - end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } + end + context 'bad suexec_user_group' do + let :params do + { + 'docroot' => '/rspec/docroot', + 'suexec_user_group' => 'bogus', + } end - - describe 'when wsgi_daemon_process and wsgi_daemon_process_options are specified' do - let :params do default_params.merge({ - :wsgi_daemon_process => 'example.org', - :wsgi_daemon_process_options => { 'processes' => '2', 'threads' => '15' }, - }) end - it 'should set wsgi_daemon_process_options' do - should contain_file("25-#{title}.conf").with_content( - /^ WSGIDaemonProcess example.org processes=2 threads=15$/ - ) - end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } + end + context 'bad wsgi_script_alias' do + let :params do + { + 'docroot' => '/rspec/docroot', + 'wsgi_script_alias' => 'bogus', + } end - - describe 'when wsgi_import_script and wsgi_import_script_options are specified' do - let :params do default_params.merge({ - :wsgi_import_script => '/var/www/demo.wsgi', - :wsgi_import_script_options => { 'application-group' => '%{GLOBAL}', 'process-group' => 'wsgi' }, - }) end - it 'should set wsgi_import_script_options' do - should contain_file("25-#{title}.conf").with_content( - /^ WSGIImportScript \/var\/www\/demo.wsgi application-group=%{GLOBAL} process-group=wsgi$/ - ) - end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } + end + context 'bad wsgi_daemon_process_options' do + let :params do + { + 'docroot' => '/rspec/docroot', + 'wsgi_daemon_process_options' => 'bogus', + } end - - describe 'when rewrites are specified' do - let :params do default_params.merge({ - :rewrites => [ - { - 'comment' => 'test rewrites', - 'rewrite_base' => '/mytestpath/', - 'rewrite_cond' => ['%{HTTP_USER_AGENT} ^Lynx/ [OR]', '%{HTTP_USER_AGENT} ^Mozilla/[12]'], - 'rewrite_rule' => ['^index\.html$ welcome.html', '^index\.cgi$ index.php'], - } - ] - }) end - it 'should set RewriteConds and RewriteRules' do - should contain_file("25-#{title}.conf").with_content( - /^ #test rewrites$/ - ) - should contain_file("25-#{title}.conf").with_content( - /^ RewriteCond %\{HTTP_USER_AGENT\} \^Lynx\/ \[OR\]$/ - ) - should contain_file("25-#{title}.conf").with_content( - /^ RewriteBase \/mytestpath\/$/ - ) - should contain_file("25-#{title}.conf").with_content( - /^ RewriteCond %\{HTTP_USER_AGENT\} \^Mozilla\/\[12\]$/ - ) - should contain_file("25-#{title}.conf").with_content( - /^ RewriteRule \^index\\.html\$ welcome.html$/ - ) - should contain_file("25-#{title}.conf").with_content( - /^ RewriteRule \^index\\.cgi\$ index.php$/ - ) - end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } + end + context 'bad wsgi_import_script_alias' do + let :params do + { + 'docroot' => '/rspec/docroot', + 'wsgi_import_script_alias' => 'bogus', + } end - - describe 'when rewrite_rule and rewrite_cond are specified' do - let :params do default_params.merge({ - :rewrite_cond => '%{HTTPS} off', - :rewrite_rule => '(.*) https://%{HTTPS_HOST}%{REQUEST_URI}', - }) end - it 'should set RewriteCond' do - should contain_file("25-#{title}.conf").with_content( - /^ RewriteCond %\{HTTPS\} off$/ - ) - end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } + end + context 'bad itk' do + let :params do + { + 'docroot' => '/rspec/docroot', + 'itk' => 'bogus', + } end - - describe 'when suphp_engine is on and suphp_configpath is specified' do - let :params do default_params.merge({ - :suphp_engine => 'on', - :suphp_configpath => '/etc/php5/apache2', - }) end - it 'should set suphp_configpath' do - should contain_file("25-#{title}.conf").with_content( - /^ suPHP_ConfigPath "\/etc\/php5\/apache2"$/ - ) - end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } + end + context 'bad logroot_ensure' do + let :params do + { + 'docroot' => '/rspec/docroot', + 'log_level' => 'bogus', + } end - - describe 'when suphp_engine is on and suphp_addhandler is specified' do - let :params do default_params.merge({ - :suphp_engine => 'on', - :suphp_addhandler => 'x-httpd-php', - }) end - it 'should set suphp_addhandler' do - should contain_file("25-#{title}.conf").with_content( - /^ suPHP_AddHandler x-httpd-php/ - ) - end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } + end + context 'bad log_level' do + let :params do + { + 'docroot' => '/rspec/docroot', + 'log_level' => 'bogus', + } end - - describe 'when suphp_engine is on and suphp { user & group } is specified' do - let :params do default_params.merge({ - :suphp_engine => 'on', - :directories => { 'path' => '/srv/www', - 'suphp' => { 'user' => 'myappuser', 'group' => 'myappgroup' }, - } - }) end - it 'should set suphp_UserGroup' do - should contain_file("25-#{title}.conf").with_content( - /^ suPHP_UserGroup myappuser myappgroup/ - ) - end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } + end + context 'access_log_file and access_log_pipe' do + let :params do + { + 'docroot' => '/rspec/docroot', + 'access_log_file' => 'bogus', + 'access_log_pipe' => 'bogus', + } end - - describe 'priority/default settings' do - describe 'when neither priority/default is specified' do - let :params do default_params end - it { should contain_file("25-#{title}.conf").with_path( - /25-#{title}.conf/ - ) } - end - describe 'when both priority/default_vhost is specified' do - let :params do - default_params.merge({ - :priority => 15, - :default_vhost => true, - }) - end - it { should contain_file("15-#{title}.conf").with_path( - /15-#{title}.conf/ - ) } - end - describe 'when only priority is specified' do - let :params do - default_params.merge({ :priority => 14, }) - end - it { should contain_file("14-#{title}.conf").with_path( - /14-#{title}.conf/ - ) } - end - describe 'when only default is specified' do - let :params do - default_params.merge({ :default_vhost => true, }) - end - it { should contain_file("10-#{title}.conf").with_path( - /10-#{title}.conf/ - ) } - end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } + end + context 'error_log_file and error_log_pipe' do + let :params do + { + 'docroot' => '/rspec/docroot', + 'error_log_file' => 'bogus', + 'error_log_pipe' => 'bogus', + } end - - describe 'various ip/port combos' do - describe 'when ip_based is true' do - let :params do default_params.merge({ :ip_based => true }) end - it 'should not specify a NameVirtualHost' do - should contain_apache__listen(params[:port]) - should_not contain_apache__namevirtualhost("*:#{params[:port]}") - end - end - - describe 'when ip_based is default' do - let :params do default_params end - it 'should specify a NameVirtualHost' do - should contain_apache__listen(params[:port]) - should contain_apache__namevirtualhost("*:#{params[:port]}") - end - end - - describe 'when an ip is set' do - let :params do default_params.merge({ :ip => '10.0.0.1' }) end - it 'should specify a NameVirtualHost for the ip' do - should_not contain_apache__listen(params[:port]) - should contain_apache__listen("10.0.0.1:#{params[:port]}") - should contain_apache__namevirtualhost("10.0.0.1:#{params[:port]}") - end - end - - describe 'an ip_based vhost without a port' do - let :params do - { - :docroot => '/fake', - :ip => '10.0.0.1', - :ip_based => true, - } - end - it 'should specify a NameVirtualHost for the ip' do - should_not contain_apache__listen(params[:ip]) - should_not contain_apache__namevirtualhost(params[:ip]) - should contain_file("25-#{title}.conf").with_content %r{} - end - end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } + end + context 'bad fallbackresource' do + let :params do + { + 'docroot' => '/rspec/docroot', + 'fallbackresource' => 'bogus', + } end - - describe 'redirect rules' do - describe 'without lockstep arrays' do - let :params do - default_params.merge({ - :redirect_source => [ - '/login', - '/logout', - ], - :redirect_dest => [ - 'http://10.0.0.10/login', - 'http://10.0.0.10/logout', - ], - :redirect_status => [ - 'permanent', - '', - ], - }) - end - - it { should contain_file("25-#{title}.conf").with_content %r{ Redirect permanent /login http://10\.0\.0\.10/login} } - it { should contain_file("25-#{title}.conf").with_content %r{ Redirect /logout http://10\.0\.0\.10/logout} } - end - describe 'redirect match rules' do - let :params do - default_params.merge({ - :redirectmatch_status => [ - '404', - ], - :redirectmatch_regexp => [ - '/\.git(/.*|$)', - ], - }) - end - - it { should contain_file("25-#{title}.conf").with_content %r{ RedirectMatch 404 } } - end - describe 'without a status' do - let :params do - default_params.merge({ - :redirect_source => [ - '/login', - '/logout', - ], - :redirect_dest => [ - 'http://10.0.0.10/login', - 'http://10.0.0.10/logout', - ], - }) - end - - it { should contain_file("25-#{title}.conf").with_content %r{ Redirect /login http://10\.0\.0\.10/login} } - it { should contain_file("25-#{title}.conf").with_content %r{ Redirect /logout http://10\.0\.0\.10/logout} } - end - describe 'with a single status and dest' do - let :params do - default_params.merge({ - :redirect_source => [ - '/login', - '/logout', - ], - :redirect_dest => 'http://10.0.0.10/test', - :redirect_status => 'permanent', - }) - end - - it { should contain_file("25-#{title}.conf").with_content %r{ Redirect permanent /login http://10\.0\.0\.10/test} } - it { should contain_file("25-#{title}.conf").with_content %r{ Redirect permanent /logout http://10\.0\.0\.10/test} } - end - - describe 'with a directoryindex specified' do - let :params do - default_params.merge({ - :directoryindex => 'index.php' - }) - end - it { should contain_file("25-#{title}.conf").with_content %r{DirectoryIndex index.php} } - end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } + end + context 'bad custom_fragment' do + let :params do + { + 'docroot' => '/rspec/docroot', + 'custom_fragment' => true, + } end + let :facts do default_facts end + it { expect { is_expected.to compile }.to raise_error } end end end diff --git a/apache/spec/fixtures/files/negotiation.conf b/apache/spec/fixtures/files/negotiation.conf new file mode 100644 index 000000000..c0bb8b9fd --- /dev/null +++ b/apache/spec/fixtures/files/negotiation.conf @@ -0,0 +1,4 @@ +# This is a file only for spec testing + +LanguagePriority en ca cs da de el eo es et fr he hr it ja ko ltz nl nn no pl pt pt-BR ru sv zh-CN zh-TW +ForceLanguagePriority Prefer Fallback diff --git a/apache/spec/fixtures/files/spec b/apache/spec/fixtures/files/spec new file mode 100644 index 000000000..76e9a1446 --- /dev/null +++ b/apache/spec/fixtures/files/spec @@ -0,0 +1 @@ +# This is a file only for spec testing diff --git a/apache/spec/fixtures/templates/negotiation.conf.erb b/apache/spec/fixtures/templates/negotiation.conf.erb new file mode 100644 index 000000000..557502246 --- /dev/null +++ b/apache/spec/fixtures/templates/negotiation.conf.erb @@ -0,0 +1,4 @@ +# This is a template only for spec testing + +LanguagePriority en ca cs da de el eo es et fr he hr it ja ko ltz nl nn no pl pt pt-BR ru sv zh-CN zh-TW +ForceLanguagePriority Prefer Fallback diff --git a/apache/spec/spec.opts b/apache/spec/spec.opts index de653df4b..91cd6427e 100644 --- a/apache/spec/spec.opts +++ b/apache/spec/spec.opts @@ -1,4 +1,6 @@ ---format s +--format +s --colour ---loadby mtime +--loadby +mtime --backtrace diff --git a/apache/spec/spec_helper.rb b/apache/spec/spec_helper.rb index 2c6f56649..65379ee38 100644 --- a/apache/spec/spec_helper.rb +++ b/apache/spec/spec_helper.rb @@ -1 +1,25 @@ require 'puppetlabs_spec_helper/module_spec_helper' + +RSpec.configure do |c| + c.treat_symbols_as_metadata_keys_with_true_values = true + + c.before :each do + # Ensure that we don't accidentally cache facts and environment + # between test cases. + Facter::Util::Loader.any_instance.stubs(:load_all) + Facter.clear + Facter.clear_messages + + # Store any environment variables away to be restored later + @old_env = {} + ENV.each_key {|k| @old_env[k] = ENV[k]} + + if ENV['STRICT_VARIABLES'] == 'yes' + Puppet.settings[:strict_variables]=true + end + end +end + +shared_examples :compile, :compile => true do + it { should compile.with_all_deps } +end diff --git a/apache/spec/spec_helper_acceptance.rb b/apache/spec/spec_helper_acceptance.rb index 7d334ae9b..94178fbd7 100644 --- a/apache/spec/spec_helper_acceptance.rb +++ b/apache/spec/spec_helper_acceptance.rb @@ -1,14 +1,19 @@ require 'beaker-rspec/spec_helper' require 'beaker-rspec/helpers/serverspec' -hosts.each do |host| - if host['platform'] =~ /debian/ - on host, 'echo \'export PATH=/var/lib/gems/1.8/bin/:${PATH}\' >> ~/.bashrc' - end - if host.is_pe? - install_pe - else - install_puppet + +unless ENV['RS_PROVISION'] == 'no' + # This will install the latest available package on el and deb based + # systems fail on windows and osx, and install via gem on other *nixes + foss_opts = { :default_action => 'gem_install' } + + if default.is_pe?; then install_pe; else install_puppet( foss_opts ); end + + hosts.each do |host| + if host['platform'] =~ /debian/ + on host, 'echo \'export PATH=/var/lib/gems/1.8/bin/:${PATH}\' >> ~/.bashrc' + end + on host, "mkdir -p #{host['distmoduledir']}" end end @@ -25,12 +30,16 @@ # Configure all nodes in nodeset c.before :suite do # Install module and dependencies - puppet_module_install(:source => proj_root, :module_name => 'apache') hosts.each do |host| + copy_module_to(host, :source => proj_root, :module_name => 'apache') # Required for mod_passenger tests. if fact('osfamily') == 'RedHat' on host, puppet('module','install','stahnma/epel'), { :acceptable_exit_codes => [0,1] } end + # Required for manifest to make mod_pagespeed repository available + if fact('osfamily') == 'Debian' + on host, puppet('module','install','puppetlabs-apt'), { :acceptable_exit_codes => [0,1] } + end on host, puppet('module','install','puppetlabs-stdlib'), { :acceptable_exit_codes => [0,1] } on host, puppet('module','install','puppetlabs-concat'), { :acceptable_exit_codes => [0,1] } end diff --git a/apache/spec/unit/provider/a2mod/gentoo_spec.rb b/apache/spec/unit/provider/a2mod/gentoo_spec.rb index ddb9dddda..78f902bf7 100644 --- a/apache/spec/unit/provider/a2mod/gentoo_spec.rb +++ b/apache/spec/unit/provider/a2mod/gentoo_spec.rb @@ -11,7 +11,7 @@ [:conf_file, :instances, :modules, :initvars, :conf_file, :clear].each do |method| it "should respond to the class method #{method}" do - provider_class.should respond_to(method) + expect(provider_class).to respond_to(method) end end @@ -24,21 +24,21 @@ @filetype.expects(:read).returns(%Q{APACHE2_OPTS="-D FOO -D BAR -D BAZ"\n}) provider_class.expects(:filetype).returns(@filetype) - provider_class.modules.should == %w{bar baz foo} + expect(provider_class.modules).to eq(%w{bar baz foo}) end it "should cache the module list" do @filetype.expects(:read).once.returns(%Q{APACHE2_OPTS="-D FOO -D BAR -D BAZ"\n}) provider_class.expects(:filetype).once.returns(@filetype) - 2.times { provider_class.modules.should == %w{bar baz foo} } + 2.times { expect(provider_class.modules).to eq(%w{bar baz foo}) } end it "should normalize parameters" do @filetype.expects(:read).returns(%Q{APACHE2_OPTS="-D FOO -D BAR -D BAR"\n}) provider_class.expects(:filetype).returns(@filetype) - provider_class.modules.should == %w{bar foo} + expect(provider_class.modules).to eq(%w{bar foo}) end end diff --git a/apache/spec/unit/puppet/parser/functions/bool2httpd_spec.rb b/apache/spec/unit/puppet/parser/functions/bool2httpd_spec.rb new file mode 100644 index 000000000..b0bcbb622 --- /dev/null +++ b/apache/spec/unit/puppet/parser/functions/bool2httpd_spec.rb @@ -0,0 +1,54 @@ +#! /usr/bin/env ruby -S rspec +require 'spec_helper' + +describe "the bool2httpd function" do + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it "should exist" do + expect(Puppet::Parser::Functions.function("bool2httpd")).to eq("function_bool2httpd") + end + + it "should raise a ParseError if there is less than 1 arguments" do + expect { scope.function_bool2httpd([]) }.to( raise_error(Puppet::ParseError)) + end + + it "should convert true to 'On'" do + result = scope.function_bool2httpd([true]) + expect(result).to(eq('On')) + end + + it "should convert true to a string" do + result = scope.function_bool2httpd([true]) + expect(result.class).to(eq(String)) + end + + it "should convert false to 'Off'" do + result = scope.function_bool2httpd([false]) + expect(result).to(eq('Off')) + end + + it "should convert false to a string" do + result = scope.function_bool2httpd([false]) + expect(result.class).to(eq(String)) + end + + it "should accept (and return) any string" do + result = scope.function_bool2httpd(["mail"]) + expect(result).to(eq('mail')) + end + + it "should accept a nil value (and return Off)" do + result = scope.function_bool2httpd([nil]) + expect(result).to(eq('Off')) + end + + it "should accept an undef value (and return 'Off')" do + result = scope.function_bool2httpd([:undef]) + expect(result).to(eq('Off')) + end + + it "should return a default value on non-matches" do + result = scope.function_bool2httpd(['foo']) + expect(result).to(eq('foo')) + end +end diff --git a/apache/templates/fastcgi/server.erb b/apache/templates/fastcgi/server.erb new file mode 100644 index 000000000..9cb25b76e --- /dev/null +++ b/apache/templates/fastcgi/server.erb @@ -0,0 +1,3 @@ +FastCGIExternalServer <%= @faux_path %> -idle-timeout <%= @timeout %> <%= if @flush then '-flush' end %> -host <%= @host %> +Alias <%= @fcgi_alias %> <%= @faux_path %> +Action <%= @file_type %> <%= @fcgi_alias %> diff --git a/apache/templates/httpd.conf.erb b/apache/templates/httpd.conf.erb index 66b70836b..54d24c8ae 100644 --- a/apache/templates/httpd.conf.erb +++ b/apache/templates/httpd.conf.erb @@ -8,7 +8,7 @@ ServerRoot "<%= @server_root %>" PidFile <%= @pidfile %> Timeout <%= @timeout %> KeepAlive <%= @keepalive %> -MaxKeepAliveRequests 100 +MaxKeepAliveRequests <%= @max_keepalive_requests %> KeepAliveTimeout <%= @keepalive_timeout %> User <%= @user %> @@ -16,7 +16,7 @@ Group <%= @group %> AccessFileName .htaccess -<%- if @apache_version >= '2.4' -%> +<%- if scope.function_versioncmp([@apache_version, '2.4']) >= 0 -%> Require all denied <%- else -%> Order allow,deny @@ -35,6 +35,9 @@ HostnameLookups Off ErrorLog "<%= @logroot %>/<%= @error_log %>" LogLevel <%= @log_level %> EnableSendfile <%= @sendfile %> +<%- if @allow_encoded_slashes -%> +AllowEncodedSlashes <%= @allow_encoded_slashes %> +<%- end -%> #Listen 80 @@ -55,14 +58,23 @@ LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combine LogFormat "%h %l %u %t \"%r\" %>s %b" common LogFormat "%{Referer}i -> %U" referer LogFormat "%{User-agent}i" agent +<% if @log_formats and !@log_formats.empty? -%> + <%- @log_formats.sort.each do |nickname,format| -%> +LogFormat "<%= format -%>" <%= nickname %> + <%- end -%> +<% end -%> -<%- if @apache_version >= '2.4' -%> +<%- if scope.function_versioncmp([@apache_version, '2.4']) >= 0 -%> IncludeOptional "<%= @confd_dir %>/*.conf" <%- else -%> Include "<%= @confd_dir %>/*.conf" <%- end -%> <% if @vhost_load_dir != @confd_dir -%> -Include "<%= @vhost_load_dir %>/*.conf" +<%- if scope.function_versioncmp([@apache_version, '2.4']) >= 0 -%> +IncludeOptional "<%= @vhost_load_dir %>/*" +<%- else -%> +Include "<%= @vhost_load_dir %>/*" +<%- end -%> <% end -%> <% if @error_documents -%> @@ -74,7 +86,7 @@ Alias /error/ "<%= @error_documents_path %>/" Options IncludesNoExec AddOutputFilter Includes html AddHandler type-map var -<%- if @apache_version == '2.4' -%> +<%- if scope.function_versioncmp([@apache_version, '2.4']) >= 0 -%> Require all granted <%- else -%> Order allow,deny diff --git a/apache/templates/mod/alias.conf.erb b/apache/templates/mod/alias.conf.erb index 0a0c81593..151a806c9 100644 --- a/apache/templates/mod/alias.conf.erb +++ b/apache/templates/mod/alias.conf.erb @@ -3,7 +3,7 @@ Alias /icons/ "<%= @icons_path %>/" "> Options Indexes MultiViews AllowOverride None -<%- if @apache_version == '2.4' -%> +<%- if scope.function_versioncmp([@apache_version, '2.4']) >= 0 -%> Require all granted <%- else -%> Order allow,deny diff --git a/apache/templates/mod/deflate.conf.erb b/apache/templates/mod/deflate.conf.erb index d0997dfeb..ede8b2e76 100644 --- a/apache/templates/mod/deflate.conf.erb +++ b/apache/templates/mod/deflate.conf.erb @@ -1,4 +1,7 @@ -AddOutputFilterByType DEFLATE text/html text/plain text/xml -AddOutputFilterByType DEFLATE text/css -AddOutputFilterByType DEFLATE application/x-javascript application/javascript application/ecmascript -AddOutputFilterByType DEFLATE application/rss+xml +<%- @types.sort.each do |type| -%> +AddOutputFilterByType DEFLATE <%= type %> +<%- end -%> + +<%- @notes.sort.each do |type,note| -%> +DeflateFilterNote <%= type %> <%=note %> +<%- end -%> diff --git a/apache/templates/mod/fcgid.conf.erb b/apache/templates/mod/fcgid.conf.erb new file mode 100644 index 000000000..a82bc30df --- /dev/null +++ b/apache/templates/mod/fcgid.conf.erb @@ -0,0 +1,5 @@ + +<% @options.sort_by {|key, value| key}.each do |key, value| -%> + <%= key %> <%= value %> +<% end -%> + diff --git a/apache/templates/mod/info.conf.erb b/apache/templates/mod/info.conf.erb index 0747da430..1a025b7a6 100644 --- a/apache/templates/mod/info.conf.erb +++ b/apache/templates/mod/info.conf.erb @@ -1,10 +1,19 @@ SetHandler server-info - <%- if @apache_version >= '2.4' -%> +<%- if @restrict_access -%> + <%- if scope.function_versioncmp([@apache_version, '2.4']) >= 0 -%> Require ip <%= Array(@allow_from).join(" ") %> - <%- else -%> + <%- else -%> Order deny,allow Deny from all - Allow from <%= Array(@allow_from).join(" ") %> + <%- if @allow_from and ! @allow_from.empty? -%> + <%- @allow_from.each do |allowed| -%> + Allow from <%= allowed %> + <%- end -%> + <%- else -%> + Allow from 127.0.0.1 + Allow from ::1 <%- end -%> + <%- end -%> +<%- end -%> diff --git a/apache/templates/mod/ldap.conf.erb b/apache/templates/mod/ldap.conf.erb index 14f33ab2b..001977617 100644 --- a/apache/templates/mod/ldap.conf.erb +++ b/apache/templates/mod/ldap.conf.erb @@ -1,7 +1,11 @@ SetHandler ldap-status + <%- if scope.function_versioncmp([@apache_version, '2.4']) >= 0 -%> + Require ip 127.0.0.1 ::1 + <%- else -%> Order deny,allow Deny from all Allow from 127.0.0.1 ::1 Satisfy all + <%- end -%> diff --git a/apache/templates/mod/load.erb b/apache/templates/mod/load.erb new file mode 100644 index 000000000..51f45edb2 --- /dev/null +++ b/apache/templates/mod/load.erb @@ -0,0 +1,7 @@ +<% if @loadfiles -%> +<% Array(@loadfiles).each do |loadfile| -%> +LoadFile <%= loadfile %> +<% end -%> + +<% end -%> +LoadModule <%= @_id %> <%= @_path %> diff --git a/apache/templates/mod/negotiation.conf.erb b/apache/templates/mod/negotiation.conf.erb index 50921019b..2fb4700d6 100644 --- a/apache/templates/mod/negotiation.conf.erb +++ b/apache/templates/mod/negotiation.conf.erb @@ -1,2 +1,2 @@ -LanguagePriority en ca cs da de el eo es et fr he hr it ja ko ltz nl nn no pl pt pt-BR ru sv zh-CN zh-TW -ForceLanguagePriority Prefer Fallback +LanguagePriority <%= Array(@language_priority).join(' ') %> +ForceLanguagePriority <%= Array(@force_language_priority).join(' ') %> diff --git a/apache/templates/mod/pagespeed.conf.erb b/apache/templates/mod/pagespeed.conf.erb new file mode 100644 index 000000000..a1b6f117a --- /dev/null +++ b/apache/templates/mod/pagespeed.conf.erb @@ -0,0 +1,98 @@ +ModPagespeed on + +ModPagespeedInheritVHostConfig <%= @inherit_vhost_config %> +AddOutputFilterByType MOD_PAGESPEED_OUTPUT_FILTER text/html +<% if @filter_xhtml -%> +AddOutputFilterByType MOD_PAGESPEED_OUTPUT_FILTER application/xhtml+xml +<% end -%> +ModPagespeedFileCachePath "<%= @cache_path %>" +ModPagespeedLogDir "<%= @log_dir %>" + +<% @memcache_servers.each do |server| -%> +ModPagespeedMemcachedServers <%= server %> +<% end -%> + +ModPagespeedRewriteLevel <%= @rewrite_level -%> + +<% @disable_filters.each do |filter| -%> +ModPagespeedDisableFilters <%= filter %> +<% end -%> + +<% @enable_filters.each do |filter| -%> +ModPagespeedEnableFilters <%= filter %> +<% end -%> + +<% @forbid_filters.each do |filter| -%> +ModPagespeedForbidFilters <%= filter %> +<% end -%> + +ModPagespeedRewriteDeadlinePerFlushMs <%= @rewrite_deadline_per_flush_ms %> + +<% if @additional_domains -%> +ModPagespeedDomain <%= @additional_domains -%> +<% end -%> + +ModPagespeedFileCacheSizeKb <%= @file_cache_size_kb %> +ModPagespeedFileCacheCleanIntervalMs <%= @file_cache_clean_interval_ms %> +ModPagespeedLRUCacheKbPerProcess <%= @lru_cache_per_process %> +ModPagespeedLRUCacheByteLimit <%= @lru_cache_byte_limit %> +ModPagespeedCssFlattenMaxBytes <%= @css_flatten_max_bytes %> +ModPagespeedCssInlineMaxBytes <%= @css_inline_max_bytes %> +ModPagespeedCssImageInlineMaxBytes <%= @css_image_inline_max_bytes %> +ModPagespeedImageInlineMaxBytes <%= @image_inline_max_bytes %> +ModPagespeedJsInlineMaxBytes <%= @js_inline_max_bytes %> +ModPagespeedCssOutlineMinBytes <%= @css_outline_min_bytes %> +ModPagespeedJsOutlineMinBytes <%= @js_outline_min_bytes %> + + +ModPagespeedFileCacheInodeLimit <%= @inode_limit %> +ModPagespeedImageMaxRewritesAtOnce <%= @image_max_rewrites_at_once %> + +ModPagespeedNumRewriteThreads <%= @num_rewrite_threads %> +ModPagespeedNumExpensiveRewriteThreads <%= @num_expensive_rewrite_threads %> + +ModPagespeedStatistics <%= @collect_statistics %> + + + # You may insert other "Allow from" lines to add hosts you want to + # allow to look at generated statistics. Another possibility is + # to comment out the "Order" and "Allow" options from the config + # file, to allow any client that can reach your server to examine + # statistics. This might be appropriate in an experimental setup or + # if the Apache server is protected by a reverse proxy that will + # filter URLs in some fashion. + <%- if scope.function_versioncmp([@apache_version, '2.4']) >= 0 -%> + Require ip 127.0.0.1 ::1 <%= Array(@allow_view_stats).join(" ") %> + <%- else -%> + Order allow,deny + Allow from 127.0.0.1 ::1 <%= Array(@allow_view_stats).join(" ") %> + <%- end -%> + SetHandler mod_pagespeed_statistics + + +ModPagespeedStatisticsLogging <%= @statistics_logging %> + + <%- if scope.function_versioncmp([@apache_version, '2.4']) >= 0 -%> + Require ip 127.0.0.1 ::1 <%= Array(@allow_pagespeed_console).join(" ") %> + <%- else -%> + Order allow,deny + Allow from 127.0.0.1 ::1 <%= Array(@allow_pagespeed_console).join(" ") %> + <%- end -%> + SetHandler pagespeed_console + + +ModPagespeedMessageBufferSize <%= @message_buffer_size %> + + + <%- if scope.function_versioncmp([@apache_version, '2.4']) >= 0 -%> + Require ip 127.0.0.1 ::1 <%= Array(@allow_pagespeed_message).join(" ") %> + <%- else -%> + Order allow,deny + Allow from 127.0.0.1 ::1 <%= Array(@allow_pagespeed_message).join(" ") %> + <%- end -%> + SetHandler mod_pagespeed_message + + +<% @additional_configuration.each_pair do |key, value| -%> +<%= key %> <%= value %> +<% end -%> diff --git a/apache/templates/mod/passenger.conf.erb b/apache/templates/mod/passenger.conf.erb index 63c3f9e61..dd9eee3b1 100644 --- a/apache/templates/mod/passenger.conf.erb +++ b/apache/templates/mod/passenger.conf.erb @@ -7,6 +7,9 @@ <%- if @passenger_ruby -%> PassengerRuby "<%= @passenger_ruby %>" <%- end -%> + <%- if @passenger_default_ruby -%> + PassengerDefaultRuby "<%= @passenger_default_ruby %>" + <%- end -%> <%- if @passenger_high_performance -%> PassengerHighPerformance <%= @passenger_high_performance %> <%- end -%> diff --git a/apache/templates/mod/php5.conf.erb b/apache/templates/mod/php5.conf.erb index 9eef7628a..44df2ae06 100644 --- a/apache/templates/mod/php5.conf.erb +++ b/apache/templates/mod/php5.conf.erb @@ -14,7 +14,7 @@ # # Cause the PHP interpreter to handle files with a .php extension. # -AddHandler php5-script .php +AddHandler php5-script <%= @extensions.flatten.compact.join(' ') %> AddType text/html .php # diff --git a/apache/templates/mod/proxy.conf.erb b/apache/templates/mod/proxy.conf.erb index 1f4a4129c..5ea829eeb 100644 --- a/apache/templates/mod/proxy.conf.erb +++ b/apache/templates/mod/proxy.conf.erb @@ -10,9 +10,13 @@ <% if @proxy_requests != 'Off' or ( @allow_from and ! @allow_from.empty? ) -%> + <%- if scope.function_versioncmp([@apache_version, '2.4']) >= 0 -%> + Require ip <%= Array(@allow_from).join(" ") %> + <%- else -%> Order deny,allow Deny from all Allow from <%= Array(@allow_from).join(" ") %> + <%- end -%> <% end -%> diff --git a/apache/templates/mod/proxy_html.conf.erb b/apache/templates/mod/proxy_html.conf.erb index 7f5898ef7..fea15f393 100644 --- a/apache/templates/mod/proxy_html.conf.erb +++ b/apache/templates/mod/proxy_html.conf.erb @@ -1,9 +1,3 @@ -<% if @proxy_html_loadfiles -%> -<% Array(@proxy_html_loadfiles).each do |loadfile| -%> -LoadFile <%= loadfile %> -<% end -%> - -<% end -%> ProxyHTMLLinks a href ProxyHTMLLinks area href ProxyHTMLLinks link href diff --git a/apache/templates/mod/reqtimeout.conf.erb b/apache/templates/mod/reqtimeout.conf.erb index 9a18800da..6ffc5ffe2 100644 --- a/apache/templates/mod/reqtimeout.conf.erb +++ b/apache/templates/mod/reqtimeout.conf.erb @@ -1,2 +1,3 @@ -RequestReadTimeout header=20-40,minrate=500 -RequestReadTimeout body=10,minrate=500 +<% Array(@timeouts).each do |timeout| -%> +RequestReadTimeout <%= timeout %> +<%- end -%> diff --git a/apache/templates/mod/ssl.conf.erb b/apache/templates/mod/ssl.conf.erb index e1597f2f8..e92e37e7a 100644 --- a/apache/templates/mod/ssl.conf.erb +++ b/apache/templates/mod/ssl.conf.erb @@ -11,17 +11,17 @@ SSLSessionCache "shmcb:<%= @session_cache %>" SSLSessionCacheTimeout 300 <% if @ssl_compression -%> - SSLCompression Off + SSLCompression On <% end -%> - <% if @apache_version >= '2.4' -%> + <% if scope.function_versioncmp([@apache_version, '2.4']) >= 0 -%> Mutex <%= @ssl_mutex %> <% else -%> SSLMutex <%= @ssl_mutex %> <% end -%> SSLCryptoDevice builtin SSLHonorCipherOrder On - SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5 - SSLProtocol all -SSLv2 + SSLCipherSuite <%= @ssl_cipher %> + SSLProtocol <%= @ssl_protocol.compact.join(' ') %> <% if @ssl_options -%> SSLOptions <%= @ssl_options.compact.join(' ') %> <% end -%> diff --git a/apache/templates/mod/status.conf.erb b/apache/templates/mod/status.conf.erb index c00c16a78..84f2e0343 100644 --- a/apache/templates/mod/status.conf.erb +++ b/apache/templates/mod/status.conf.erb @@ -1,8 +1,12 @@ SetHandler server-status + <%- if scope.function_versioncmp([@apache_version, '2.4']) >= 0 -%> + Require ip <%= Array(@allow_from).join(" ") %> + <%- else -%> Order deny,allow Deny from all Allow from <%= Array(@allow_from).join(" ") %> + <%- end -%> ExtendedStatus <%= @extended_status %> diff --git a/apache/templates/mod/userdir.conf.erb b/apache/templates/mod/userdir.conf.erb index e4c6ba55d..add525d5e 100644 --- a/apache/templates/mod/userdir.conf.erb +++ b/apache/templates/mod/userdir.conf.erb @@ -8,12 +8,20 @@ AllowOverride FileInfo AuthConfig Limit Indexes Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec + <%- if scope.function_versioncmp([@apache_version, '2.4']) >= 0 -%> + Require all denied + <%- else -%> Order allow,deny Allow from all + <%- end -%> - Order deny,allow - Deny from all + <%- if scope.function_versioncmp([@apache_version, '2.4']) >= 0 -%> + Require all denied + <%- else -%> + Order allow,deny + Allow from all + <%- end -%> diff --git a/apache/templates/mod/worker.conf.erb b/apache/templates/mod/worker.conf.erb index f0bba3908..597e05f8d 100644 --- a/apache/templates/mod/worker.conf.erb +++ b/apache/templates/mod/worker.conf.erb @@ -6,4 +6,5 @@ MaxSpareThreads <%= @maxsparethreads %> ThreadsPerChild <%= @threadsperchild %> MaxRequestsPerChild <%= @maxrequestsperchild %> + ThreadLimit <%= @threadlimit %> diff --git a/apache/templates/vhost.conf.erb b/apache/templates/vhost.conf.erb index 0eb69b009..859a3ae7f 100644 --- a/apache/templates/vhost.conf.erb +++ b/apache/templates/vhost.conf.erb @@ -22,14 +22,17 @@ <% if @fallbackresource -%> FallbackResource <%= @fallbackresource %> <% end -%> +<%- if @allow_encoded_slashes -%> + AllowEncodedSlashes <%= @allow_encoded_slashes %> +<%- end -%> ## Directories, there should at least be a declaration for <%= @docroot %> <%= scope.function_template(['apache/vhost/_directories.erb']) -%> ## Load additional static includes -<% Array(@additional_includes).each do |include| %> +<% Array(@additional_includes).each do |include| -%> Include "<%= include %>" -<% end %> +<% end -%> ## Logging <% if @error_log -%> @@ -44,6 +47,7 @@ <% elsif @access_log -%> CustomLog "<%= @access_log_destination %>" <%= @_access_log_format %> <% end -%> +<%= scope.function_template(['apache/vhost/_action.erb']) -%> <%= scope.function_template(['apache/vhost/_block.erb']) -%> <%= scope.function_template(['apache/vhost/_error_document.erb']) -%> <%= scope.function_template(['apache/vhost/_proxy.erb']) -%> @@ -61,4 +65,5 @@ <%= scope.function_template(['apache/vhost/_wsgi.erb']) -%> <%= scope.function_template(['apache/vhost/_custom_fragment.erb']) -%> <%= scope.function_template(['apache/vhost/_fastcgi.erb']) -%> +<%= scope.function_template(['apache/vhost/_suexec.erb']) -%> diff --git a/apache/templates/vhost/_access_log.erb b/apache/templates/vhost/_access_log.erb new file mode 100644 index 000000000..1ec47ff71 --- /dev/null +++ b/apache/templates/vhost/_access_log.erb @@ -0,0 +1,5 @@ +<% if @access_log and @_access_log_env_var -%> + CustomLog "<%= @access_log_destination %>" <%= @_access_log_format %> <%= @_access_log_env_var %> +<% elsif @access_log -%> + CustomLog "<%= @access_log_destination %>" <%= @_access_log_format %> +<% end -%> diff --git a/apache/templates/vhost/_action.erb b/apache/templates/vhost/_action.erb new file mode 100644 index 000000000..8a0229059 --- /dev/null +++ b/apache/templates/vhost/_action.erb @@ -0,0 +1,4 @@ +<% if @action -%> + + Action <%= @action %> /cgi-bin virtual +<% end -%> diff --git a/apache/templates/vhost/_additional_includes.erb b/apache/templates/vhost/_additional_includes.erb new file mode 100644 index 000000000..d4d5f9134 --- /dev/null +++ b/apache/templates/vhost/_additional_includes.erb @@ -0,0 +1,5 @@ +<% Array(@additional_includes).each do |include| -%> + + ## Load additional static includes + Include "<%= include %>" +<% end -%> diff --git a/apache/templates/vhost/_aliases.erb b/apache/templates/vhost/_aliases.erb index 5fdd76ba2..f9771bc72 100644 --- a/apache/templates/vhost/_aliases.erb +++ b/apache/templates/vhost/_aliases.erb @@ -6,6 +6,10 @@ Alias <%= alias_statement["alias"] %> "<%= alias_statement["path"] %>" <%- elsif alias_statement["aliasmatch"] and alias_statement["aliasmatch"] != '' -%> AliasMatch <%= alias_statement["aliasmatch"] %> "<%= alias_statement["path"] %>" + <%- elsif alias_statement["scriptalias"] and alias_statement["scriptalias"] != '' -%> + ScriptAlias <%= alias_statement["scriptalias"] %> "<%= alias_statement["path"] %>" + <%- elsif alias_statement["scriptaliasmatch"] and alias_statement["scriptaliasmatch"] != '' -%> + ScriptAliasMatch <%= alias_statement["scriptaliasmatch"] %> "<%= alias_statement["path"] %>" <%- end -%> <%- end -%> <%- end -%> diff --git a/apache/templates/vhost/_block.erb b/apache/templates/vhost/_block.erb index f3c835d2c..d0776829d 100644 --- a/apache/templates/vhost/_block.erb +++ b/apache/templates/vhost/_block.erb @@ -4,7 +4,7 @@ <% if @block.include? 'scm' -%> # Block access to SCM directories. - <%- if @apache_version >= '2.4' -%> + <%- if scope.function_versioncmp([@apache_version, '2.4']) >= 0 -%> Require all denied <%- else -%> Deny From All diff --git a/apache/templates/vhost/_charsets.erb b/apache/templates/vhost/_charsets.erb new file mode 100644 index 000000000..ef83def4b --- /dev/null +++ b/apache/templates/vhost/_charsets.erb @@ -0,0 +1,4 @@ +<% if @add_default_charset -%> + + AddDefaultCharset <%= @add_default_charset %> +<% end -%> diff --git a/apache/templates/vhost/_directories.erb b/apache/templates/vhost/_directories.erb index 516d0798d..e756875f2 100644 --- a/apache/templates/vhost/_directories.erb +++ b/apache/templates/vhost/_directories.erb @@ -1,4 +1,6 @@ <% if @_directories and ! @_directories.empty? -%> + + ## Directories, there should at least be a declaration for <%= @docroot %> <%- [@_directories].flatten.compact.each do |directory| -%> <%- if directory['path'] and directory['path'] != '' -%> <%- if directory['provider'] and directory['provider'].match('(directory|location|files)') -%> @@ -10,7 +12,7 @@ <%- else -%> <%- provider = 'Directory' -%> <%- end -%> - <%- path = directory['path'] %> + <%- path = directory['path'] -%> <<%= provider %> "<%= path %>"> <%- if directory['headers'] -%> @@ -34,13 +36,20 @@ AllowOverride None <%- end -%> <%- end -%> - <%- if @apache_version == '2.4' -%> + <%- if scope.function_versioncmp([@apache_version, '2.4']) >= 0 -%> <%- if directory['require'] and directory['require'] != '' -%> Require <%= Array(directory['require']).join(' ') %> - <%- else -%> + <%- end -%> + <%- if directory['auth_require'] -%> + Require <%= directory['auth_require'] %> + <%- end -%> + <%- if !(directory['require'] and directory['require'] != '') && !(directory['auth_require']) -%> Require all granted <%- end -%> <%- else -%> + <%- if directory['auth_require'] -%> + Require <%= directory['auth_require'] %> + <%- end -%> <%- if directory['order'] and directory['order'] != '' -%> Order <%= Array(directory['order']).join(',') %> <%- else -%> @@ -50,19 +59,31 @@ Deny <%= directory['deny'] %> <%- end -%> <%- if directory['allow'] and ! [ false, 'false', '' ].include?(directory['allow']) -%> + <%- if directory['allow'].kind_of?(Array) -%> + <%- Array(directory['allow']).each do |access| -%> + Allow <%= access %> + <%- end -%> + <%- else -%> Allow <%= directory['allow'] %> + <%- end -%> <%- elsif [ 'from all', 'from All' ].include?(directory['deny']) -%> <%- elsif ! directory['deny'] and [ false, 'false', '' ].include?(directory['allow']) -%> Deny from all <%- else -%> Allow from all <%- end -%> + <%- if directory['satisfy'] and directory['satisfy'] != '' -%> + Satisfy <%= directory['satisfy'] %> + <%- end -%> <%- end -%> <%- if directory['addhandlers'] and ! directory['addhandlers'].empty? -%> <%- [directory['addhandlers']].flatten.compact.each do |addhandler| -%> AddHandler <%= addhandler['handler'] %> <%= Array(addhandler['extensions']).join(' ') %> <%- end -%> <%- end -%> + <%- if directory['sethandler'] and directory['sethandler'] != '' -%> + SetHandler <%= directory['sethandler'] %> + <%- end -%> <%- if directory['passenger_enabled'] and directory['passenger_enabled'] != '' -%> PassengerEnabled <%= directory['passenger_enabled'] %> <%- end -%> @@ -124,9 +145,6 @@ <%- if directory['auth_group_file'] -%> AuthGroupFile <%= directory['auth_group_file'] %> <%- end -%> - <%- if directory['auth_require'] -%> - Require <%= directory['auth_require'] %> - <%- end -%> <%- if directory['fallbackresource'] -%> FallbackResource <%= directory['fallbackresource'] %> <%- end -%> @@ -150,6 +168,46 @@ <%- if directory['suphp'] and @suphp_engine == 'on' -%> suPHP_UserGroup <%= directory['suphp']['user'] %> <%= directory['suphp']['group'] %> <%- end -%> + <%- if directory['fcgiwrapper'] -%> + FcgidWrapper <%= directory['fcgiwrapper']['command'] %> <%= directory['fcgiwrapper']['suffix'] %> <%= directory['fcgiwrapper']['virtual'] %> + <%- end -%> + <%- if directory['rewrites'] -%> + # Rewrite rules + RewriteEngine On + <%- directory['rewrites'].flatten.compact.each do |rewrite_details| -%> + <%- if rewrite_details['comment'] -%> + #<%= rewrite_details['comment'] %> + <%- end -%> + <%- if rewrite_details['rewrite_base'] -%> + RewriteBase <%= rewrite_details['rewrite_base'] %> + <%- end -%> + <%- if rewrite_details['rewrite_cond'] -%> + <%- Array(rewrite_details['rewrite_cond']).each do |commands| -%> + <%- Array(commands).each do |command| -%> + RewriteCond <%= command %> + <%- end -%> + <%- end -%> + <%- end -%> + <%- Array(rewrite_details['rewrite_rule']).each do |commands| -%> + <%- Array(commands).each do |command| -%> + RewriteRule <%= command %> + <%- end -%> + <%- end -%> + <%- end -%> + <%- end -%> + <%- if @shibboleth_enabled -%> + <%- if directory['shib_require_session'] and ! directory['shib_require_session'].empty? -%> + ShibRequireSession <%= directory['shib_require_session'] %> + <%- end -%> + <%- if directory['shib_request_settings'] and ! directory['shib_request_settings'].empty? -%> + <%- directory['shib_request_settings'].each do |key,value| -%> + ShibRequestSetting <%= key %> <%= value %> + <%- end -%> + <%- end -%> + <%- if directory['shib_use_headers'] and ! directory['shib_use_headers'].empty? -%> + ShibUseHeaders <%= directory['shib_use_headers'] %> + <%- end -%> + <%- end -%> <%- if directory['custom_fragment'] -%> <%= directory['custom_fragment'] %> <%- end -%> diff --git a/apache/templates/vhost/_docroot.erb b/apache/templates/vhost/_docroot.erb new file mode 100644 index 000000000..6039fa63c --- /dev/null +++ b/apache/templates/vhost/_docroot.erb @@ -0,0 +1,7 @@ + + ## Vhost docroot +<% if @virtual_docroot -%> + VirtualDocumentRoot "<%= @virtual_docroot %>" +<% else -%> + DocumentRoot "<%= @docroot %>" +<% end -%> diff --git a/apache/templates/vhost/_fallbackresource.erb b/apache/templates/vhost/_fallbackresource.erb new file mode 100644 index 000000000..f1e4c35dc --- /dev/null +++ b/apache/templates/vhost/_fallbackresource.erb @@ -0,0 +1,4 @@ +<% if @fallbackresource -%> + + FallbackResource <%= @fallbackresource %> +<% end -%> diff --git a/apache/templates/vhost/_fastcgi.erb b/apache/templates/vhost/_fastcgi.erb index 07129bc19..3a2baa559 100644 --- a/apache/templates/vhost/_fastcgi.erb +++ b/apache/templates/vhost/_fastcgi.erb @@ -8,7 +8,7 @@ Options +ExecCGI AllowOverride All SetHandler fastcgi-script - <%- if @apache_version >= '2.4' -%> + <%- if scope.function_versioncmp([@apache_version, '2.4']) >= 0 -%> Require all granted <%- else -%> Order allow,deny diff --git a/apache/templates/vhost/_file_footer.erb b/apache/templates/vhost/_file_footer.erb new file mode 100644 index 000000000..84035efa4 --- /dev/null +++ b/apache/templates/vhost/_file_footer.erb @@ -0,0 +1 @@ + diff --git a/apache/templates/vhost/_file_header.erb b/apache/templates/vhost/_file_header.erb new file mode 100644 index 000000000..e6f2f95e7 --- /dev/null +++ b/apache/templates/vhost/_file_header.erb @@ -0,0 +1,10 @@ +# ************************************ +# Vhost template in module puppetlabs-apache +# Managed by Puppet +# ************************************ + +> + ServerName <%= @servername %> +<% if @serveradmin -%> + ServerAdmin <%= @serveradmin %> +<% end -%> diff --git a/apache/templates/vhost/_itk.erb b/apache/templates/vhost/_itk.erb index 2971c7a7d..803a73db7 100644 --- a/apache/templates/vhost/_itk.erb +++ b/apache/templates/vhost/_itk.erb @@ -1,4 +1,5 @@ <% if @itk and ! @itk.empty? -%> + ## ITK statement <%- if @itk["user"] and @itk["group"] -%> diff --git a/apache/templates/vhost/_logging.erb b/apache/templates/vhost/_logging.erb new file mode 100644 index 000000000..35a924d29 --- /dev/null +++ b/apache/templates/vhost/_logging.erb @@ -0,0 +1,10 @@ +<% if @error_log or @log_level -%> + + ## Logging +<% end -%> +<% if @error_log -%> + ErrorLog "<%= @error_log_destination %>" +<% end -%> +<% if @log_level -%> + LogLevel <%= @log_level %> +<% end -%> diff --git a/apache/templates/vhost/_passenger.erb b/apache/templates/vhost/_passenger.erb new file mode 100644 index 000000000..df2a86d37 --- /dev/null +++ b/apache/templates/vhost/_passenger.erb @@ -0,0 +1,15 @@ +<% if @passenger_app_root -%> + PassengerAppRoot <%= @passenger_app_root %> +<% end -%> +<% if @passenger_ruby -%> + PassengerRuby <%= @passenger_ruby %> +<% end -%> +<% if @passenger_min_instances -%> + PassengerMinInstances <%= @passenger_min_instances %> +<% end -%> +<% if @passenger_start_timeout -%> + PassengerStartTimeout <%= @passenger_start_timeout %> +<% end -%> +<% if @passenger_pre_start -%> + PassengerPreStart <%= @passenger_pre_start %> +<% end -%> diff --git a/apache/templates/vhost/_php_admin.erb b/apache/templates/vhost/_php_admin.erb index 59536cbc9..c0c8dd60a 100644 --- a/apache/templates/vhost/_php_admin.erb +++ b/apache/templates/vhost/_php_admin.erb @@ -1,12 +1,12 @@ <% if @php_admin_values and not @php_admin_values.empty? -%> -<% @php_admin_values.each do |key,value| -%> + <%- @php_admin_values.sort.each do |key,value| -%> php_admin_value <%= key %> <%= value %> -<% end -%> + <%- end -%> <% end -%> <% if @php_admin_flags and not @php_admin_flags.empty? -%> -<% @php_admin_flags.each do |key,flag| -%> -<%# normalize flag -%> -<% if flag =~ /true|yes|on|1/i then flag = 'on' else flag = 'off' end -%> + <%- @php_admin_flags.sort.each do |key,flag| -%> + <%-# normalize flag -%> + <%- if flag =~ /true|yes|on|1/i then flag = 'on' else flag = 'off' end -%> php_admin_flag <%= key %> <%= flag %> -<% end -%> + <%- end -%> <% end -%> diff --git a/apache/templates/vhost/_proxy.erb b/apache/templates/vhost/_proxy.erb index 7e0221f95..dd25e2c87 100644 --- a/apache/templates/vhost/_proxy.erb +++ b/apache/templates/vhost/_proxy.erb @@ -3,16 +3,31 @@ ## Proxy rules ProxyRequests Off <%- end -%> -<% [@proxy_pass].flatten.compact.each do |proxy| %> - ProxyPass <%= proxy['path'] %> <%= proxy['url'] %> +<% if @proxy_preserve_host -%> + ProxyPreserveHost On +<%- end -%> +<%- [@proxy_pass].flatten.compact.each do |proxy| -%> + ProxyPass <%= proxy['path'] %> <%= proxy['url'] -%> + <%- if proxy['params'] -%> + <%- proxy['params'].each_pair do |key, value| -%> <%= key %>=<%= value -%> + <%- end -%> + <%- end -%> + <%- if proxy['keywords'] %> <%= proxy['keywords'].join(' ') -%> + <%- end %> > + <%- if proxy['reverse_urls'].nil? -%> ProxyPassReverse <%= proxy['url'] %> + <%- else -%> + <%- Array(proxy['reverse_urls']).each do |reverse_url| -%> + ProxyPassReverse <%= reverse_url %> + <%- end -%> + <%- end -%> -<% end %> +<% end -%> <% if @proxy_dest -%> -<% Array(@no_proxy_uris).each do |uri| %> +<%- Array(@no_proxy_uris).each do |uri| -%> ProxyPass <%= uri %> ! -<% end %> +<% end -%> ProxyPass / <%= @proxy_dest %>/ ProxyPassReverse <%= @proxy_dest %>/ diff --git a/apache/templates/vhost/_redirect.erb b/apache/templates/vhost/_redirect.erb index e865bd9af..69bbfd09d 100644 --- a/apache/templates/vhost/_redirect.erb +++ b/apache/templates/vhost/_redirect.erb @@ -4,21 +4,22 @@ <% @redirect_status_a = Array(@redirect_status) -%> ## Redirect rules -<% @redirect_source_a.each_with_index do |source, i| -%> + <%- @redirect_source_a.each_with_index do |source, i| -%> <% @redirect_dest_a[i] ||= @redirect_dest_a[0] -%> <% @redirect_status_a[i] ||= @redirect_status_a[0] -%> Redirect <%= "#{@redirect_status_a[i]} " %><%= source %> <%= @redirect_dest_a[i] %> + <%- end -%> <% end -%> -<% end -%> - -<%- if @redirectmatch_status and @redirectmatch_regexp -%> +<%- if @redirectmatch_status and @redirectmatch_regexp and @redirectmatch_dest -%> <% @redirectmatch_status_a = Array(@redirectmatch_status) -%> <% @redirectmatch_regexp_a = Array(@redirectmatch_regexp) -%> +<% @redirectmatch_dest_a = Array(@redirectmatch_dest) -%> ## RedirectMatch rules -<% @redirectmatch_status_a.each_with_index do |status, i| -%> + <%- @redirectmatch_status_a.each_with_index do |status, i| -%> <% @redirectmatch_status_a[i] ||= @redirectmatch_status_a[0] -%> <% @redirectmatch_regexp_a[i] ||= @redirectmatch_regexp_a[0] -%> - RedirectMatch <%= "#{@redirectmatch_status_a[i]} " %> <%= @redirectmatch_regexp_a[i] %> -<% end -%> +<% @redirectmatch_dest_a[i] ||= @redirectmatch_dest_a[0] -%> + RedirectMatch <%= "#{@redirectmatch_status_a[i]} " %> <%= @redirectmatch_regexp_a[i] %> <%= @redirectmatch_dest_a[i] %> + <%- end -%> <% end -%> diff --git a/apache/templates/vhost/_rewrite.erb b/apache/templates/vhost/_rewrite.erb index af8b45001..46dd95691 100644 --- a/apache/templates/vhost/_rewrite.erb +++ b/apache/templates/vhost/_rewrite.erb @@ -27,17 +27,17 @@ <%- end -%> <%- end -%> <%- end -%> -<%# reverse compatibility %> +<%# reverse compatibility -%> <% if @rewrite_rule and !@rewrites -%> ## Rewrite rules RewriteEngine On -<% if @rewrite_base -%> + <%- if @rewrite_base -%> RewriteBase <%= @rewrite_base %> -<% end -%> -<% if @rewrite_cond -%> -<% Array(@rewrite_cond).each do |cond| -%> + <%- end -%> + <%- if @rewrite_cond -%> + <%- Array(@rewrite_cond).each do |cond| -%> RewriteCond <%= cond %> -<% end -%> -<% end -%> + <%- end -%> + <%- end -%> RewriteRule <%= @rewrite_rule %> <%- end -%> diff --git a/apache/templates/vhost/_scriptalias.erb b/apache/templates/vhost/_scriptalias.erb index 5a757f617..bb4f6b316 100644 --- a/apache/templates/vhost/_scriptalias.erb +++ b/apache/templates/vhost/_scriptalias.erb @@ -10,7 +10,7 @@ ## Script alias directives <%# Combine scriptalais and scriptaliases into a single data structure -%> <%# for backward compatibility and ease of implementation -%> -<%- aliases << { 'alias' => '/cgi-bin/', 'path' => @scriptalias } if @scriptalias -%> +<%- aliases << { 'alias' => '/cgi-bin', 'path' => @scriptalias } if @scriptalias -%> <%- aliases.flatten.compact! -%> <%- aliases.each do |salias| -%> <%- if salias["path"] != '' -%> diff --git a/apache/templates/vhost/_serveralias.erb b/apache/templates/vhost/_serveralias.erb index 278b6ddc5..e08a55e32 100644 --- a/apache/templates/vhost/_serveralias.erb +++ b/apache/templates/vhost/_serveralias.erb @@ -1,7 +1,7 @@ <% if @serveraliases and ! @serveraliases.empty? -%> ## Server aliases -<% Array(@serveraliases).each do |serveralias| -%> + <%- Array(@serveraliases).each do |serveralias| -%> ServerAlias <%= serveralias %> -<% end -%> + <%- end -%> <% end -%> diff --git a/apache/templates/vhost/_serversignature.erb b/apache/templates/vhost/_serversignature.erb new file mode 100644 index 000000000..ff13aaf45 --- /dev/null +++ b/apache/templates/vhost/_serversignature.erb @@ -0,0 +1 @@ + ServerSignature Off diff --git a/apache/templates/vhost/_setenv.erb b/apache/templates/vhost/_setenv.erb index d5f9ea845..ce1fa955e 100644 --- a/apache/templates/vhost/_setenv.erb +++ b/apache/templates/vhost/_setenv.erb @@ -1,12 +1,12 @@ <% if @setenv and ! @setenv.empty? -%> ## SetEnv/SetEnvIf for environment variables -<% Array(@setenv).each do |envvar| -%> + <%- Array(@setenv).each do |envvar| -%> SetEnv <%= envvar %> -<% end -%> + <%- end -%> <% end -%> <% if @setenvif and ! @setenvif.empty? -%> -<% Array(@setenvif).each do |envifvar| -%> + <%- Array(@setenvif).each do |envifvar| -%> SetEnvIf <%= envifvar %> -<% end -%> + <%- end -%> <% end -%> diff --git a/apache/templates/vhost/_ssl.erb b/apache/templates/vhost/_ssl.erb index 03c78ef42..516992558 100644 --- a/apache/templates/vhost/_ssl.erb +++ b/apache/templates/vhost/_ssl.erb @@ -4,38 +4,43 @@ SSLEngine on SSLCertificateFile "<%= @ssl_cert %>" SSLCertificateKeyFile "<%= @ssl_key %>" -<% if @ssl_chain -%> + <%- if @ssl_chain -%> SSLCertificateChainFile "<%= @ssl_chain %>" -<% end -%> + <%- end -%> + <%- if @ssl_certs_dir && @ssl_certs_dir != '' -%> SSLCACertificatePath "<%= @ssl_certs_dir %>" -<% if @ssl_ca -%> + <%- end -%> + <%- if @ssl_ca -%> SSLCACertificateFile "<%= @ssl_ca %>" -<% end -%> -<% if @ssl_crl_path -%> + <%- end -%> + <%- if @ssl_crl_path -%> SSLCARevocationPath "<%= @ssl_crl_path %>" -<% end -%> -<% if @ssl_crl -%> + <%- end -%> + <%- if @ssl_crl -%> SSLCARevocationFile "<%= @ssl_crl %>" -<% end -%> -<% if @ssl_proxyengine -%> + <%- end -%> + <%- if @ssl_crl_check && scope.function_versioncmp([@apache_version, '2.4']) >= 0 -%> + SSLCARevocationCheck "<%= @ssl_crl_check %>" + <%- end -%> + <%- if @ssl_proxyengine -%> SSLProxyEngine On -<% end -%> -<% if @ssl_protocol -%> + <%- end -%> + <%- if @ssl_protocol -%> SSLProtocol <%= @ssl_protocol %> -<% end -%> -<% if @ssl_cipher -%> + <%- end -%> + <%- if @ssl_cipher -%> SSLCipherSuite <%= @ssl_cipher %> -<% end -%> -<% if @ssl_honorcipherorder -%> + <%- end -%> + <%- if @ssl_honorcipherorder -%> SSLHonorCipherOrder <%= @ssl_honorcipherorder %> -<% end -%> -<% if @ssl_verify_client -%> + <%- end -%> + <%- if @ssl_verify_client -%> SSLVerifyClient <%= @ssl_verify_client %> -<% end -%> -<% if @ssl_verify_depth -%> + <%- end -%> + <%- if @ssl_verify_depth -%> SSLVerifyDepth <%= @ssl_verify_depth %> -<% end -%> -<% if @ssl_options -%> + <%- end -%> + <%- if @ssl_options -%> SSLOptions <%= Array(@ssl_options).join(' ') %> -<% end -%> + <%- end -%> <% end -%> diff --git a/apache/templates/vhost/_suexec.erb b/apache/templates/vhost/_suexec.erb new file mode 100644 index 000000000..8a7ae0f17 --- /dev/null +++ b/apache/templates/vhost/_suexec.erb @@ -0,0 +1,4 @@ +<% if @suexec_user_group -%> + + SuexecUserGroup <%= @suexec_user_group %> +<% end -%> diff --git a/apache/templates/vhost/_suphp.erb b/apache/templates/vhost/_suphp.erb index 938958180..e394b6f94 100644 --- a/apache/templates/vhost/_suphp.erb +++ b/apache/templates/vhost/_suphp.erb @@ -1,11 +1,11 @@ <% if @suphp_engine == 'on' -%> -<% if @suphp_addhandler -%> + <%- if @suphp_addhandler -%> suPHP_AddHandler <%= @suphp_addhandler %> -<% end -%> -<% if @suphp_engine -%> + <%- end -%> + <%- if @suphp_engine -%> suPHP_Engine <%= @suphp_engine %> -<% end -%> -<% if @suphp_configpath -%> + <%- end -%> + <%- if @suphp_configpath -%> suPHP_ConfigPath "<%= @suphp_configpath %>" -<% end -%> + <%- end -%> <% end -%> diff --git a/apache/templates/vhost/_wsgi.erb b/apache/templates/vhost/_wsgi.erb index 474c30ff1..a0d4ded65 100644 --- a/apache/templates/vhost/_wsgi.erb +++ b/apache/templates/vhost/_wsgi.erb @@ -19,3 +19,9 @@ <%- end -%> <%- end -%> <% end -%> +<% if @wsgi_pass_authorization -%> + WSGIPassAuthorization <%= @wsgi_pass_authorization %> +<% end -%> +<% if @wsgi_chunked_request -%> + WSGIChunkedRequest <%= @wsgi_chunked_request %> +<% end -%> diff --git a/apache/tests/dev.pp b/apache/tests/dev.pp index 805ad7e37..6c4f95571 100644 --- a/apache/tests/dev.pp +++ b/apache/tests/dev.pp @@ -1 +1 @@ -include apache::dev +include apache::mod::dev diff --git a/apache/tests/vhost.pp b/apache/tests/vhost.pp index f0d3f58e4..4ffb78299 100644 --- a/apache/tests/vhost.pp +++ b/apache/tests/vhost.pp @@ -13,12 +13,13 @@ docroot => '/var/www/first', } -# Vhost with different docroot owner/group +# Vhost with different docroot owner/group/mode apache::vhost { 'second.example.com': port => '80', docroot => '/var/www/second', docroot_owner => 'third', docroot_group => 'third', + docroot_mode => '0770', } # Vhost with serveradmin @@ -142,14 +143,14 @@ # Vhost to redirect non-ssl to ssl apache::vhost { 'sixteenth.example.com non-ssl': - servername => 'sixteenth.example.com', - port => '80', - docroot => '/var/www/sixteenth', - rewrites => [ + servername => 'sixteenth.example.com', + port => '80', + docroot => '/var/www/sixteenth', + rewrites => [ { - comment => 'redirect non-SSL traffic to SSL site', - rewrite_cond => ['%{HTTPS} off'], - rewrite_rule => ['(.*) https://%{HTTPS_HOST}%{REQUEST_URI}'], + comment => 'redirect non-SSL traffic to SSL site', + rewrite_cond => ['%{HTTPS} off'], + rewrite_rule => ['(.*) https://%{HTTPS_HOST}%{REQUEST_URI}'], } ] } @@ -214,18 +215,18 @@ # Vhost with SSLProtocol,SSLCipherSuite, SSLHonorCipherOrder apache::vhost { 'securedomain.com': - priority => '10', - vhost_name => 'www.securedomain.com', - port => '443', - docroot => '/var/www/secure', - ssl => true, - ssl_cert => '/etc/ssl/securedomain.cert', - ssl_key => '/etc/ssl/securedomain.key', - ssl_chain => '/etc/ssl/securedomain.crt', - ssl_protocol => '-ALL +SSLv3 +TLSv1', - ssl_cipher => 'ALL:!aNULL:!ADH:!eNULL:!LOW:!EXP:RC4+RSA:+HIGH:+MEDIUM', - ssl_honorcipherorder => 'On', - add_listen => false, + priority => '10', + vhost_name => 'www.securedomain.com', + port => '443', + docroot => '/var/www/secure', + ssl => true, + ssl_cert => '/etc/ssl/securedomain.cert', + ssl_key => '/etc/ssl/securedomain.key', + ssl_chain => '/etc/ssl/securedomain.crt', + ssl_protocol => '-ALL +SSLv3 +TLSv1', + ssl_cipher => 'ALL:!aNULL:!ADH:!eNULL:!LOW:!EXP:RC4+RSA:+HIGH:+MEDIUM', + ssl_honorcipherorder => 'On', + add_listen => false, } # Vhost with access log environment variables writing control diff --git a/apache/tests/vhost_proxypass.pp b/apache/tests/vhost_proxypass.pp new file mode 100644 index 000000000..e911f85f9 --- /dev/null +++ b/apache/tests/vhost_proxypass.pp @@ -0,0 +1,66 @@ +## vhost with proxyPass directive +# NB: Please see the other vhost_*.pp example files for further +# examples. + +# Base class. Declares default vhost on port 80 and default ssl +# vhost on port 443 listening on all interfaces and serving +# $apache::docroot +class { 'apache': } + +# Most basic vhost with proxy_pass +apache::vhost { 'first.example.com': + port => 80, + docroot => '/var/www/first', + proxy_pass => [ + { + 'path' => '/first', + 'url' => 'http://localhost:8080/first' + }, + ], +} + +# vhost with proxy_pass and parameters +apache::vhost { 'second.example.com': + port => 80, + docroot => '/var/www/second', + proxy_pass => [ + { + 'path' => '/second', + 'url' => 'http://localhost:8080/second', + 'params' => { + 'retry' => '0', + 'timeout' => '5' + } + }, + ], +} + +# vhost with proxy_pass and keywords +apache::vhost { 'third.example.com': + port => 80, + docroot => '/var/www/third', + proxy_pass => [ + { + 'path' => '/third', + 'url' => 'http://localhost:8080/third', + 'keywords' => ['noquery', 'interpolate'] + }, + ], +} + +# vhost with proxy_pass, parameters and keywords +apache::vhost { 'fourth.example.com': + port => 80, + docroot => '/var/www/fourth', + proxy_pass => [ + { + 'path' => '/fourth', + 'url' => 'http://localhost:8080/fourth', + 'params' => { + 'retry' => '0', + 'timeout' => '5' + }, + 'keywords' => ['noquery', 'interpolate'] + }, + ], +} diff --git a/apt/.fixtures.yml b/apt/.fixtures.yml new file mode 100644 index 000000000..dbd5621de --- /dev/null +++ b/apt/.fixtures.yml @@ -0,0 +1,5 @@ +fixtures: + repositories: + "stdlib": "git://github.com/puppetlabs/puppetlabs-stdlib.git" + symlinks: + "apt": "#{source_dir}" diff --git a/apt/.gitignore b/apt/.gitignore new file mode 100644 index 000000000..b77434bea --- /dev/null +++ b/apt/.gitignore @@ -0,0 +1,4 @@ +*.swp +pkg/ +Gemfile.lock +spec/fixtures/manifests diff --git a/apt/.nodeset.yml b/apt/.nodeset.yml new file mode 100644 index 000000000..ad056fe65 --- /dev/null +++ b/apt/.nodeset.yml @@ -0,0 +1,19 @@ +--- +default_set: 'ubuntu-server-12042-x64' +sets: + 'debian-607-x64': + nodes: + "main.foo.vm": + prefab: 'debian-607-x64' + 'debian-70rc1-x64': + nodes: + "main.foo.vm": + prefab: 'debian-70rc1-x64' + 'ubuntu-server-10044-x64': + nodes: + "main.foo.vm": + prefab: 'ubuntu-server-10044-x64' + 'ubuntu-server-12042-x64': + nodes: + "main.foo.vm": + prefab: 'ubuntu-server-12042-x64' diff --git a/apt/.project b/apt/.project new file mode 100644 index 000000000..6523c6daf --- /dev/null +++ b/apt/.project @@ -0,0 +1,23 @@ + + + apt + + + + + + org.cloudsmith.geppetto.pp.dsl.ui.modulefileBuilder + + + + + org.eclipse.xtext.ui.shared.xtextBuilder + + + + + + org.cloudsmith.geppetto.pp.dsl.ui.puppetNature + org.eclipse.xtext.ui.shared.xtextNature + + diff --git a/apt/.travis.yml b/apt/.travis.yml new file mode 100644 index 000000000..00924bac0 --- /dev/null +++ b/apt/.travis.yml @@ -0,0 +1,40 @@ +--- +branches: + only: + - master +language: ruby +bundler_args: --without development +script: bundle exec rake spec SPEC_OPTS='--format documentation' +after_success: + - git clone -q git://github.com/puppetlabs/ghpublisher.git .forge-release + - .forge-release/publish +rvm: + - 1.8.7 + - 1.9.3 + - 2.0.0 +env: + matrix: + - PUPPET_GEM_VERSION="~> 2.7.0" + - PUPPET_GEM_VERSION="~> 3.0.0" + - PUPPET_GEM_VERSION="~> 3.1.0" + - PUPPET_GEM_VERSION="~> 3.2.0" + global: + - PUBLISHER_LOGIN=puppetlabs + - secure: |- + ipB/CV1rVSTXU9ZDuzrFOlzJrRmJob36tKns2xszuH4r9s5P9qivNAngRGdV + msb69xvOlzQykM0WRF+4kJ6TZ7AbMiDI+VZ8GDtsRaU5/q3BpsvFe8aato+6 + QeyFtBG62OsosTEhGws4mqiFsPDu3dHlakuJc9zevlTuhNwbKSs= +matrix: + exclude: + - rvm: 1.9.3 + env: PUPPET_GEM_VERSION="~> 2.7.0" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 2.7.0" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 3.0.0" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 3.1.0" + - rvm: 1.8.7 + env: PUPPET_GEM_VERSION="~> 3.2.0" +notifications: + email: false diff --git a/apt/CHANGELOG b/apt/CHANGELOG new file mode 100644 index 000000000..b6ec7cf7e --- /dev/null +++ b/apt/CHANGELOG @@ -0,0 +1,183 @@ +2013-10-08 1.4.0 + +Summary: + +Minor bugfix and allow the timeout to be adjusted. + +Features: +- Add an `updates_timeout` to apt::params + +Fixes: +- Ensure apt::ppa can read a ppa removed by hand. + +Summary + +1.3.0 +===== + +Summary: + +This major feature in this release is the new apt::unattended_upgrades class, +allowing you to handle Ubuntu's unattended feature. This allows you to select +specific packages to automatically upgrade without any further user +involvement. + +In addition we extend our Wheezy support, add proxy support to apt:ppa and do +various cleanups and tweaks. + +Features: +- Add apt::unattended_upgrades support for Ubuntu. +- Add wheezy backports support. +- Use the geoDNS http.debian.net instead of the main debian ftp server. +- Add `options` parameter to apt::ppa in order to pass options to apt-add-repository command. +- Add proxy support for apt::ppa (uses proxy_host and proxy_port from apt). + +Bugfixes: +- Fix regsubst() calls to quote single letters (for future parser). +- Fix lint warnings and other misc cleanup. + +1.2.0 +===== + +Features: +- Add geppetto `.project` natures +- Add GH auto-release +- Add `apt::key::key_options` parameter +- Add complex pin support using distribution properties for `apt::pin` via new properties: + - `apt::pin::codename` + - `apt::pin::release_version` + - `apt::pin::component` + - `apt::pin::originator` + - `apt::pin::label` +- Add source architecture support to `apt::source::architecture` + +Bugfixes: +- Use apt-get instead of aptitude in apt::force +- Update default backports location +- Add dependency for required packages before apt-get update + + +1.1.1 +===== + +This is a bug fix release that resolves a number of issues: + +* By changing template variable usage, we remove the deprecation warnings + for Puppet 3.2.x +* Fixed proxy file removal, when proxy absent + +Some documentation, style and whitespaces changes were also merged. This +release also introduced proper rspec-puppet unit testing on Travis-CI to help +reduce regression. + +Thanks to all the community contributors below that made this patch possible. + +#### Detail Changes + +* fix minor comment type (Chris Rutter) +* whitespace fixes (Michael Moll) +* Update travis config file (William Van Hevelingen) +* Build all branches on travis (William Van Hevelingen) +* Standardize travis.yml on pattern introduced in stdlib (William Van Hevelingen) +* Updated content to conform to README best practices template (Lauren Rother) +* Fix apt::release example in readme (Brian Galey) +* add @ to variables in template (Peter Hoeg) +* Remove deprecation warnings for pin.pref.erb as well (Ken Barber) +* Update travis.yml to latest versions of puppet (Ken Barber) +* Fix proxy file removal (Scott Barber) +* Add spec test for removing proxy configuration (Dean Reilly) +* Fix apt::key listing longer than 8 chars (Benjamin Knofe) + + +--------------------------------------- + +1.1.0 +===== + +This release includes Ubuntu 12.10 (Quantal) support for PPAs. + +--------------------------------------- + +2012-05-25 Puppet Labs - 0.0.4 + * Fix ppa list filename when there is a period in the PPA name + * Add .pref extension to apt preferences files + * Allow preferences to be purged + * Extend pin support + +2012-05-04 Puppet Labs - 0.0.3 + * only invoke apt-get update once + * only install python-software-properties if a ppa is added + * support 'ensure => absent' for all defined types + * add apt::conf + * add apt::backports + * fixed Modulefile for module tool dependency resolution + * configure proxy before doing apt-get update + * use apt-get update instead of aptitude for apt::ppa + * add support to pin release + + +2012-03-26 Puppet Labs - 0.0.2 +41cedbb (#13261) Add real examples to smoke tests. +d159a78 (#13261) Add key.pp smoke test +7116c7a (#13261) Replace foo source with puppetlabs source +1ead0bf Ignore pkg directory. +9c13872 (#13289) Fix some more style violations +0ea4ffa (#13289) Change test scaffolding to use a module & manifest dir fixture path +a758247 (#13289) Clean up style violations and fix corresponding tests +99c3fd3 (#13289) Add puppet lint tests to Rakefile +5148cbf (#13125) Apt keys should be case insensitive +b9607a4 Convert apt::key to use anchors + +2012-03-07 Puppet Labs - 0.0.1 +d4fec56 Modify apt::source release parameter test +1132a07 (#12917) Add contributors to README +8cdaf85 (#12823) Add apt::key defined type and modify apt::source to use it +7c0d10b (#12809) $release should use $lsbdistcodename and fall back to manual input +be2cc3e (#12522) Adjust spec test for splitting purge +7dc60ae (#12522) Split purge option to spare sources.list +9059c4e Fix source specs to test all key permutations +8acb202 Add test for python-software-properties package +a4af11f Check if python-software-properties is defined before attempting to define it. +1dcbf3d Add tests for required_packages change +f3735d2 Allow duplicate $required_packages +74c8371 (#12430) Add tests for changes to apt module +97ebb2d Test two sources with the same key +1160bcd (#12526) Add ability to reverse apt { disable_keys => true } +2842d73 Add Modulefile to puppet-apt +c657742 Allow the use of the same key in multiple sources +8c27963 (#12522) Adding purge option to apt class +997c9fd (#12529) Add unit test for apt proxy settings +50f3cca (#12529) Add parameter to support setting a proxy for apt +d522877 (#12094) Replace chained .with_* with a hash +8cf1bd0 (#12094) Remove deprecated spec.opts file +2d688f4 (#12094) Add rspec-puppet tests for apt +0fb5f78 (#12094) Replace name with path in file resources +f759bc0 (#11953) Apt::force passes $version to aptitude +f71db53 (#11413) Add spec test for apt::force to verify changes to unless +2f5d317 (#11413) Update dpkg query used by apt::force +cf6caa1 (#10451) Add test coverage to apt::ppa +0dd697d include_src parameter in example; Whitespace cleanup +b662eb8 fix typos in "repositories" +1be7457 Fix (#10451) - apt::ppa fails to "apt-get update" when new PPA source is added +864302a Set the pin priority before adding the source (Fix #10449) +1de4e0a Refactored as per mlitteken +1af9a13 Added some crazy bash madness to check if the ppa is installed already. Otherwise the manifest tries to add it on every run! +52ca73e (#8720) Replace Apt::Ppa with Apt::Builddep +5c05fa0 added builddep command. +a11af50 added the ability to specify the content of a key +c42db0f Fixes ppa test. +77d2b0d reformatted whitespace to match recommended style of 2 space indentation. +27ebdfc ignore swap files. +377d58a added smoke tests for module. +18f614b reformatted apt::ppa according to recommended style. +d8a1e4e Created a params class to hold global data. +636ae85 Added two params for apt class +148fc73 Update LICENSE. +ed2d19e Support ability to add more than one PPA +420d537 Add call to apt-update after add-apt-repository in apt::ppa +945be77 Add package definition for python-software-properties +71fc425 Abs paths for all commands +9d51cd1 Adding LICENSE +71796e3 Heading fix in README +87777d8 Typo in README +f848bac First commit diff --git a/apt/Gemfile b/apt/Gemfile new file mode 100644 index 000000000..15bd29daf --- /dev/null +++ b/apt/Gemfile @@ -0,0 +1,16 @@ +source 'https://rubygems.org' + +group :development, :test do + gem 'rake', :require => false + gem 'rspec-puppet', :require => false + gem 'puppetlabs_spec_helper', :require => false + gem 'rspec-system', :require => false + gem 'rspec-system-puppet', :require => false + gem 'rspec-system-serverspec', :require => false +end + +if puppetversion = ENV['PUPPET_GEM_VERSION'] + gem 'puppet', puppetversion, :require => false +else + gem 'puppet', :require => false +end diff --git a/apt/LICENSE b/apt/LICENSE new file mode 100644 index 000000000..638c347b1 --- /dev/null +++ b/apt/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Evolving Web Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/apt/Modulefile b/apt/Modulefile new file mode 100644 index 000000000..72aa142e5 --- /dev/null +++ b/apt/Modulefile @@ -0,0 +1,14 @@ +name 'puppetlabs-apt' +version '1.4.0' +source 'https://github.com/puppetlabs/puppetlabs-apt' +author 'Evolving Web / Puppet Labs' +license 'Apache License 2.0' +summary 'Puppet Labs Apt Module' +description 'APT Module for Puppet' +project_page 'https://github.com/puppetlabs/puppetlabs-apt' + +## Add dependencies, if any: +#dependency 'puppetlabs/stdlib', '2.x' +# The dependency should be written as above but librarian-puppet +# does not support the expression as the PMT does. +dependency 'puppetlabs/stdlib', '>= 2.2.1' diff --git a/apt/README.md b/apt/README.md new file mode 100644 index 000000000..d07bbb0ec --- /dev/null +++ b/apt/README.md @@ -0,0 +1,229 @@ +apt +=== + +[![Build Status](https://travis-ci.org/puppetlabs/puppetlabs-apt.png?branch=master)](https://travis-ci.org/puppetlabs/puppetlabs-apt) + +## Description + +Provides helpful definitions for dealing with Apt. + +======= + +Overview +-------- + +The APT module provides a simple interface for managing APT source, key, and definitions with Puppet. + +Module Description +------------------ + +APT automates obtaining and installing software packages on *nix systems. + +Setup +----- + +**What APT affects:** + +* package/service/configuration files for APT +* your system's `sources.list` file and `sources.list.d` directory + * NOTE: Setting the `purge_sources_list` and `purge_sources_list_d` parameters to 'true' will destroy any existing content that was not declared with Puppet. The default for these parameters is 'false'. +* system repositories +* authentication keys +* wget (optional) + +### Beginning with APT + +To begin using the APT module with default parameters, declare the class + + include apt + +Puppet code that uses anything from the APT module requires that the core apt class be declared. + +Usage +----- + +Using the APT module consists predominantly in declaring classes that provide desired functionality and features. + +### apt + +`apt` provides a number of common resources and options that are shared by the various defined types in this module, so you MUST always include this class in your manifests. + +The parameters for `apt` are not required in general and are predominantly for development environment use-cases. + + class { 'apt': + always_apt_update => false, + disable_keys => undef, + proxy_host => false, + proxy_port => '8080', + purge_sources_list => false, + purge_sources_list_d => false, + purge_preferences_d => false, + update_timeout => undef + } + +Puppet will manage your system's `sources.list` file and `sources.list.d` directory but will do its best to respect existing content. + +If you declare your apt class with `purge_sources_list` and `purge_sources_list_d` set to 'true', Puppet will unapologetically purge any existing content it finds that wasn't declared with Puppet. + +### apt::builddep + +Installs the build depends of a specified package. + + apt::builddep { 'glusterfs-server': } + +### apt::force + +Forces a package to be installed from a specific release. This class is particularly useful when using repositories, like Debian, that are unstable in Ubuntu. + + apt::force { 'glusterfs-server': + release => 'unstable', + version => '3.0.3', + require => Apt::Source['debian_unstable'], + } + +### apt::key + +Adds a key to the list of keys used by APT to authenticate packages. + + apt::key { 'puppetlabs': + key => '4BD6EC30', + key_server => 'pgp.mit.edu', + } + + apt::key { 'jenkins': + key => 'D50582E6', + key_source => 'http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key', + } + +Note that use of `key_source` requires wget to be installed and working. + +### apt::pin + +Adds an apt pin for a certain release. + + apt::pin { 'karmic': priority => 700 } + apt::pin { 'karmic-updates': priority => 700 } + apt::pin { 'karmic-security': priority => 700 } + +Note you can also specifying more complex pins using distribution properties. + + apt::pin { 'stable': + priority => -10, + originator => 'Debian', + release_version => '3.0', + component => 'main', + label => 'Debian' + } + +### apt::ppa + +Adds a ppa repository using `add-apt-repository`. + + apt::ppa { 'ppa:drizzle-developers/ppa': } + +### apt::release + +Sets the default apt release. This class is particularly useful when using repositories, like Debian, that are unstable in Ubuntu. + + class { 'apt::release': + release_id => 'precise', + } + +### apt::source + +Adds an apt source to `/etc/apt/sources.list.d/`. + + apt::source { 'debian_unstable': + location => 'http://debian.mirror.iweb.ca/debian/', + release => 'unstable', + repos => 'main contrib non-free', + required_packages => 'debian-keyring debian-archive-keyring', + key => '55BE302B', + key_server => 'subkeys.pgp.net', + pin => '-10', + include_src => true + } + +If you would like to configure your system so the source is the Puppet Labs APT repository + + apt::source { 'puppetlabs': + location => 'http://apt.puppetlabs.com', + repos => 'main', + key => '4BD6EC30', + key_server => 'pgp.mit.edu', + } + +### Testing + +The APT module is mostly a collection of defined resource types, which provide reusable logic that can be leveraged to manage APT. It does provide smoke tests for testing functionality on a target system, as well as spec tests for checking a compiled catalog against an expected set of resources. + +#### Example Test + +This test will set up a Puppet Labs apt repository. Start by creating a new smoke test in the apt module's test folder. Call it puppetlabs-apt.pp. Inside, declare a single resource representing the Puppet Labs APT source and gpg key + + apt::source { 'puppetlabs': + location => 'http://apt.puppetlabs.com', + repos => 'main', + key => '4BD6EC30', + key_server => 'pgp.mit.edu', + } + +This resource creates an apt source named puppetlabs and gives Puppet information about the repository's location and key used to sign its packages. Puppet leverages Facter to determine the appropriate release, but you can set it directly by adding the release type. + +Check your smoke test for syntax errors + + $ puppet parser validate tests/puppetlabs-apt.pp + +If you receive no output from that command, it means nothing is wrong. Then apply the code + + $ puppet apply --verbose tests/puppetlabs-apt.pp + notice: /Stage[main]//Apt::Source[puppetlabs]/File[puppetlabs.list]/ensure: defined content as '{md5}3be1da4923fb910f1102a233b77e982e' + info: /Stage[main]//Apt::Source[puppetlabs]/File[puppetlabs.list]: Scheduling refresh of Exec[puppetlabs apt update] + notice: /Stage[main]//Apt::Source[puppetlabs]/Exec[puppetlabs apt update]: Triggered 'refresh' from 1 events> + +The above example used a smoke test to easily lay out a resource declaration and apply it on your system. In production, you may want to declare your APT sources inside the classes where they’re needed. + +Implementation +-------------- + +### apt::backports + +Adds the necessary components to get backports for Ubuntu and Debian. The release name defaults to `$lsbdistcodename`. Setting this manually can cause undefined behavior (read: universe exploding). + +Limitations +----------- + +This module should work across all versions of Debian/Ubuntu and support all major APT repository management features. + +Development +------------ + +Puppet Labs modules on the Puppet Forge are open projects, and community contributions are essential for keeping them great. We can’t access the huge number of platforms and myriad of hardware, software, and deployment configurations that Puppet is intended to serve. + +We want to keep it as easy as possible to contribute changes so that our modules work in your environment. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things. + +You can read the complete module contribution guide [on the Puppet Labs wiki.](http://projects.puppetlabs.com/projects/module-site/wiki/Module_contributing) + +Contributors +------------ + +A lot of great people have contributed to this module. A somewhat current list follows: + +* Ben Godfrey +* Branan Purvine-Riley +* Christian G. Warden +* Dan Bode +* Garrett Honeycutt +* Jeff Wallace +* Ken Barber +* Matthaus Litteken +* Matthias Pigulla +* Monty Taylor +* Peter Drake +* Reid Vandewiele +* Robert Navarro +* Ryan Coleman +* Scott McLeod +* Spencer Krum +* William Van Hevelingen +* Zach Leslie diff --git a/galera/Rakefile b/apt/Rakefile similarity index 100% rename from galera/Rakefile rename to apt/Rakefile diff --git a/apt/manifests/backports.pp b/apt/manifests/backports.pp new file mode 100644 index 000000000..e9180c48c --- /dev/null +++ b/apt/manifests/backports.pp @@ -0,0 +1,48 @@ +# This adds the necessary components to get backports for ubuntu and debian +# +# == Parameters +# +# [*release*] +# The ubuntu/debian release name. Defaults to $lsbdistcodename. Setting this +# manually can cause undefined behavior. (Read: universe exploding) +# +# == Examples +# +# include apt::backports +# +# class { 'apt::backports': +# release => 'natty', +# } +# +# == Authors +# +# Ben Hughes, I think. At least blame him if this goes wrong. +# I just added puppet doc. +# +# == Copyright +# +# Copyright 2011 Puppet Labs Inc, unless otherwise noted. +class apt::backports( + $release = $::lsbdistcodename, + $location = $apt::params::backports_location +) inherits apt::params { + + $release_real = downcase($release) + $key = $::lsbdistid ? { + 'debian' => '55BE302B', + 'ubuntu' => '437D05B5', + } + $repos = $::lsbdistid ? { + 'debian' => 'main contrib non-free', + 'ubuntu' => 'main universe multiverse restricted', + } + + apt::source { 'backports': + location => $location, + release => "${release_real}-backports", + repos => $repos, + key => $key, + key_server => 'pgp.mit.edu', + pin => '200', + } +} diff --git a/apt/manifests/builddep.pp b/apt/manifests/builddep.pp new file mode 100644 index 000000000..3294f7133 --- /dev/null +++ b/apt/manifests/builddep.pp @@ -0,0 +1,16 @@ +# builddep.pp + +define apt::builddep() { + include apt::update + + exec { "apt-builddep-${name}": + command => "/usr/bin/apt-get -y --force-yes build-dep ${name}", + logoutput => 'on_failure', + notify => Exec['apt_update'], + } + + # Need anchor to provide containment for dependencies. + anchor { "apt::builddep::${name}": + require => Class['apt::update'], + } +} diff --git a/apt/manifests/conf.pp b/apt/manifests/conf.pp new file mode 100644 index 000000000..3c4cb1975 --- /dev/null +++ b/apt/manifests/conf.pp @@ -0,0 +1,18 @@ +define apt::conf ( + $content, + $ensure = present, + $priority = '50' +) { + + include apt::params + + $apt_conf_d = $apt::params::apt_conf_d + + file { "${apt_conf_d}/${priority}${name}": + ensure => $ensure, + content => $content, + owner => root, + group => root, + mode => '0644', + } +} diff --git a/apt/manifests/debian/testing.pp b/apt/manifests/debian/testing.pp new file mode 100644 index 000000000..45133470f --- /dev/null +++ b/apt/manifests/debian/testing.pp @@ -0,0 +1,21 @@ +# testing.pp + +class apt::debian::testing { + include apt + + # deb http://debian.mirror.iweb.ca/debian/ testing main contrib non-free + # deb-src http://debian.mirror.iweb.ca/debian/ testing main contrib non-free + # Key: 55BE302B Server: subkeys.pgp.net + # debian-keyring + # debian-archive-keyring + + apt::source { 'debian_testing': + location => 'http://debian.mirror.iweb.ca/debian/', + release => 'testing', + repos => 'main contrib non-free', + required_packages => 'debian-keyring debian-archive-keyring', + key => '55BE302B', + key_server => 'subkeys.pgp.net', + pin => '-10', + } +} diff --git a/apt/manifests/debian/unstable.pp b/apt/manifests/debian/unstable.pp new file mode 100644 index 000000000..401c9c5cd --- /dev/null +++ b/apt/manifests/debian/unstable.pp @@ -0,0 +1,21 @@ +# unstable.pp + +class apt::debian::unstable { + include apt + + # deb http://debian.mirror.iweb.ca/debian/ unstable main contrib non-free + # deb-src http://debian.mirror.iweb.ca/debian/ unstable main contrib non-free + # Key: 55BE302B Server: subkeys.pgp.net + # debian-keyring + # debian-archive-keyring + + apt::source { 'debian_unstable': + location => 'http://debian.mirror.iweb.ca/debian/', + release => 'unstable', + repos => 'main contrib non-free', + required_packages => 'debian-keyring debian-archive-keyring', + key => '55BE302B', + key_server => 'subkeys.pgp.net', + pin => '-10', + } +} diff --git a/apt/manifests/force.pp b/apt/manifests/force.pp new file mode 100644 index 000000000..152bb6735 --- /dev/null +++ b/apt/manifests/force.pp @@ -0,0 +1,42 @@ +# force.pp +# force a package from a specific release + +define apt::force( + $release = false, + $version = false, + $timeout = 300 +) { + + $provider = $apt::params::provider + + $version_string = $version ? { + false => undef, + default => "=${version}", + } + + $release_string = $release ? { + false => undef, + default => "-t ${release}", + } + + if $version == false { + if $release == false { + $install_check = "/usr/bin/dpkg -s ${name} | grep -q 'Status: install'" + } else { + # If installed version and candidate version differ, this check returns 1 (false). + $install_check = "/usr/bin/test \$(/usr/bin/apt-cache policy -t ${release} ${name} | /bin/grep -E 'Installed|Candidate' | /usr/bin/uniq -s 14 | /usr/bin/wc -l) -eq 1" + } + } else { + if $release == false { + $install_check = "/usr/bin/dpkg -s ${name} | grep -q 'Version: ${version}'" + } else { + $install_check = "/usr/bin/apt-cache policy -t ${release} ${name} | /bin/grep -q 'Installed: ${version}'" + } + } + + exec { "${provider} -y ${release_string} install ${name}${version_string}": + unless => $install_check, + logoutput => 'on_failure', + timeout => $timeout, + } +} diff --git a/apt/manifests/init.pp b/apt/manifests/init.pp new file mode 100644 index 000000000..b106ad490 --- /dev/null +++ b/apt/manifests/init.pp @@ -0,0 +1,121 @@ +# Class: apt +# +# This module manages the initial configuration of apt. +# +# Parameters: +# The parameters listed here are not required in general and were +# added for use cases related to development environments. +# disable_keys - disables the requirement for all packages to be signed +# always_apt_update - rather apt should be updated on every run (intended +# for development environments where package updates are frequent) +# purge_sources_list - Accepts true or false. Defaults to false If set to +# true, Puppet will purge all unmanaged entries from sources.list +# purge_sources_list_d - Accepts true or false. Defaults to false. If set +# to true, Puppet will purge all unmanaged entries from sources.list.d +# update_timeout - Overrides the exec timeout in seconds for apt-get update. +# If not set defaults to Exec's default (300) +# +# Actions: +# +# Requires: +# puppetlabs/stdlib +# Sample Usage: +# class { 'apt': } + +class apt( + $always_apt_update = false, + $disable_keys = undef, + $proxy_host = false, + $proxy_port = '8080', + $purge_sources_list = false, + $purge_sources_list_d = false, + $purge_preferences_d = false, + $update_timeout = undef +) { + + include apt::params + include apt::update + + validate_bool($purge_sources_list, $purge_sources_list_d, $purge_preferences_d) + + $sources_list_content = $purge_sources_list ? { + false => undef, + true => "# Repos managed by puppet.\n", + } + + if $always_apt_update == true { + Exec <| title=='apt_update' |> { + refreshonly => false, + } + } + + $root = $apt::params::root + $apt_conf_d = $apt::params::apt_conf_d + $sources_list_d = $apt::params::sources_list_d + $preferences_d = $apt::params::preferences_d + $provider = $apt::params::provider + + file { 'sources.list': + ensure => present, + path => "${root}/sources.list", + owner => root, + group => root, + mode => '0644', + content => $sources_list_content, + notify => Exec['apt_update'], + } + + file { 'sources.list.d': + ensure => directory, + path => $sources_list_d, + owner => root, + group => root, + purge => $purge_sources_list_d, + recurse => $purge_sources_list_d, + notify => Exec['apt_update'], + } + + file { 'preferences.d': + ensure => directory, + path => $preferences_d, + owner => root, + group => root, + purge => $purge_preferences_d, + recurse => $purge_preferences_d, + } + + case $disable_keys { + true: { + file { '99unauth': + ensure => present, + content => "APT::Get::AllowUnauthenticated 1;\n", + path => "${apt_conf_d}/99unauth", + } + } + false: { + file { '99unauth': + ensure => absent, + path => "${apt_conf_d}/99unauth", + } + } + undef: { } # do nothing + default: { fail('Valid values for disable_keys are true or false') } + } + + $proxy_set = $proxy_host ? { + false => absent, + default => present + } + + file { 'configure-apt-proxy': + ensure => $proxy_set, + path => "${apt_conf_d}/proxy", + content => "Acquire::http::Proxy \"http://${proxy_host}:${proxy_port}\";", + notify => Exec['apt_update'], + } + + # Need anchor to provide containment for dependencies. + anchor { 'apt::update': + require => Class['apt::update'], + } +} diff --git a/apt/manifests/key.pp b/apt/manifests/key.pp new file mode 100644 index 000000000..c78bf658c --- /dev/null +++ b/apt/manifests/key.pp @@ -0,0 +1,90 @@ +define apt::key ( + $key = $title, + $ensure = present, + $key_content = false, + $key_source = false, + $key_server = 'keyserver.ubuntu.com', + $key_options = false +) { + + include apt::params + + $upkey = upcase($key) + # trim the key to the last 8 chars so we can match longer keys with apt-key list too + $trimmedkey = regsubst($upkey, '^.*(.{8})$', '\1') + + if $key_content { + $method = 'content' + } elsif $key_source { + $method = 'source' + } elsif $key_server { + $method = 'server' + } + + # This is a hash of the parts of the key definition that we care about. + # It is used as a unique identifier for this instance of apt::key. It gets + # hashed to ensure that the resource name doesn't end up being pages and + # pages (e.g. in the situation where key_content is specified). + $digest = sha1("${upkey}/${key_content}/${key_source}/${key_server}/") + + # Allow multiple ensure => present for the same key to account for many + # apt::source resources that all reference the same key. + case $ensure { + present: { + + anchor { "apt::key/${title}": } + + if defined(Exec["apt::key ${upkey} absent"]) { + fail("Cannot ensure Apt::Key[${upkey}] present; ${upkey} already ensured absent") + } + + if !defined(Anchor["apt::key ${upkey} present"]) { + anchor { "apt::key ${upkey} present": } + } + + if $key_options{ + $options_string = "--keyserver-options ${key_options}" + } + else{ + $options_string = '' + } + + if !defined(Exec[$digest]) { + $digest_command = $method ? { + 'content' => "echo '${key_content}' | /usr/bin/apt-key add -", + 'source' => "wget -q '${key_source}' -O- | apt-key add -", + 'server' => "apt-key adv --keyserver '${key_server}' ${options_string} --recv-keys '${upkey}'", + } + exec { $digest: + command => $digest_command, + path => '/bin:/usr/bin', + unless => "/usr/bin/apt-key list | /bin/grep '${trimmedkey}'", + logoutput => 'on_failure', + before => Anchor["apt::key ${upkey} present"], + } + } + + Anchor["apt::key ${upkey} present"] -> Anchor["apt::key/${title}"] + + } + absent: { + + if defined(Anchor["apt::key ${upkey} present"]) { + fail("Cannot ensure Apt::Key[${upkey}] absent; ${upkey} already ensured present") + } + + exec { "apt::key ${upkey} absent": + command => "apt-key del '${upkey}'", + path => '/bin:/usr/bin', + onlyif => "apt-key list | grep '${trimmedkey}'", + user => 'root', + group => 'root', + logoutput => 'on_failure', + } + } + + default: { + fail "Invalid 'ensure' value '${ensure}' for aptkey" + } + } +} diff --git a/apt/manifests/params.pp b/apt/manifests/params.pp new file mode 100644 index 000000000..955954fe8 --- /dev/null +++ b/apt/manifests/params.pp @@ -0,0 +1,33 @@ +class apt::params { + $root = '/etc/apt' + $provider = '/usr/bin/apt-get' + $sources_list_d = "${root}/sources.list.d" + $apt_conf_d = "${root}/apt.conf.d" + $preferences_d = "${root}/preferences.d" + + case $::lsbdistid { + 'debian': { + case $::lsbdistcodename { + 'squeeze': { + $backports_location = 'http://backports.debian.org/debian-backports' + } + 'wheezy': { + $backports_location = 'http://ftp.debian.org/debian/' + } + default: { + $backports_location = 'http://http.debian.net/debian/' + } + } + } + 'ubuntu': { + case $::lsbdistcodename { + 'hardy','lucid','maverick','natty','oneiric','precise': { + $backports_location = 'http://us.archive.ubuntu.com/ubuntu' + } + default: { + $backports_location = 'http://old-releases.ubuntu.com/ubuntu' + } + } + } + } +} diff --git a/apt/manifests/pin.pp b/apt/manifests/pin.pp new file mode 100644 index 000000000..402e79ede --- /dev/null +++ b/apt/manifests/pin.pp @@ -0,0 +1,73 @@ +# pin.pp +# pin a release in apt, useful for unstable repositories + +define apt::pin( + $ensure = present, + $explanation = "${::caller_module_name}: ${name}", + $order = '', + $packages = '*', + $priority = 0, + $release = '', # a= + $origin = '', + $version = '', + $codename = '', # n= + $release_version = '', # v= + $component = '', # c= + $originator = '', # o= + $label = '' # l= +) { + + include apt::params + + $preferences_d = $apt::params::preferences_d + + if $order != '' and !is_integer($order) { + fail('Only integers are allowed in the apt::pin order param') + } + + $pin_release_array = [ + $release, + $codename, + $release_version, + $component, + $originator, + $label] + $pin_release = join($pin_release_array, '') + + # Read the manpage 'apt_preferences(5)', especially the chapter + # 'Thea Effect of APT Preferences' to understand the following logic + # and the difference between specific and general form + if $packages != '*' { # specific form + + if ( $pin_release != '' and ( $origin != '' or $version != '' )) or + ( $origin != '' and ( $pin_release != '' or $version != '' )) or + ( $version != '' and ( $pin_release != '' or $origin != '' )) { + fail('parameters release, origin, and version are mutually exclusive') + } + + } else { # general form + + if $version != '' { + fail('parameter version cannot be used in general form') + } + + if ( $pin_release != '' and $origin != '' ) or + ( $origin != '' and $pin_release != '' ) { + fail('parmeters release and origin are mutually exclusive') + } + + } + + $path = $order ? { + '' => "${preferences_d}/${name}.pref", + default => "${preferences_d}/${order}-${name}.pref", + } + file { "${name}.pref": + ensure => $ensure, + path => $path, + owner => root, + group => root, + mode => '0644', + content => template('apt/pin.pref.erb'), + } +} diff --git a/apt/manifests/ppa.pp b/apt/manifests/ppa.pp new file mode 100644 index 000000000..730bf7d19 --- /dev/null +++ b/apt/manifests/ppa.pp @@ -0,0 +1,73 @@ +# ppa.pp + +define apt::ppa( + $ensure = 'present', + $release = $::lsbdistcodename, + $options = '-y' +) { + include apt::params + include apt::update + + $sources_list_d = $apt::params::sources_list_d + + if ! $release { + fail('lsbdistcodename fact not available: release parameter required') + } + + $filename_without_slashes = regsubst($name, '/', '-', 'G') + $filename_without_dots = regsubst($filename_without_slashes, '\.', '_', 'G') + $filename_without_ppa = regsubst($filename_without_dots, '^ppa:', '', 'G') + $sources_list_d_filename = "${filename_without_ppa}-${release}.list" + + if $ensure == 'present' { + $package = $::lsbdistrelease ? { + /^[1-9]\..*|1[01]\..*|12.04$/ => 'python-software-properties', + default => 'software-properties-common', + } + + if ! defined(Package[$package]) { + package { $package: } + } + + if defined(Class[apt]) { + $proxy_host = $apt::proxy_host + $proxy_port = $apt::proxy_port + case $proxy_host { + false, '': { + $proxy_env = [] + } + default: {$proxy_env = ["http_proxy=http://${proxy_host}:${proxy_port}", "https_proxy=http://${proxy_host}:${proxy_port}"]} + } + } else { + $proxy_env = [] + } + exec { "add-apt-repository-${name}": + environment => $proxy_env, + command => "/usr/bin/add-apt-repository ${options} ${name}", + unless => "/usr/bin/test -s ${sources_list_d}/${sources_list_d_filename}", + logoutput => 'on_failure', + notify => Exec['apt_update'], + require => [ + File['sources.list.d'], + Package[$package], + ], + } + + file { "${sources_list_d}/${sources_list_d_filename}": + ensure => file, + require => Exec["add-apt-repository-${name}"], + } + } + else { + + file { "${sources_list_d}/${sources_list_d_filename}": + ensure => 'absent', + notify => Exec['apt_update'], + } + } + + # Need anchor to provide containment for dependencies. + anchor { "apt::ppa::${name}": + require => Class['apt::update'], + } +} diff --git a/apt/manifests/release.pp b/apt/manifests/release.pp new file mode 100644 index 000000000..6e0a38f73 --- /dev/null +++ b/apt/manifests/release.pp @@ -0,0 +1,17 @@ +# release.pp + +class apt::release ( + $release_id +) { + + include apt::params + + $root = $apt::params::root + + file { "${root}/apt.conf.d/01release": + owner => root, + group => root, + mode => '0644', + content => "APT::Default-Release \"${release_id}\";" + } +} diff --git a/apt/manifests/source.pp b/apt/manifests/source.pp new file mode 100644 index 000000000..bc93ad9d5 --- /dev/null +++ b/apt/manifests/source.pp @@ -0,0 +1,87 @@ +# source.pp +# add an apt source + +define apt::source( + $ensure = present, + $location = '', + $release = 'UNDEF', + $repos = 'main', + $include_src = true, + $required_packages = false, + $key = false, + $key_server = 'keyserver.ubuntu.com', + $key_content = false, + $key_source = false, + $pin = false, + $architecture = undef +) { + + include apt::params + include apt::update + + $sources_list_d = $apt::params::sources_list_d + $provider = $apt::params::provider + + if $release == 'UNDEF' { + if $::lsbdistcodename == undef { + fail('lsbdistcodename fact not available: release parameter required') + } else { + $release_real = $::lsbdistcodename + } + } else { + $release_real = $release + } + + file { "${name}.list": + ensure => $ensure, + path => "${sources_list_d}/${name}.list", + owner => root, + group => root, + mode => '0644', + content => template("${module_name}/source.list.erb"), + notify => Exec['apt_update'], + } + + + if ($pin != false) { + # Get the host portion out of the url so we can pin to origin + $url_split = split($location, '/') + $host = $url_split[2] + + apt::pin { $name: + ensure => $ensure, + priority => $pin, + before => File["${name}.list"], + origin => $host, + } + } + + if ($required_packages != false) and ($ensure == 'present') { + exec { "Required packages: '${required_packages}' for ${name}": + command => "${provider} -y install ${required_packages}", + logoutput => 'on_failure', + refreshonly => true, + tries => 3, + try_sleep => 1, + subscribe => File["${name}.list"], + before => Exec['apt_update'], + } + } + + # We do not want to remove keys when the source is absent. + if ($key != false) and ($ensure == 'present') { + apt::key { "Add key: ${key} from Apt::Source ${title}": + ensure => present, + key => $key, + key_server => $key_server, + key_content => $key_content, + key_source => $key_source, + before => File["${name}.list"], + } + } + + # Need anchor to provide containment for dependencies. + anchor { "apt::source::${name}": + require => Class['apt::update'], + } +} diff --git a/apt/manifests/unattended_upgrades.pp b/apt/manifests/unattended_upgrades.pp new file mode 100644 index 000000000..f006bd528 --- /dev/null +++ b/apt/manifests/unattended_upgrades.pp @@ -0,0 +1,68 @@ +# Class: apt::unattended_upgrades +# +# This class manages the unattended-upgrades package and related configuration +# files for ubuntu +# +# origins are the repositories to automatically upgrade included packages +# blacklist is a list of packages to not automatically upgrade +# update is how often to run "apt-get update" in days +# download is how often to run "apt-get upgrade --download-only" in days +# upgrade is how often to upgrade packages included in the origins list in days +# autoclean is how often to run "apt-get autoclean" in days +# +# information on the other options can be found in the 50unattended-upgrades +# file and in /etc/cron.daily/apt +# +class apt::unattended_upgrades ( + $origins = ['${distro_id}:${distro_codename}-security'], + $blacklist = [], + $update = "1", + $download = "1", + $upgrade = "1", + $autoclean = "7", + $auto_fix = true, + $minimal_steps = false, + $install_on_shutdown = false, + $mail_to = "NONE", + $mail_only_on_error = false, + $remove_unused = true, + $auto_reboot = false, + $dl_limit = "NONE", + $enable = "1", + $backup_interval = "0", + $backup_level = "3", + $max_age = "0", + $min_age = "0", + $max_size = "0", + $download_delta = "0", + $verbose = "0", +) { + + validate_bool( + $auto_fix, + $minimal_steps, + $install_on_shutdown, + $mail_only_on_error, + $remove_unused, + $auto_reboot + ) + + package { 'unattended-upgrades': + ensure => present, + } + + File { + ensure => file, + owner => 'root', + group => 'root', + mode => '0644', + require => Package['unattended-upgrades'], + } + + file { + '/etc/apt/apt.conf.d/50unattended-upgrades': + content => template('apt/50unattended-upgrades.erb'); + '/etc/apt/apt.conf.d/10periodic': + content => template('apt/10periodic.erb'); + } +} diff --git a/apt/manifests/update.pp b/apt/manifests/update.pp new file mode 100644 index 000000000..ce0b78fbd --- /dev/null +++ b/apt/manifests/update.pp @@ -0,0 +1,10 @@ +class apt::update { + include apt::params + + exec { 'apt_update': + command => "${apt::params::provider} update", + logoutput => 'on_failure', + refreshonly => true, + timeout => $apt::update_timeout, + } +} diff --git a/apt/metadata.json b/apt/metadata.json new file mode 100644 index 000000000..ccb445844 --- /dev/null +++ b/apt/metadata.json @@ -0,0 +1,26 @@ +{ + "name": "puppetlabs/apt", + "version": "1.3.0", + "summary": "Apt key and source management", + "source": "git@github.com/puppetlabs/puppetlabs-apt.git", + "project_page": "http://github.com/puppetlabs/puppetlabs-apt", + "author": "Puppet Labs", + "license": "Apache-2.0", + "operatingsystem_support": [ + "Debian", + "Ubuntu" + ], + "puppet_version": [ + 2.7, + 3.0, + 3.1, + 3.2, + 3.3 + ], + "dependencies": [ + { + "name": "puppetlabs/stdlib", + "version_requirement": ">= 2.2.1" + } + ] +} diff --git a/apt/spec/classes/apt_spec.rb b/apt/spec/classes/apt_spec.rb new file mode 100644 index 000000000..a66feac78 --- /dev/null +++ b/apt/spec/classes/apt_spec.rb @@ -0,0 +1,133 @@ +require 'spec_helper' +describe 'apt', :type => :class do + let :default_params do + { + :disable_keys => :undef, + :always_apt_update => false, + :purge_sources_list => false, + :purge_sources_list_d => false, + } + end + + [{}, + { + :disable_keys => true, + :always_apt_update => true, + :proxy_host => true, + :proxy_port => '3128', + :purge_sources_list => true, + :purge_sources_list_d => true, + }, + { + :disable_keys => false + } + ].each do |param_set| + describe "when #{param_set == {} ? "using default" : "specifying"} class parameters" do + let :param_hash do + default_params.merge(param_set) + end + + let :params do + param_set + end + + let :refresh_only_apt_update do + if param_hash[:always_apt_update] + false + else + true + end + end + + it { should include_class("apt::params") } + + it { + if param_hash[:purge_sources_list] + should contain_file("sources.list").with({ + 'path' => "/etc/apt/sources.list", + 'ensure' => "present", + 'owner' => "root", + 'group' => "root", + 'mode' => "0644", + "content" => "# Repos managed by puppet.\n" + }) + else + should contain_file("sources.list").with({ + 'path' => "/etc/apt/sources.list", + 'ensure' => "present", + 'owner' => "root", + 'group' => "root", + 'mode' => "0644", + 'content' => nil + }) + end + } + it { + if param_hash[:purge_sources_list_d] + should create_file("sources.list.d").with({ + 'path' => "/etc/apt/sources.list.d", + 'ensure' => "directory", + 'owner' => "root", + 'group' => "root", + 'purge' => true, + 'recurse' => true, + 'notify' => 'Exec[apt_update]' + }) + else + should create_file("sources.list.d").with({ + 'path' => "/etc/apt/sources.list.d", + 'ensure' => "directory", + 'owner' => "root", + 'group' => "root", + 'purge' => false, + 'recurse' => false, + 'notify' => 'Exec[apt_update]' + }) + end + } + + it { + should contain_exec("apt_update").with({ + 'command' => "/usr/bin/apt-get update", + 'refreshonly' => refresh_only_apt_update + }) + } + + it { + if param_hash[:disable_keys] == true + should create_file("99unauth").with({ + 'content' => "APT::Get::AllowUnauthenticated 1;\n", + 'ensure' => "present", + 'path' => "/etc/apt/apt.conf.d/99unauth" + }) + elsif param_hash[:disable_keys] == false + should create_file("99unauth").with({ + 'ensure' => "absent", + 'path' => "/etc/apt/apt.conf.d/99unauth" + }) + elsif param_hash[:disable_keys] != :undef + should_not create_file("99unauth").with({ + 'path' => "/etc/apt/apt.conf.d/99unauth" + }) + end + } + describe 'when setting a proxy' do + it { + if param_hash[:proxy_host] + should contain_file('configure-apt-proxy').with( + 'path' => '/etc/apt/apt.conf.d/proxy', + 'content' => "Acquire::http::Proxy \"http://#{param_hash[:proxy_host]}:#{param_hash[:proxy_port]}\";", + 'notify' => "Exec[apt_update]" + ) + else + should contain_file('configure-apt-proxy').with( + 'path' => '/etc/apt/apt.conf.d/proxy', + 'notify' => 'Exec[apt_update]', + 'ensure' => 'absent' + ) + end + } + end + end + end +end diff --git a/apt/spec/classes/backports_spec.rb b/apt/spec/classes/backports_spec.rb new file mode 100644 index 000000000..27c6708f2 --- /dev/null +++ b/apt/spec/classes/backports_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper' +describe 'apt::backports', :type => :class do + + describe 'when turning on backports for ubuntu karmic' do + + let :facts do + { + 'lsbdistcodename' => 'Karmic', + 'lsbdistid' => 'Ubuntu' + } + end + + it { should contain_apt__source('backports').with({ + 'location' => 'http://old-releases.ubuntu.com/ubuntu', + 'release' => 'karmic-backports', + 'repos' => 'main universe multiverse restricted', + 'key' => '437D05B5', + 'key_server' => 'pgp.mit.edu', + 'pin' => '200', + }) + } + end + + describe "when turning on backports for debian squeeze" do + + let :facts do + { + 'lsbdistcodename' => 'Squeeze', + 'lsbdistid' => 'Debian', + } + end + + it { should contain_apt__source('backports').with({ + 'location' => 'http://backports.debian.org/debian-backports', + 'release' => 'squeeze-backports', + 'repos' => 'main contrib non-free', + 'key' => '55BE302B', + 'key_server' => 'pgp.mit.edu', + 'pin' => '200', + }) + } + end + + describe "when turning on backports for debian squeeze but using your own mirror" do + + let :facts do + { + 'lsbdistcodename' => 'Squeeze', + 'lsbdistid' => 'Debian' + } + end + + let :location do + 'http://mirrors.example.com/debian-backports' + end + + let :params do + { 'location' => location } + end + + it { should contain_apt__source('backports').with({ + 'location' => location, + 'release' => 'squeeze-backports', + 'repos' => 'main contrib non-free', + 'key' => '55BE302B', + 'key_server' => 'pgp.mit.edu', + 'pin' => '200', + }) + } + end +end diff --git a/apt/spec/classes/debian_testing_spec.rb b/apt/spec/classes/debian_testing_spec.rb new file mode 100644 index 000000000..6006afb41 --- /dev/null +++ b/apt/spec/classes/debian_testing_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' +describe 'apt::debian::testing', :type => :class do + it { + should contain_apt__source("debian_testing").with({ + "location" => "http://debian.mirror.iweb.ca/debian/", + "release" => "testing", + "repos" => "main contrib non-free", + "required_packages" => "debian-keyring debian-archive-keyring", + "key" => "55BE302B", + "key_server" => "subkeys.pgp.net", + "pin" => "-10" + }) + } +end diff --git a/apt/spec/classes/debian_unstable_spec.rb b/apt/spec/classes/debian_unstable_spec.rb new file mode 100644 index 000000000..411182df1 --- /dev/null +++ b/apt/spec/classes/debian_unstable_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' +describe 'apt::debian::unstable', :type => :class do + it { + should contain_apt__source("debian_unstable").with({ + "location" => "http://debian.mirror.iweb.ca/debian/", + "release" => "unstable", + "repos" => "main contrib non-free", + "required_packages" => "debian-keyring debian-archive-keyring", + "key" => "55BE302B", + "key_server" => "subkeys.pgp.net", + "pin" => "-10" + }) + } +end diff --git a/apt/spec/classes/params_spec.rb b/apt/spec/classes/params_spec.rb new file mode 100644 index 000000000..f2790b0ad --- /dev/null +++ b/apt/spec/classes/params_spec.rb @@ -0,0 +1,13 @@ +require 'spec_helper' +describe 'apt::params', :type => :class do + let (:title) { 'my_package' } + + it { should contain_apt__params } + + # There are 4 resources in this class currently + # there should not be any more resources because it is a params class + # The resources are class[apt::params], class[main], class[settings], stage[main] + it "Should not contain any resources" do + subject.resources.size.should == 4 + end +end diff --git a/apt/spec/classes/release_spec.rb b/apt/spec/classes/release_spec.rb new file mode 100644 index 000000000..d131b2281 --- /dev/null +++ b/apt/spec/classes/release_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' +describe 'apt::release', :type => :class do + let (:title) { 'my_package' } + + let :param_set do + { :release_id => 'precise' } + end + + let (:params) { param_set } + + it { should include_class("apt::params") } + + it { + should contain_file("/etc/apt/apt.conf.d/01release").with({ + "mode" => "0644", + "owner" => "root", + "group" => "root", + "content" => "APT::Default-Release \"#{param_set[:release_id]}\";" + }) + } +end + diff --git a/apt/spec/classes/unattended_upgrades_spec.rb b/apt/spec/classes/unattended_upgrades_spec.rb new file mode 100644 index 000000000..e83c6e4c3 --- /dev/null +++ b/apt/spec/classes/unattended_upgrades_spec.rb @@ -0,0 +1,204 @@ +require 'spec_helper' +describe 'apt::unattended_upgrades', :type => :class do + let(:file_unattended) { '/etc/apt/apt.conf.d/50unattended-upgrades' } + let(:file_periodic) { '/etc/apt/apt.conf.d/10periodic' } + + it { should contain_package("unattended-upgrades") } + + it { + should create_file("/etc/apt/apt.conf.d/50unattended-upgrades").with({ + "owner" => "root", + "group" => "root", + "mode" => "0644", + "require" => "Package[unattended-upgrades]", + }) + } + + it { + should create_file("/etc/apt/apt.conf.d/10periodic").with({ + "owner" => "root", + "group" => "root", + "mode" => "0644", + "require" => "Package[unattended-upgrades]", + }) + } + + describe "origins" do + describe "with param defaults" do + let(:params) {{ }} + it { should contain_file(file_unattended).with_content(/^Unattended-Upgrade::Allowed-Origins \{\n\t"\$\{distro_id\}:\$\{distro_codename\}-security";\n\};$/) } + end + + describe "with origins => ['ubuntu:precise-security']" do + let :params do + { :origins => ['ubuntu:precise-security'] } + end + it { should contain_file(file_unattended).with_content(/^Unattended-Upgrade::Allowed-Origins \{\n\t"ubuntu:precise-security";\n\};$/) } + end + end + + describe "blacklist" do + describe "with param defaults" do + let(:params) {{ }} + it { should contain_file(file_unattended).with_content(/^Unattended-Upgrade::Package-Blacklist \{\n\};$/) } + end + + describe "with blacklist => []" do + let :params do + { :blacklist => ['libc6', 'libc6-dev'] } + end + it { should contain_file(file_unattended).with_content(/^Unattended-Upgrade::Package-Blacklist \{\n\t"libc6";\n\t"libc6-dev";\n\};$/) } + end + end + + describe "with update => 2" do + let :params do + { :update => "2" } + end + it { should contain_file(file_periodic).with_content(/^APT::Periodic::Update-Package-Lists "2";$/) } + end + + describe "with download => 2" do + let :params do + { :download => "2" } + end + it { should contain_file(file_periodic).with_content(/^APT::Periodic::Download-Upgradeable-Packages "2";$/) } + end + + describe "with upgrade => 2" do + let :params do + { :upgrade => "2" } + end + it { should contain_file(file_periodic).with_content(/^APT::Periodic::Unattended-Upgrade "2";$/) } + end + + describe "with autoclean => 2" do + let :params do + { :autoclean => "2" } + end + it { should contain_file(file_periodic).with_content(/^APT::Periodic::AutocleanInterval "2";$/) } + end + + describe "with auto_fix => false" do + let :params do + { :auto_fix => false } + end + it { should contain_file(file_unattended).with_content(/^Unattended-Upgrade::AutoFixInterruptedDpkg "false";$/) } + end + + describe "with minimal_steps => true" do + let :params do + { :minimal_steps => true } + end + it { should contain_file(file_unattended).with_content(/^Unattended-Upgrade::MinimalSteps "true";$/) } + end + + describe "with install_on_shutdown => true" do + let :params do + { :install_on_shutdown => true } + end + it { should contain_file(file_unattended).with_content(/^Unattended-Upgrade::InstallOnShutdown "true";$/) } + end + + describe "mail_to" do + describe "param defaults" do + let(:params) {{ }} + it { should_not contain_file(file_unattended).with_content(/^Unattended-Upgrade::Mail /) } + it { should_not contain_file(file_unattended).with_content(/^Unattended-Upgrade::MailOnlyOnError /) } + end + + describe "with mail_to => user@website, mail_only_on_error => true" do + let :params do + { :mail_to => "user@website", + :mail_only_on_error => true } + end + it { should contain_file(file_unattended).with_content(/^Unattended-Upgrade::Mail "user@website";$/) } + it { should contain_file(file_unattended).with_content(/^Unattended-Upgrade::MailOnlyOnError "true";$/) } + end + end + + describe "with remove_unused => false" do + let :params do + { :remove_unused => false } + end + it { should contain_file(file_unattended).with_content(/^Unattended-Upgrade::Remove-Unused-Dependencies "false";$/) } + end + + describe "with auto_reboot => true" do + let :params do + { :auto_reboot => true } + end + it { should contain_file(file_unattended).with_content(/^Unattended-Upgrade::Automatic-Reboot "true";$/) } + end + + describe "dl_limit" do + describe "param defaults" do + let(:params) {{ }} + it { should_not contain_file(file_unattended).with_content(/^Acquire::http::Dl-Limit /) } + end + + describe "with dl_limit => 70" do + let :params do + { :dl_limit => "70" } + end + it { should contain_file(file_unattended).with_content(/^Acquire::http::Dl-Limit "70";$/) } + end + end + + describe "with enable => 0" do + let :params do + { :enable => "0" } + end + it { should contain_file(file_periodic).with_content(/^APT::Periodic::Enable "0";$/) } + end + + describe "with backup_interval => 1" do + let :params do + { :backup_interval => "1" } + end + it { should contain_file(file_periodic).with_content(/^APT::Periodic::BackUpArchiveInterval "1";$/) } + end + + describe "with backup_level => 0" do + let :params do + { :backup_level => "0" } + end + it { should contain_file(file_periodic).with_content(/^APT::Periodic::BackUpLevel "0";$/) } + end + + describe "with max_age => 1" do + let :params do + { :max_age => "1" } + end + it { should contain_file(file_periodic).with_content(/^APT::Periodic::MaxAge "1";$/) } + end + + describe "with min_age => 1" do + let :params do + { :min_age => "1" } + end + it { should contain_file(file_periodic).with_content(/^APT::Periodic::MinAge "1";$/) } + end + + describe "with max_size => 1" do + let :params do + { :max_size => "1" } + end + it { should contain_file(file_periodic).with_content(/^APT::Periodic::MaxSize "1";$/) } + end + + describe "with download_delta => 2" do + let :params do + { :download_delta => "2" } + end + it { should contain_file(file_periodic).with_content(/^APT::Periodic::Download-Upgradeable-Packages-Debdelta "2";$/) } + end + + describe "with verbose => 2" do + let :params do + { :verbose => "2" } + end + it { should contain_file(file_periodic).with_content(/^APT::Periodic::Verbose "2";$/) } + end + +end diff --git a/apt/spec/defines/builddep_spec.rb b/apt/spec/defines/builddep_spec.rb new file mode 100644 index 000000000..4e2b698d9 --- /dev/null +++ b/apt/spec/defines/builddep_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' +describe 'apt::builddep', :type => :define do + + let(:title) { 'my_package' } + + describe "should require apt-get update" do + it { should contain_exec("apt_update").with({ + 'command' => "/usr/bin/apt-get update", + 'refreshonly' => true + }) + } + it { should contain_anchor("apt::builddep::my_package").with({ + 'require' => 'Class[Apt::Update]', + }) + } + end + +end diff --git a/apt/spec/defines/conf_spec.rb b/apt/spec/defines/conf_spec.rb new file mode 100644 index 000000000..5a81b5148 --- /dev/null +++ b/apt/spec/defines/conf_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' +describe 'apt::conf', :type => :define do + let :title do + 'norecommends' + end + + describe "when creating an apt preference" do + let :params do + { + :priority => '00', + :content => "Apt::Install-Recommends 0;\nApt::AutoRemove::InstallRecommends 1;\n" + } + end + + let :filename do + "/etc/apt/apt.conf.d/00norecommends" + end + + it { should contain_apt__conf('norecommends').with({ + 'priority' => '00', + 'content' => "Apt::Install-Recommends 0;\nApt::AutoRemove::InstallRecommends 1;\n" + }) + } + + it { should contain_file(filename).with({ + 'ensure' => 'present', + 'content' => "Apt::Install-Recommends 0;\nApt::AutoRemove::InstallRecommends 1;\n", + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + } + end + + describe "when removing an apt preference" do + let :params do + { + :ensure => 'absent', + :priority => '00', + :content => "Apt::Install-Recommends 0;\nApt::AutoRemove::InstallRecommends 1;\n" + } + end + + let :filename do + "/etc/apt/apt.conf.d/00norecommends" + end + + it { should contain_file(filename).with({ + 'ensure' => 'absent', + 'content' => "Apt::Install-Recommends 0;\nApt::AutoRemove::InstallRecommends 1;\n", + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + } + end +end diff --git a/apt/spec/defines/force_spec.rb b/apt/spec/defines/force_spec.rb new file mode 100644 index 000000000..84231fa23 --- /dev/null +++ b/apt/spec/defines/force_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' +describe 'apt::force', :type => :define do + let :pre_condition do + 'include apt::params' + end + + let :title do + 'my_package' + end + + let :default_params do + { + :release => false, + :version => false + } + end + + describe "when using default parameters" do + let :params do + default_params + end + it { should contain_exec("/usr/bin/apt-get -y install #{title}").with( + :unless => "/usr/bin/dpkg -s #{title} | grep -q 'Status: install'", + :timeout => '300' + ) } + end + + describe "when specifying release parameter" do + let :params do + default_params.merge(:release => 'testing') + end + it { should contain_exec("/usr/bin/apt-get -y -t #{params[:release]} install #{title}").with( + :unless => "/usr/bin/test \$(/usr/bin/apt-cache policy -t #{params[:release]} #{title} | /bin/grep -E 'Installed|Candidate' | /usr/bin/uniq -s 14 | /usr/bin/wc -l) -eq 1" + ) } + end + + describe "when specifying version parameter" do + let :params do + default_params.merge(:version => '1') + end + it { should contain_exec("/usr/bin/apt-get -y install #{title}=#{params[:version]}").with( + :unless => "/usr/bin/dpkg -s #{title} | grep -q 'Version: #{params[:version]}'" + ) } + end + + describe "when specifying release and version parameters" do + let :params do + default_params.merge( + :release => 'testing', + :version => '1' + ) + end + it { should contain_exec("/usr/bin/apt-get -y -t #{params[:release]} install #{title}=1").with( + :unless => "/usr/bin/apt-cache policy -t #{params[:release]} #{title} | /bin/grep -q 'Installed: #{params[:version]}'" + ) } + end +end diff --git a/apt/spec/defines/key_spec.rb b/apt/spec/defines/key_spec.rb new file mode 100644 index 000000000..aea197a7b --- /dev/null +++ b/apt/spec/defines/key_spec.rb @@ -0,0 +1,123 @@ +require 'spec_helper' +describe 'apt::key', :type => :define do + let :title do + '8347A27F' + end + + let :default_params do + { + :key => title, + :ensure => 'present', + :key_server => "keyserver.ubuntu.com", + :key_source => false, + :key_content => false + } + end + + [{}, + { + :ensure => 'absent' + }, + { + :ensure => 'random' + }, + { + :key_source => 'ftp://ftp.example.org/key', + }, + { + :key_content => 'deadbeef', + } + ].each do |param_set| + + let :param_hash do + param_hash = default_params.merge(param_set) + param_hash[:key].upcase! if param_hash[:key] + param_hash + end + + let :params do + param_set + end + + let :digest do + str = String.new + str << param_hash[:key].to_s << '/' + str << param_hash[:key_content].to_s << '/' + str << param_hash[:key_source].to_s << '/' + str << param_hash[:key_server].to_s << '/' + Digest::SHA1.hexdigest(str) + end + + describe "when #{param_set == {} ? "using default" : "specifying"} define parameters" do + + it { + if [:present, 'present', :absent, 'absent'].include? param_hash[:ensure] + should contain_apt__params + end + } + + it { + if [:present, 'present'].include? param_hash[:ensure] + should_not contain_exec("apt::key #{param_hash[:key]} absent") + should contain_anchor("apt::key #{param_hash[:key]} present") + should contain_exec(digest).with({ + "path" => "/bin:/usr/bin", + "unless" => "/usr/bin/apt-key list | /bin/grep '#{param_hash[:key]}'" + }) + elsif [:absent, 'absent'].include? param_hash[:ensure] + should_not contain_anchor("apt::key #{param_hash[:key]} present") + should contain_exec("apt::key #{param_hash[:key]} absent").with({ + "path" => "/bin:/usr/bin", + "onlyif" => "apt-key list | grep '#{param_hash[:key]}'", + "command" => "apt-key del '#{param_hash[:key]}'" + }) + else + expect { should raise_error(Puppet::Error) } + end + } + + it { + if [:present, 'present'].include? param_hash[:ensure] + if param_hash[:key_content] + should contain_exec(digest).with({ + "command" => "echo '#{param_hash[:key_content]}' | /usr/bin/apt-key add -" + }) + elsif param_hash[:key_source] + should contain_exec(digest).with({ + "command" => "wget -q '#{param_hash[:key_source]}' -O- | apt-key add -" + }) + elsif param_hash[:key_server] + should contain_exec(digest).with({ + "command" => "apt-key adv --keyserver '#{param_hash[:key_server]}' --recv-keys '#{param_hash[:key]}'" + }) + end + end + } + + end + end + + [{ :ensure => 'present' }, { :ensure => 'absent' }].each do |param_set| + describe "should correctly handle duplicate definitions" do + + let :pre_condition do + "apt::key { 'duplicate': key => '#{title}'; }" + end + + let(:params) { param_set } + + it { + if param_set[:ensure] == 'present' + should contain_anchor("apt::key #{title} present") + should contain_apt__key(title) + should contain_apt__key("duplicate") + elsif param_set[:ensure] == 'absent' + expect { should raise_error(Puppet::Error) } + end + } + + end + end + +end + diff --git a/apt/spec/defines/pin_spec.rb b/apt/spec/defines/pin_spec.rb new file mode 100644 index 000000000..a4cb1e26e --- /dev/null +++ b/apt/spec/defines/pin_spec.rb @@ -0,0 +1,101 @@ +require 'spec_helper' +describe 'apt::pin', :type => :define do + let(:title) { 'my_pin' } + + let :default_params do + { + :ensure => 'present', + :order => '', + :packages => '*', + :priority => '0', + :release => nil + } + end + + [ + { :params => {}, + :content => "# my_pin\nExplanation: : my_pin\nPackage: *\nPin: release a=my_pin\nPin-Priority: 0\n" + }, + { + :params => { + :packages => 'apache', + :priority => '1' + }, + :content => "# my_pin\nExplanation: : my_pin\nPackage: apache\nPin: release a=my_pin\nPin-Priority: 1\n" + }, + { + :params => { + :order => 50, + :packages => 'apache', + :priority => '1' + }, + :content => "# my_pin\nExplanation: : my_pin\nPackage: apache\nPin: release a=my_pin\nPin-Priority: 1\n" + }, + { + :params => { + :ensure => 'absent', + :packages => 'apache', + :priority => '1' + }, + :content => "# my_pin\nExplanation: : my_pin\nPackage: apache\nPin: release a=my_pin\nPin-Priority: 1\n" + }, + { + :params => { + :packages => 'apache', + :priority => '1', + :release => 'my_newpin' + }, + :content => "# my_pin\nExplanation: : my_pin\nPackage: apache\nPin: release a=my_newpin\nPin-Priority: 1\n" + }, + { + :params => { + :packages => 'apache', + :priority => '1', + :version => '2.2.16*' + }, + :content => "# my_pin\nExplanation: : my_pin\nPackage: apache\nPin: version 2.2.16*\nPin-Priority: 1\n" + }, + { + :params => { + :priority => '1', + :origin => 'ftp.de.debian.org' + }, + :content => "# my_pin\nExplanation: : my_pin\nPackage: *\nPin: origin \"ftp.de.debian.org\"\nPin-Priority: 1\n" + }, + { + :params => { + :packages => 'apache', + :priority => '1', + :release => 'stable', + :codename => 'wheezy', + :release_version => '3.0', + :component => 'main', + :originator => 'Debian', + :label => 'Debian' + }, + :content => "# my_pin\nExplanation: : my_pin\nPackage: apache\nPin: release a=stable, n=wheezy, v=3.0, c=main, o=Debian, l=Debian\nPin-Priority: 1\n" + }, + ].each do |param_set| + describe "when #{param_set == {} ? "using default" : "specifying"} define parameters" do + let :param_hash do + default_params.merge(param_set[:params]) + end + + let :params do + param_set[:params] + end + + it { should include_class("apt::params") } + + it { should contain_file("#{title}.pref").with({ + 'ensure' => param_hash[:ensure], + 'path' => "/etc/apt/preferences.d/#{param_hash[:order] == '' ? "" : "#{param_hash[:order]}-"}#{title}.pref", + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + 'content' => param_set[:content], + }) + } + end + end +end diff --git a/apt/spec/defines/ppa_spec.rb b/apt/spec/defines/ppa_spec.rb new file mode 100644 index 000000000..a05ca3064 --- /dev/null +++ b/apt/spec/defines/ppa_spec.rb @@ -0,0 +1,143 @@ +require 'spec_helper' +describe 'apt::ppa', :type => :define do + [ { :lsbdistrelease => '11.04', + :lsbdistcodename => 'natty', + :package => 'python-software-properties'}, + { :lsbdistrelease => '12.10', + :lsbdistcodename => 'quantal', + :package => 'software-properties-common'}, + ].each do |platform| + context "on #{platform[:lsbdistcodename]}" do + let :facts do + { + :lsbdistrelease => platform[:lsbdistrelease], + :lsbdistcodename => platform[:lsbdistcodename], + } + end + let :release do + "#{platform[:lsbdistcodename]}" + end + let :package do + "#{platform[:package]}" + end + let :options do + "-y" + end + ['ppa:dans_ppa', 'dans_ppa','ppa:dans-daily/ubuntu'].each do |t| + describe "with title #{t}" do + let :pre_condition do + 'class { "apt": }' + end + let :title do + t + end + let :filename do + t.sub(/^ppa:/,'').gsub('/','-') << "-" << "#{release}.list" + end + + it { should contain_package("#{package}") } + + it { should contain_exec("apt_update").with( + 'command' => '/usr/bin/apt-get update', + 'refreshonly' => true + ) + } + + it { should contain_exec("add-apt-repository-#{t}").with( + 'command' => "/usr/bin/add-apt-repository #{options} #{t}", + 'unless' => "/usr/bin/test -s /etc/apt/sources.list.d/#{filename}", + 'require' => ["File[sources.list.d]", "Package[#{package}]"], + 'notify' => "Exec[apt_update]" + ) + } + + it { should create_file("/etc/apt/sources.list.d/#{filename}").with( + 'ensure' => 'file', + 'require' => "Exec[add-apt-repository-#{t}]" + ) + } + end + end + describe 'without a proxy defined' do + let :title do + 'rspec_ppa' + end + let :pre_condition do + 'class { "apt": + proxy_host => false + }' + end + let :filename do + "#{title}-#{release}.list" + end + + it { should contain_exec("add-apt-repository-#{title}").with( + 'environment' => [], + 'command' => "/usr/bin/add-apt-repository #{options} #{title}", + 'unless' => "/usr/bin/test -s /etc/apt/sources.list.d/#{filename}", + 'require' => ["File[sources.list.d]", "Package[#{package}]"], + 'notify' => "Exec[apt_update]" + ) + } + end + + describe 'behind a proxy' do + let :title do + 'rspec_ppa' + end + let :pre_condition do + 'class { "apt": + proxy_host => "user:pass@proxy", + }' + end + let :filename do + "#{title}-#{release}.list" + end + + it { should contain_exec("add-apt-repository-#{title}").with( + 'environment' => [ + "http_proxy=http://user:pass@proxy:8080", + "https_proxy=http://user:pass@proxy:8080", + ], + 'command' => "/usr/bin/add-apt-repository #{options} #{title}", + 'unless' => "/usr/bin/test -s /etc/apt/sources.list.d/#{filename}", + 'require' => ["File[sources.list.d]", "Package[#{package}]"], + 'notify' => "Exec[apt_update]" + ) + } + end + end + end + + [ { :lsbdistcodename => 'natty', + :package => 'python-software-properties' }, + { :lsbdistcodename => 'quantal', + :package => 'software-properties-common'}, + ].each do |platform| + context "on #{platform[:lsbdistcodename]}" do + describe "it should not error if package['#{platform[:package]}'] is already defined" do + let :pre_condition do + 'class {"apt": }' + + 'package { "#{platform[:package]}": }->Apt::Ppa["ppa"]' + end + let :facts do + {:lsbdistcodename => '#{platform[:lsbdistcodename]}'} + end + let(:title) { "ppa" } + let(:release) { "#{platform[:lsbdistcodename]}" } + it { should contain_package('#{platform[:package]}') } + end + end + end + + describe "without Class[apt] should raise a Puppet::Error" do + let(:release) { "natty" } + let(:title) { "ppa" } + it { expect { should contain_apt__ppa(title) }.to raise_error(Puppet::Error) } + end + + describe "without release should raise a Puppet::Error" do + let(:title) { "ppa:" } + it { expect { should contain_apt__ppa(:release) }.to raise_error(Puppet::Error) } + end +end diff --git a/apt/spec/defines/source_spec.rb b/apt/spec/defines/source_spec.rb new file mode 100644 index 000000000..215d1e692 --- /dev/null +++ b/apt/spec/defines/source_spec.rb @@ -0,0 +1,166 @@ +require 'spec_helper' +describe 'apt::source', :type => :define do + let :title do + 'my_source' + end + + let :default_params do + { + :ensure => 'present', + :location => '', + :release => 'karmic', + :repos => 'main', + :include_src => true, + :required_packages => false, + :key => false, + :key_server => 'keyserver.ubuntu.com', + :key_content => false, + :key_source => false, + :pin => false + } + end + + [{}, + { + :location => 'http://example.com', + :release => 'precise', + :repos => 'security', + :include_src => false, + :required_packages => 'apache', + :key => 'key_name', + :key_server => 'keyserver.debian.com', + :pin => '600', + :key_content => 'ABCD1234' + }, + { + :key => 'key_name', + :key_server => 'keyserver.debian.com', + :key_content => false, + }, + { + :ensure => 'absent', + :location => 'http://example.com', + :release => 'precise', + :repos => 'security', + }, + { + :release => '', + }, + { + :release => 'custom', + }, + { + :architecture => 'amd64', + } + ].each do |param_set| + describe "when #{param_set == {} ? "using default" : "specifying"} class parameters" do + let :param_hash do + default_params.merge(param_set) + end + + let :facts do + {:lsbdistcodename => 'karmic'} + end + + let :params do + param_set + end + + let :filename do + "/etc/apt/sources.list.d/#{title}.list" + end + + let :content do + content = "# #{title}" + if param_hash[:architecture] + arch = "[arch=#{param_hash[:architecture]}] " + end + content << "\ndeb #{arch}#{param_hash[:location]} #{param_hash[:release]} #{param_hash[:repos]}\n" + + if param_hash[:include_src] + content << "deb-src #{arch}#{param_hash[:location]} #{param_hash[:release]} #{param_hash[:repos]}\n" + end + content + end + + it { should contain_apt__params } + + it { should contain_file("#{title}.list").with({ + 'ensure' => param_hash[:ensure], + 'path' => filename, + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + 'content' => content, + }) + } + + it { + if param_hash[:pin] + should contain_apt__pin(title).with({ + "priority" => param_hash[:pin], + "before" => "File[#{title}.list]" + }) + else + should_not contain_apt__pin(title).with({ + "priority" => param_hash[:pin], + "before" => "File[#{title}.list]" + }) + end + } + + it { + should contain_exec("apt_update").with({ + "command" => "/usr/bin/apt-get update", + "refreshonly" => true + }) + } + + it { + if param_hash[:required_packages] + should contain_exec("Required packages: '#{param_hash[:required_packages]}' for #{title}").with({ + "command" => "/usr/bin/apt-get -y install #{param_hash[:required_packages]}", + "subscribe" => "File[#{title}.list]", + "refreshonly" => true, + "before" => 'Exec[apt_update]', + }) + else + should_not contain_exec("Required packages: '#{param_hash[:required_packages]}' for #{title}").with({ + "command" => "/usr/bin/apt-get -y install #{param_hash[:required_packages]}", + "subscribe" => "File[#{title}.list]", + "refreshonly" => true + }) + end + } + + it { + if param_hash[:key] + should contain_apt__key("Add key: #{param_hash[:key]} from Apt::Source #{title}").with({ + "key" => param_hash[:key], + "ensure" => :present, + "key_server" => param_hash[:key_server], + "key_content" => param_hash[:key_content], + "key_source" => param_hash[:key_source], + "before" => "File[#{title}.list]" + }) + else + should_not contain_apt__key("Add key: #{param_hash[:key]} from Apt::Source #{title}").with({ + "key" => param_hash[:key], + "ensure" => :present, + "key_server" => param_hash[:key_server], + "key_content" => param_hash[:key_content], + "key_source" => param_hash[:key_source], + "before" => "File[#{title}.list]" + }) + end + } + end + end + describe "without release should raise a Puppet::Error" do + let(:default_params) { Hash.new } + let(:facts) { Hash.new } + it { expect { should raise_error(Puppet::Error) } } + let(:facts) { { :lsbdistcodename => 'lucid' } } + it { should contain_apt__source(title) } + end +end diff --git a/galera/spec/spec_helper.rb b/apt/spec/spec_helper.rb similarity index 100% rename from galera/spec/spec_helper.rb rename to apt/spec/spec_helper.rb diff --git a/apt/spec/spec_helper_system.rb b/apt/spec/spec_helper_system.rb new file mode 100644 index 000000000..490a6017e --- /dev/null +++ b/apt/spec/spec_helper_system.rb @@ -0,0 +1,30 @@ +require 'rspec-system/spec_helper' +require 'rspec-system-puppet/helpers' +require 'rspec-system-serverspec/helpers' + +include RSpecSystemPuppet::Helpers + +include Serverspec::Helper::RSpecSystem +include Serverspec::Helper::DetectOS + +RSpec.configure do |c| + # Project root + proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) + + # Enable colour + c.tty = true + + c.include RSpecSystemPuppet::Helpers + + # This is where we 'setup' the nodes before running our tests + c.before :suite do + # May as well update here as this can only run on apt-get machines. + shell('apt-get update') + # Install puppet + puppet_install + + # Install modules and dependencies + puppet_module_install(:source => proj_root, :module_name => 'apt') + shell('puppet module install puppetlabs-stdlib') + end +end diff --git a/apt/spec/system/apt_builddep_spec.rb b/apt/spec/system/apt_builddep_spec.rb new file mode 100644 index 000000000..fa16ab42f --- /dev/null +++ b/apt/spec/system/apt_builddep_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper_system' + +describe 'apt::builddep' do + + context 'reset' do + it 'removes packages' do + shell('apt-get -y remove glusterfs-server') + shell('apt-get -y remove g++') + end + end + + context 'apt::builddep' do + it 'should work with no errors' do + pp = <<-EOS + include '::apt' + apt::builddep { 'glusterfs-server': } + EOS + + puppet_apply(pp) do |r| + r.exit_code.should_not == 1 + end + end + + describe 'should install g++ as a dependency' do + describe package('g++') do + it { should be_installed } + end + end + end + + context 'reset' do + it 'removes packages' do + shell('apt-get -y remove glusterfs-server') + shell('apt-get -y remove g++') + end + end + +end diff --git a/apt/spec/system/apt_key_spec.rb b/apt/spec/system/apt_key_spec.rb new file mode 100644 index 000000000..4842cb597 --- /dev/null +++ b/apt/spec/system/apt_key_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper_system' + +describe 'apt::key' do + + context 'reset' do + it 'clean up keys' do + shell('apt-key del 4BD6EC30') + shell('apt-key del D50582E6') + end + end + + context 'apt::key' do + it 'should work with no errors' do + pp = <<-EOS + include '::apt' + apt::key { 'puppetlabs': + key => '4BD6EC30', + key_server => 'pgp.mit.edu', + } + + apt::key { 'jenkins': + key => 'D50582E6', + key_source => 'http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key', + } + EOS + + puppet_apply(pp) do |r| + r.exit_code.should_not == 1 + end + end + + describe 'keys should exist' do + it 'finds puppetlabs key' do + shell('apt-key list | grep 4BD6EC30') do |r| + r.exit_code.should be_zero + end + end + it 'finds jenkins key' do + shell('apt-key list | grep D50582E6') do |r| + r.exit_code.should be_zero + end + end + end + end + + context 'reset' do + it 'clean up keys' do + shell('apt-key del 4BD6EC30') + shell('apt-key del D50582E6') + end + end + +end diff --git a/apt/spec/system/apt_ppa_spec.rb b/apt/spec/system/apt_ppa_spec.rb new file mode 100644 index 000000000..2e2baf5d2 --- /dev/null +++ b/apt/spec/system/apt_ppa_spec.rb @@ -0,0 +1,59 @@ +require 'spec_helper_system' + +describe 'apt::ppa' do + + context 'reset' do + it 'removes ppa' do + shell('rm /etc/apt/sources.list.d/drizzle-developers-ppa*') + shell('rm /etc/apt/sources.list.d/raravena80-collectd5-*') + end + end + + context 'adding a ppa that doesnt exist' do + it 'should work with no errors' do + pp = <<-EOS + include '::apt' + apt::ppa { 'ppa:drizzle-developers/ppa': } + EOS + + puppet_apply(pp) do |r| + r.exit_code.should_not == 1 + end + end + + describe 'contains the source file' do + it 'contains a drizzle ppa source' do + shell('ls /etc/apt/sources.list.d/drizzle-developers-ppa-*.list') do |r| + r.exit_code.should be_zero + end + end + end + end + + context 'readding a removed ppa.' do + it 'setup' do + shell('add-apt-repository -y ppa:raravena80/collectd5') + # This leaves a blank file + shell('add-apt-repository --remove ppa:raravena80/collectd5') + end + + it 'should readd it successfully' do + pp = <<-EOS + include '::apt' + apt::ppa { 'ppa:raravena80/collectd5': } + EOS + + puppet_apply(pp) do |r| + r.exit_code.should_not == 1 + end + end + end + + context 'reset' do + it 'removes added ppas' do + shell('rm /etc/apt/sources.list.d/drizzle-developers-ppa*') + shell('rm /etc/apt/sources.list.d/raravena80-collectd5-*') + end + end + +end diff --git a/apt/spec/system/apt_source_spec.rb b/apt/spec/system/apt_source_spec.rb new file mode 100644 index 000000000..6a445e13e --- /dev/null +++ b/apt/spec/system/apt_source_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper_system' + +describe 'apt::source' do + + context 'reset' do + it 'clean up puppetlabs repo' do + shell('apt-key del 4BD6EC30') + shell('rm /etc/apt/sources.list.d/puppetlabs.list') + end + end + + context 'apt::source' do + it 'should work with no errors' do + pp = <<-EOS + include '::apt' + apt::source { 'puppetlabs': + location => 'http://apt.puppetlabs.com', + repos => 'main', + key => '4BD6EC30', + key_server => 'pgp.mit.edu', + } + EOS + + puppet_apply(pp) do |r| + r.exit_code.should_not == 1 + end + end + + describe 'key should exist' do + it 'finds puppetlabs key' do + shell('apt-key list | grep 4BD6EC30') do |r| + r.exit_code.should be_zero + end + end + end + + describe 'source should exist' do + describe file('/etc/apt/sources.list.d/puppetlabs.list') do + it { should be_file } + end + end + end + + context 'reset' do + it 'clean up puppetlabs repo' do + shell('apt-key del 4BD6EC30') + shell('rm /etc/apt/sources.list.d/puppetlabs.list') + end + end + +end diff --git a/apt/spec/system/basic_spec.rb b/apt/spec/system/basic_spec.rb new file mode 100644 index 000000000..300835635 --- /dev/null +++ b/apt/spec/system/basic_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper_system' + +describe 'basic tests:' do + # Using puppet_apply as a subject + context puppet_apply 'notice("foo")' do + its(:stdout) { should =~ /foo/ } + its(:stderr) { should be_empty } + its(:exit_code) { should be_zero } + end +end diff --git a/apt/spec/system/class_spec.rb b/apt/spec/system/class_spec.rb new file mode 100644 index 000000000..7bb6b8d64 --- /dev/null +++ b/apt/spec/system/class_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper_system' + +describe 'apt class' do + + context 'default parameters' do + # Using puppet_apply as a helper + it 'should work with no errors' do + pp = <<-EOS + class { 'apt': } + EOS + + # Run it twice and test for idempotency + puppet_apply(pp) do |r| + r.exit_code.should_not == 1 + r.refresh + r.exit_code.should be_zero + end + end + end +end diff --git a/apt/templates/10periodic.erb b/apt/templates/10periodic.erb new file mode 100644 index 000000000..5737c9ac2 --- /dev/null +++ b/apt/templates/10periodic.erb @@ -0,0 +1,12 @@ +APT::Periodic::Enable "<%= @enable %>"; +APT::Periodic::BackUpArchiveInterval "<%= @backup_interval %>"; +APT::Periodic::BackUpLevel "<%= @backup_level %>"; +APT::Periodic::MaxAge "<%= @max_age %>"; +APT::Periodic::MinAge "<%= @min_age %>"; +APT::Periodic::MaxSize "<%= @max_size %>"; +APT::Periodic::Update-Package-Lists "<%= @update %>"; +APT::Periodic::Download-Upgradeable-Packages "<%= @download %>"; +APT::Periodic::Download-Upgradeable-Packages-Debdelta "<%= @download_delta %>"; +APT::Periodic::Unattended-Upgrade "<%= @upgrade %>"; +APT::Periodic::AutocleanInterval "<%= @autoclean %>"; +APT::Periodic::Verbose "<%= @verbose %>"; diff --git a/apt/templates/50unattended-upgrades.erb b/apt/templates/50unattended-upgrades.erb new file mode 100644 index 000000000..4df0f7440 --- /dev/null +++ b/apt/templates/50unattended-upgrades.erb @@ -0,0 +1,53 @@ +// Automatically upgrade packages from these (origin:archive) pairs +Unattended-Upgrade::Allowed-Origins { +<% @origins.each do |origin| -%> + "<%= origin %>"; +<% end -%> +}; + +// List of packages to not update +Unattended-Upgrade::Package-Blacklist { +<% @blacklist.each do |package| -%> + "<%= package %>"; +<% end -%> +}; + +// This option allows you to control if on a unclean dpkg exit +// unattended-upgrades will automatically run +// dpkg --force-confold --configure -a +// The default is true, to ensure updates keep getting installed +Unattended-Upgrade::AutoFixInterruptedDpkg "<%= @auto_fix %>"; + +// Split the upgrade into the smallest possible chunks so that +// they can be interrupted with SIGUSR1. This makes the upgrade +// a bit slower but it has the benefit that shutdown while a upgrade +// is running is possible (with a small delay) +Unattended-Upgrade::MinimalSteps "<%= @minimal_steps %>"; + +// Install all unattended-upgrades when the machine is shuting down +// instead of doing it in the background while the machine is running +// This will (obviously) make shutdown slower +Unattended-Upgrade::InstallOnShutdown "<%= @install_on_shutdown %>"; + +// Send email to this address for problems or packages upgrades +// If empty or unset then no email is sent, make sure that you +// have a working mail setup on your system. A package that provides +// 'mailx' must be installed. +<% if @mail_to != "NONE" %>Unattended-Upgrade::Mail "<%= @mail_to %>";<% end %> + +// Set this value to "true" to get emails only on errors. Default +// is to always send a mail if Unattended-Upgrade::Mail is set +<% if @mail_to != "NONE" %>Unattended-Upgrade::MailOnlyOnError "<%= @mail_only_on_error %>";<% end %> + +// Do automatic removal of new unused dependencies after the upgrade +// (equivalent to apt-get autoremove) +Unattended-Upgrade::Remove-Unused-Dependencies "<%= @remove_unused %>"; + +// Automatically reboot *WITHOUT CONFIRMATION* if a +// the file /var/run/reboot-required is found after the upgrade +Unattended-Upgrade::Automatic-Reboot "<%= @auto_reboot %>"; + + +// Use apt bandwidth limit feature, this example limits the download +// speed to 70kb/sec +<% if @dl_limit != "NONE" %>Acquire::http::Dl-Limit "<%= @dl_limit %>";<% end %> diff --git a/apt/templates/pin.pref.erb b/apt/templates/pin.pref.erb new file mode 100644 index 000000000..62c44c724 --- /dev/null +++ b/apt/templates/pin.pref.erb @@ -0,0 +1,22 @@ +<%- +@pin = "release a=#{@name}" # default value +if @pin_release.length > 0 + options = [] + options.push("a=#{@release}") if @release.length > 0 + options.push("n=#{@codename}") if @codename.length > 0 + options.push("v=#{@release_version}") if @release_version.length > 0 + options.push("c=#{@component}") if @component.length > 0 + options.push("o=#{@originator}") if @originator.length > 0 + options.push("l=#{@label}") if @label.length > 0 + @pin = "release #{options.join(', ')}" +elsif @version.length > 0 + @pin = "version #{@version}" +elsif @origin.length > 0 + @pin = "origin \"#{@origin}\"" +end +-%> +# <%= @name %> +Explanation: <%= @explanation %> +Package: <%= @packages %> +Pin: <%= @pin %> +Pin-Priority: <%= @priority %> diff --git a/apt/templates/source.list.erb b/apt/templates/source.list.erb new file mode 100644 index 000000000..9946966ee --- /dev/null +++ b/apt/templates/source.list.erb @@ -0,0 +1,5 @@ +# <%= @name %> +deb <% if @architecture %>[arch=<%= @architecture %>] <% end %><%= @location %> <%= @release_real %> <%= @repos %> +<%- if @include_src then -%> +deb-src <% if @architecture %>[arch=<%= @architecture %>] <% end %><%= @location %> <%= @release_real %> <%= @repos %> +<%- end -%> diff --git a/apt/tests/builddep.pp b/apt/tests/builddep.pp new file mode 100644 index 000000000..8b4f79640 --- /dev/null +++ b/apt/tests/builddep.pp @@ -0,0 +1,2 @@ +class { 'apt': } +apt::builddep{ 'glusterfs-server': } diff --git a/apt/tests/debian/testing.pp b/apt/tests/debian/testing.pp new file mode 100644 index 000000000..8245b3a33 --- /dev/null +++ b/apt/tests/debian/testing.pp @@ -0,0 +1,2 @@ +class { 'apt': } +class { 'apt::debian::testing': } diff --git a/apt/tests/debian/unstable.pp b/apt/tests/debian/unstable.pp new file mode 100644 index 000000000..860517929 --- /dev/null +++ b/apt/tests/debian/unstable.pp @@ -0,0 +1,2 @@ +class { 'apt': } +class { 'apt::debian::unstable': } diff --git a/apt/tests/force.pp b/apt/tests/force.pp new file mode 100644 index 000000000..59ad8f1b5 --- /dev/null +++ b/apt/tests/force.pp @@ -0,0 +1,17 @@ +# force.pp + +# force a package from a specific release +apt::force { 'package1': + release => 'backports', +} + +# force a package to be a specific version +apt::force { 'package2': + version => '1.0.0-1', +} + +# force a package from a specific release to be a specific version +apt::force { 'package3': + release => 'sid', + version => '2.0.0-1', +} diff --git a/apt/tests/init.pp b/apt/tests/init.pp new file mode 100644 index 000000000..abc75afa2 --- /dev/null +++ b/apt/tests/init.pp @@ -0,0 +1 @@ +class { 'apt': } diff --git a/apt/tests/key.pp b/apt/tests/key.pp new file mode 100644 index 000000000..79e0e1b74 --- /dev/null +++ b/apt/tests/key.pp @@ -0,0 +1,6 @@ +# Declare Apt key for apt.puppetlabs.com source +apt::key { 'puppetlabs': + key => '4BD6EC30', + key_server => 'pgp.mit.edu', + key_options => 'http-proxy="http://proxyuser:proxypass@example.org:3128"', +} diff --git a/apt/tests/params.pp b/apt/tests/params.pp new file mode 100644 index 000000000..5ddf3c655 --- /dev/null +++ b/apt/tests/params.pp @@ -0,0 +1 @@ +include apt::params diff --git a/apt/tests/pin.pp b/apt/tests/pin.pp new file mode 100644 index 000000000..6a9024c23 --- /dev/null +++ b/apt/tests/pin.pp @@ -0,0 +1,5 @@ +# pin a release in apt, useful for unstable repositories +apt::pin { 'foo': + packages => '*', + priority => 0, +} diff --git a/apt/tests/ppa.pp b/apt/tests/ppa.pp new file mode 100644 index 000000000..e728f6f10 --- /dev/null +++ b/apt/tests/ppa.pp @@ -0,0 +1,4 @@ +class { 'apt': } + +# Example declaration of an Apt PPA +apt::ppa{ 'ppa:openstack-ppa/bleeding-edge': } diff --git a/apt/tests/release.pp b/apt/tests/release.pp new file mode 100644 index 000000000..823f5861f --- /dev/null +++ b/apt/tests/release.pp @@ -0,0 +1,4 @@ +class { 'apt': } +class { 'apt::release': + release_id => 'karmic' +} diff --git a/apt/tests/source.pp b/apt/tests/source.pp new file mode 100644 index 000000000..d83cb9787 --- /dev/null +++ b/apt/tests/source.pp @@ -0,0 +1,29 @@ +# Declare the apt class to manage /etc/apt/sources.list and /etc/sources.list.d +class { 'apt': } + +# Install the puppetlabs apt source +# Release is automatically obtained from lsbdistcodename fact if available. +apt::source { 'puppetlabs': + location => 'http://apt.puppetlabs.com', + repos => 'main', + key => '4BD6EC30', + key_server => 'pgp.mit.edu', +} + +# test two sources with the same key +apt::source { 'debian_testing': + location => 'http://debian.mirror.iweb.ca/debian/', + release => 'testing', + repos => 'main contrib non-free', + key => '55BE302B', + key_server => 'subkeys.pgp.net', + pin => '-10', +} +apt::source { 'debian_unstable': + location => 'http://debian.mirror.iweb.ca/debian/', + release => 'unstable', + repos => 'main contrib non-free', + key => '55BE302B', + key_server => 'subkeys.pgp.net', + pin => '-10', +} diff --git a/apt/tests/unattended-upgrades.pp b/apt/tests/unattended-upgrades.pp new file mode 100644 index 000000000..7f65ab4f5 --- /dev/null +++ b/apt/tests/unattended-upgrades.pp @@ -0,0 +1 @@ +include apt::unattended-upgrades diff --git a/boolean/CHANGELOG b/boolean/CHANGELOG new file mode 100644 index 000000000..1b406f929 --- /dev/null +++ b/boolean/CHANGELOG @@ -0,0 +1,8 @@ +CHANGELOG + +1.0.1 +----- + +This is a backwards compatible bugfix release + + * Correct permissions to make plugin world readable. diff --git a/boolean/LICENSE b/boolean/LICENSE new file mode 100644 index 000000000..bb2c3b5b2 --- /dev/null +++ b/boolean/LICENSE @@ -0,0 +1,14 @@ +Copyright 2013 Adrien Thebo + +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. + diff --git a/boolean/Modulefile b/boolean/Modulefile new file mode 100644 index 000000000..c67568ae1 --- /dev/null +++ b/boolean/Modulefile @@ -0,0 +1,13 @@ +name 'adrien-boolean' +version '1.0.1' +author 'Adrien Thebo ' +license 'Apache 2.0' + +summary 'Boolean normalizing property for Puppet types' +description < true) + + newparam(:more_explosions, :parent => Puppet::Parameter::Boolean) do + desc "Indicate that more explosions are neccessary" + defaultto true # When it doubt, we want more explosions + end + + newproperty(:better_than_sliced_bread, :parent => Puppet::Property::Boolean) do + desc "Determine if the thing is more awesome than sliced bread" + defaultto true # It's not hard to be more awesome than sliced bread + end + + newproperty(:better_than_rocket_boots, :parent => Puppet::Property::Boolean) do + desc "Determine if the thing is more often than rocket boots" + defaultto false # Rocket boots are pretty hard to beat + end + + newproperty(:will_get_you_eaten_by_sharks, :parent => Puppet::Property::Boolean) do + desc "Determine if this is so awesome that it'll get you eaten by sharks" + defaultto :false # Use a symbol for the default value and it'll still be false + end + + newproperty(:suitable_for_human_consumption, :parent => Puppet::Property::Boolean) do + desc "Determine if the thing is both awesome and edible" + defaultto :false # The are more non-edible things than edible things + end + end + +**Type usage**: + + awesome { 'actual booleans': + more_explosions => 'yes', # Use yes as a quoted string! + better_than_rocket_boots => true, # Use an unquoted string! + better_than_sliced_bread => 'true', # Use a quoted string! + suitable_for_human_consumption => no, # Use yes and no! It doesn't matter! + } + +Contact +------- + + * source code: https://github.com/adrienthebo/puppet-boolean + * issue tracker: https://github.com/adrienthebo/puppet-boolean/issues + +If you have questions or concerns about this module, contact finch on #puppet +on Freenode, or email adrien@puppetlabs.com. diff --git a/boolean/lib/puppet/parameter/boolean.rb b/boolean/lib/puppet/parameter/boolean.rb new file mode 100644 index 000000000..6af398992 --- /dev/null +++ b/boolean/lib/puppet/parameter/boolean.rb @@ -0,0 +1,6 @@ +require 'puppet_x/util/boolean' +require 'puppet/parameter' + +class Puppet::Parameter::Boolean < Puppet::Parameter + include PuppetX::Util::Boolean +end diff --git a/boolean/lib/puppet/property/boolean.rb b/boolean/lib/puppet/property/boolean.rb new file mode 100644 index 000000000..4eb37f478 --- /dev/null +++ b/boolean/lib/puppet/property/boolean.rb @@ -0,0 +1,17 @@ +require 'puppet/property' + +begin + require 'puppet_x/util/boolean' +rescue LoadError => e + libdir = Pathname.new(__FILE__).parent.parent.parent + require File.join(libdir, 'puppet_x/util/boolean') +end + +class Puppet::Property::Boolean < Puppet::Property + include PuppetX::Util::Boolean + + # Determine if the boolean property is in sync. + def insync?(is) + munge(is) == should + end +end diff --git a/boolean/lib/puppet_x/util/boolean.rb b/boolean/lib/puppet_x/util/boolean.rb new file mode 100644 index 000000000..270791850 --- /dev/null +++ b/boolean/lib/puppet_x/util/boolean.rb @@ -0,0 +1,62 @@ +module PuppetX + module Util + end +end + +module PuppetX::Util::Boolean + module Ontology + + # All values that are considered 'true' by Puppet internals + def true_values + [true, 'true', :true, :yes, 'yes'] + end + + # All values that are considered 'false' by Puppet internals + def false_values + [false, 'false', :false, :no, 'no', :undef, nil] + end + + # Normalize Boolean values + # + # @param [Object] v Something that vaguely resembles a boolean + # + # @raise [ArgumentError] The supplied parameter cannot be normalized. + # + # @return [true, false] + def munge(v) + if true_values.include? v + true + elsif false_values.include? v + false + else + raise ArgumentError, "Value '#{value}':#{value.class} cannot be determined as a boolean value" + end + end + end + + include Ontology + extend Ontology + + def self.defaultvalues + newvalue(true) + newvalue(false) + + aliasvalue(:true, true) + aliasvalue(:false, false) + + aliasvalue('true', true) + aliasvalue('false', false) + + aliasvalue(:yes, true) + aliasvalue(:no, false) + + aliasvalue('yes', true) + aliasvalue('no', false) + + # Ensure provided values are reasonable by trying to munge them, and if that + # fails then let munge throw the exception and propagate that up. + validate do |value| + munge(value) + end + end +end diff --git a/ceilometer/Gemfile b/ceilometer/Gemfile index b470b7654..d965fa900 100644 --- a/ceilometer/Gemfile +++ b/ceilometer/Gemfile @@ -3,8 +3,6 @@ source 'https://rubygems.org' group :development, :test do gem 'puppetlabs_spec_helper', :require => false gem 'puppet-lint', '~> 0.3.2' - gem 'metadata-json-lint' - gem 'puppet-syntax' gem 'rake', '10.1.1' gem 'rspec', '< 2.99' gem 'json' diff --git a/ceilometer/Modulefile b/ceilometer/Modulefile new file mode 100644 index 000000000..aa446a02a --- /dev/null +++ b/ceilometer/Modulefile @@ -0,0 +1,13 @@ +name 'puppetlabs-ceilometer' +version '4.0.0' +author 'eNovance and StackForge Contributors' +license 'Apache License 2.0' +summary 'Puppet module for OpenStack Ceilometer' +description 'Installs and configures OpenStack Ceilometer (Telemetry).' +project_page 'https://launchpad.net/puppet-ceilometer' +source 'https://github.com/stackforge/puppet-ceilometer' + +dependency 'puppetlabs/inifile', '>=1.0.0 <2.0.0' +dependency 'puppetlabs/keystone', '>=4.0.0 <5.0.0' +dependency 'puppetlabs/stdlib', '>= 3.2.0' +dependency 'stackforge/openstacklib', '>=5.0.0' diff --git a/ceilometer/Rakefile b/ceilometer/Rakefile index c022216ce..4c2b2ed07 100644 --- a/ceilometer/Rakefile +++ b/ceilometer/Rakefile @@ -1,17 +1,6 @@ require 'puppetlabs_spec_helper/rake_tasks' require 'puppet-lint/tasks/puppet-lint' -require 'puppet-syntax/tasks/puppet-syntax' PuppetLint.configuration.fail_on_warnings = true PuppetLint.configuration.send('disable_80chars') PuppetLint.configuration.send('disable_class_parameter_defaults') -PuppetLint.configuration.send('disable_only_variable_string') - -exclude_tests_paths = ['pkg/**/*','vendor/**/*'] -PuppetLint.configuration.ignore_paths = exclude_tests_paths -PuppetSyntax.exclude_paths = exclude_tests_paths - -desc "Lint metadata.json file" -task :metadata do - sh "metadata-json-lint metadata.json" -end diff --git a/ceilometer/manifests/agent/central.pp b/ceilometer/manifests/agent/central.pp index c42d9060b..0a0785613 100644 --- a/ceilometer/manifests/agent/central.pp +++ b/ceilometer/manifests/agent/central.pp @@ -2,22 +2,10 @@ # # == Parameters # [*enabled*] -# (optional) Should the service be enabled. -# Defaults to true. +# Should the service be enabled. Optional. Defauls to true # -# [*manage_service*] -# (optional) Whether the service should be managed by Puppet. -# Defaults to true. -# -# [*package_ensure*] -# (optional) ensure state for package. -# Defaults to 'present' -# - class ceilometer::agent::central ( - $manage_service = true, $enabled = true, - $package_ensure = 'present', ) { include ceilometer::params @@ -26,16 +14,14 @@ Package['ceilometer-agent-central'] -> Service['ceilometer-agent-central'] package { 'ceilometer-agent-central': - ensure => $package_ensure, + ensure => installed, name => $::ceilometer::params::agent_central_package_name, } - if $manage_service { - if $enabled { - $service_ensure = 'running' - } else { - $service_ensure = 'stopped' - } + if $enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' } Package['ceilometer-common'] -> Service['ceilometer-agent-central'] diff --git a/ceilometer/manifests/agent/compute.pp b/ceilometer/manifests/agent/compute.pp index d9d922f98..fde3705d6 100644 --- a/ceilometer/manifests/agent/compute.pp +++ b/ceilometer/manifests/agent/compute.pp @@ -1,23 +1,13 @@ -#The ceilometer::agent::compute class installs the ceilometer compute agent +# The ceilometer::agent::compute class installs the ceilometer compute agent # Include this class on all nova compute nodes # # == Parameters # [*enabled*] -# (optional) Should the service be enabled. -# Defaults to true. -# -# [*manage_service*] -# (optional) Whether the service should be managed by Puppet. -# Defaults to true. -# -# [*package_ensure*] -# (optional) ensure state for package. -# Defaults to 'present' +# should the service be started or not +# Optional. Defaults to true # class ceilometer::agent::compute ( - $manage_service = true, $enabled = true, - $package_ensure = 'present', ) inherits ceilometer { include ceilometer::params @@ -26,7 +16,7 @@ Package['ceilometer-agent-compute'] -> Service['ceilometer-agent-compute'] package { 'ceilometer-agent-compute': - ensure => $package_ensure, + ensure => installed, name => $::ceilometer::params::agent_compute_package_name, } @@ -40,15 +30,13 @@ } } - if $manage_service { - if $enabled { - $service_ensure = 'running' - } else { - $service_ensure = 'stopped' - } + if $enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' } - Package['nova-common'] -> Package['ceilometer-common'] -> Service['ceilometer-agent-compute'] + Package['ceilometer-common'] -> Service['ceilometer-agent-compute'] service { 'ceilometer-agent-compute': ensure => $service_ensure, name => $::ceilometer::params::agent_compute_service_name, diff --git a/ceilometer/manifests/agent/notification.pp b/ceilometer/manifests/agent/notification.pp index 9a6922440..29993622f 100644 --- a/ceilometer/manifests/agent/notification.pp +++ b/ceilometer/manifests/agent/notification.pp @@ -23,13 +23,10 @@ # file with the OVS plugin if both are on the same machine. # # === Parameters -# [*enabled*] -# (optional) Should the service be enabled. -# Defaults to true. # -# [*manage_service*] -# (optional) Whether the service should be managed by Puppet. -# Defaults to true. +# [*enabled*] +# (optional) Should the service be started or not +# Defaults to true # # [*ack_on_event_error*] # (optional) Acknowledge message when event persistence fails. @@ -39,36 +36,24 @@ # (optional) Save event details. # Defaults to false # -# [*package_ensure*] -# (optional) ensure state for package. -# Defaults to 'present' -# class ceilometer::agent::notification ( - $manage_service = true, $enabled = true, $ack_on_event_error = true, - $store_events = false, - $package_ensure = 'present', + $store_events = false ) { include ceilometer::params Ceilometer_config<||> ~> Service['ceilometer-agent-notification'] - Package[$::ceilometer::params::agent_notification_package_name] -> - Service['ceilometer-agent-notification'] - - ensure_resource('package', [$::ceilometer::params::agent_notification_package_name], - { ensure => $package_ensure } - ) + Package[$::ceilometer::params::agent_notification_package_name] -> Service['ceilometer-agent-notification'] + ensure_packages([$::ceilometer::params::agent_notification_package_name]) - if $manage_service { - if $enabled { - $service_ensure = 'running' - } else { - $service_ensure = 'stopped' - } + if $enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' } Package['ceilometer-common'] -> Service['ceilometer-agent-notification'] diff --git a/ceilometer/manifests/alarm/evaluator.pp b/ceilometer/manifests/alarm/evaluator.pp index c634f3ba6..262153491 100644 --- a/ceilometer/manifests/alarm/evaluator.pp +++ b/ceilometer/manifests/alarm/evaluator.pp @@ -2,32 +2,18 @@ # # == Params # [*enabled*] -# (optional) Should the service be enabled. -# Defaults to true. -# -# [*manage_service*] -# (optional) Whether the service should be managed by Puppet. -# Defaults to true. -# +# should the service be enabled # [*evaluation_interval*] -# (optional) Define the time interval for the alarm evaluator -# Defaults to 60. -# +# define the time interval for the alarm evaluator # [*evaluation_service*] -# (optional) Define which service use for the evaluator -# Defaults to 'ceilometer.alarm.service.SingletonAlarmService'. -# +# define which service use for the evaluator # [*partition_rpc_topic*] -# (optional) Define which topic the alarm evaluator should access -# Defaults to 'alarm_partition_coordination'. -# +# define which topic the alarm evaluator should access # [*record_history*] -# (optional) Record alarm change events -# Defaults to true. +# Record alarm change events # class ceilometer::alarm::evaluator ( - $manage_service = true, - $enabled = true, + $enabled = true, $evaluation_interval = 60, $evaluation_service = 'ceilometer.alarm.service.SingletonAlarmService', $partition_rpc_topic = 'alarm_partition_coordination', @@ -36,7 +22,7 @@ include ceilometer::params - validate_re("${evaluation_interval}",'^(\d+)$') + validate_re($evaluation_interval,'^(\d+)$') Ceilometer_config<||> ~> Service['ceilometer-alarm-evaluator'] @@ -44,12 +30,10 @@ Package[$::ceilometer::params::alarm_package_name] -> Package<| title == 'ceilometer-alarm' |> ensure_packages($::ceilometer::params::alarm_package_name) - if $manage_service { - if $enabled { - $service_ensure = 'running' - } else { - $service_ensure = 'stopped' - } + if $enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' } Package['ceilometer-common'] -> Service['ceilometer-alarm-evaluator'] diff --git a/ceilometer/manifests/alarm/notifier.pp b/ceilometer/manifests/alarm/notifier.pp index 4bad22100..1b09baaf1 100644 --- a/ceilometer/manifests/alarm/notifier.pp +++ b/ceilometer/manifests/alarm/notifier.pp @@ -2,31 +2,18 @@ # # == Params # [*enabled*] -# (optional) Should the service be enabled. -# Defaults to true. -# -# [*manage_service*] -# (optional) Whether the service should be managed by Puppet. -# Defaults to true. -# +# should the service be enabled # [*notifier_rpc_topic*] -# (optional) Define on which topic the notifier will have access. -# Defaults to undef. -# +# define on which topic the notifier will have +# access # [*rest_notifier_certificate_key*] -# (optional) Define the certificate key for the rest service. -# Defaults to undef. -# +# define the certificate key for the rest service # [*rest_notifier_certificate_file*] -# (optional) Define the certificate file for the rest service. -# Defaults to undef. -# +# define the certificate file for the rest service # [*rest_notifier_ssl_verify*] -# (optional) Should the ssl verify parameter be enabled. -# Defaults to true. +# should the ssl verify parameter be enabled # class ceilometer::alarm::notifier ( - $manage_service = true, $enabled = true, $notifier_rpc_topic = undef, $rest_notifier_certificate_key = undef, @@ -44,12 +31,10 @@ Package[$::ceilometer::params::alarm_package_name] -> Package<| title == 'ceilometer-alarm' |> ensure_packages($::ceilometer::params::alarm_package_name) - if $manage_service { - if $enabled { - $service_ensure = 'running' - } else { - $service_ensure = 'stopped' - } + if $enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' } Package['ceilometer-common'] -> Service['ceilometer-alarm-notifier'] diff --git a/ceilometer/manifests/api.pp b/ceilometer/manifests/api.pp index c3789cb04..bdb0976f9 100644 --- a/ceilometer/manifests/api.pp +++ b/ceilometer/manifests/api.pp @@ -2,60 +2,42 @@ # # == Parameters # [*enabled*] -# (optional) Should the service be enabled. -# Defaults to true -# -# [*manage_service*] -# (optional) Whether the service should be managed by Puppet. -# Defaults to true. +# should the service be enabled. Optional. Defaults to true # # [*keystone_host*] -# (optional) Keystone's admin endpoint IP/Host. -# Defaults to '127.0.0.1' +# keystone's admin endpoint IP/Host. Optional. Defaults to 127.0.0.1 # # [*keystone_port*] -# (optional) Keystone's admin endpoint port. -# Defaults to 35357 +# keystone's admin endpoint port. Optional. Defaults to 35357 # # [*keystone_auth_admin_prefix*] -# (optional) 'path' to the keystone admin endpoint. +# 'path' to the keystone admin endpoint. Optional. Defaults to false (empty) # Define to a path starting with a '/' and without trailing '/'. # Eg.: '/keystone/admin' to match keystone::wsgi::apache default. -# Defaults to false (empty) # -# [*keystone_protocol*] -# (optional) 'http' or 'https' -# Defaults to 'https'. +# [*keystone_protocol*] http/https +# Optional. Defaults to https # -# [*keytone_user*] -# (optional) User to authenticate with. -# Defaults to 'ceilometer'. +# [*keytone_user*] user to authenticate with +# Optional. Defaults to ceilometer # -# [*keystone_tenant*] -# (optional) Tenant to authenticate with. -# Defaults to 'services'. +# [*keystone_tenant*] tenant to authenticate with +# Optional. Defaults to services # -# [*keystone_password*] -# Password to authenticate with. +# [*keystone_password*] password to authenticate with # Mandatory. # -# [*host*] -# (optional) The ceilometer api bind address. -# Defaults to 0.0.0.0 -# -# [*port*] -# (optional) The ceilometer api port. -# Defaults to 8777 +# [*host*] +# (optional) The ceilometer api bind address +# Defaults to 0.0.0.0 # -# [*package_ensure*] -# (optional) ensure state for package. -# Defaults to 'present' +# [*port*] +# (optional) The ceilometer api port +# Defaults to 8777 # class ceilometer::api ( - $manage_service = true, $enabled = true, - $package_ensure = 'present', $keystone_host = '127.0.0.1', $keystone_port = '35357', $keystone_auth_admin_prefix = false, @@ -69,27 +51,22 @@ ) { include ceilometer::params - include ceilometer::policy validate_string($keystone_password) Ceilometer_config<||> ~> Service['ceilometer-api'] - Class['ceilometer::policy'] ~> Service['ceilometer-api'] Package['ceilometer-api'] -> Ceilometer_config<||> Package['ceilometer-api'] -> Service['ceilometer-api'] - Package['ceilometer-api'] -> Class['ceilometer::policy'] package { 'ceilometer-api': - ensure => $package_ensure, + ensure => installed, name => $::ceilometer::params::api_package_name, } - if $manage_service { - if $enabled { - $service_ensure = 'running' - } else { - $service_ensure = 'stopped' - } + if $enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' } Package['ceilometer-common'] -> Service['ceilometer-api'] diff --git a/ceilometer/manifests/collector.pp b/ceilometer/manifests/collector.pp index 440538c56..2506efbe7 100644 --- a/ceilometer/manifests/collector.pp +++ b/ceilometer/manifests/collector.pp @@ -2,21 +2,10 @@ # # == Params # [*enabled*] -# (optional) Should the service be enabled. -# Defaults to true. -# -# [*manage_service*] -# (optional) Whether the service should be managed by Puppet. -# Defaults to true. -# -# [*package_ensure*] -# (optional) ensure state for package. -# Defaults to 'present' +# should the service be enabled # class ceilometer::collector ( - $manage_service = true, - $enabled = true, - $package_ensure = 'present', + $enabled = true, ) { include ceilometer::params @@ -24,18 +13,14 @@ Ceilometer_config<||> ~> Service['ceilometer-collector'] Package[$::ceilometer::params::collector_package_name] -> Service['ceilometer-collector'] - ensure_resource( 'package', [$::ceilometer::params::collector_package_name], - { ensure => $package_ensure } - ) + ensure_packages([$::ceilometer::params::collector_package_name]) - if $manage_service { - if $enabled { - $service_ensure = 'running' - Class['ceilometer::db'] -> Service['ceilometer-collector'] - Exec['ceilometer-dbsync'] ~> Service['ceilometer-collector'] - } else { - $service_ensure = 'stopped' - } + if $enabled { + $service_ensure = 'running' + Class['ceilometer::db'] -> Service['ceilometer-collector'] + Exec['ceilometer-dbsync'] ~> Service['ceilometer-collector'] + } else { + $service_ensure = 'stopped' } Package['ceilometer-common'] -> Service['ceilometer-collector'] diff --git a/ceilometer/manifests/keystone/auth.pp b/ceilometer/manifests/keystone/auth.pp index b17a7d013..18f4c4e1e 100644 --- a/ceilometer/manifests/keystone/auth.pp +++ b/ceilometer/manifests/keystone/auth.pp @@ -16,12 +16,6 @@ # [*configure_endpoint*] # Should Ceilometer endpoint be configured? Optional. Defaults to 'true'. # -# [*configure_user*] -# Should Ceilometer service user be configured? Optional. Defaults to 'true'. -# -# [*configure_user_role*] -# Should roles be configured on Ceilometer service user? Optional. Defaults to 'true'. -# # [*service_name*] # Name of the service. Optional. Defaults to value of auth_name. # @@ -77,26 +71,24 @@ # Setting this variable overrides other $internal_* parameters. # class ceilometer::keystone::auth ( - $password = false, - $email = 'ceilometer@localhost', - $auth_name = 'ceilometer', - $configure_user = true, - $configure_user_role = true, - $service_name = undef, - $service_type = 'metering', - $public_address = '127.0.0.1', - $admin_address = '127.0.0.1', - $internal_address = '127.0.0.1', - $port = '8777', - $region = 'RegionOne', - $tenant = 'services', - $public_protocol = 'http', - $admin_protocol = 'http', - $internal_protocol = 'http', - $configure_endpoint = true, - $public_url = undef, - $admin_url = undef, - $internal_url = undef, + $password = false, + $email = 'ceilometer@localhost', + $auth_name = 'ceilometer', + $service_name = undef, + $service_type = 'metering', + $public_address = '127.0.0.1', + $admin_address = '127.0.0.1', + $internal_address = '127.0.0.1', + $port = '8777', + $region = 'RegionOne', + $tenant = 'services', + $public_protocol = 'http', + $admin_protocol = 'http', + $internal_protocol = 'http', + $configure_endpoint = true, + $public_url = undef, + $admin_url = undef, + $internal_url = undef, ) { validate_string($password) @@ -125,31 +117,25 @@ $real_service_name = $auth_name } - if $configure_user { - keystone_user { $auth_name: - ensure => present, - password => $password, - email => $email, - tenant => $tenant, - } - } - - if $configure_user_role { - Keystone_user_role["${auth_name}@${tenant}"] ~> - Service <| name == 'ceilometer-api' |> + Keystone_user_role["${auth_name}@${tenant}"] ~> + Service <| name == 'ceilometer-api' |> - if !defined(Keystone_role['ResellerAdmin']) { - keystone_role { 'ResellerAdmin': - ensure => present, - } - } - keystone_user_role { "${auth_name}@${tenant}": - ensure => present, - roles => ['admin', 'ResellerAdmin'], - require => Keystone_role['ResellerAdmin'], + keystone_user { $auth_name: + ensure => present, + password => $password, + email => $email, + tenant => $tenant, + } + if !defined(Keystone_role['ResellerAdmin']) { + keystone_role { 'ResellerAdmin': + ensure => present, } } - + keystone_user_role { "${auth_name}@${tenant}": + ensure => present, + roles => ['admin', 'ResellerAdmin'], + require => Keystone_role['ResellerAdmin'], + } keystone_service { $real_service_name: ensure => present, type => $service_type, diff --git a/ceilometer/manifests/policy.pp b/ceilometer/manifests/policy.pp deleted file mode 100644 index 41df0a1a4..000000000 --- a/ceilometer/manifests/policy.pp +++ /dev/null @@ -1,29 +0,0 @@ -# == Class: ceilometer::policy -# -# Configure the ceilometer policies -# -# === Parameters -# -# [*policies*] -# (optional) Set of policies to configure for ceilometer -# Example : { 'ceilometer-context_is_admin' => {'context_is_admin' => 'true'}, 'ceilometer-default' => {'default' => 'rule:admin_or_owner'} } -# Defaults to empty hash. -# -# [*policy_path*] -# (optional) Path to the ceilometer policy.json file -# Defaults to /etc/ceilometer/policy.json -# -class ceilometer::policy ( - $policies = {}, - $policy_path = '/etc/ceilometer/policy.json', -) { - - validate_hash($policies) - - Openstacklib::Policy::Base { - file_path => $policy_path, - } - - create_resources('openstacklib::policy::base', $policies) - -} diff --git a/ceilometer/metadata.json b/ceilometer/metadata.json deleted file mode 100644 index 77a27e3e2..000000000 --- a/ceilometer/metadata.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "stackforge-ceilometer", - "version": "5.0.0", - "author": "eNovance and StackForge Contributors", - "summary": "Puppet module for OpenStack Ceilometer", - "license": "Apache License 2.0", - "source": "git://github.com/stackforge/puppet-ceilometer.git", - "project_page": "https://launchpad.net/puppet-ceilometer", - "issues_url": "https://bugs.launchpad.net/puppet-ceilometer", - "requirements": [ - { "name": "pe","version_requirement": "3.x" }, - { "name": "puppet","version_requirement": "3.x" } - ], - "operatingsystem_support": [ - { - "operatingsystem": "Debian", - "operatingsystemrelease": ["7"] - }, - { - "operatingsystem": "Fedora", - "operatingsystemrelease": ["20"] - }, - { - "operatingsystem": "RedHat", - "operatingsystemrelease": ["6.5","7"] - }, - { - "operatingsystem": "Ubuntu", - "operatingsystemrelease": ["12.04","14.04"] - } - ], - "description": "Installs and configures OpenStack Ceilometer (Telemetry).", - "dependencies": [ - { "name": "puppetlabs/inifile", "version_requirement": ">=1.0.0 <2.0.0" }, - { "name": "stackforge/keystone", "version_requirement": ">=5.0.0 <6.0.0" }, - { "name": "puppetlabs/stdlib", "version_requirement": ">=4.0.0 <5.0.0" }, - { "name": "stackforge/openstacklib", "version_requirement": ">=5.0.0" } - ] -} diff --git a/ceilometer/spec/classes/ceilometer_agent_central_spec.rb b/ceilometer/spec/classes/ceilometer_agent_central_spec.rb index 0e20c0bd5..5fab4c924 100644 --- a/ceilometer/spec/classes/ceilometer_agent_central_spec.rb +++ b/ceilometer/spec/classes/ceilometer_agent_central_spec.rb @@ -7,9 +7,7 @@ end let :params do - { :enabled => true, - :manage_service => true, - :package_ensure => 'latest' } + { :enabled => true } end shared_examples_for 'ceilometer-agent-central' do @@ -18,7 +16,7 @@ it 'installs ceilometer-agent-central package' do should contain_package('ceilometer-agent-central').with( - :ensure => 'latest', + :ensure => 'installed', :name => platform_params[:agent_package_name], :before => 'Service[ceilometer-agent-central]' ) @@ -30,40 +28,14 @@ ) end - [{:enabled => true}, {:enabled => false}].each do |param_hash| - context "when service should be #{param_hash[:enabled] ? 'enabled' : 'disabled'}" do - before do - params.merge!(param_hash) - end - - it 'configures ceilometer-agent-central service' do - should contain_service('ceilometer-agent-central').with( - :ensure => (params[:manage_service] && params[:enabled]) ? 'running' : 'stopped', - :name => platform_params[:agent_service_name], - :enable => params[:enabled], - :hasstatus => true, - :hasrestart => true - ) - end - end - end - - context 'with disabled service managing' do - before do - params.merge!({ - :manage_service => false, - :enabled => false }) - end - - it 'configures ceilometer-agent-central service' do - should contain_service('ceilometer-agent-central').with( - :ensure => nil, - :name => platform_params[:agent_service_name], - :enable => false, - :hasstatus => true, - :hasrestart => true - ) - end + it 'configures ceilometer-agent-central service' do + should contain_service('ceilometer-agent-central').with( + :ensure => 'running', + :name => platform_params[:agent_service_name], + :enable => true, + :hasstatus => true, + :hasrestart => true + ) end end diff --git a/ceilometer/spec/classes/ceilometer_agent_compute_spec.rb b/ceilometer/spec/classes/ceilometer_agent_compute_spec.rb index c0dd64ed3..16f19a048 100644 --- a/ceilometer/spec/classes/ceilometer_agent_compute_spec.rb +++ b/ceilometer/spec/classes/ceilometer_agent_compute_spec.rb @@ -9,9 +9,7 @@ end let :params do - { :enabled => true, - :manage_service => true, - :package_ensure => 'installed' } + { :enabled => true } end shared_examples_for 'ceilometer-agent-compute' do @@ -40,10 +38,14 @@ ) end - it 'ensures nova-common is installed before the package ceilometer-common' do - should contain_package('nova-common').with( - :before => /Package\[ceilometer-common\]/ - ) + it 'configures ceilometer-agent-compute service' do + should contain_service('ceilometer-agent-compute').with( + :ensure => 'running', + :name => platform_params[:agent_service_name], + :enable => true, + :hasstatus => true, + :hasrestart => true + ) end it 'configures nova notification driver' do @@ -59,43 +61,6 @@ ) end - [{:enabled => true}, {:enabled => false}].each do |param_hash| - context "when service should be #{param_hash[:enabled] ? 'enabled' : 'disabled'}" do - before do - params.merge!(param_hash) - end - - it 'configures ceilometer-agent-compute service' do - - should contain_service('ceilometer-agent-compute').with( - :ensure => (params[:manage_service] && params[:enabled]) ? 'running' : 'stopped', - :name => platform_params[:agent_service_name], - :enable => params[:enabled], - :hasstatus => true, - :hasrestart => true - ) - end - end - end - - context 'with disabled service managing' do - before do - params.merge!({ - :manage_service => false, - :enabled => false }) - end - - it 'configures ceilometer-agent-compute service' do - should contain_service('ceilometer-agent-compute').with( - :ensure => nil, - :name => platform_params[:agent_service_name], - :enable => false, - :hasstatus => true, - :hasrestart => true - ) - end - end - end context 'on Debian platforms' do diff --git a/ceilometer/spec/classes/ceilometer_agent_notification_spec.rb b/ceilometer/spec/classes/ceilometer_agent_notification_spec.rb index 62ca2889d..227b6b6bd 100644 --- a/ceilometer/spec/classes/ceilometer_agent_notification_spec.rb +++ b/ceilometer/spec/classes/ceilometer_agent_notification_spec.rb @@ -27,9 +27,7 @@ end let :params do - { :manage_service => true, - :enabled => true, - :ack_on_event_error => true, + { :ack_on_event_error => true, :store_events => false } end @@ -38,8 +36,16 @@ it { should contain_class('ceilometer::params') } it 'installs ceilometer agent notification package' do - should contain_package(platform_params[:agent_notification_package_name]).with( - :ensure => 'present' + should contain_package(platform_params[:agent_notification_package_name]) + end + + it 'configures ceilometer agent notification service' do + should contain_service('ceilometer-agent-notification').with( + :ensure => 'running', + :name => platform_params[:agent_notification_service_name], + :enable => true, + :hasstatus => true, + :hasrestart => true ) end @@ -48,42 +54,6 @@ should contain_ceilometer_config('notification/store_events').with_value( params[:store_events] ) end - [{:enabled => true}, {:enabled => false}].each do |param_hash| - context "when service should be #{param_hash[:enabled] ? 'enabled' : 'disabled'}" do - before do - params.merge!(param_hash) - end - - it 'configures ceilometer agent notification service' do - should contain_service('ceilometer-agent-notification').with( - :ensure => (params[:manage_service] && params[:enabled]) ? 'running' : 'stopped', - :name => platform_params[:agent_notification_service_name], - :enable => params[:enabled], - :hasstatus => true, - :hasrestart => true - ) - end - end - end - - context 'with disabled service managing' do - before do - params.merge!({ - :manage_service => false, - :enabled => false }) - end - - it 'configures ceilometer-agent-notification service' do - should contain_service('ceilometer-agent-notification').with( - :ensure => nil, - :name => platform_params[:agent_notification_service_name], - :enable => false, - :hasstatus => true, - :hasrestart => true - ) - end - end - end context 'on Debian platforms' do diff --git a/ceilometer/spec/classes/ceilometer_alarm_evaluator_spec.rb b/ceilometer/spec/classes/ceilometer_alarm_evaluator_spec.rb index b2c4c48b0..d017695e9 100644 --- a/ceilometer/spec/classes/ceilometer_alarm_evaluator_spec.rb +++ b/ceilometer/spec/classes/ceilometer_alarm_evaluator_spec.rb @@ -12,7 +12,6 @@ :partition_rpc_topic => 'alarm_partition_coordination', :record_history => true, :enabled => true, - :manage_service => true, } end @@ -33,6 +32,17 @@ ) end + it 'configures ceilometer-alarm-evaluator service' do + should contain_service('ceilometer-alarm-evaluator').with( + :ensure => 'running', + :name => platform_params[:alarm_evaluator_service_name], + :enable => true, + :hasstatus => true, + :hasrestart => true + ) + end + + it 'configures alarm evaluator' do should contain_ceilometer_config('alarm/evaluation_interval').with_value( params[:evaluation_interval] ) should contain_ceilometer_config('alarm/evaluation_service').with_value( params[:evaluation_service] ) @@ -53,50 +63,14 @@ it { should contain_ceilometer_config('alarm/partition_rpc_topic').with_value(params[:partition_rpc_topic]) } end - context 'when override the evaluation interval with a non numeric value' do - before do - params.merge!(:evaluation_interval => 'NaN') - end - - it { expect { should contain_ceilometer_config('alarm/evaluation_interval') }.to\ - raise_error(Puppet::Error, /validate_re\(\): .* does not match/) } - end - - [{:enabled => true}, {:enabled => false}].each do |param_hash| - context "when service should be #{param_hash[:enabled] ? 'enabled' : 'disabled'}" do + context 'when override the evaluation interval with a non numeric value' do before do - params.merge!(param_hash) + params.merge!(:evaluation_interval => 'NaN') end - it 'configures ceilometer-alarm-evaluator service' do - should contain_service('ceilometer-alarm-evaluator').with( - :ensure => (params[:manage_service] && params[:enabled]) ? 'running' : 'stopped', - :name => platform_params[:alarm_evaluator_service_name], - :enable => params[:enabled], - :hasstatus => true, - :hasrestart => true - ) - end - end - end - - context 'with disabled service managing' do - before do - params.merge!({ - :manage_service => false, - :enabled => false }) - end - - it 'configures ceilometer-alarm-evaluator service' do - should contain_service('ceilometer-alarm-evaluator').with( - :ensure => nil, - :name => platform_params[:alarm_evaluator_service_name], - :enable => false, - :hasstatus => true, - :hasrestart => true - ) + it { expect { should contain_ceilometer_config('alarm/evaluation_interval') }.to\ + raise_error(Puppet::Error, /validate_re\(\): .* does not match/) } end - end end context 'on Debian platforms' do diff --git a/ceilometer/spec/classes/ceilometer_alarm_notifier_spec.rb b/ceilometer/spec/classes/ceilometer_alarm_notifier_spec.rb index d4e65a1e3..6c40d433a 100644 --- a/ceilometer/spec/classes/ceilometer_alarm_notifier_spec.rb +++ b/ceilometer/spec/classes/ceilometer_alarm_notifier_spec.rb @@ -13,7 +13,6 @@ #:rest_notifier_certificate_file => 'UNSET', #:rest_notifier_ssl_verify => true, :enabled => true, - :manage_service => true, } end @@ -34,6 +33,17 @@ ) end + it 'configures ceilometer-alarm-notifier service' do + should contain_service('ceilometer-alarm-notifier').with( + :ensure => 'running', + :name => platform_params[:alarm_notifier_service_name], + :enable => true, + :hasstatus => true, + :hasrestart => true + ) + end + + it 'configures alarm notifier' do should_not contain_ceilometer_config('alarm/notifier_rpc_topic') should_not contain_ceilometer_config('alarm/rest_notifier_certificate_key') @@ -54,42 +64,6 @@ it { should contain_ceilometer_config('alarm/rest_notifier_ssl_verify').with_value(params[:rest_notifier_ssl_verify]) } end - [{:enabled => true}, {:enabled => false}].each do |param_hash| - context "when service should be #{param_hash[:enabled] ? 'enabled' : 'disabled'}" do - before do - params.merge!(param_hash) - end - - it 'configures ceilometer-alarm-notifier service' do - should contain_service('ceilometer-alarm-notifier').with( - :ensure => (params[:manage_service] && params[:enabled]) ? 'running' : 'stopped', - :name => platform_params[:alarm_notifier_service_name], - :enable => params[:enabled], - :hasstatus => true, - :hasrestart => true - ) - end - end - end - - context 'with disabled service managing' do - before do - params.merge!({ - :manage_service => false, - :enabled => false }) - end - - it 'configures ceilometer-alarm-notifier service' do - should contain_service('ceilometer-alarm-notifier').with( - :ensure => nil, - :name => platform_params[:alarm_notifier_service_name], - :enable => false, - :hasstatus => true, - :hasrestart => true - ) - end - end - end context 'on Debian platforms' do diff --git a/ceilometer/spec/classes/ceilometer_api_spec.rb b/ceilometer/spec/classes/ceilometer_api_spec.rb index c5538a6cc..3075d29ce 100644 --- a/ceilometer/spec/classes/ceilometer_api_spec.rb +++ b/ceilometer/spec/classes/ceilometer_api_spec.rb @@ -8,7 +8,6 @@ let :params do { :enabled => true, - :manage_service => true, :keystone_host => '127.0.0.1', :keystone_port => '35357', :keystone_protocol => 'http', @@ -16,8 +15,7 @@ :keystone_password => 'ceilometer-passw0rd', :keystone_tenant => 'services', :host => '0.0.0.0', - :port => '8777', - :package_ensure => 'latest', + :port => '8777' } end @@ -29,15 +27,26 @@ end it { should contain_class('ceilometer::params') } - it { should contain_class('ceilometer::policy') } it 'installs ceilometer-api package' do should contain_package('ceilometer-api').with( - :ensure => 'latest', + :ensure => 'installed', :name => platform_params[:api_package_name] ) end + it 'configures ceilometer-api service' do + should contain_service('ceilometer-api').with( + :ensure => 'running', + :name => platform_params[:api_service_name], + :enable => true, + :hasstatus => true, + :hasrestart => true, + :require => 'Class[Ceilometer::Db]', + :subscribe => 'Exec[ceilometer-dbsync]' + ) + end + it 'configures keystone authentication middleware' do should contain_ceilometer_config('keystone_authtoken/auth_host').with_value( params[:keystone_host] ) should contain_ceilometer_config('keystone_authtoken/auth_port').with_value( params[:keystone_port] ) @@ -74,44 +83,6 @@ end end end - - [{:enabled => true}, {:enabled => false}].each do |param_hash| - context "when service should be #{param_hash[:enabled] ? 'enabled' : 'disabled'}" do - before do - params.merge!(param_hash) - end - - it 'configures ceilometer-api service' do - should contain_service('ceilometer-api').with( - :ensure => (params[:manage_service] && params[:enabled]) ? 'running' : 'stopped', - :name => platform_params[:api_service_name], - :enable => params[:enabled], - :hasstatus => true, - :hasrestart => true, - :require => 'Class[Ceilometer::Db]', - :subscribe => 'Exec[ceilometer-dbsync]' - ) - end - end - end - - context 'with disabled service managing' do - before do - params.merge!({ - :manage_service => false, - :enabled => false }) - end - - it 'configures ceilometer-api service' do - should contain_service('ceilometer-api').with( - :ensure => nil, - :name => platform_params[:api_service_name], - :enable => false, - :hasstatus => true, - :hasrestart => true - ) - end - end end context 'on Debian platforms' do diff --git a/ceilometer/spec/classes/ceilometer_collector_spec.rb b/ceilometer/spec/classes/ceilometer_collector_spec.rb index 72b5668f2..25e6d650b 100644 --- a/ceilometer/spec/classes/ceilometer_collector_spec.rb +++ b/ceilometer/spec/classes/ceilometer_collector_spec.rb @@ -16,9 +16,7 @@ it { should contain_class('ceilometer::params') } it 'installs ceilometer-collector package' do - should contain_package(platform_params[:collector_package_name]).with( - :ensure => 'present' - ) + should contain_package(platform_params[:collector_package_name]) end it 'configures ceilometer-collector service' do @@ -44,33 +42,6 @@ # Catalog compilation does not crash for lack of ceilometer::db it { should compile } - - it 'configures ceilometer-collector service' do - should contain_service('ceilometer-collector').with( - :ensure => 'stopped', - :name => platform_params[:collector_service_name], - :enable => false, - :hasstatus => true, - :hasrestart => true - ) - end - end - - context 'when service management is disabled' do - let :params do - { :enabled => false, - :manage_service => false } - end - - it 'configures ceilometer-collector service' do - should contain_service('ceilometer-collector').with( - :ensure => nil, - :name => platform_params[:collector_service_name], - :enable => false, - :hasstatus => true, - :hasrestart => true - ) - end end end diff --git a/ceilometer/spec/classes/ceilometer_keystone_auth_spec.rb b/ceilometer/spec/classes/ceilometer_keystone_auth_spec.rb index 1960d34c2..cf06a01d4 100644 --- a/ceilometer/spec/classes/ceilometer_keystone_auth_spec.rb +++ b/ceilometer/spec/classes/ceilometer_keystone_auth_spec.rb @@ -164,39 +164,6 @@ end end - context 'when disabling user configuration' do - before do - params.merge!( :configure_user => false ) - end - - it { should_not contain_keystone_user('ceilometer') } - it { should contain_keystone_user_role('ceilometer@services') } - - it { should contain_keystone_service('ceilometer').with( - :ensure => 'present', - :type => 'metering', - :description => 'Openstack Metering Service' - )} - end - - context 'when disabling user and role configuration' do - before do - params.merge!( - :configure_user => false, - :configure_user_role => false - ) - end - - it { should_not contain_keystone_user('ceilometer') } - it { should_not contain_keystone_user_role('ceilometer@services') } - - it { should contain_keystone_service('ceilometer').with( - :ensure => 'present', - :type => 'metering', - :description => 'Openstack Metering Service' - )} - end - end context 'on Debian platforms' do diff --git a/ceilometer/spec/classes/ceilometer_policy_spec.rb b/ceilometer/spec/classes/ceilometer_policy_spec.rb deleted file mode 100644 index 79338dc36..000000000 --- a/ceilometer/spec/classes/ceilometer_policy_spec.rb +++ /dev/null @@ -1,41 +0,0 @@ -require 'spec_helper' - -describe 'ceilometer::policy' do - - shared_examples_for 'ceilometer policies' do - let :params do - { - :policy_path => '/etc/ceilometer/policy.json', - :policies => { - 'context_is_admin' => { - 'key' => 'context_is_admin', - 'value' => 'foo:bar' - } - } - } - end - - it 'set up the policies' do - should contain_openstacklib__policy__base('context_is_admin').with({ - :key => 'context_is_admin', - :value => 'foo:bar' - }) - end - end - - context 'on Debian platforms' do - let :facts do - { :osfamily => 'Debian' } - end - - it_configures 'ceilometer policies' - end - - context 'on RedHat platforms' do - let :facts do - { :osfamily => 'RedHat' } - end - - it_configures 'ceilometer policies' - end -end diff --git a/ceph/.fixtures.yml b/ceph/.fixtures.yml new file mode 100644 index 000000000..e503158f8 --- /dev/null +++ b/ceph/.fixtures.yml @@ -0,0 +1,7 @@ +fixtures: + repositories: + "apt": "git://github.com/puppetlabs/puppetlabs-apt.git" + "concat": "git://github.com/puppetlabs/puppetlabs-concat.git" + "stdlib": "git://github.com/puppetlabs/puppetlabs-stdlib.git" + symlinks: + "ceph": "#{source_dir}" diff --git a/ceph/.forgeignore b/ceph/.forgeignore new file mode 100644 index 000000000..5c2e8f4fb --- /dev/null +++ b/ceph/.forgeignore @@ -0,0 +1,12 @@ +- pkg/ +- spec/ +- Rakefile +- coverage/ +- Vagrantfile +- .git/ +- .forgeignore +- .travis.yml +- .gitignore +- Gemfile +- Gemfile.lock +- .fixtures.yml diff --git a/ceph/.gitignore b/ceph/.gitignore new file mode 100644 index 000000000..062d83d39 --- /dev/null +++ b/ceph/.gitignore @@ -0,0 +1,3 @@ +*.vdi +*.swp +Gemfile.lock diff --git a/ceph/.travis.yml b/ceph/.travis.yml new file mode 100644 index 000000000..7d6b76121 --- /dev/null +++ b/ceph/.travis.yml @@ -0,0 +1,20 @@ +language: ruby +bundler_args: --without development +script: "bundle exec rake spec SPEC_OPTS='--format documentation --color --backtrace'" +rvm: + - 1.8.7 + - 1.9.3 + - 2.0.0 +env: + matrix: + - PUPPET_VERSION="~> 2.7.0" + - PUPPET_VERSION="~> 3.3.0" + - PUPPET_VERSION="~> 3.4.0" +matrix: + exclude: + - rvm: 1.9.3 + env: PUPPET_VERSION="~> 2.7.0" + - rvm: 2.0.0 + env: PUPPET_VERSION="~> 2.7.0" +notifications: + email: false diff --git a/common/COPYING b/ceph/AGPLv3.txt similarity index 100% rename from common/COPYING rename to ceph/AGPLv3.txt diff --git a/ceph/CHANGELOG.md b/ceph/CHANGELOG.md new file mode 100644 index 000000000..04fdc5c60 --- /dev/null +++ b/ceph/CHANGELOG.md @@ -0,0 +1,29 @@ +##2014-10-08 - release 1.2.0 + +###Summary +* mon: try to create admin keyring only after service started +* Reduce recovery priority (new feature) +* Use sane default XFS options (same as ceph-disk) (remove '-n size=64k') + +####Bugfixes +* Fix deprecation warning in ceph::osd::device +* Increase ceph keyring timeout +* Fixing admin keyring exported resources +* future parser is using strict var types when comparing +* ceph_osd_bootstrap_key fact: increase timeout + +##2014-06-23 - Features release 1.1.0 + +###Summary +* Allow to configure ceph's apt source +* Allow custom configuration for ceph conf (config hash) +* Ensure a consistent ordering of config keys +* Manage mon and osd in /var/lib/ceph + +####Bugfixes +* Fix spec tests and RSpec 3.x formater + +##2014-04-04 - First version 1.0.0 + +###Summary +* First stable version. diff --git a/ceph/Gemfile b/ceph/Gemfile new file mode 100644 index 000000000..c6a901cb3 --- /dev/null +++ b/ceph/Gemfile @@ -0,0 +1,17 @@ +source "https://rubygems.org" + +group :development, :test do + gem 'puppetlabs_spec_helper', :require => false + gem 'puppet-lint', '~> 0.3.2' + gem 'rake', '10.1.0' + gem 'rspec', '< 2.99' + gem 'puppet-syntax' +end + +if puppetversion = ENV['PUPPET_VERSION'] + gem 'puppet', puppetversion, :require => false +else + gem 'puppet', :require => false +end + +# vim:ft=ruby diff --git a/ceph/LICENSE b/ceph/LICENSE new file mode 100644 index 000000000..f860ebcf7 --- /dev/null +++ b/ceph/LICENSE @@ -0,0 +1,18 @@ +Copyright © 2012,2013 eNovance SAS + +Authors: + François Charlier + Sébastien Han + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . diff --git a/ceph/README.rst b/ceph/README.rst new file mode 100644 index 000000000..ec894d076 --- /dev/null +++ b/ceph/README.rst @@ -0,0 +1,209 @@ +=========== +puppet-ceph +=========== + +About +===== + +|BuildStatus|_ |PuppetForge|_ |License|_ + +.. |BuildStatus| image:: https://api.travis-ci.org/enovance/puppet-ceph.png?branch=master +.. _BuildStatus: https://travis-ci.org/enovance/puppet-ceph +.. |PuppetForge| image:: http://img.shields.io/puppetforge/v/eNovance/cloud.svg +.. _PuppetForge: https://forge.puppetlabs.com/eNovance/ceph +.. |License| image:: http://img.shields.io/:license-agpl-blue.svg +.. _License: https://gnu.org/licenses/agpl.html + +This is a Puppet_ module to install a Ceph_ cluster. + +.. _Puppet: http://www.puppetlabs.com/ +.. _Ceph: http://ceph.com/ + +Status +====== + +Developped/tested on Debian GNU/Linux Wheezy, targetting the Bobtail Ceph release. + +Features +======== + +* Ceph package ✓ + +* Ceph MONs ✓ + + • MON configuration ✓ + + • MON service key ✓ + + • MON filesystem creation ✓ + + • MON service ✓ + + • MON cluster ✓ + + • admin key ✓ + +* Ceph OSDs ✓ + + • OSD configuration ✓ + + • OSD bootstrap key ✓ + + • OSD device ✓ + + - OSD device formatting ✓ + + - OSD device mounting ✓ + + - OSD filesystem creation ✓ + + - OSD service key ✓ + + • OSD service ✓ + + • OSD registration ✓ + + • Insert OSD into crushmap ✓ + + • Working OSD ✓ + +TODO +==== + +* Finish writing the rspec tests + +* Better OSD device placement possibilities + +* Test/finish MDS/RadosGW code + +Contributing +============ + +Contributions are welcome, just fork on GitHub and send a pull-request ! + +* Please try to keep each new feature / change / fix in a separate pull-request, it will greatly help speeding the merge of the pull-requests ;) + +* When adding features, don't forget to add unit tests. + +* puppet-lint (https://github.com/rodjek/puppet-lint) should not produce too much errors too :) + +* Some repo-collaborators might ask you some questions or to update your code. All these questions/requests are open to discussion, but please answer, dont just drop your code & abandon it after ! + +Using +===== + +To install a Ceph cluster you'll need at least *one* host to act as a MON and with the current crushmap defaults *two* hosts to act as OSDs. (The MON *might* be the same as an OSD, but has not been tested yet). And of course one puppetmaster :-) + +This module requires the puppet master to have `storeconfigs = true` set and a storage backend configured. On the puppet agents `pluginsync = true` is required too. + +Minimum Puppet manifest for all members of the Ceph cluster +----------------------------------------------------------- + +A Ceph cluster needs a cluster `fsid` : get one with `uuidgen -r` (Install it with `apt-get install uuid-runtime`):: + + $fsid ='some uuid from uuidgen -r' + +The general configuration:: + + class { 'ceph::conf': + fsid => $fsid, + auth_type => 'cephx', # Currently only cephx is supported AND required + cluster_network => '10.0.0.0/24', # The cluster's «internal» network + public_network => '192.168.0.0/24', # The cluster's «public» (where clients are) network + } + +APT configuration to install from the official Ceph repositories:: + + include ceph::apt::ceph + + +Puppet manifest for a MON +------------------------- + +A MON host also needs the MONs secret : get it with `ceph-authtool --create /path/to/keyring --gen-key -n mon.`:: + + $mon_secret = 'AQD7kyJQQGoOBhAAqrPAqSopSwPrrfMMomzVdw==' + +An Id:: + + $id = 0 # must be unique for each MON in the cluster + +And the mon declaration:: + + ceph::mon { $id: + monitor_secret => $mon_secret, + mon_addr => '192.168.0.10', # The host's «public» IP address + } + +Then on **ONLY ONE** MON, export the admin key (required by the OSDs):: + + if !empty($::ceph_admin_key) { + @@ceph::key { 'admin': + secret => $::ceph_admin_key, + keyring_path => '/etc/ceph/keyring', + } + } + + +**NOTE**: The puppet agent needs to be ran 3 times for the MON to be up and the admin key exported. + +Puppet manifest for an OSD +-------------------------- + +An OSD host also needs the global host configuration for OSDs:: + + class { 'ceph::osd': + public_address => '192.168.0.100', + cluster_address => '10.0.0.100', + } + +And for each disk/device the path of the physical device to format:: + + ceph::osd::device { '/dev/sdb': } + +**WARNING**: this previous step will trash all the data from your disk !!! + +On an OSD, the puppet agent must be ran at least 4 times for the OSD to be formatted, registered on the OSDs and in the crushmap. + +Testing +======= + +Using Vagrant +------------- + +Clone the repo & enter the created directory :: + + git clone git://github.com/enovance/puppet-ceph.git + cd puppet-ceph + +Launch three MONs :: + + vagrant up mon0 + vagrant up mon1 + vagrant up mon2 + +Run puppet one more time to update the ceph configuration (uses exported resources) :: + + vagrant ssh mon0 -c 'sudo puppet agent -vt' + vagrant ssh mon1 -c 'sudo puppet agent -vt' + vagrant ssh mon2 -c 'sudo puppet agent -vt' + +Ceph MONs should be up :: + + vagrant ssh mon0 -c "sudo ceph mon stat" + e3: 3 mons at {0=192.168.251.10:6789/0,1=192.168.251.11:6789/0,2=192.168.251.12:6789/0}, election epoch 4, quorum 0,1 0,1 + +Launch at least 2 OSDs :: + + vagrant up osd0 + vagrant up osd1 + vagrant up osd2 + +Now login on mon0 (for example) & check ceph health :: + + vagrant ssh mon0 -c 'sudo ceph -s' + health HEALTH_OK + monmap e2: 2 mons at {0=192.168.252.10:6789/0,1=192.168.252.11:6789/0}, election epoch 4, quorum 0,1 0,1 + osdmap e35: 6 osds: 6 up, 6 in + pgmap v158: 192 pgs: 192 active+clean; 0 bytes data, 242 MB used, 23601 MB / 23844 MB avail + diff --git a/ceph/Rakefile b/ceph/Rakefile new file mode 100644 index 000000000..d73691b09 --- /dev/null +++ b/ceph/Rakefile @@ -0,0 +1,27 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : +# +NAME = 'eNovance-ceph' +TDIR = File.expand_path(File.dirname(__FILE__)) + +require 'puppetlabs_spec_helper/rake_tasks' +require 'puppet-lint/tasks/puppet-lint' +require 'puppet-syntax/tasks/puppet-syntax' + +PuppetLint.configuration.fail_on_warnings = true +PuppetLint.configuration.send('disable_80chars') +PuppetLint.configuration.send('disable_class_parameter_defaults') + +exclude_tests_paths = ['pkg/**/*','vendor/**/*','spec/**/*','examples/**/*'] +PuppetLint.configuration.ignore_paths = exclude_tests_paths +PuppetSyntax.exclude_paths = exclude_tests_paths + +task(:default).clear +task :default => [:syntax,:lint,:spec] + +namespace :module do + desc "Build #{NAME} module (in a clean env) Please use this for puppetforge" + task :build do + exec "rsync -rv --exclude-from=#{TDIR}/.forgeignore . /tmp/#{NAME};cd /tmp/#{NAME};puppet module build" + end +end diff --git a/ceph/Vagrantfile b/ceph/Vagrantfile new file mode 100644 index 000000000..9fd8312cb --- /dev/null +++ b/ceph/Vagrantfile @@ -0,0 +1,45 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +Vagrant.configure("2") do |config| + config.vm.box = "wheezy64" + config.vm.box_url = "http://os.enocloud.com:8080/v1/AUTH_fc47c4103c9b4aaf9271c581776a268f/public/wheezy64.box" + + config.vm.provider :virtualbox do |vb| + vb.customize ["modifyvm", :id, "--nictype1", "virtio"] + vb.customize ["modifyvm", :id, "--nictype2", "virtio"] + vb.customize ["modifyvm", :id, "--nictype3", "virtio"] + end + + (0..2).each do |i| + config.vm.define "mon#{i}" do |mon| + mon.vm.hostname = "ceph-mon#{i}.test" + mon.vm.network :private_network, ip: "192.168.251.1#{i}" + mon.vm.network :private_network, ip: "192.168.252.1#{i}" + mon.vm.provision :shell, :path => "examples/mon.sh" + end + end + + (0..2).each do |i| + config.vm.define "osd#{i}" do |osd| + osd.vm.hostname = "ceph-osd#{i}.test" + osd.vm.network :private_network, ip: "192.168.251.10#{i}" + osd.vm.network :private_network, ip: "192.168.252.10#{i}" + osd.vm.provision :shell, :path => "examples/osd.sh" + (0..1).each do |d| + osd.vm.provider :virtualbox do |vb| + vb.customize [ "createhd", "--filename", "disk-#{i}-#{d}", "--size", "5000" ] + vb.customize [ "storageattach", :id, "--storagectl", "SATA Controller", "--port", 3+d, "--device", 0, "--type", "hdd", "--medium", "disk-#{i}-#{d}.vdi" ] + end + end + end + end + + (0..1).each do |i| + config.vm.define "mds#{i}" do |mds| + mds.vm.hostname = "ceph-mds#{i}.test" + mds.vm.network :private_network, ip: "192.168.251.15#{i}" + mds.vm.provision :shell, :path => "examples/mds.sh" + end + end +end diff --git a/ceph/examples/common.sh b/ceph/examples/common.sh new file mode 100755 index 000000000..4ce217fb5 --- /dev/null +++ b/ceph/examples/common.sh @@ -0,0 +1,90 @@ +#!/bin/bash + +set -x +set -e + +AGENT_OPTIONS="--onetime --verbose --ignorecache --no-daemonize --no-usecacheonfailure --no-splay --show_diff --debug" + +# ensure a correct domain name is set from dhclient +grep -q 'supersede domain-name "test";' /etc/dhcp/dhclient.conf || { + echo 'supersede domain-name "test";' >> /etc/dhcp/dhclient.conf + pkill -9 dhclient + dhclient eth0 +} + +# add hosts to /etc/hosts +grep -q "ceph-mon0" /etc/hosts || echo "192.168.251.10 ceph-mon0 ceph-mon0.test" >> /etc/hosts +grep -q "ceph-mon1" /etc/hosts || echo "192.168.251.11 ceph-mon1 ceph-mon1.test" >> /etc/hosts +grep -q "ceph-mon2" /etc/hosts || echo "192.168.251.12 ceph-mon2 ceph-mon2.test" >> /etc/hosts +grep -q "ceph-osd0" /etc/hosts || echo "192.168.251.100 ceph-osd0 ceph-osd0.test" >> /etc/hosts +grep -q "ceph-osd1" /etc/hosts || echo "192.168.251.101 ceph-osd1 ceph-osd1.test" >> /etc/hosts +grep -q "ceph-osd2" /etc/hosts || echo "192.168.251.102 ceph-osd2 ceph-osd2.test" >> /etc/hosts +grep -q "ceph-mds0" /etc/hosts || echo "192.168.251.150 ceph-mds0 ceph-mds0.test" >> /etc/hosts +grep -q "ceph-mds1" /etc/hosts || echo "192.168.251.151 ceph-mds1 ceph-mds1.test" >> /etc/hosts + +aptitude update + +# Install ruby 1.8 and ensure it is the default +aptitude install -y ruby1.8 +update-alternatives --set ruby /usr/bin/ruby1.8 + + +# Install puppetmaster, etc. … +if hostname | grep -q "ceph-mon0"; then + aptitude install -y puppetmaster sqlite3 libsqlite3-ruby libactiverecord-ruby git augeas-tools puppet ruby1.8-dev libruby1.8 + + # This lens seems to be broken currently on wheezy/sid ? + # test -f /usr/share/augeas/lenses/dist/cgconfig.aug && rm -f /usr/share/augeas/lenses/dist/cgconfig.aug + augtool << EOT +set /files/etc/puppet/puppet.conf/agent/pluginsync true +set /files/etc/puppet/puppet.conf/agent/server ceph-mon0.test +set /files/etc/puppet/puppet.conf/master/storeconfigs true +set /files/etc/puppet/puppet.conf/master/dbadapter sqlite3 +save +EOT + + # Autosign certificates from our test setup + echo "*.test" > /etc/puppet/autosign.conf + + test -f /etc/puppet/modules/concat || puppet module install ripienaar/concat + test -f /etc/puppet/modules/apt || puppet module install puppetlabs/apt + test -f /etc/puppet/modules/ceph || ln -s /vagrant /etc/puppet/modules/ceph + + test -h /etc/puppet/manifests/site.pp || ln -s /vagrant/examples/site.pp /etc/puppet/manifests/ + + service puppetmaster restart +else + aptitude install -y augeas-tools + + # This lens seems to be broken currently on wheezy/sid ? + test -f /usr/share/augeas/lenses/dist/cgconfig.aug && rm -f /usr/share/augeas/lenses/dist/cgconfig.aug + augtool << EOT +set /files/etc/puppet/puppet.conf/agent/pluginsync true +set /files/etc/puppet/puppet.conf/agent/server ceph-mon0.test +save +EOT + +fi + +# And finally, run the puppet agent +puppet agent $AGENT_OPTIONS + +# Run two more times on MON servers to generate & export the admin key +if hostname | grep -q "ceph-mon"; then + puppet agent $AGENT_OPTIONS + puppet agent $AGENT_OPTIONS +fi + +# Run 4/5 more times on OSD servers to get the admin key, format devices, get osd ids, etc. … +if hostname | grep -q "ceph-osd"; then + for STEP in $(seq 0 4); do + echo ================ + echo STEP $STEP + echo ================ + blkid > /tmp/blkid_step_$STEP + facter --puppet|egrep "blkid|ceph" > /tmp/facter_step_$STEP + ceph osd dump > /tmp/ceph-osd-dump_step_$STEP + + puppet agent $AGENT_OPTIONS + done +fi diff --git a/ceph/examples/mds.sh b/ceph/examples/mds.sh new file mode 100755 index 000000000..7d48b3a4b --- /dev/null +++ b/ceph/examples/mds.sh @@ -0,0 +1,3 @@ +#! /bin/sh + +/vagrant/examples/common.sh diff --git a/ceph/examples/mon.sh b/ceph/examples/mon.sh new file mode 100755 index 000000000..aded1fd58 --- /dev/null +++ b/ceph/examples/mon.sh @@ -0,0 +1,3 @@ +#! /bin/bash + +/vagrant/examples/common.sh diff --git a/ceph/examples/osd.sh b/ceph/examples/osd.sh new file mode 100755 index 000000000..aded1fd58 --- /dev/null +++ b/ceph/examples/osd.sh @@ -0,0 +1,3 @@ +#! /bin/bash + +/vagrant/examples/common.sh diff --git a/ceph/examples/site.pp b/ceph/examples/site.pp new file mode 100644 index 000000000..1fc0bce76 --- /dev/null +++ b/ceph/examples/site.pp @@ -0,0 +1,83 @@ +$fsid = '07d28faa-48ae-4356-a8e3-19d5b81e159e' +$mon_secret = 'AQD7kyJQQGoOBhAAqrPAqSopSwPrrfMMomzVdw==' + +Exec { + path => '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin' +} + +# +class role_ceph ( + $fsid, + $auth_type = 'cephx' +) { + + class { 'ceph::conf': + fsid => $fsid, + auth_type => $auth_type, + cluster_network => "${::network_eth2}/24", + public_network => "${::network_eth1}/24" + } + + include ceph::apt::ceph + +} + +# +class role_ceph_mon ( + $id +) { + + class { 'role_ceph': + fsid => $::fsid, + auth_type => 'cephx', + } + + ceph::mon { $id: + monitor_secret => $::mon_secret, + mon_port => 6789, + mon_addr => $ipaddress_eth2, + } + +} + +node 'ceph-mon0.test' { + if !empty($::ceph_admin_key) { + @@ceph::key { 'admin': + secret => $::ceph_admin_key, + keyring_path => '/etc/ceph/keyring', + } + } + class { 'role_ceph_mon': id => 0 } +} + +node 'ceph-mon1.test' { + class { 'role_ceph_mon': id => 1 } +} + +node 'ceph-mon2.test' { + class { 'role_ceph_mon': id => 2 } +} + +node /ceph-osd.?\.test/ { + + class { 'role_ceph': + fsid => $::fsid, + auth_type => 'cephx', + } + + class { 'ceph::osd' : + public_address => $ipaddress_eth1, + cluster_address => $ipaddress_eth2, + } + + ceph::osd::device { '/dev/sdb': } + ceph::osd::device { '/dev/sdc': } +} + +node 'ceph-mds0.test' { + class { 'ceph_mds': id => 0 } +} + +node 'ceph-mds1.test' { + class { 'ceph_mds': id => 1 } +} diff --git a/ceph/lib/facter/ceph_keyring.rb b/ceph/lib/facter/ceph_keyring.rb new file mode 100644 index 000000000..535ba9e3b --- /dev/null +++ b/ceph/lib/facter/ceph_keyring.rb @@ -0,0 +1,30 @@ +# Fact: ceph_keyring +# +# Purpose: +# Fetch client keyring (cinder, glance, ...) +# +require 'facter' +require 'json' + +timeout = 10 +cmd_timeout = 10 + +begin + Timeout::timeout(timeout) { + # if ceph isn't configured => Error initializing cluster client: Error + if system("timeout #{cmd_timeout} ceph -s > /dev/null 2>&1") + raw_auth = %x(timeout #{cmd_timeout} ceph auth list -f json) + json_auth = JSON.parse(raw_auth) + json_auth['auth_dump'].each do |k| + if k['entity'] =~ /client(.*)/ + if k['entity'] != 'client.admin' + keyring = k['entity'].gsub!(/^client\./,'') + Facter.add("ceph_keyring_#{keyring}") { setcode { k['key'] } } + end # if !client.admin + end # match client + end # all entity + end # if system + } +rescue Timeout::Error + Facter.warnonce('ceph command timeout in ceph_keyring fact') +end diff --git a/ceph/lib/facter/ceph_osd_bootstrap_key.rb b/ceph/lib/facter/ceph_osd_bootstrap_key.rb new file mode 100644 index 000000000..491eb611c --- /dev/null +++ b/ceph/lib/facter/ceph_osd_bootstrap_key.rb @@ -0,0 +1,58 @@ +# Fact: ceph_osd_bootstrap_key +# +# Purpose: +# +# Resolution: +# +# Caveats: +# + +require 'facter' +require 'timeout' + +timeout = 10 +cmd_timeout = 10 + +# ceph_osd_bootstrap_key +# Fact that gets the ceph key "client.bootstrap-osd" + +Facter.add(:ceph_admin_key, :timeout => timeout) do + if system("timeout #{cmd_timeout} ceph -s > /dev/null 2>&1") + setcode { Facter::Util::Resolution.exec("timeout #{cmd_timeout} ceph auth get-key client.admin") } + end +end + +## blkid_uuid_#{device} / ceph_osd_id_#{device} +## Facts that export partitions uuids & ceph osd id of device + +# Load the osds/uuids from ceph + +ceph_osds = Hash.new +begin + Timeout::timeout(timeout) { + if system("timeout #{cmd_timeout} ceph -s > /dev/null 2>&1") + ceph_osd_dump = Facter::Util::Resolution.exec("timeout #{cmd_timeout} ceph osd dump") + ceph_osd_dump and ceph_osd_dump.each_line do |line| + if line =~ /^osd\.(\d+).* ([a-f0-9\-]+)$/ + ceph_osds[$2] = $1 + end + end + + end + } +rescue Timeout::Error + Facter.warnonce('ceph command timeout in ceph_osd_bootstrap_key fact') +end + +# Load the disks uuids + +blkid = Facter::Util::Resolution.exec("blkid") +blkid and blkid.each_line do |line| + if line =~ /^\/dev\/(.+):\s*UUID="([a-fA-F0-9\-]+)"/ + device = $1 + uuid = $2 + + Facter.add("blkid_uuid_#{device}") { setcode { uuid } } + Facter.add("ceph_osd_id_#{device}") { setcode { ceph_osds[uuid] } } + end +end diff --git a/ceph/manifests/apt/ceph.pp b/ceph/manifests/apt/ceph.pp new file mode 100644 index 000000000..c70719e6d --- /dev/null +++ b/ceph/manifests/apt/ceph.pp @@ -0,0 +1,44 @@ +# Configure ceph APT repository +# +# == Parameters +# +# [*release*] The ceph release name +# Optional. Default to bobtail +# +# == Dependencies +# +# none +# +# == Authors +# +# François Charlier francois.charlier@enovance.com +# +# == Copyright +# +# Copyright 2012 eNovance +# +class ceph::apt::ceph ( + $release = 'bobtail', + $apt_key_source = 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc', + $apt_key_id = '17ED316D', + $apt_source_location = undef +) { + + if (undef == $apt_source_location) { + $real_apt_source_location = "http://ceph.com/debian-${release}/" + } else { + $real_apt_source_location = $apt_source_location + } + + apt::key { 'ceph': + key => $apt_key_id, + key_source => $apt_key_source, + } + + apt::source { 'ceph': + location => $real_apt_source_location, + release => $::lsbdistcodename, + require => Apt::Key['ceph'], + before => Package['ceph'], + } +} diff --git a/ceph/manifests/conf.pp b/ceph/manifests/conf.pp new file mode 100644 index 000000000..3f00c1bdb --- /dev/null +++ b/ceph/manifests/conf.pp @@ -0,0 +1,77 @@ +# Creates the ceph configuration file +# +# == Parameters +# [*fsid*] The cluster's fsid. +# Mandatory. Get one with `uuidgen -r`. +# +# [*auth_type*] Auth type. +# Optional. none or 'cephx'. Defaults to 'cephx'. +# +# == Dependencies +# +# none +# +# == Authors +# +# François Charlier francois.charlier@enovance.com +# Sébastien Han sebastien.han@enovance.com +# +# == Copyright +# +# Copyright 2012 eNovance +# +class ceph::conf ( + $fsid, + $auth_type = 'cephx', + $signatures_require = undef, + $signatures_cluster = undef, + $signatures_service = undef, + $signatures_sign_msgs = undef, + $pool_default_size = '3', + $pool_default_pg_num = '1024', + $pool_default_pgp_num = '1024', + $pool_default_min_size = undef, + $pool_default_crush_rule = undef, + $journal_size_mb = '4096', + $cluster_network = undef, + $public_network = undef, + $mon_data = '/var/lib/ceph/mon/mon.$id', + $mon_init_members = undef, + $osd_data = '/var/lib/ceph/osd/ceph-$id', + $osd_journal = undef, + $mds_data = '/var/lib/ceph/mds/mds.$id', + $enable_service = false, + $conf_owner = 'root', + $conf_group = '0', + $config = {}, + $osd_recovery_max_active = 1, + $osd_max_backfills = 1, + $osd_recovery_op_priority = 1, +) { + validate_hash($config) + + include 'ceph::package' + + if $osd_journal { + $osd_journal_real = $osd_journal + } else { + $osd_journal_real = "${osd_data}/journal" + } + + concat { '/etc/ceph/ceph.conf': + owner => $conf_owner, + group => $conf_group, + mode => '0664', + require => Package['ceph'], + } + + Concat::Fragment <<| target == '/etc/ceph/ceph.conf' |>> + + concat::fragment { 'ceph.conf': + target => '/etc/ceph/ceph.conf', + order => '01', + content => template('ceph/ceph.conf.erb'), + } + + service { 'ceph': enable => $enable_service } +} diff --git a/ceph/manifests/conf/mds.pp b/ceph/manifests/conf/mds.pp new file mode 100644 index 000000000..4000a43a7 --- /dev/null +++ b/ceph/manifests/conf/mds.pp @@ -0,0 +1,15 @@ +# Define a mds +# +define ceph::conf::mds ( + $mds_data, + $config = {}, +) { + validate_hash($config) + + @@concat::fragment { "ceph-mds-${name}.conf": + target => '/etc/ceph/ceph.conf', + order => '60', + content => template('ceph/ceph.conf-mds.erb'), + } + +} diff --git a/ceph/manifests/conf/mon.pp b/ceph/manifests/conf/mon.pp new file mode 100644 index 000000000..db79cb54a --- /dev/null +++ b/ceph/manifests/conf/mon.pp @@ -0,0 +1,16 @@ +# Define a mon +# +define ceph::conf::mon ( + $mon_addr, + $mon_port, + $config = {}, +) { + validate_hash($config) + + @@concat::fragment { "ceph-mon-${name}.conf": + target => '/etc/ceph/ceph.conf', + order => '50', + content => template('ceph/ceph.conf-mon.erb'), + } + +} diff --git a/ceph/manifests/conf/osd.pp b/ceph/manifests/conf/osd.pp new file mode 100644 index 000000000..c3f0b6e74 --- /dev/null +++ b/ceph/manifests/conf/osd.pp @@ -0,0 +1,19 @@ +# Define a osd +# +define ceph::conf::osd ( + $device, + $cluster_addr = undef, + $public_addr = undef, + $journal = undef, + $journalsize = undef, + $config = {}, +) { + validate_hash($config) + + concat::fragment { "ceph-osd-${name}.conf": + target => '/etc/ceph/ceph.conf', + order => '80', + content => template('ceph/ceph.conf-osd.erb'), + } + +} diff --git a/ceph/manifests/key.pp b/ceph/manifests/key.pp new file mode 100644 index 000000000..210254a44 --- /dev/null +++ b/ceph/manifests/key.pp @@ -0,0 +1,14 @@ +# Create the ceph keyring +# +define ceph::key ( + $secret, + $keyring_path = "/var/lib/ceph/tmp/${name}.keyring", +) { + + exec { "ceph-key-${name}": + command => "ceph-authtool ${keyring_path} --create-keyring --name='client.${name}' --add-key='${secret}'", + unless => "grep ${secret} ${keyring_path}", + require => Package['ceph'], + } + +} diff --git a/ceph/manifests/mds.pp b/ceph/manifests/mds.pp new file mode 100644 index 000000000..bfd10692b --- /dev/null +++ b/ceph/manifests/mds.pp @@ -0,0 +1,67 @@ +# Configure a ceph mds +# +# == Name +# This resource's name is the mon's id and must be numeric. +# == Parameters +# [*fsid*] The cluster's fsid. +# Mandatory. Get one with `uuidgen -r`. +# +# [*auth_type*] Auth type. +# Optional. undef or 'cephx'. Defaults to 'cephx'. +# +# [*mds_data*] Base path for mon data. Data will be put in a mon.$id folder. +# Optional. Defaults to '/var/lib/ceph/mds. +# +# == Dependencies +# +# none +# +# == Authors +# +# Sébastien Han sebastien.han@enovance.com +# François Charlier francois.charlier@enovance.com +# +# == Copyright +# +# Copyright 2012 eNovance +# + +define ceph::mds ( + $fsid, + $auth_type = 'cephx', + $mds_data = '/var/lib/ceph/mds', +) { + + include 'ceph::package' + include 'ceph::params' + + class { 'ceph::conf': + fsid => $fsid, + auth_type => $auth_type, + } + + $mds_data_expanded = "${mds_data}/mds.${name}" + + file { $mds_data_expanded: + ensure => directory, + owner => 'root', + group => 0, + mode => '0755', + } + + exec { 'ceph-mds-keyring': + command =>"ceph auth get-or-create mds.${name} mds 'allow ' osd 'allow *' mon 'allow rwx'", + creates => "/var/lib/ceph/mds/mds.${name}/keyring", + before => Service["ceph-mds.${name}"], + require => Package['ceph'], + } + + service { "ceph-mds.${name}": + ensure => running, + provider => $::ceph::params::service_provider, + start => "service ceph start mds.${name}", + stop => "service ceph stop mds.${name}", + status => "service ceph status mds.${name}", + require => Exec['ceph-mds-keyring'], + } +} diff --git a/ceph/manifests/mon.pp b/ceph/manifests/mon.pp new file mode 100644 index 000000000..6877a799f --- /dev/null +++ b/ceph/manifests/mon.pp @@ -0,0 +1,103 @@ +# Configure a ceph mon +# +# == Name +# This resource's name is the mon's id and must be numeric. +# == Parameters +# [*fsid*] The cluster's fsid. +# Mandatory. Get one with `uuidgen -r`. +# +# [*mon_secret*] The cluster's mon's secret key. +# Mandatory. Get one with `ceph-authtool /dev/stdout --name=mon. --gen-key`. +# +# [*mon_port*] The mon's port. +# Optional. Defaults to 6789. +# +# [*mon_addr*] The mon's address. +# Optional. Defaults to the $ipaddress fact. +# +# == Dependencies +# +# none +# +# == Authors +# +# François Charlier francois.charlier@enovance.com +# +# == Copyright +# +# Copyright 2012 eNovance +# +define ceph::mon ( + $monitor_secret, + $mon_port = 6789, + $mon_addr = $ipaddress +) { + + include 'ceph::package' + include 'ceph::conf' + include 'ceph::params' + + $mon_data_real = regsubst($::ceph::conf::mon_data, '\$id', $name) + + ceph::conf::mon { $name: + mon_addr => $mon_addr, + mon_port => $mon_port, + } + + #FIXME: monitor_secret will appear in "ps" output … + exec { 'ceph-mon-keyring': + command => "ceph-authtool /var/lib/ceph/tmp/keyring.mon.${name} \ +--create-keyring \ +--name=mon. \ +--add-key='${monitor_secret}' \ +--cap mon 'allow *'", + creates => "/var/lib/ceph/tmp/keyring.mon.${name}", + before => Exec['ceph-mon-mkfs'], + require => Package['ceph'], + } + + exec { 'ceph-mon-mkfs': + command => "ceph-mon --mkfs -i ${name} \ +--keyring /var/lib/ceph/tmp/keyring.mon.${name}", + creates => "${mon_data_real}/keyring", + require => [ + Package['ceph'], + Concat['/etc/ceph/ceph.conf'], + File[$mon_data_real] + ], + } + + file { $mon_data_real: + ensure => 'directory', + owner => 'root', + group => 'root', + mode => '0755', + require => Package['ceph'] + } + + service { "ceph-mon.${name}": + ensure => running, + provider => $::ceph::params::service_provider, + start => "service ceph start mon.${name}", + stop => "service ceph stop mon.${name}", + status => "service ceph status mon.${name}", + require => Exec['ceph-mon-mkfs'], + } + + exec { 'ceph-admin-key': + command => "ceph-authtool /etc/ceph/keyring \ +--create-keyring \ +--name=client.admin \ +--add-key \ +$(ceph --name mon. --keyring ${mon_data_real}/keyring \ + auth get-or-create-key client.admin \ + mon 'allow *' \ + osd 'allow *' \ + mds allow)", + creates => '/etc/ceph/keyring', + require => [Package['ceph'], Service["ceph-mon.${name}"]], + onlyif => "ceph --admin-daemon /var/run/ceph/ceph-mon.${name}.asok \ +mon_status|egrep -v '\"state\": \"(leader|peon)\"'", + } + +} diff --git a/ceph/manifests/osd.pp b/ceph/manifests/osd.pp new file mode 100644 index 000000000..23fe3b258 --- /dev/null +++ b/ceph/manifests/osd.pp @@ -0,0 +1,31 @@ +# Configure a ceph osd node +# +# == Parameters +# +# [*osd_addr*] The osd's address. +# Optional. Defaults to the $ipaddress fact. +# +# == Dependencies +# +# none +# +# == Authors +# +# François Charlier francois.charlier@enovance.com +# +# == Copyright +# +# Copyright 2012 eNovance +# + +class ceph::osd ( + $public_address = $::ipaddress, + $cluster_address = $::ipaddress, +) { + + include 'ceph::package' + + ensure_packages( [ 'xfsprogs', 'parted' ] ) + + Package['ceph'] -> Ceph::Key <<| title == 'admin' |>> +} diff --git a/ceph/manifests/osd/device.pp b/ceph/manifests/osd/device.pp new file mode 100644 index 000000000..27de8c025 --- /dev/null +++ b/ceph/manifests/osd/device.pp @@ -0,0 +1,130 @@ +# Configure a ceph osd device +# +# == Namevar +# the resource name is the full path to the device to be used. +# +# == Dependencies +# +# none +# +# == Authors +# +# François Charlier francois.charlier@enovance.com +# +# == Copyright +# +# Copyright 2013 eNovance +# + +define ceph::osd::device ( + $journal = undef, + $journalsize = undef, +) { + + include ceph::osd + include ceph::conf + include ceph::params + + $devname = regsubst($name, '.*/', '') + + exec { "mktable_gpt_${devname}": + command => "parted -a optimal --script ${name} mktable gpt", + unless => "parted --script ${name} print|grep -sq 'Partition Table: gpt'", + require => Package['parted'] + } + + exec { "mkpart_${devname}": + command => "parted -a optimal -s ${name} mkpart ceph 0% 100%", + unless => "parted ${name} print | egrep '^ 1.*ceph$'", + require => [Package['parted'], Exec["mktable_gpt_${devname}"]] + } + + exec { "mkfs_${devname}": + command => "mkfs.xfs -f -i size=2048 ${name}1", + unless => "xfs_admin -l ${name}1", + require => [Package['xfsprogs'], Exec["mkpart_${devname}"]], + } + + $blkid_uuid_fact = "blkid_uuid_${devname}1" + notify { "BLKID FACT ${devname}: ${blkid_uuid_fact}": } + $blkid = inline_template('<%= scope.lookupvar(@blkid_uuid_fact) or "undefined" %>') + notify { "BLKID ${devname}: ${blkid}": } + + if $blkid != 'undefined' and defined( Ceph::Key['admin'] ){ + exec { "ceph_osd_create_${devname}": + command => "ceph osd create ${blkid}", + unless => "ceph osd dump | grep -sq ${blkid}", + require => Ceph::Key['admin'], + } + + $osd_id_fact = "ceph_osd_id_${devname}1" + notify { "OSD ID FACT ${devname}: ${osd_id_fact}": } + $osd_id = inline_template('<%= scope.lookupvar(osd_id_fact) or "undefined" %>') + notify { "OSD ID ${devname}: ${osd_id}":} + + if $osd_id != 'undefined' { + + ceph::conf::osd { $osd_id: + device => $name, + cluster_addr => $::ceph::osd::cluster_address, + public_addr => $::ceph::osd::public_address, + journal => $journal, + journalsize => $journalsize, + } + + $osd_data = regsubst($::ceph::conf::osd_data, '\$id', $osd_id) + + file { $osd_data: + ensure => directory, + } + + mount { $osd_data: + ensure => mounted, + device => "${name}1", + atboot => true, + fstype => 'xfs', + options => 'rw,noatime,inode64', + pass => 2, + require => [ + Exec["mkfs_${devname}"], + File[$osd_data] + ], + } + + exec { "ceph-osd-mkfs-${osd_id}": + command => "ceph-osd -c /etc/ceph/ceph.conf \ +-i ${osd_id} \ +--mkfs \ +--mkkey \ +--osd-uuid ${blkid} +", + creates => "${osd_data}/keyring", + unless => "ceph auth list | egrep '^osd.${osd_id}$'", + require => [ + Mount[$osd_data], + Concat['/etc/ceph/ceph.conf'], + ], + } + + exec { "ceph-osd-register-${osd_id}": + command => "\ +ceph auth add osd.${osd_id} osd 'allow *' mon 'allow rwx' \ +-i ${osd_data}/keyring", + unless => "ceph auth list | egrep '^osd.${osd_id}$'", + require => Exec["ceph-osd-mkfs-${osd_id}"], + } + + service { "ceph-osd.${osd_id}": + ensure => running, + provider => $::ceph::params::service_provider, + start => "service ceph start osd.${osd_id}", + stop => "service ceph stop osd.${osd_id}", + status => "service ceph status osd.${osd_id}", + subscribe => Concat['/etc/ceph/ceph.conf'], + } + + } + + } + +} diff --git a/ceph/manifests/package.pp b/ceph/manifests/package.pp new file mode 100644 index 000000000..f660bd11e --- /dev/null +++ b/ceph/manifests/package.pp @@ -0,0 +1,36 @@ +# Install and configure base ceph components +# +# == Parameters +# [*package_ensure*] The ensure state for the ceph package. +# Optional. Defaults to present. +# +# == Dependencies +# +# none +# +# == Authors +# +# François Charlier francois.charlier@enovance.com +# +# == Copyright +# +# Copyright 2012 eNovance +# +class ceph::package ( + $package_ensure = 'present' +) { + + package { 'ceph': + ensure => $package_ensure + } + + #FIXME: Ensure ceph user/group + + ensure_resource('file', ['/var/run/ceph', '/var/lib/ceph', '/var/lib/ceph/mon', '/var/lib/ceph/osd'], { + 'ensure' => 'directory', + 'owner' => 'root', + 'group' => '0', + 'mode' => '0755' + }) + +} diff --git a/ceph/manifests/params.pp b/ceph/manifests/params.pp new file mode 100644 index 000000000..53b890e0b --- /dev/null +++ b/ceph/manifests/params.pp @@ -0,0 +1,17 @@ +# these parameters need to be accessed from several locations and +# should be considered to be constant +class ceph::params { + + case $::osfamily { + 'Debian': { + case $::operatingsystem { + 'Ubuntu': { + $service_provider = 'init' + } + default: { + $service_provider = undef + } + } + } + } +} diff --git a/ceph/manifests/yum/ceph.pp b/ceph/manifests/yum/ceph.pp new file mode 100644 index 000000000..4dd4b03d3 --- /dev/null +++ b/ceph/manifests/yum/ceph.pp @@ -0,0 +1,16 @@ +# Configure yum repository +# +class ceph::yum::ceph ( + $release = 'cuttlefish' +) { + yumrepo { 'ceph': + descr => "Ceph ${release} repository", + baseurl => "http://ceph.com/rpm-${release}/el6/x86_64/", + gpgkey => + 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc', + gpgcheck => 1, + enabled => 1, + priority => 5, + before => Package['ceph'], + } +} diff --git a/ceph/metadata.json b/ceph/metadata.json new file mode 100644 index 000000000..9b151be9c --- /dev/null +++ b/ceph/metadata.json @@ -0,0 +1,25 @@ +{ + "name": "eNovance-ceph", + "version": "1.2.0", + "author": "Francois Charlier ", + "summary": "A puppet module to install & configure Ceph", + "license": "AGPLv3", + "source": "https://github.com/enovance/puppet-ceph", + "project_page": "https://github.com/enovance/puppet-ceph", + "issues_url": "https://github.com/enovance/puppet-ceph/issues", + "operatingsystem_support": [ + {"operatingsystem": "Debian"}, + {"operatingsystem": "Ubuntu"}, + {"operatingsystem": "RedHat"} + ], + "requirements": [ + {"name": "pe","version_requirement": "3.2.x"}, + {"name": "puppet","version_requirement": "3.x"} + ], + "description": "A puppet module to install & configure Ceph", + "dependencies": [ + {"name":"puppetlabs/apt","version_requirement":">= 1.1.0"}, + {"name":"puppetlabs/concat","version_requirement":">= 0.2.0"}, + {"name":"puppetlabs/stdlib","version_requirement":">= 4.1.0"} + ] +} diff --git a/ceph/spec/classes/ceph_apt_ceph_spec.rb b/ceph/spec/classes/ceph_apt_ceph_spec.rb new file mode 100644 index 000000000..8814eb305 --- /dev/null +++ b/ceph/spec/classes/ceph_apt_ceph_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +describe 'ceph::apt::ceph' do + + describe "when dist codename is wheezy" do + + let :facts do + { + :lsbdistcodename => 'wheezy', + :lsbdistid => 'debian' + } + end + + describe "with default params" do + + it { should contain_apt__key('ceph').with( + 'key' => '17ED316D', + 'key_source' => 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc' + ) } + + it { should contain_apt__source('ceph').with( + 'location' => 'http://ceph.com/debian-bobtail/', + 'release' => 'wheezy', + 'require' => 'Apt::Key[ceph]', + 'before' => 'Package[ceph]' + ) } + + end + + describe "when overriding ceph release" do + let :params do + { 'release' => 'octopuss' } + end + + it { should contain_apt__source('ceph').with( + 'location' => 'http://ceph.com/debian-octopuss/', + 'release' => 'wheezy', + 'require' => 'Apt::Key[ceph]', + 'before' => 'Package[ceph]' + ) } + end + + end + +end diff --git a/ceph/spec/classes/ceph_conf_spec.rb b/ceph/spec/classes/ceph_conf_spec.rb new file mode 100644 index 000000000..55287a006 --- /dev/null +++ b/ceph/spec/classes/ceph_conf_spec.rb @@ -0,0 +1,143 @@ +require 'spec_helper' + +describe 'ceph::conf' do + + let :facts do + { :concat_basedir => "/var/lib/puppet/concat" } + end + + let :params do + { :fsid => 'qwertyuiop', } + end + + let :fragment_path do + "/var/lib/puppet/concat/_etc_ceph_ceph.conf/fragments/01_ceph.conf" + end + + it { should contain_class('ceph::package') } + + describe "with default parameters" do + + it { should contain_concat('/etc/ceph/ceph.conf').with( + 'owner' => 'root', + 'group' => 0, + 'mode' => '0664', + 'require' => 'Package[ceph]' + ) } + + it { should contain_concat__fragment('ceph.conf').with( + 'target' => '/etc/ceph/ceph.conf', + 'order' => '01' + ) } + + it 'should create the configuration fragment with the correct content' do + verify_contents( + subject, + fragment_path, + [ + '[global]', + ' auth cluster required = cephx', + ' auth service required = cephx', + ' auth client required = cephx', + ' keyring = /etc/ceph/keyring', + ' fsid = qwertyuiop', + '[mon]', + ' mon data = /var/lib/ceph/mon/mon.$id', + '[osd]', + ' osd journal size = 4096', + ' filestore flusher = false', + ' osd data = /var/lib/ceph/osd/ceph-$id', + ' osd journal = /var/lib/ceph/osd/ceph-$id/journal', + ' osd mkfs type = xfs', + ' keyring = /var/lib/ceph/osd/ceph-$id/keyring', + '[mds]', + ' mds data = /var/lib/ceph/mds/mds.$id', + ' keyring = /var/lib/ceph/mds/mds.$id/keyring' + ] + ) + end + + end + + describe "when overriding default parameters" do + + let :params do + { + :fsid => 'qwertyuiop', + :auth_type => 'dummy', + :signatures_require => 'true', + :signatures_cluster => 'true', + :signatures_service => 'true', + :signatures_sign_msgs => 'true', + :pool_default_pg_num => 16, + :pool_default_pgp_num => 16, + :pool_default_size => 3, + :pool_default_min_size => 8, + :pool_default_crush_rule => 1, + :journal_size_mb => 8192, + :cluster_network => '10.0.0.0/16', + :public_network => '10.1.0.0/16', + :mon_data => '/opt/ceph/mon._id', + :mon_init_members => 'a , b , c', + :osd_data => '/opt/ceph/osd._id', + :osd_journal => '/opt/ceph/journal/osd._id', + :mds_data => '/opt/ceph/mds._id', + :config => { + 'osd max backfills' => 1, + 'osd recovery max active' => 1 + } + } + end + + it { should contain_concat('/etc/ceph/ceph.conf').with( + 'owner' => 'root', + 'group' => 0, + 'mode' => '0664', + 'require' => 'Package[ceph]' + ) } + + it 'should create the configuration fragment with the correct content' do + verify_contents( + subject, + fragment_path, + [ + '[global]', + ' auth cluster required = dummy', + ' auth service required = dummy', + ' auth client required = dummy', + ' cephx require signatures = true', + ' cephx cluster require signatures = true', + ' cephx service require signatures = true', + ' cephx sign messages = true', + ' keyring = /etc/ceph/keyring', + ' cluster network = 10.0.0.0/16', + ' public network = 10.1.0.0/16', + ' osd pool default pg num = 16', + ' osd pool default pgp num = 16', + ' osd pool default size = 3', + ' osd pool default min size = 8', + ' osd pool default crush rule = 1', + ' osd max backfills = 1', + ' osd recovery max active = 1', + ' fsid = qwertyuiop', + '[mon]', + ' mon initial members = a , b , c', + ' mon data = /opt/ceph/mon._id', + '[osd]', + ' osd journal size = 8192', + ' filestore flusher = false', + ' osd data = /opt/ceph/osd._id', + ' osd journal = /opt/ceph/journal/osd._id', + ' osd mkfs type = xfs', + ' keyring = /opt/ceph/osd._id/keyring', + ' osd recovery op priority = 1', + '[mds]', + ' mds data = /opt/ceph/mds._id', + ' keyring = /opt/ceph/mds._id/keyring' + ] + ) + end + + end + +end diff --git a/ceph/spec/classes/ceph_osd_spec.rb b/ceph/spec/classes/ceph_osd_spec.rb new file mode 100644 index 000000000..193868c17 --- /dev/null +++ b/ceph/spec/classes/ceph_osd_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe "ceph::osd" do + + let :facts do + { :ipaddress => '2.4.6.8' } + end + + it { should contain_class('ceph::package') } + + it { should contain_package('xfsprogs') } + it { should contain_package('parted') } + +end diff --git a/ceph/spec/classes/ceph_package_spec.rb b/ceph/spec/classes/ceph_package_spec.rb new file mode 100644 index 000000000..c19693902 --- /dev/null +++ b/ceph/spec/classes/ceph_package_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +describe 'ceph::package' do + describe "with default parameters" do + it { should contain_package('ceph').with_ensure('present') } + + it { should contain_file('/var/lib/ceph').with( + 'ensure' => 'directory', + 'owner' => 'root', + 'group' => 0, + 'mode' => '0755' + ) } + + it { should contain_file('/var/lib/ceph/mon').with( + 'ensure' => 'directory', + 'owner' => 'root', + 'group' => 0, + 'mode' => '0755' + ) } + + it { should contain_file('/var/lib/ceph/osd').with( + 'ensure' => 'directory', + 'owner' => 'root', + 'group' => 0, + 'mode' => '0755' + ) } + + it { should contain_file('/var/run/ceph').with( + 'ensure' => 'directory', + 'owner' => 'root', + 'group' => 0, + 'mode' => '0755' + ) } + end + + describe "when overriding parameters" do + let :params do { :package_ensure => 'latest' } end + + it { should contain_package('ceph').with_ensure('latest') } + end +end diff --git a/ceph/spec/classes/ceph_yum_ceph_spec.rb b/ceph/spec/classes/ceph_yum_ceph_spec.rb new file mode 100644 index 000000000..6949bcc5d --- /dev/null +++ b/ceph/spec/classes/ceph_yum_ceph_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe 'ceph::yum::ceph' do + + describe "with default params" do + + it { should contain_yumrepo('ceph').with( + 'descr' => "Ceph cuttlefish repository", + 'baseurl' => 'http://ceph.com/rpm-cuttlefish/el6/x86_64/', + 'gpgkey' => 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc', + 'gpgcheck' => '1', + 'enabled' => '1', + 'priority' => '5', + 'before' => 'Package[ceph]' + ) } + + end + + describe "when overriding ceph release" do + let :params do + { 'release' => 'octopuss' } + end + + it { should contain_yumrepo('ceph').with( + 'descr' => "Ceph octopuss repository", + 'baseurl' => 'http://ceph.com/rpm-octopuss/el6/x86_64/', + 'gpgkey' => 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc', + 'gpgcheck' => '1', + 'enabled' => '1', + 'priority' => '5', + 'before' => 'Package[ceph]' + ) } + + end + +end diff --git a/ceph/spec/defines/ceph_conf_mon_spec.rb b/ceph/spec/defines/ceph_conf_mon_spec.rb new file mode 100644 index 000000000..737e6e74b --- /dev/null +++ b/ceph/spec/defines/ceph_conf_mon_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +describe 'ceph::conf::mon' do + + let :pre_condition do +' +class { "ceph::conf": fsid => "1234567890" } +' + end + + let :title do + '42' + end + + let :params do + { + 'mon_addr' => '1.2.3.4', + 'mon_port' => '1234', + } + end + + let :facts do + { + :concat_basedir => '/var/lib/puppet/concat', + :hostname => 'some-host.foo.tld', + } + end + + let :fragment_file do + '/var/lib/puppet/concat/_etc_ceph_ceph.conf/fragments/50_ceph-mon-42.conf' + end + + describe "writes the mon configuration file" do + # Need to work around the exported resources problem + xit { should contain_file(fragment_file).with_content(/[mon.42]/) } + xit { should contain_file(fragment_file).with_content(/ host = some-host.foo.tld/) } + xit { should contain_file(fragment_file).with_content(/ mon addr = 1.2.3.4:1234/) } + end + +end diff --git a/ceph/spec/defines/ceph_key_spec.rb b/ceph/spec/defines/ceph_key_spec.rb new file mode 100644 index 000000000..71d16c581 --- /dev/null +++ b/ceph/spec/defines/ceph_key_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe 'ceph::key' do + + let :title do + 'dummy' + end + + describe 'with default parameter' do + let :params do + { :secret => 'shhh_dont_tell_anyone' } + end + it { should contain_exec('ceph-key-dummy').with( + 'command' => "ceph-authtool /var/lib/ceph/tmp/dummy.keyring --create-keyring --name='client.dummy' --add-key='shhh_dont_tell_anyone'", + 'unless' => "grep shhh_dont_tell_anyone /var/lib/ceph/tmp/dummy.keyring", + 'require' => 'Package[ceph]' + )} + end + + describe 'when setting secret and overriding keyring_path' do + let :params do + { :secret => 'shhh_dont_tell_anyone', + :keyring_path => '/dummy/path/for/keyring' } + end + it { should contain_exec('ceph-key-dummy').with( + 'command' => "ceph-authtool /dummy/path/for/keyring --create-keyring --name='client.dummy' --add-key='shhh_dont_tell_anyone'", + 'unless' => 'grep shhh_dont_tell_anyone /dummy/path/for/keyring', + 'require' => 'Package[ceph]' + )} + end + +end diff --git a/ceph/spec/defines/ceph_mon_spec.rb b/ceph/spec/defines/ceph_mon_spec.rb new file mode 100644 index 000000000..6bb04d22d --- /dev/null +++ b/ceph/spec/defines/ceph_mon_spec.rb @@ -0,0 +1,97 @@ +require 'spec_helper' + +describe 'ceph::mon' do + + let :title do + '42' + end + + let :pre_condition do +' +class { "ceph::conf": fsid => "1234567890" } +' + end + + let :default_params do + { :monitor_secret => 'hardtoguess' } + end + + let :params do + default_params + end + + let :facts do + { + :ipaddress => '169.254.0.1', + :concat_basedir => '/var/lib/puppet/concat' + } + end + + it { should contain_class('ceph::package') } + it { should contain_class('ceph::conf') } + + + describe 'with default parameters' do + it { should contain_ceph__conf__mon('42').with_mon_addr('169.254.0.1') } + + it { should contain_exec('ceph-mon-keyring').with( + 'command' => "ceph-authtool /var/lib/ceph/tmp/keyring.mon.42 \ +--create-keyring --name=mon. --add-key='hardtoguess' \ +--cap mon 'allow *'", + 'creates' => '/var/lib/ceph/tmp/keyring.mon.42', + 'before' => 'Exec[ceph-mon-mkfs]', + 'require' => 'Package[ceph]' + )} + + it { should contain_exec('ceph-mon-mkfs').with( + 'command' => "ceph-mon --mkfs -i 42 --keyring /var/lib/ceph/tmp/keyring.mon.42", + 'creates' => '/var/lib/ceph/mon/mon.42/keyring', + 'require' => ['Package[ceph]','Concat[/etc/ceph/ceph.conf]', + 'File[/var/lib/ceph/mon/mon.42]'] + )} + + it { should contain_file('/var/lib/ceph/mon/mon.42').with( + 'ensure' => 'directory', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0755' + )} + + it { should contain_service('ceph-mon.42').with( + 'ensure' => 'running', + 'start' => 'service ceph start mon.42', + 'stop' => 'service ceph stop mon.42', + 'status' => 'service ceph status mon.42', + 'require' => 'Exec[ceph-mon-mkfs]' + )} + + it { should contain_exec('ceph-admin-key').with( + 'command' => "ceph-authtool /etc/ceph/keyring \ +--create-keyring --name=client.admin --add-key \ +$(ceph --name mon. --keyring /var/lib/ceph/mon/mon.42/keyring \ + auth get-or-create-key client.admin \ + mon 'allow *' \ + osd 'allow *' \ + mds allow)", + 'creates' => '/etc/ceph/keyring', + 'require' => ['Package[ceph]', 'Service[ceph-mon.42]'], + 'onlyif' => "ceph --admin-daemon /var/run/ceph/ceph-mon.42.asok \ +mon_status|egrep -v '\"state\": \"(leader|peon)\"'" + )} + end + + describe 'when overriding mon addr/port' do + let :params do + default_params.merge({ + 'mon_addr' => '10.0.0.254', + 'mon_port' => '9876', + }) + end + + it { should contain_ceph__conf__mon('42').with( + 'mon_addr' => '10.0.0.254', + 'mon_port' => 9876 + ) } + end + +end diff --git a/ceph/spec/defines/ceph_osd_device_spec.rb b/ceph/spec/defines/ceph_osd_device_spec.rb new file mode 100644 index 000000000..9259e4bcb --- /dev/null +++ b/ceph/spec/defines/ceph_osd_device_spec.rb @@ -0,0 +1,137 @@ +require 'spec_helper' + +describe 'ceph::osd::device' do + + let :title do + '/dev/device' + end + + let(:params) { {:journal => '/dev/journal', :journalsize => '1024'} } + + let :pre_condition do + "class { 'ceph::conf': fsid => '12345' } +class { 'ceph::osd': + public_address => '10.1.0.156', + cluster_address => '10.0.0.56' +} +" + end + + let :facts do + { + :concat_basedir => '/var/lib/puppet/lib/concat', + :processorcount => 8, + :hostname => 'dummy-host' + } + end + + describe 'when the device is empty' do + + it { should contain_class('ceph::osd') } + it { should contain_class('ceph::conf') } + + it { should contain_exec('mktable_gpt_device').with( + 'command' => 'parted -a optimal --script /dev/device mktable gpt', + 'unless' => "parted --script /dev/device print|grep -sq 'Partition Table: gpt'", + 'require' => 'Package[parted]' + ) } + + it { should contain_exec('mkpart_device').with( + 'command' => 'parted -a optimal -s /dev/device mkpart ceph 0% 100%', + 'unless' => "parted /dev/device print | egrep '^ 1.*ceph$'", + 'require' => ['Package[parted]', 'Exec[mktable_gpt_device]'] + ) } + + it { should contain_exec('mkfs_device').with( + 'command' => 'mkfs.xfs -f -i size=2048 /dev/device1', + 'unless' => 'xfs_admin -l /dev/device1', + 'require' => ['Package[xfsprogs]', 'Exec[mkpart_device]'] + ) } + + end + + describe 'when the partition is created' do + + let :pre_condition do + "class { 'ceph::conf': fsid => '12345' } +class { 'ceph::osd': + public_address => '10.1.0.156', + cluster_address => '10.0.0.56' +} +ceph::key { 'admin': + secret => 'dummy' +} +" + end + let :facts do + { + :concat_basedir => '/var/lib/puppet/lib/concat', + :blkid_uuid_device1 => 'dummy-uuid-1234', + :hostname => 'dummy-host' + } + end + + it { should contain_exec('ceph_osd_create_device').with( + 'command' => 'ceph osd create dummy-uuid-1234', + 'unless' => 'ceph osd dump | grep -sq dummy-uuid-1234', + 'require' => 'Ceph::Key[admin]' + ) } + + describe 'when the osd is created' do + let :facts do + { + :concat_basedir => '/var/lib/puppet/lib/concat', + :blkid_uuid_device1 => 'dummy-uuid-1234', + :ceph_osd_id_device1 => '56', + :hostname => 'dummy-host' + } + end + + it { should contain_ceph__conf__osd('56').with( + 'device' => '/dev/device', + 'public_addr' => '10.1.0.156', + 'cluster_addr' => '10.0.0.56', + 'journal' => '/dev/journal', + 'journalsize' => '1024' + ) } + + it { should contain_file('/var/lib/ceph/osd/ceph-56').with( + 'ensure' => 'directory' + ) } + + it { should contain_mount('/var/lib/ceph/osd/ceph-56').with( + 'ensure' => 'mounted', + 'device' => '/dev/device1', + 'atboot' => true, + 'fstype' => 'xfs', + 'options' => 'rw,noatime,inode64', + 'pass' => 2, + 'require' => ['Exec[mkfs_device]', 'File[/var/lib/ceph/osd/ceph-56]'] + ) } + + it { should contain_exec('ceph-osd-mkfs-56').with( + 'command' => 'ceph-osd -c /etc/ceph/ceph.conf -i 56 --mkfs --mkkey --osd-uuid dummy-uuid-1234 +', + 'creates' => '/var/lib/ceph/osd/ceph-56/keyring', + 'require' => ['Mount[/var/lib/ceph/osd/ceph-56]', 'Concat[/etc/ceph/ceph.conf]'] + ) } + + it { should contain_exec('ceph-osd-register-56').with( + 'command' => "ceph auth add osd.56 osd 'allow *' mon 'allow rwx' -i /var/lib/ceph/osd/ceph-56/keyring", + 'require' => 'Exec[ceph-osd-mkfs-56]' + ) } + + it { should contain_service('ceph-osd.56').with( + 'ensure' => 'running', + 'start' => 'service ceph start osd.56', + 'stop' => 'service ceph stop osd.56', + 'status' => 'service ceph status osd.56', + 'subscribe' => 'Concat[/etc/ceph/ceph.conf]' + ) } + end + + end + + + +end diff --git a/ceph/spec/fixtures/manifests/.emptyfile b/ceph/spec/fixtures/manifests/.emptyfile new file mode 100644 index 000000000..e69de29bb diff --git a/ceph/spec/spec.opts b/ceph/spec/spec.opts new file mode 100644 index 000000000..91cd6427e --- /dev/null +++ b/ceph/spec/spec.opts @@ -0,0 +1,6 @@ +--format +s +--colour +--loadby +mtime +--backtrace diff --git a/openstack/spec/spec_helper.rb b/ceph/spec/spec_helper.rb similarity index 100% rename from openstack/spec/spec_helper.rb rename to ceph/spec/spec_helper.rb diff --git a/ceph/templates/ceph.conf-mds.erb b/ceph/templates/ceph.conf-mds.erb new file mode 100644 index 000000000..395ff5c07 --- /dev/null +++ b/ceph/templates/ceph.conf-mds.erb @@ -0,0 +1,6 @@ +[mds.<%= @name %>] + host = <%= @hostname %> +<% @config.sort.each do |key, value| -%> + <%= key %> = <%= value %> +<% end %> + diff --git a/ceph/templates/ceph.conf-mon.erb b/ceph/templates/ceph.conf-mon.erb new file mode 100644 index 000000000..10c79de77 --- /dev/null +++ b/ceph/templates/ceph.conf-mon.erb @@ -0,0 +1,7 @@ +[mon.<%= @name %>] + host = <%= @hostname %> + mon addr = <%= @mon_addr %>:<%= @mon_port %> +<% @config.sort.each do |key, value| -%> + <%= key %> = <%= value %> +<% end %> + diff --git a/ceph/templates/ceph.conf-osd.erb b/ceph/templates/ceph.conf-osd.erb new file mode 100644 index 000000000..527ae34c8 --- /dev/null +++ b/ceph/templates/ceph.conf-osd.erb @@ -0,0 +1,19 @@ +[osd.<%= @name %>] + host = <%= @hostname %> + devs = <%= @device %>1 +<% if @cluster_addr -%> + cluster addr = <%= @cluster_addr %> +<% end -%> +<% if @public_addr -%> + public addr = <%= @public_addr %> +<% end -%> +<% if @journal -%> + osd journal = <%= @journal %> +<% end -%> +<% if @journalsize -%> + osd journal size = <%= @journalsize %> +<% end -%> +<% @config.sort.each do |key, value| -%> + <%= key %> = <%= value %> +<% end %> + diff --git a/ceph/templates/ceph.conf.erb b/ceph/templates/ceph.conf.erb new file mode 100644 index 000000000..192f54345 --- /dev/null +++ b/ceph/templates/ceph.conf.erb @@ -0,0 +1,67 @@ +[global] +<% if @auth_type -%> + auth cluster required = <%= @auth_type %> + auth service required = <%= @auth_type %> + auth client required = <%= @auth_type %> +<% end -%> +<% if @signatures_require -%> + cephx require signatures = <%= @signatures_require %> +<% end -%> +<% if @signatures_cluster -%> + cephx cluster require signatures = <%= @signatures_cluster %> +<% end -%> +<% if @signatures_service -%> + cephx service require signatures = <%= @signatures_service %> +<% end -%> +<% if @signatures_sign_msgs -%> + cephx sign messages = <%= @signatures_sign_msgs %> +<% end -%> + keyring = /etc/ceph/keyring +<% if @cluster_network && @public_network -%> + cluster network = <%= @cluster_network %> + public network = <%= @public_network %> +<% end -%> +<% if @pool_default_pg_num -%> + osd pool default pg num = <%= @pool_default_pg_num %> +<% end -%> +<% if @pool_default_pgp_num -%> + osd pool default pgp num = <%= @pool_default_pgp_num %> +<% end -%> +<% if @pool_default_size -%> + osd pool default size = <%= @pool_default_size %> +<% end -%> +<% if @pool_default_min_size -%> + osd pool default min size = <%= @pool_default_min_size %> +<% end -%> +<% if @pool_default_crush_rule -%> + osd pool default crush rule = <%= @pool_default_crush_rule %> +<% end -%> +<% @config.sort.each do |key, value| -%> + <%= key %> = <%= value %> +<% end %> + + fsid = <%= @fsid %> + +[mon] +<% if @mon_init_members -%> + mon initial members = <%= @mon_init_members %> +<% end -%> + mon data = <%= @mon_data %> + +[osd] + osd journal size = <%= @journal_size_mb %> + filestore flusher = false + osd data = <%= @osd_data %> + osd journal = <%= @osd_journal_real %> + osd mkfs type = xfs + keyring = <%= @osd_data %>/keyring + + # reduce recovery priority (less CPU impact) + osd recovery max active = <%= @osd_recovery_max_active %> + osd max backfills = <%= @osd_max_backfills %> + osd recovery op priority = <%= @osd_recovery_op_priority %> + +[mds] + mds data = <%= @mds_data %> + keyring = <%= @mds_data %>/keyring + diff --git a/certmonger/LICENSE b/certmonger/LICENSE deleted file mode 100644 index 3fa0da30c..000000000 --- a/certmonger/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 2013 Red Hat, Inc. - -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. diff --git a/certmonger/Modulefile b/certmonger/Modulefile deleted file mode 100644 index 5ec7c4b56..000000000 --- a/certmonger/Modulefile +++ /dev/null @@ -1,10 +0,0 @@ -name 'rcritten/certmonger' -version '1.0.3' -source 'git://github.com/rcritten/puppet-certmonger.git' -author 'Rob Crittenden ' -license 'Apache' -summary 'Certmonger Puppet Module' -description 'This module tracks certificates using certmonger.' -project_page 'https://github.com/rcritten/puppet-certmonger' - -dependency 'puppetlabs/stdlib', '>= 0.0.1' diff --git a/certmonger/README.md b/certmonger/README.md deleted file mode 100644 index edcb46d38..000000000 --- a/certmonger/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# certmonger puppet module - -very simple puppet module to request IPA certs via certmonger. - -This requires that the machine already be enrolled in an IPA server - -When using an NSS database this has a side-effect of creating a file -in the enrolled subdirectory of the NSS database named after the principal. -This is an indicator that the certificate has already been requested. - -Be aware of SELinux too. You can't just put NSS databases in any directory -you want. The certmonger status will probably be in NEED_KEY_PAIR in the -case of a AVC. - -certmonger uses the host principal on the machine to communicate with IPA. -By default, host principals do not have permission to add new services. -This means you'll probably need to pre-create the services, otherwise -you'll get a status like this: - -Request ID '20130823131914': - status: CA_REJECTED - ca-error: Server denied our request, giving up: 2100 (RPC failed at server. Insufficient access: You need to be a member of the serviceadmin role to add services). - -When generating an OpenSSL certificate we need to let certmonger create the -key and cert files. If puppet does it first then certmonger will think that -the user provided a key to use (a blank file) and things fail. The current -workaround is to call getcert -f /path/to/cert to make sure the key exists. - -TESTING - -Between OpenSSL invocations you'll want to clean up. This is what I do: - -getcert stop-tracking -i `getcert list -f /tmp/test.crt |grep ID |cut -d\' -f2` -rm -f /tmp/test.key /tmp/test.crt diff --git a/certmonger/manifests/request_ipa_cert.pp b/certmonger/manifests/request_ipa_cert.pp deleted file mode 100644 index c3f63b716..000000000 --- a/certmonger/manifests/request_ipa_cert.pp +++ /dev/null @@ -1,121 +0,0 @@ -# Request a new certificate from IPA using certmonger -# -# Parameters: -# $dbname - required for nss, unused for openssl. The directory -# to store the db -# $seclib, - required - select nss or openssl -# $principal - required - the IPA principal to associate the -# certificate with -# $nickname - required for nss, unused for openssl. The NSS -# certificate nickname -# $cert - required for openssl, unused for nss. The full file -# path to store the certificate in. -# $key - required for openssl, unused for nss. The full file -# path to store the key in -# $basedir - The base directory for $dbname, defaults -# to '/etc/pki'. Not used with openssl. -# $owner_id - owner of OpenSSL cert and key files -# $group_id - group of OpenSSL cert and key files -# $hostname - hostname in the subject of the certificate. -# defaults to current fqdn. -# -# Actions: -# Submits a certificate request to an IPA server for a new certificate. -# -# Requires: -# The NSS db must already exist. It can be created using the nssdb -# module. -# -# Sample Usage: -# -# NSS: -# certmonger::request_ipa_cert {'test': -# seclib => "nss", -# nickname => "broker", -# principal => "qpid/${fqdn}"} -# -# OpenSSL: -# certmonger::request_ipa_cert {'test': -# seclib => "openssl", -# principal => "qpid/${fqdn}", -# key => "/etc/pki/test2/test2.key", -# cert => "/etc/pki/test2/test2.crt", -# owner_id => 'qpidd', -# group_id => 'qpidd'} - -define certmonger::request_ipa_cert ( - $dbname = $title, - $seclib, - $principal, - $nickname = undef, - $cert = undef, - $key = undef, - $basedir = '/etc/pki', - $owner_id = undef, - $group_id = undef, - $hostname = undef -) { - include certmonger::server - - $principal_no_slash = regsubst($principal, '\/', '_') - - # Only execute certmonger if IPA client is configured - $onlyif = "/usr/bin/test -s /etc/ipa/default.conf" - - if $hostname == undef { - $subject = '' - } else { - $subject = "-N cn=${hostname}" - } - - if $seclib == 'nss' { - $options = "-d ${basedir}/${dbname} -n ${nickname} -p ${basedir}/${dbname}/password.conf" - $unless = "/usr/bin/getcert list -d ${basedir}/${dbname} -n ${nickname}" - - exec {"get_cert_nss_${title}": - command => "/usr/bin/ipa-getcert request ${options} -K ${principal} ${subject}", - onlyif => "${onlyif}", - unless => "${unless}", - require => [ - Service['certmonger'], - File["${basedir}/${dbname}/password.conf"], - ], - } - } - elsif $seclib == 'openssl' { - - $options = "-k ${key} -f ${cert}" - $unless = "/usr/bin/getcert list -f ${cert}" - - exec {"get_cert_openssl_${title}": - command => "/usr/bin/ipa-getcert request ${options} -K ${principal} ${subject}", - onlyif => "${onlyif}", - unless => "${unless}", - require => [ - Service['certmonger'], - ], - notify => Exec["wait_for_certmonger_${title}"], - } - - # We need certmonger to finish creating the key before we - # can proceed. Use onlyif as a way to execute multiple - # commands without restorting to shipping a shell script. - # This will call getcert to check the status of our cert - # 5 times. This doesn't short circuit though, so all 5 will - # always run, causing a 5-second delay. - exec {"wait_for_certmonger_${title}": - command => "true", - onlyif => [ - "sleep 1 && getcert list -f ${cert}", - "sleep 1 && getcert list -f ${cert}", - "sleep 1 && getcert list -f ${cert}", - "sleep 1 && getcert list -f ${cert}", - "sleep 1 && getcert list -f ${cert}", - ], - path => "/usr/bin:/bin", - refreshonly => true, - } - } else { - fail("Unrecognized security library: ${seclib}") - } -} diff --git a/certmonger/manifests/server.pp b/certmonger/manifests/server.pp deleted file mode 100644 index 0755f1b5e..000000000 --- a/certmonger/manifests/server.pp +++ /dev/null @@ -1,11 +0,0 @@ -class certmonger::server { - - package { 'certmonger': ensure => present } - - service { 'certmonger': - name => 'certmonger', - ensure => running, - enable => true, - require => Package['certmonger'], - } -} diff --git a/certmonger/tests/nss_ipa.pp b/certmonger/tests/nss_ipa.pp deleted file mode 100644 index 1b8de5f93..000000000 --- a/certmonger/tests/nss_ipa.pp +++ /dev/null @@ -1,6 +0,0 @@ -certmonger::request_ipa_cert {'test': - seclib => "nss", - nickname => "Server-Cert", - principal => "nss_test/${fqdn}", - basedir => '/tmp/nssdb', -} diff --git a/certmonger/tests/openssl_ipa.pp b/certmonger/tests/openssl_ipa.pp deleted file mode 100644 index 80e843d21..000000000 --- a/certmonger/tests/openssl_ipa.pp +++ /dev/null @@ -1,8 +0,0 @@ -certmonger::request_ipa_cert {'test': - seclib => "openssl", - principal => "openssl_test/${fqdn}", - key => "/tmp/test.key", - cert => "/tmp/test.crt", - owner_id => 'rcrit', - group_id => 'rcrit' -} diff --git a/cinder/Modulefile b/cinder/Modulefile new file mode 100644 index 000000000..d68b5159a --- /dev/null +++ b/cinder/Modulefile @@ -0,0 +1,15 @@ +name 'puppetlabs-cinder' +version '4.0.0' +author 'Puppet Labs and StackForge Contributors' +license 'Apache License 2.0' +summary 'Puppet module for OpenStack Cinder' +description 'Installs and configures OpenStack Cinder (Block Storage).' +project_page 'https://launchpad.net/puppet-cinder' +source 'https://github.com/stackforge/puppet-cinder' + +dependency 'dprince/qpid', '>=1.0.0 <2.0.0' +dependency 'puppetlabs/inifile', '>=1.0.0 <2.0.0' +dependency 'puppetlabs/keystone', '>=4.0.0 <5.0.0' +dependency 'puppetlabs/rabbitmq', '>=2.0.2 <4.0.0' +dependency 'puppetlabs/stdlib', '>=4.0.0' +dependency 'stackforge/openstacklib', '>=5.0.0' diff --git a/cinder/manifests/api.pp b/cinder/manifests/api.pp index 8db92829e..efdbfbbdb 100644 --- a/cinder/manifests/api.pp +++ b/cinder/manifests/api.pp @@ -106,14 +106,11 @@ ) { include cinder::params - include cinder::policy Cinder_config<||> ~> Service['cinder-api'] Cinder_api_paste_ini<||> ~> Service['cinder-api'] - Class['cinder::policy'] ~> Service['cinder-api'] if $::cinder::params::api_package { - Package['cinder-api'] -> Class['cinder::policy'] Package['cinder-api'] -> Cinder_config<||> Package['cinder-api'] -> Cinder_api_paste_ini<||> Package['cinder-api'] -> Service['cinder-api'] diff --git a/cinder/manifests/backend/eqlx.pp b/cinder/manifests/backend/eqlx.pp index b2d47eaab..32ab5b427 100644 --- a/cinder/manifests/backend/eqlx.pp +++ b/cinder/manifests/backend/eqlx.pp @@ -15,7 +15,7 @@ # # [*san_thin_provision*] # (optional) Whether or not to use thin provisioning for volumes. -# Defaults to true +# Defaults to false # # [*volume_backend_name*] # (optional) The backend name. @@ -53,7 +53,7 @@ $san_ip, $san_login, $san_password, - $san_thin_provision = true, + $san_thin_provision = false, $volume_backend_name = $name, $eqlx_group_name = 'group-0', $eqlx_pool = 'default', diff --git a/cinder/manifests/backup/ceph.pp b/cinder/manifests/backup/ceph.pp index dac0adc8b..9ac208ab8 100644 --- a/cinder/manifests/backup/ceph.pp +++ b/cinder/manifests/backup/ceph.pp @@ -21,10 +21,6 @@ # # === Parameters # -# [*backup_driver*] -# (optional) Which cinder backup driver to use -# Defaults to 'cinder.backup.drivers.ceph' -# # [*backup_ceph_conf*] # (optional) Ceph config file to use. # Should be a valid ceph configuration file @@ -58,7 +54,7 @@ # class cinder::backup::ceph ( - $backup_driver = 'cinder.backup.drivers.ceph', + $backup_driver = 'cinder.backup.driver.ceph', $backup_ceph_conf = '/etc/ceph/ceph.conf', $backup_ceph_user = 'cinder', $backup_ceph_chunk_size = '134217728', diff --git a/cinder/manifests/policy.pp b/cinder/manifests/policy.pp deleted file mode 100644 index 322dd0daa..000000000 --- a/cinder/manifests/policy.pp +++ /dev/null @@ -1,29 +0,0 @@ -# == Class: cinder::policy -# -# Configure the cinder policies -# -# === Parameters -# -# [*policies*] -# (optional) Set of policies to configure for cinder -# Example : { 'cinder-context_is_admin' => {'context_is_admin' => 'true'}, 'cinder-default' => {'default' => 'rule:admin_or_owner'} } -# Defaults to empty hash. -# -# [*policy_path*] -# (optional) Path to the cinder policy.json file -# Defaults to /etc/cinder/policy.json -# -class cinder::policy ( - $policies = {}, - $policy_path = '/etc/cinder/policy.json', -) { - - validate_hash($policies) - - Openstacklib::Policy::Base { - file_path => $policy_path, - } - - create_resources('openstacklib::policy::base', $policies) - -} diff --git a/cinder/manifests/type.pp b/cinder/manifests/type.pp index 435f9b293..a0c78bce3 100644 --- a/cinder/manifests/type.pp +++ b/cinder/manifests/type.pp @@ -60,7 +60,7 @@ exec {"cinder type-create ${volume_name}": command => "cinder type-create ${volume_name}", - unless => "cinder type-list | grep -qP '\\b${volume_name}\\b'", + unless => "cinder type-list | grep ${volume_name}", environment => concat($cinder_env, $region_env), require => Package['python-cinderclient'], path => ['/usr/bin', '/bin'], diff --git a/cinder/manifests/volume/eqlx.pp b/cinder/manifests/volume/eqlx.pp index 6c89ec6bd..7906eefdb 100644 --- a/cinder/manifests/volume/eqlx.pp +++ b/cinder/manifests/volume/eqlx.pp @@ -15,7 +15,7 @@ # # [*san_thin_provision*] # (optional) Whether or not to use thin provisioning for volumes. -# Defaults to true +# Defaults to false # # [*eqlx_group_name*] # (optional) The CLI prompt message without '>'. @@ -49,7 +49,7 @@ $san_ip, $san_login, $san_password, - $san_thin_provision = true, + $san_thin_provision = false, $eqlx_group_name = 'group-0', $eqlx_pool = 'default', $eqlx_use_chap = false, diff --git a/cinder/metadata.json b/cinder/metadata.json deleted file mode 100644 index e288f3ca9..000000000 --- a/cinder/metadata.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "stackforge-cinder", - "version": "5.0.0", - "author": "Puppet Labs and StackForge Contributors", - "summary": "Puppet module for OpenStack Cinder", - "license": "Apache License 2.0", - "source": "git://github.com/stackforge/puppet-cinder.git", - "project_page": "https://launchpad.net/puppet-cinder", - "issues_url": "https://bugs.launchpad.net/puppet-cinder", - "requirements": [ - { "name": "pe","version_requirement": "3.x" }, - { "name": "puppet","version_requirement": "3.x" } - ], - "operatingsystem_support": [ - { - "operatingsystem": "Debian", - "operatingsystemrelease": ["7"] - }, - { - "operatingsystem": "Fedora", - "operatingsystemrelease": ["20"] - }, - { - "operatingsystem": "RedHat", - "operatingsystemrelease": ["6.5","7"] - }, - { - "operatingsystem": "Ubuntu", - "operatingsystemrelease": ["12.04","14.04"] - } - ], - "description": "Installs and configures OpenStack Cinder (Block Storage).", - "dependencies": [ - { "name": "dprince/qpid", "version_requirement": ">=1.0.0 <2.0.0" }, - { "name": "puppetlabs/inifile", "version_requirement": ">=1.0.0 <2.0.0" }, - { "name": "stackforge/keystone", "version_requirement": ">=5.0.0 <6.0.0" }, - { "name": "puppetlabs/rabbitmq", "version_requirement": ">=2.0.2 <4.0.0" }, - { "name": "puppetlabs/stdlib", "version_requirement": ">=4.0.0 <5.0.0" }, - { "name": "stackforge/openstacklib", "version_requirement": ">=5.0.0" } - ] -} diff --git a/cinder/spec/classes/cinder_backup_ceph_spec.rb b/cinder/spec/classes/cinder_backup_ceph_spec.rb index 8c3a55218..bedd7cdc5 100644 --- a/cinder/spec/classes/cinder_backup_ceph_spec.rb +++ b/cinder/spec/classes/cinder_backup_ceph_spec.rb @@ -41,7 +41,7 @@ end it 'configures cinder.conf' do - should contain_cinder_config('DEFAULT/backup_driver').with_value('cinder.backup.drivers.ceph') + should contain_cinder_config('DEFAULT/backup_driver').with_value('cinder.backup.driver.ceph') should contain_cinder_config('DEFAULT/backup_ceph_conf').with_value(p[:backup_ceph_conf]) should contain_cinder_config('DEFAULT/backup_ceph_user').with_value(p[:backup_ceph_user]) should contain_cinder_config('DEFAULT/backup_ceph_chunk_size').with_value(p[:backup_ceph_chunk_size]) diff --git a/cinder/spec/classes/cinder_policy_spec.rb b/cinder/spec/classes/cinder_policy_spec.rb deleted file mode 100644 index e9b6d46d2..000000000 --- a/cinder/spec/classes/cinder_policy_spec.rb +++ /dev/null @@ -1,41 +0,0 @@ -require 'spec_helper' - -describe 'cinder::policy' do - - shared_examples_for 'cinder policies' do - let :params do - { - :policy_path => '/etc/cinder/policy.json', - :policies => { - 'context_is_admin' => { - 'key' => 'context_is_admin', - 'value' => 'foo:bar' - } - } - } - end - - it 'set up the policies' do - should contain_openstacklib__policy__base('context_is_admin').with({ - :key => 'context_is_admin', - :value => 'foo:bar' - }) - end - end - - context 'on Debian platforms' do - let :facts do - { :osfamily => 'Debian' } - end - - it_configures 'cinder policies' - end - - context 'on RedHat platforms' do - let :facts do - { :osfamily => 'RedHat' } - end - - it_configures 'cinder policies' - end -end diff --git a/cinder/spec/defines/cinder_type_spec.rb b/cinder/spec/defines/cinder_type_spec.rb index 4fa2ac981..8d763ff9d 100644 --- a/cinder/spec/defines/cinder_type_spec.rb +++ b/cinder/spec/defines/cinder_type_spec.rb @@ -24,7 +24,7 @@ 'OS_USERNAME=admin', 'OS_PASSWORD=asdf', 'OS_AUTH_URL=http://127.127.127.1:5000/v2.0/'], - :unless => "cinder type-list | grep -qP '\\bhippo\\b'", + :unless => 'cinder type-list | grep hippo', :require => 'Package[python-cinderclient]') should contain_exec('cinder type-key hippo set volume_backend_name=name1') should contain_exec('cinder type-key hippo set volume_backend_name=name2') diff --git a/cloud/.forgeignore b/cloud/.forgeignore new file mode 100644 index 000000000..76033ecf6 --- /dev/null +++ b/cloud/.forgeignore @@ -0,0 +1,14 @@ +- pkg/ +- spec/ +- Rakefile +- Puppetfile +- coverage/ +- .git/ +- .forgeignore +- .travis.yml +- .gitignore +- doc/ +- .yardoc/ +- Gemfile +- Gemfile.lock +- .fixtures.yml diff --git a/cloud/.gitignore b/cloud/.gitignore new file mode 100644 index 000000000..e9cf80623 --- /dev/null +++ b/cloud/.gitignore @@ -0,0 +1,9 @@ +*.swp +spec/fixtures/modules/* +spec/fixtures/manifests/site.pp +Gemfile.lock +.vendor +doc/ +.yardoc +.librarian/ +.tmp/ diff --git a/cloud/.gitreview b/cloud/.gitreview new file mode 100644 index 000000000..d238e955f --- /dev/null +++ b/cloud/.gitreview @@ -0,0 +1,4 @@ +[gerrit] +host=review.openstack.org +port=29418 +project=stackforge/puppet-openstack-cloud.git diff --git a/cloud/.travis.yml b/cloud/.travis.yml new file mode 100644 index 000000000..b445b4c98 --- /dev/null +++ b/cloud/.travis.yml @@ -0,0 +1,15 @@ +language: ruby +script: "bundle exec rake test COV=y SPEC_OPTS='--format documentation --color --backtrace'" +rvm: + - 1.9.3 + - 2.0.0 +matrix: + fast_finish: true +env: + matrix: + - PUPPET_GEM_VERSION="~> 3.3.0" + - PUPPET_GEM_VERSION="~> 3.4.0" + - PUPPET_GEM_VERSION="~> 3.6.0" + - PUPPET_GEM_VERSION="~> 3.7.0" +notifications: + email: false diff --git a/cloud/CHANGELOG.md b/cloud/CHANGELOG.md new file mode 100644 index 000000000..b7e06eb0e --- /dev/null +++ b/cloud/CHANGELOG.md @@ -0,0 +1,120 @@ +##2014-10-24 - Features release 2.2.0 +###Summary +* Sensu as first implementation of monitoring system +* Glance now supports NFS image storage backend +* Cinder now supports EMC VNX & iSCSI volume backends +* Nova now supports NFS instance storage backend +* Neutron now supports Cisco plugins with N1KV hardware (experimental) +* RabbitMQ can now be load-balanced by HAproxy +* Keystone roles for Heat are now created automatically +* Support for keepalived authentification +* MongoDB replicaset is now an option, so MongoDB can be standalone +* MySQL Galera has been tweaked to have better performances at scale +* Nova configuration has been tweaked to use read-only database feature and have better performances at scale +* Trove has been disabled by default since it's still in experimental status +* HAproxy: Allow user to bind multiple public/private IPs +* keepalived: allow vrrp traffic on a dedicated interface +* When running KVM, we check if VTX is really enabled +* HAproxy checks have been improve for OpenStack services +* Neutron: allow to specify tunnel type (i.e. VXLAN) +* Horizon: ALLOWED_HOST can now be controlled by the module +* Horizon: Allow user to speficy broader apache vhost settings +* Nova/RBD: support for RHEL 7 + +####Bugfixes +* Fix correct Puppet Ceph dependencies which could lead to bootstrap issues +* Fix issues with instance live migration support (nova configuration) +* Fix HAproxy checks for Spice (TCP instead of HTTP) + +####Known Bugs +* No known bugs + +##2014-07-15 - Features release 2.1.0 +###Summary +* Advanced logging support with kibana3, elasticsearch and fluentd +* Improve SSL termination support +* File backend support for Glance +* OpenStack Database as a Service support (Trove) as experimental +* Pacemaker support in Red-Hat +* heat-engine is no more managed as a single point of failure + +####Bugfixes +* Fix heat-cfn & heat-cloudwatch HAproxy binding +* Fix issues when using SSL termination + +####Known Bugs +* No known bugs + +##2014-06-19 - Features release 2.0.0 +###Summary +* Icehouse release support +* OpenStack Object Storage support (Swift) +* Neutron Metadata multi-worker +* RBD flexibility on compute nodes +* Keystone and Nova v3 API support +* SSL termination support + +####Bugfixes +* Fix nova-compute service when using RBD backend +* Fix cinder-volume service when creating a volume type +* Enable to have Swift Storage & Ceph OSD on same nodes + +####Known Bugs +* No known bugs + +##2014-05-06 - Features release 1.3.0 +###Summary +* High Availability refactorization +* OpenStack services separation in different classes +* DHCP Agent: Add support of DNS server declaration +* Defaults values for all puppet parameters, can now support Hiera. +* Fix all unit tests to pass Travis + +####Bugfixes +* Fix HAproxy configuration for Heat API binding + +####Known Bugs +* When using RBD as Nova Backend, nova-compute should be notified +* When creating a volume type, cinder-volume should be notified +* Impossible to attach a volume backend by RBD if not using RBD backend for Nova + +##2014-04-22 - Features release 1.2.0 +###Summary +* Now supports Ubuntu 12.04 +* Now supports Now supports Red Hat OpenStack Platform 4 +* Can be deployed on 3 nodes +* Add cluster note type support for RabbitMQ configuration +* Block storage can now be backend by multiple RBD pools + +####Bugfixes +* Fix a bug in Horizon in HTTP/HTTPS binding + +####Known Bugs +* No known bugs + +##2014-04-01 - Features release 1.1.0 +###Summary +* Updated puppetlabs-rabbitmq to 3.1.0 (RabbitMQ to 3.2.4) +* Add Cinder Muli-backend support +* NetApp support for Cinder as a backend +* Keystone uses now MySQL for tokens storage (due to several issues with Memcache backend) +* Back to upstream puppet-horizon from stackforge +* Servername parameter support in Horizon configuration to allow SSL redirections +* puppet-openstack-cloud module QA is done by Travis +* network: add dhcp\_lease\_duration parameter support + +####Bugfixes +* neutron: increase agent polling interval + +####Known Bugs +* Bug in Horizon in HTTP/HTTPS binding (fixed in 1.2.0) + +##2014-03-13 - First stable version 1.0.0 +###Summary +* First stable version. + +####Bugfixes +* No + +####Known Bugs +* No known bugs diff --git a/cloud/Gemfile b/cloud/Gemfile new file mode 100644 index 000000000..790afc57c --- /dev/null +++ b/cloud/Gemfile @@ -0,0 +1,24 @@ +source 'https://rubygems.org' + +group :development, :test do + gem 'puppetlabs_spec_helper', :require => false + gem 'puppet-lint' + gem 'puppet-lint-param-docs', '1.1.0' + gem 'metadata-json-lint' + gem 'rake', '10.1.1' + gem 'puppet-syntax' + gem 'rspec-puppet', :git => 'https://github.com/rodjek/rspec-puppet.git' + gem 'rspec' + gem 'json' + gem 'webmock' + gem 'r10k' + gem 'librarian-puppet-simple', '~> 0.0.3' +end + +if puppetversion = ENV['PUPPET_GEM_VERSION'] + gem 'puppet', puppetversion, :require => false +else + gem 'puppet', :require => false +end + +# vim:ft=ruby diff --git a/cloud/LICENSE b/cloud/LICENSE new file mode 100644 index 000000000..68c771a09 --- /dev/null +++ b/cloud/LICENSE @@ -0,0 +1,176 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + diff --git a/cloud/README.md b/cloud/README.md new file mode 100644 index 000000000..16786471b --- /dev/null +++ b/cloud/README.md @@ -0,0 +1,143 @@ +# puppet-openstack-cloud + +[![Build Status](https://api.travis-ci.org/enovance/puppet-openstack-cloud.svg?branch=master)](https://travis-ci.org/enovance/puppet-openstack-cloud) +[![Puppet Forge](http://img.shields.io/puppetforge/v/eNovance/cloud.svg)](https://forge.puppetlabs.com/eNovance/cloud) +[![License](http://img.shields.io/:license-apache-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) + +#### Table of Contents + +1. [Overview - What is the cloud module?](#overview) +2. [Module Description - What does the module do?](#module-description) +3. [Setup - The basics of getting started with puppet-openstack-cloud](#setup) +4. [Implementation - An under-the-hood peek at what the module is doing](#implementation) +5. [Limitations - OS compatibility, etc.](#limitations) +6. [Getting Involved - How to go deeper](#involved) +7. [Development - Guide for contributing to the module](#development) +8. [Contributors - Those with commits](#contributors) +9. [Release Notes - Notes on the most recent updates to the module](#release-notes) + +## Overview + +The [puppet-openstack-cloud](https://wiki.openstack.org/wiki/Puppet-openstack/puppet-openstack-cloud) module is a flexible Puppet composition layer capable of configuring the core [OpenStack](http://docs.openstack.org/) services: + +* [Nova](https://github.com/stackforge/puppet-nova) (compute) +* [Glance](https://github.com/stackforge/puppet-glance) (image) +* [Keystone](https://github.com/stackforge/puppet-keystone) (identity) +* [Cinder](https://github.com/stackforge/puppet-cinder) (volume) +* [Horizon](https://github.com/stackforge/puppet-horizon) (dashboard) +* [Heat](https://github.com/stackforge/puppet-heat) (orchestration) +* [Ceilometer](https://github.com/stackforge/puppet-ceilometer) (telemetry) +* [Neutron](https://github.com/stackforge/puppet-neutron) (networking) +* [Swift](https://github.com/stackforge/puppet-swift) (object storage) +* [Trove](https://github.com/stackforge/puppet-trove) (database as a service) + +Cinder, Glance and Nova can use Ceph as backend storage, using [puppet-ceph](https://github.com/enovance/puppet-ceph). + +Only KVM and QEMU are supported as hypervisors, for now. +Neutron use ML2 plugin with GRE and Open-vSwitch drivers. +Cinder has multi-backend support: +* RBD (default) +* NetAPP +* iSCSI +* EMC VNX direct +* NFS +Glance supports different backends: +* RBD (default) +* file +* NFS (mount a NFS share by using file backend) +* Swift +Neutron supports: +* ML2 plugin with OVS agent (GRE + VXLAN supported) +* Cisco plugin with N1KV agent (non-ML2) +Trove support is now experimental. + +[Puppet Modules](http://docs.puppetlabs.com/learning/modules1.html#modules) are a collection of related contents that can be used to model the configuration of a discrete service. + +These Puppet modules are based on the [openstack documentation](http://docs.openstack.org/). + +## Module Description + +There are a lot of moving pieces in OpenStack, consequently there are several Puppet modules needed to cover all these pieces. Each module is then made up of several class definitions, resource declarations, defined resources, and custom types/providers. A common pattern to reduce this complexity in Puppet is to create a composite module that bundles all these component type modules into a common set of configurations. The cloud module is doing this compositing and exposing a set of variables needed to be successful in getting a functional stack up and running. + +### Pre-module Dependencies + +* [Puppet](http://docs.puppetlabs.com/puppet/) 3 or greater +* [Facter](http://www.puppetlabs.com/puppet/related-projects/facter/) 1.6.1 or greater (versions that support the osfamily fact) + +### Notes about Puppet3 + +Puppet 3.x isn't yet available on Debian/RedHat stable osfamily, but hopefully puppet provides a Official repository, please see [this page](http://docs.puppetlabs.com/guides/puppetlabs_package_repositories.html) for the setup. + +**Platforms** + +These modules have been fully tested on Ubuntu Precise and Debian Wheezy and RHEL 6. + +## Setup + +**What the cloud module affects** + +* The entirety of OpenStack! + +### Installing Puppet + +Puppet Labs provides two tools for getting started with managing configuration modeling with Puppet, Puppet Enterprise or its underlying opensource projects, i.e. Puppet and MCollective. + +* [Puppet Enterprise](http://docs.puppetlabs.com/#puppet-enterprisepelatest) is a complete configuration management platform, with an optimized set of components proven to work well together. Is free up to 10 nodes so if you're just using Puppet for OpenStack management this might just work perfectly. It will come configured with a handful of extra components that make for a richer experience, like a web interface for managing the orchestration of Puppet and certificate management. +* [Puppet](http://docs.puppetlabs.com/#puppetpuppet) manages your servers: you describe machine configurations in an easy-to-read declarative language, and Puppet will bring your systems into the desired state and keep them there. This is the opensource version of Puppet and should be available in your operating system's package repositories but it is generally suggested you use the [yum](http://yum.puppetlabs.com) or [apt](http://apt.puppetlabs.com) repositories from Puppet Labs if possible. + +Consult the documentation linked above to help you make your decision but don't fret about the choice to much, opensource Puppet agents are compatible with Puppet Enterprise Puppet masters. + +### Optional Puppet features + +The swift portions of this module needs Puppet's [exported resources](http://docs.puppetlabs.com/puppet/3/reference/lang_exported.html). Exported resources leverages the PuppetDB to export and share data across other Puppet managed nodes. + +### Installing latest unstable cloud module from source + + cd /etc/puppet/modules + git clone git@github.com:redhat-openstack/openstack-puppet-modules.git modules + cd modules + git checkout -b puppet-openstack-cloud origin/enovance + gem install --no-ri --no-rdoc r10k + # a debian package is available in jessie + PUPPETFILE=./Puppetfile PUPPETFILE_DIR=../ r10k --verbose 3 puppetfile install + +**Pre-puppet setup** + +The things that follow can be handled by Puppet but are out of scope of this document and are not included in the cloud module. + +### Beginning with puppet-openstack-cloud + +Utilization of this module can come in many forms. It was designed to be capable of deploying all services to a single node or distributed across several. This is not an exhaustive list, we recommend you consult and understand all the manifests included in this module and the [core openstack](http://docs.openstack.org) documentation. + + +## Implementation + +(more doc should be written here) + +## Limitations + +* Deploys only with rabbitmq and mysql RPC/data backends. +* Not backwards compatible with pre-2.x release of the cloud modules. + +## Getting Involved + +Need a feature? Found a bug? Let us know! + +We are extremely interested in growing a community of OpenStack experts and users around these modules so they can serve as an example of consolidated best practices of how to deploy openstack. + +The best way to get help with this set of modules is to email the group associated with this project: + + dev [at] enovance [dot] com + +Issues should be opened here: + + https://github.com/enovance/puppet-openstack-cloud/issues + + +## Contributors + +* https://github.com/enovance/puppet-openstack-cloud/graphs/contributors + +## Release Notes + +See [CHANGELOG](https://github.com/enovance/puppet-openstack-cloud/blob/master/CHANGELOG.md) file. diff --git a/cloud/Rakefile b/cloud/Rakefile new file mode 100644 index 000000000..71731ec42 --- /dev/null +++ b/cloud/Rakefile @@ -0,0 +1,90 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : +# +NAME = 'eNovance-cloud' +TDIR = File.expand_path(File.dirname(__FILE__)) + +require 'puppetlabs_spec_helper/rake_tasks' +require 'puppet-lint/tasks/puppet-lint' +require 'puppet-syntax/tasks/puppet-syntax' +require 'net/http' + +PuppetLint.configuration.fail_on_warnings = true +PuppetLint.configuration.send('disable_80chars') +# for manifest loadbalancer.pp +39 (default value as an array of variables) +PuppetLint.configuration.send('disable_class_parameter_defaults') +# manifests/image/api.pp - WARNING: string containing only a variable on line 189 +PuppetLint.configuration.send('disable_only_variable_string') +# For stonith-enabled (it's a string not a bool) +PuppetLint.configuration.send('disable_quoted_booleans') +# Ignore all upstream modules +exclude_paths = ['spec/**/*','pkg/**/*','vendor/**/*'] +exclude_lint_paths = exclude_paths + +PuppetLint.configuration.ignore_paths = exclude_lint_paths +PuppetSyntax.exclude_paths = exclude_paths + + +task(:default).clear +task :default => :test + +desc 'Run syntax, lint and spec tests' +task :test => [:syntax,:lint,:validate_puppetfile,:validate_metadata_json,:spec] + +desc 'Run syntax, lint and spec tests (without fixture purge = train/airplane)' +task :test_keep => [:syntax,:lint,:validate_puppetfile,:validate_metadata_json,:spec_prep,:spec_standalone] + +if ENV['COV'] + desc 'Run syntax, lint, spec tests and coverage' + task :cov => [:syntax,:lint,:validate_puppetfile,:validate_metadata_json,:spec_prep,:spec_standalone] +end + +desc "Validate the Puppetfile syntax" +task :validate_puppetfile do + $stderr.puts "---> syntax:puppetfile" + sh "r10k puppetfile check" +end + +desc "Validate the metadata.json syntax" +task :validate_metadata_json do + $stderr.puts "---> syntax:metadata.json" + sh "metadata-json-lint metadata.json" +end + +namespace :module do + desc "Build #{NAME} module (in a clean env) Please use this for puppetforge" + task :build do + exec "rsync -rv --exclude-from=#{TDIR}/.forgeignore . /tmp/#{NAME};cd /tmp/#{NAME};puppet module build" + end +end + +puppetfile_url = ENV['PUPPETFILE'] || 'https://raw.githubusercontent.com/redhat-openstack/openstack-puppet-modules/enovance/Puppetfile' + +Rake::Task[:spec_prep].clear +desc 'Create the fixtures directory' +task :spec_prep do + puts "Puppetfile: #{puppetfile_url}" + # If puppetfile exist on local fs, let use this file (provided by Puppetfile env arg) + if !File.exists?(puppetfile_url) + uri = URI.parse(puppetfile_url) + puppetfile = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https').request(Net::HTTP::Get.new(uri.request_uri)).body + end + File.open('Puppetfile', 'w') { |file| file.write(puppetfile) } + FileUtils::mkdir_p('spec/fixtures/modules') + FileUtils::mkdir_p('spec/fixtures/manifests') + FileUtils::touch('spec/fixtures/manifests/site.pp') + sh 'librarian-puppet install --path=spec/fixtures/modules' + if File.exists?('spec/fixtures/modules/cloud') + FileUtils::rm_rf('spec/fixtures/modules/cloud') + FileUtils::ln_s(TDIR, 'spec/fixtures/modules/cloud') + end +end + +Rake::Task[:spec_clean].clear +desc 'Clean up the fixtures directory' +task :spec_clean do + sh 'librarian-puppet clean --path=spec/fixtures/modules' + if File.zero?('spec/fixtures/manifests/site.pp') + FileUtils::rm_f('spec/fixtures/manifests/site.pp') + end +end diff --git a/cloud/files/heartbeat/README.md b/cloud/files/heartbeat/README.md new file mode 100644 index 000000000..6c4f7e89a --- /dev/null +++ b/cloud/files/heartbeat/README.md @@ -0,0 +1,3 @@ +# Pacemaker High Availability resource agents for OpenStack + +https://github.com/madkiss/openstack-resource-agents diff --git a/cloud/files/heartbeat/ceilometer-agent-central b/cloud/files/heartbeat/ceilometer-agent-central new file mode 100644 index 000000000..9c460a37c --- /dev/null +++ b/cloud/files/heartbeat/ceilometer-agent-central @@ -0,0 +1,345 @@ +#!/bin/sh +# +# +# OpenStack Ceilometer Central Agent Service (ceilometer-agent-central) +# +# Description: Manages an OpenStack Ceilometer Central Agent Service (ceilometer-agent-central) process as an HA resource +# +# Authors: Emilien Macchi +# Mainly inspired by the Nova Scheduler resource agent written by Sebastien Han +# +# Support: openstack@lists.launchpad.net +# License: Apache Software License (ASL) 2.0 +# +# +# See usage() function below for more details ... +# +# OCF instance parameters: +# OCF_RESKEY_binary +# OCF_RESKEY_config +# OCF_RESKEY_user +# OCF_RESKEY_pid +# OCF_RESKEY_monitor_binary +# OCF_RESKEY_amqp_server_port +# OCF_RESKEY_additional_parameters +####################################################################### +# Initialization: + +: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} +. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs + +####################################################################### + +# Fill in some defaults if no values are specified + +OCF_RESKEY_binary_default="ceilometer-agent-central" +OCF_RESKEY_config_default="/etc/ceilometer/ceilometer.conf" +OCF_RESKEY_user_default="ceilometer" +OCF_RESKEY_pid_default="$HA_RSCTMP/$OCF_RESOURCE_INSTANCE.pid" +OCF_RESKEY_amqp_server_port_default="5672" + +: ${OCF_RESKEY_binary=${OCF_RESKEY_binary_default}} +: ${OCF_RESKEY_config=${OCF_RESKEY_config_default}} +: ${OCF_RESKEY_user=${OCF_RESKEY_user_default}} +: ${OCF_RESKEY_pid=${OCF_RESKEY_pid_default}} +: ${OCF_RESKEY_amqp_server_port=${OCF_RESKEY_amqp_server_port_default}} + +####################################################################### + +usage() { + cat < + + +1.0 + + +Resource agent for the OpenStack Ceilometer Central Agent Service (ceilometer-agent-central) +May manage a ceilometer-agent-central instance or a clone set that +creates a distributed ceilometer-agent-central cluster. + +Manages the OpenStack Ceilometer Central Agent Service (ceilometer-agent-central) + + + + +Location of the OpenStack Ceilometer Central Agent server binary (ceilometer-agent-central) + +OpenStack Ceilometer Central Agent server binary (ceilometer-agent-central) + + + + + +Location of the OpenStack Ceilometer Central Agent Service (ceilometer-agent-central) configuration file + +OpenStack Ceilometer Central Agent (ceilometer-agent-central registry) config file + + + + + +User running OpenStack Ceilometer Central Agent Service (ceilometer-agent-central) + +OpenStack Ceilometer Central Agent Service (ceilometer-agent-central) user + + + + + +The pid file to use for this OpenStack Ceilometer Central Agent Service (ceilometer-agent-central) instance + +OpenStack Ceilometer Central Agent Service (ceilometer-agent-central) pid file + + + + + +The listening port number of the AMQP server. Use for monitoring purposes + +AMQP listening port + + + + + + +Additional parameters to pass on to the OpenStack Ceilometer Central Agent Service (ceilometer-agent-central) + +Additional parameters for ceilometer-agent-central + + + + + + + + + + + + + + +END +} + +####################################################################### +# Functions invoked by resource manager actions + +ceilometer_agent_central_check_port() { +# This function has been taken from the squid RA and improved a bit +# The length of the integer must be 4 +# Examples of valid port: "1080", "0080" +# Examples of invalid port: "1080bad", "0", "0000", "" + + local int + local cnt + + int="$1" + cnt=${#int} + echo $int |egrep -qx '[0-9]+(:[0-9]+)?(,[0-9]+(:[0-9]+)?)*' + + if [ $? -ne 0 ] || [ $cnt -ne 4 ]; then + ocf_log err "Invalid port number: $1" + exit $OCF_ERR_CONFIGURED + fi +} + +ceilometer_agent_central_validate() { + local rc + + check_binary $OCF_RESKEY_binary + check_binary netstat + ceilometer_agent_central_check_port $OCF_RESKEY_amqp_server_port + + # A config file on shared storage that is not available + # during probes is OK. + if [ ! -f $OCF_RESKEY_config ]; then + if ! ocf_is_probe; then + ocf_log err "Config $OCF_RESKEY_config doesn't exist" + return $OCF_ERR_INSTALLED + fi + ocf_log_warn "Config $OCF_RESKEY_config not available during a probe" + fi + + getent passwd $OCF_RESKEY_user >/dev/null 2>&1 + rc=$? + if [ $rc -ne 0 ]; then + ocf_log err "User $OCF_RESKEY_user doesn't exist" + return $OCF_ERR_INSTALLED + fi + + true +} + +ceilometer_agent_central_status() { + local pid + local rc + + if [ ! -f $OCF_RESKEY_pid ]; then + ocf_log info "OpenStack Ceilometer Central Agent (ceilometer-agent-central) is not running" + return $OCF_NOT_RUNNING + else + pid=`cat $OCF_RESKEY_pid` + fi + + ocf_run -warn kill -s 0 $pid + rc=$? + if [ $rc -eq 0 ]; then + return $OCF_SUCCESS + else + ocf_log info "Old PID file found, but OpenStack Ceilometer Central Agent (ceilometer-agent-central) is not running" + return $OCF_NOT_RUNNING + fi +} + +ceilometer_agent_central_monitor() { + local rc + local pid + local scheduler_amqp_check + + ceilometer_agent_central_status + rc=$? + + # If status returned anything but success, return that immediately + if [ $rc -ne $OCF_SUCCESS ]; then + return $rc + fi + + # Check the connections according to the PID. + # We are sure to hit the scheduler process and not other Cinder process with the same connection behavior (for example cinder-api) + pid=`cat $OCF_RESKEY_pid` + scheduler_amqp_check=`netstat -punt | grep -s "$OCF_RESKEY_amqp_server_port" | grep -s "$pid" | grep -qs "ESTABLISHED"` + rc=$? + if [ $rc -ne 0 ]; then + ocf_log err "Central Agent is not connected to the AMQP server : $rc" + return $OCF_NOT_RUNNING + fi + + ocf_log debug "OpenStack Ceilometer Central Agent (ceilometer-agent-central) monitor succeeded" + return $OCF_SUCCESS +} + +ceilometer_agent_central_start() { + local rc + + ceilometer_agent_central_status + rc=$? + if [ $rc -eq $OCF_SUCCESS ]; then + ocf_log info "OpenStack Ceilometer Central Agent (ceilometer-agent-central) already running" + return $OCF_SUCCESS + fi + + # run the actual ceilometer-agent-central daemon. Don't use ocf_run as we're sending the tool's output + # straight to /dev/null anyway and using ocf_run would break stdout-redirection here. + su ${OCF_RESKEY_user} -s /bin/sh -c "${OCF_RESKEY_binary} --config-file=$OCF_RESKEY_config \ + $OCF_RESKEY_additional_parameters"' >> /dev/null 2>&1 & echo $!' > $OCF_RESKEY_pid + + # Spin waiting for the server to come up. + while true; do + ceilometer_agent_central_monitor + rc=$? + [ $rc -eq $OCF_SUCCESS ] && break + if [ $rc -ne $OCF_NOT_RUNNING ]; then + ocf_log err "OpenStack Ceilometer Central Agent (ceilometer-agent-central) start failed" + exit $OCF_ERR_GENERIC + fi + sleep 1 + done + + ocf_log info "OpenStack Ceilometer Central Agent (ceilometer-agent-central) started" + return $OCF_SUCCESS +} + +ceilometer_agent_central_stop() { + local rc + local pid + + ceilometer_agent_central_status + rc=$? + if [ $rc -eq $OCF_NOT_RUNNING ]; then + ocf_log info "OpenStack Ceilometer Central Agent (ceilometer-agent-central) already stopped" + return $OCF_SUCCESS + fi + + # Try SIGTERM + pid=`cat $OCF_RESKEY_pid` + ocf_run kill -s TERM $pid + rc=$? + if [ $rc -ne 0 ]; then + ocf_log err "OpenStack Ceilometer Central Agent (ceilometer-agent-central) couldn't be stopped" + exit $OCF_ERR_GENERIC + fi + + # stop waiting + shutdown_timeout=15 + if [ -n "$OCF_RESKEY_CRM_meta_timeout" ]; then + shutdown_timeout=$((($OCF_RESKEY_CRM_meta_timeout/1000)-5)) + fi + count=0 + while [ $count -lt $shutdown_timeout ]; do + ceilometer_agent_central_status + rc=$? + if [ $rc -eq $OCF_NOT_RUNNING ]; then + break + fi + count=`expr $count + 1` + sleep 1 + ocf_log debug "OpenStack Ceilometer Central Agent (ceilometer-agent-central) still hasn't stopped yet. Waiting ..." + done + + ceilometer_agent_central_status + rc=$? + if [ $rc -ne $OCF_NOT_RUNNING ]; then + # SIGTERM didn't help either, try SIGKILL + ocf_log info "OpenStack Ceilometer Central Agent (ceilometer-agent-central) failed to stop after ${shutdown_timeout}s \ + using SIGTERM. Trying SIGKILL ..." + ocf_run kill -s KILL $pid + fi + + ocf_log info "OpenStack Ceilometer Central Agent (ceilometer-agent-central) stopped" + + rm -f $OCF_RESKEY_pid + + return $OCF_SUCCESS +} + +####################################################################### + +case "$1" in + meta-data) meta_data + exit $OCF_SUCCESS;; + usage|help) usage + exit $OCF_SUCCESS;; +esac + +# Anything except meta-data and help must pass validation +ceilometer_agent_central_validate || exit $? + +# What kind of method was invoked? +case "$1" in + start) ceilometer_agent_central_start;; + stop) ceilometer_agent_central_stop;; + status) ceilometer_agent_central_status;; + monitor) ceilometer_agent_central_monitor;; + validate-all) ;; + *) usage + exit $OCF_ERR_UNIMPLEMENTED;; +esac diff --git a/cloud/files/logrotate/haproxy b/cloud/files/logrotate/haproxy new file mode 100644 index 000000000..0e2d70453 --- /dev/null +++ b/cloud/files/logrotate/haproxy @@ -0,0 +1,12 @@ +# Managed by Puppet +# Module cloud::loadbalancer +/var/log/haproxy.log { + rotate 7 + daily + missingok + notifempty + delaycompress + compress + postrotate + endscript +} diff --git a/cloud/files/qemu/qemu.conf b/cloud/files/qemu/qemu.conf new file mode 100644 index 000000000..5090f409d --- /dev/null +++ b/cloud/files/qemu/qemu.conf @@ -0,0 +1,11 @@ +# Managed by Puppet +# Module cloud::compute::hypervisor +user = "root" +group = "root" +cgroup_device_acl = [ + "/dev/null", "/dev/full", "/dev/zero", + "/dev/random", "/dev/urandom", + "/dev/ptmx", "/dev/kvm", "/dev/kqemu", + "/dev/rtc", "/dev/hpet", "/dev/net/tun", +] +clear_emulator_capabilities = 0 diff --git a/cloud/lib/facter/edeploy.rb b/cloud/lib/facter/edeploy.rb new file mode 100644 index 000000000..58ece7c4c --- /dev/null +++ b/cloud/lib/facter/edeploy.rb @@ -0,0 +1,40 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Fact: edeploy +# +Facter.add('edeploy_role_version') do + setcode do + if File.executable?('/usr/sbin/edeploy') + Facter::Util::Resolution.exec('/usr/sbin/edeploy version') + end + end +end + +Facter.add('edeploy_role_name') do + setcode do + if File.executable?('/usr/sbin/edeploy') + Facter::Util::Resolution.exec('/usr/sbin/edeploy role') + end + end +end + +Facter.add('edeploy_profile') do + setcode do + if File.executable?('/usr/sbin/edeploy') + Facter::Util::Resolution.exec('/usr/sbin/edeploy profile') + end + end +end diff --git a/cloud/lib/facter/vtx.rb b/cloud/lib/facter/vtx.rb new file mode 100644 index 000000000..faec42d52 --- /dev/null +++ b/cloud/lib/facter/vtx.rb @@ -0,0 +1,27 @@ +# Fact: vtx +# +# Purpose: +# Determine whether VT-X is enabled on the node. +# +# Resolution: +# Checks for vmx (intel) or svm (amd) is part of /proc/cpuinfo flags +# +# Caveats: +# + +# Author: Emilien Macchi + +Facter.add('vtx') do + confine :kernel => :linux + setcode do + result = false + begin + # test on Intel and AMD plateforms + if File.read('/proc/cpuinfo') =~ /(vmx|svm)/ + result = true + end + rescue + end + result + end +end diff --git a/cloud/manifests/cache.pp b/cloud/manifests/cache.pp new file mode 100644 index 000000000..3789bfd38 --- /dev/null +++ b/cloud/manifests/cache.pp @@ -0,0 +1,48 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::cache +# +# Install a cache server (used by OpenStack services) +# +# === Parameters: +# +# [*listen_ip*] +# (optional) IP address on which memcached instance should listen +# Defaults to '127.0.0.1' +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::cache ( + $listen_ip = '127.0.0.1', + $firewall_settings = {}, +){ + + class { 'memcached': + listen_ip => $listen_ip, + max_memory => '60%', + } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow memcached access': + port => '11211', + extras => $firewall_settings, + } + } + +} diff --git a/cloud/manifests/compute.pp b/cloud/manifests/compute.pp new file mode 100644 index 000000000..33b10c076 --- /dev/null +++ b/cloud/manifests/compute.pp @@ -0,0 +1,198 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::compute +# +# Common class for compute nodes +# +# === Parameters: +# +# [*nova_db_host*] +# (optional) Hostname or IP address to connect to nova database +# Defaults to '127.0.0.1' +# +# [*nova_db_use_slave*] +# (optional) Enable slave connection for nova, this assume +# the haproxy is used and mysql loadbalanced port for read operation is 3307 +# Defaults to false +# +# [*nova_db_user*] +# (optional) Username to connect to nova database +# Defaults to 'nova' +# +# [*nova_db_password*] +# (optional) Password to connect to nova database +# Defaults to 'novapassword' +# +# [*rabbit_hosts*] +# (optional) List of RabbitMQ servers. Should be an array. +# Defaults to ['127.0.0.1:5672'] +# +# [*rabbit_password*] +# (optional) Password to connect to nova queues. +# Defaults to 'rabbitpassword' +# +# [*ks_glance_internal_host*] +# (optional) Internal Hostname or IP to connect to Glance API +# Defaults to '127.0.0.1' +# +# [*ks_glance_internal_proto*] +# (optional) Internal protocol to connect to Glance API +# Defaults to 'http' +# +# [*glance_api_port*] +# (optional) TCP port to connect to Glance API +# Defaults to '9292' +# +# [*verbose*] +# (optional) Set log output to verbose output +# Defaults to true +# +# [*debug*] +# (optional) Set log output to debug output +# Defaults to true +# +# [*use_syslog*] +# (optional) Use syslog for logging +# Defaults to true +# +# [*log_facility*] +# (optional) Syslog facility to receive log lines +# Defaults to 'LOG_LOCAL0' +# +# [*neutron_endpoint*] +# (optional) Host running auth service. +# Defaults to '127.0.0.1' +# +# [*neutron_protocol*] +# (optional) Protocol to connect to Neutron service. +# Defaults to 'http' +# +# [*neutron_password*] +# (optional) Password to connect to Neutron service. +# Defaults to 'neutronpassword' +# +# [*neutron_region_name*] +# (optional) Name of the Neutron Region. +# Defaults to 'RegionOne' +# +# [*memcache_servers*] +# (optionnal) Memcached servers used by Keystone. Should be an array. +# Defaults to ['127.0.0.1:11211'] +# +# [*availability_zone*] +# (optional) Name of the default Nova availability zone. +# Defaults to 'RegionOne' +# +# [*cinder_endpoint_type*] +# (optional) Cinder endpoint type to use. +# Defaults to 'publicURL' +# +class cloud::compute( + $nova_db_host = '127.0.0.1', + $nova_db_use_slave = false, + $nova_db_user = 'nova', + $nova_db_password = 'novapassword', + $rabbit_hosts = ['127.0.0.1:5672'], + $rabbit_password = 'rabbitpassword', + $ks_glance_internal_host = '127.0.0.1', + $ks_glance_internal_proto = 'http', + $glance_api_port = 9292, + $verbose = true, + $debug = true, + $use_syslog = true, + $log_facility = 'LOG_LOCAL0', + $neutron_endpoint = '127.0.0.1', + $neutron_protocol = 'http', + $neutron_password = 'neutronpassword', + $neutron_region_name = 'RegionOne', + $memcache_servers = ['127.0.0.1:11211'], + $availability_zone = 'RegionOne', + $cinder_endpoint_type = 'publicURL' +) { + + if !defined(Resource['nova_config']) { + resources { 'nova_config': + purge => true; + } + } + + # Disable twice logging if syslog is enabled + if $use_syslog { + $log_dir = false + nova_config { + 'DEFAULT/logging_context_format_string': value => '%(process)d: %(levelname)s %(name)s [%(request_id)s %(user)s] %(instance)s%(message)s'; + 'DEFAULT/logging_default_format_string': value => '%(process)d: %(levelname)s %(name)s [-] %(instance)s%(message)s'; + 'DEFAULT/logging_debug_format_suffix': value => '%(funcName)s %(pathname)s:%(lineno)d'; + 'DEFAULT/logging_exception_prefix': value => '%(process)d: TRACE %(name)s %(instance)s'; + } + } else { + $log_dir = '/var/log/nova' + } + + $encoded_user = uriescape($nova_db_user) + $encoded_password = uriescape($nova_db_password) + + class { 'nova': + database_connection => "mysql://${encoded_user}:${encoded_password}@${nova_db_host}/nova?charset=utf8", + mysql_module => '2.2', + rabbit_userid => 'nova', + rabbit_hosts => $rabbit_hosts, + rabbit_password => $rabbit_password, + glance_api_servers => "${ks_glance_internal_proto}://${ks_glance_internal_host}:${glance_api_port}", + memcached_servers => $memcache_servers, + verbose => $verbose, + debug => $debug, + log_dir => $log_dir, + log_facility => $log_facility, + use_syslog => $use_syslog, + nova_shell => '/bin/bash', + } + + if $nova_db_use_slave { + nova_config {'database/slave_connection': value => "mysql://${encoded_user}:${encoded_password}@${nova_db_host}:3307/nova?charset=utf8" } + } else { + nova_config {'database/slave_connection': ensure => absent } + } + + class { 'nova::network::neutron': + neutron_admin_password => $neutron_password, + neutron_admin_auth_url => "${neutron_protocol}://${neutron_endpoint}:35357/v2.0", + neutron_url => "${neutron_protocol}://${neutron_endpoint}:9696", + neutron_region_name => $neutron_region_name + } + + nova_config { + 'DEFAULT/resume_guests_state_on_host_boot': value => true; + 'DEFAULT/default_availability_zone': value => $availability_zone; + 'DEFAULT/servicegroup_driver': value => 'mc'; + 'DEFAULT/glance_num_retries': value => '10'; + 'DEFAULT/cinder_catalog_info': value => "volume:cinder:${cinder_endpoint_type}"; + } + + # Note(EmilienM): + # We check if DB tables are created, if not we populate Nova DB. + # It's a hack to fit with our setup where we run MySQL/Galera + # TODO(Goneri) + # We have to do this only on the primary node of the galera cluster to avoid race condition + # https://github.com/enovance/puppet-openstack-cloud/issues/156 + exec {'nova_db_sync': + command => 'nova-manage db sync', + user => 'nova', + path => '/usr/bin', + unless => "/usr/bin/mysql nova -h ${nova_db_host} -u ${encoded_user} -p${encoded_password} -e \"show tables\" | /bin/grep Tables" + } + +} diff --git a/cloud/manifests/compute/api.pp b/cloud/manifests/compute/api.pp new file mode 100644 index 000000000..75abc2a33 --- /dev/null +++ b/cloud/manifests/compute/api.pp @@ -0,0 +1,123 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::compute::api +# +# Install a Nova-API node +# +# === Parameters: +# +# [*ks_keystone_internal_host*] +# (optional) Internal Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_internal_proto*] +# (optional) Protocol used to connect to Keystone API. +# Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_nova_password*] +# (optional) Password used by Nova to connect to Keystone API +# Defaults to 'novapassword' +# +# [*neutron_metadata_proxy_shared_secret*] +# (optional) Shared secret to validate proxies Neutron metadata requests +# Defaults to 'metadatapassword' +# +# [*api_eth*] +# (optional) Hostname or IP to bind Nova API. +# Defaults to '127.0.0.1' +# +# [*ks_nova_public_port*] +# (optional) TCP port for bind Nova API. +# Defaults to '8774' +# +# [*ks_ec2_public_port*] +# (optional) TCP port for bind Nova EC2 API. +# Defaults to '8773' +# +# [*ks_metadata_public_port*] +# (optional) TCP port for bind Nova metadata API. +# Defaults to '8775' +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::compute::api( + $ks_keystone_internal_host = '127.0.0.1', + $ks_keystone_internal_proto = 'http', + $ks_nova_password = 'novapassword', + $neutron_metadata_proxy_shared_secret = 'metadatapassword', + $api_eth = '127.0.0.1', + $ks_nova_public_port = '8774', + $ks_ec2_public_port = '8773', + $ks_metadata_public_port = '8775', + $firewall_settings = {}, +){ + + include 'cloud::compute' + + class { 'nova::api': + enabled => true, + auth_host => $ks_keystone_internal_host, + auth_protocol => $ks_keystone_internal_proto, + admin_password => $ks_nova_password, + api_bind_address => $api_eth, + metadata_listen => $api_eth, + neutron_metadata_proxy_shared_secret => $neutron_metadata_proxy_shared_secret, + osapi_v3 => true, + } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow nova-api access': + port => $ks_nova_public_port, + extras => $firewall_settings, + } + cloud::firewall::rule{ '100 allow nova-metadata access': + port => $ks_metadata_public_port, + extras => $firewall_settings, + } + cloud::firewall::rule{ '100 allow nova-ec2 access': + port => $ks_ec2_public_port, + extras => $firewall_settings, + } + } + + @@haproxy::balancermember{"${::fqdn}-compute_api_ec2": + listening_service => 'ec2_api_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => $ks_ec2_public_port, + options => 'check inter 2000 rise 2 fall 5' + } + + @@haproxy::balancermember{"${::fqdn}-compute_api_nova": + listening_service => 'nova_api_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => $ks_nova_public_port, + options => 'check inter 2000 rise 2 fall 5' + } + + @@haproxy::balancermember{"${::fqdn}-compute_api_metadata": + listening_service => 'metadata_api_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => $ks_metadata_public_port, + options => 'check inter 2000 rise 2 fall 5' + } +} diff --git a/cloud/manifests/compute/cert.pp b/cloud/manifests/compute/cert.pp new file mode 100644 index 000000000..e9770fc7b --- /dev/null +++ b/cloud/manifests/compute/cert.pp @@ -0,0 +1,26 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Compute Certificate node +# +class cloud::compute::cert { + + include 'cloud::compute' + + class { 'nova::cert': + enabled => true, + } + +} diff --git a/cloud/manifests/compute/conductor.pp b/cloud/manifests/compute/conductor.pp new file mode 100644 index 000000000..4ac4031df --- /dev/null +++ b/cloud/manifests/compute/conductor.pp @@ -0,0 +1,26 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Compute Conductor node +# +class cloud::compute::conductor { + + include 'cloud::compute' + + class { 'nova::conductor': + enabled => true, + } + +} diff --git a/cloud/manifests/compute/consoleauth.pp b/cloud/manifests/compute/consoleauth.pp new file mode 100644 index 000000000..4e2825980 --- /dev/null +++ b/cloud/manifests/compute/consoleauth.pp @@ -0,0 +1,26 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Compute Authenfication Console node +# +class cloud::compute::consoleauth { + + include 'cloud::compute' + + class { 'nova::consoleauth': + enabled => true, + } + +} diff --git a/cloud/manifests/compute/consoleproxy.pp b/cloud/manifests/compute/consoleproxy.pp new file mode 100644 index 000000000..30cda4db2 --- /dev/null +++ b/cloud/manifests/compute/consoleproxy.pp @@ -0,0 +1,62 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::compute::consoleproxy +# +# Compute Proxy Console node +# +# === Parameters: +# +# [*api_eth*] +# (optional) Hostname or IP to bind Nova spicehtmlproxy service. +# Defaults to '127.0.0.1' +# +# [*spice_port*] +# (optional) TCP port to bind Nova spicehtmlproxy service. +# Defaults to '6082' +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::compute::consoleproxy( + $api_eth = '127.0.0.1', + $spice_port = '6082', + $firewall_settings = {}, +){ + + include 'cloud::compute' + + class { 'nova::spicehtml5proxy': + enabled => true, + host => $api_eth + } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow spice access': + port => $spice_port, + extras => $firewall_settings, + } + } + + @@haproxy::balancermember{"${::fqdn}-compute_spice": + listening_service => 'spice_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => $spice_port, + options => 'check inter 2000 rise 2 fall 5' + } +} diff --git a/cloud/manifests/compute/hypervisor.pp b/cloud/manifests/compute/hypervisor.pp new file mode 100644 index 000000000..d37c4d7c4 --- /dev/null +++ b/cloud/manifests/compute/hypervisor.pp @@ -0,0 +1,388 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::compute::hypervisor +# +# Hypervisor Compute node +# +# === Parameters: +# +# [*server_proxyclient_address*] +# (optional) Hostname or IP used to connect to Spice service. +# Defaults to '127.0.0.1' +# +# [*libvirt_type*] +# (optional) Libvirt domain type. Options are: kvm, lxc, qemu, uml, xen +# Replaces libvirt_type +# Defaults to 'kvm' +# +# [*ks_nova_public_proto*] +# (optional) Protocol used to connect to API. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_nova_public_host*] +# (optional) Public Hostname or IP to connect to Nova API +# Defaults to '127.0.0.1' +# +# [*nova_ssh_public_key*] +# (optional) Install public key in .ssh/authorized_keys for the 'nova' user. +# Note: this parameter use the 'content' provider of Puppet, in consequence +# you must provide the entire ssh public key in this parameter. +# Defaults to undef +# +# [*nova_ssh_private_key*] +# (optional) Install private key into .ssh/id_rsa. +# Note: this parameter use the 'content' provider of Puppet, in consequence +# you must provide the entire ssh privatekey in this parameter. +# Defaults to undef +# +# [*spice_port*] +# (optional) TCP port to connect to Nova spicehtmlproxy service. +# Defaults to '6082' +# +# [*cinder_rbd_user*] +# (optional) The RADOS client name for accessing rbd volumes. +# Defaults to 'cinder' +# +# [*nova_rbd_pool*] +# (optional) The RADOS pool in which rbd volumes are stored. +# Defaults to 'vms' +# +# [*nova_rbd_secret_uuid*] +# (optional) The libvirt uuid of the secret for the cinder_rbd_user. +# Defaults to undef +# +# [*vm_rbd*] +# (optional) Enable or not ceph capabilities on compute node to store +# nova instances on ceph storage. +# Default to false. +# +# [*volume_rbd*] +# (optional) Enable or not ceph capabilities on compute node to attach +# cinder volumes backend by ceph on nova instances. +# Default to false. +# +# [*manage_tso*] +# (optional) Allow to manage or not TSO issue. +# Default to true. +# +# [*nfs_enabled*] +# (optional) Store (or not) instances on a NFS share. +# Defaults to false +# +# [*nfs_device*] +# (optional) NFS device to mount +# Example: 'nfs.example.com:/vol1' +# Required when nfs_enabled is at true. +# Defaults to false +# +# [*nfs_options*] +# (optional) NFS mount options +# Example: 'nfsvers=3,noacl' +# Defaults to 'defaults' +# +# [*filesystem_store_datadir*] +# (optional) Full path of data directory to store the instances. +# Don't modify this parameter if you don't know what you do. +# You may have side effects (SElinux for example). +# Defaults to '/var/lib/nova/instances' +# +# [*nova_shell*] +# (optional) Full path of shell to run for nova user. +# To disable live migration & resize, set it to '/bin/nologin' or false. +# Otherwise, set the value to '/bin/bash'. +# Need to be a valid shell path. +# Defaults to false +# +# [*ks_spice_public_proto*] +# (optional) Protocol used to connect to Spice service. +# Defaults to false (use nova_public_proto) +# +# [*ks_spice_public_host*] +# (optional) Hostname or IP used to connect to Spice service. +# Defaults to false (use nova_public_host) +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::compute::hypervisor( + $server_proxyclient_address = '127.0.0.1', + $libvirt_type = 'kvm', + $ks_nova_public_proto = 'http', + $ks_nova_public_host = '127.0.0.1', + $nova_ssh_private_key = undef, + $nova_ssh_public_key = undef, + $spice_port = 6082, + $cinder_rbd_user = 'cinder', + $nova_rbd_pool = 'vms', + $nova_rbd_secret_uuid = undef, + $vm_rbd = false, + $volume_rbd = false, + $manage_tso = true, + $nova_shell = false, + $firewall_settings = {}, + # when using NFS storage backend + $nfs_enabled = false, + $nfs_device = false, + $nfs_options = 'defaults', + $filesystem_store_datadir = '/var/lib/nova/instances', + $ks_spice_public_proto = 'http', + $ks_spice_public_host = '127.0.0.1', +) inherits cloud::params { + + include 'cloud::compute' + include 'cloud::params' + include 'cloud::telemetry' + include 'cloud::network' + include 'cloud::network::vswitch' + + if $libvirt_type == 'kvm' and ! $::vtx { + fail('libvirt_type is set to KVM and VTX seems to be disabled on this node.') + } + + if $nfs_enabled { + if ! $vm_rbd { + # There is no NFS backend in Nova. + # We mount the NFS share in filesystem_store_datadir to fake the + # backend. + if $nfs_device { + nova_config { 'DEFAULT/instances_path': value => $filesystem_store_datadir; } + $nfs_mount = { + "${filesystem_store_datadir}" => { + 'ensure' => 'mounted', + 'fstype' => 'nfs', + 'device' => $nfs_device, + 'options' => $nfs_options + } + } + ensure_resource('class', 'nfs', {}) + create_resources('types::mount', $nfs_mount) + + # Not using /var/lib/nova/instances may cause side effects. + if $filesystem_store_datadir != '/var/lib/nova/instances' { + warning('filesystem_store_datadir is not /var/lib/nova/instances so you may have side effects (SElinux, etc)') + } + } else { + fail('When running NFS backend, you need to provide nfs_device parameter.') + } + } else { + fail('When running NFS backend, vm_rbd parameter cannot be set to true.') + } + } + + file{ '/var/lib/nova/.ssh': + ensure => directory, + mode => '0700', + owner => 'nova', + group => 'nova', + require => Class['nova'] + } -> + file{ '/var/lib/nova/.ssh/id_rsa': + ensure => present, + mode => '0600', + owner => 'nova', + group => 'nova', + content => $nova_ssh_private_key + } -> + file{ '/var/lib/nova/.ssh/authorized_keys': + ensure => present, + mode => '0600', + owner => 'nova', + group => 'nova', + content => $nova_ssh_public_key + } -> + file{ '/var/lib/nova/.ssh/config': + ensure => present, + mode => '0600', + owner => 'nova', + group => 'nova', + content => " +Host * + StrictHostKeyChecking no +" + } + + if $nova_shell { + ensure_resource ('user', 'nova', { + 'ensure' => 'present', + 'system' => true, + 'home' => '/var/lib/nova', + 'managehome' => false, + 'shell' => $nova_shell, + }) + } + + class { 'nova::compute': + enabled => true, + vnc_enabled => false, + #TODO(EmilienM) Bug #1259545 currently WIP: + virtio_nic => false, + neutron_enabled => true + } + + class { 'nova::compute::spice': + server_listen => '0.0.0.0', + server_proxyclient_address => $server_proxyclient_address, + proxy_host => $ks_spice_public_host, + proxy_protocol => $ks_spice_public_proto, + proxy_port => $spice_port + + } + + if $::osfamily == 'RedHat' { + file { '/etc/libvirt/qemu.conf': + ensure => file, + source => 'puppet:///modules/cloud/qemu/qemu.conf', + owner => root, + group => root, + mode => '0644', + notify => Service['libvirtd'] + } + if $vm_rbd and ($::operatingsystemmajrelease < 7) { + fail("RBD image backend in Nova is not supported in RHEL ${::operatingsystemmajrelease}.") + } + } + + # Disabling TSO/GSO/GRO + if $manage_tso { + if $::osfamily == 'Debian' { + ensure_resource ('exec','enable-tso-script', { + 'command' => '/usr/sbin/update-rc.d disable-tso defaults', + 'unless' => '/bin/ls /etc/rc*.d | /bin/grep disable-tso', + 'onlyif' => '/usr/bin/test -f /etc/init.d/disable-tso' + }) + } elsif $::osfamily == 'RedHat' { + ensure_resource ('exec','enable-tso-script', { + 'command' => '/usr/sbin/chkconfig disable-tso on', + 'unless' => '/bin/ls /etc/rc*.d | /bin/grep disable-tso', + 'onlyif' => '/usr/bin/test -f /etc/init.d/disable-tso' + }) + } + ensure_resource ('exec','start-tso-script', { + 'command' => '/etc/init.d/disable-tso start', + 'unless' => '/usr/bin/test -f /var/run/disable-tso.pid', + 'onlyif' => '/usr/bin/test -f /etc/init.d/disable-tso' + }) + } + + if $::operatingsystem == 'Ubuntu' { + service { 'dbus': + ensure => running, + enable => true, + before => Class['nova::compute::libvirt'], + } + } + + Service<| title == 'dbus' |> { enable => true } + + Service<| title == 'libvirt-bin' |> { enable => true } + + class { 'nova::compute::neutron': } + + if $vm_rbd or $volume_rbd { + + include 'cloud::storage::rbd' + + $libvirt_disk_cachemodes_real = ['network=writeback'] + + # when nova uses ceph for instances storage + if $vm_rbd { + class { 'nova::compute::rbd': + libvirt_rbd_user => $cinder_rbd_user, + libvirt_images_rbd_pool => $nova_rbd_pool + } + } else { + # when nova only needs to attach ceph volumes to instances + nova_config { + 'libvirt/rbd_user': value => $cinder_rbd_user; + } + } + # we don't want puppet-nova manages keyring + nova_config { + 'libvirt/rbd_secret_uuid': value => $nova_rbd_secret_uuid; + } + + File <<| tag == 'ceph_compute_secret_file' |>> + Exec <<| tag == 'get_or_set_virsh_secret' |>> + + # After setting virsh key, we need to restart nova-compute + # otherwise nova will fail to connect to RADOS. + Exec <<| tag == 'set_secret_value_virsh' |>> ~> Service['nova-compute'] + + # If Cinder & Nova reside on the same node, we need a group + # where nova & cinder users have read permissions. + ensure_resource('group', 'cephkeyring', { + ensure => 'present' + }) + + ensure_resource ('exec','add-nova-to-group', { + 'command' => 'usermod -a -G cephkeyring nova', + 'path' => ['/usr/sbin', '/usr/bin', '/bin', '/sbin'], + 'unless' => 'groups nova | grep cephkeyring' + }) + + # Configure Ceph keyring + Ceph::Key <<| title == $cinder_rbd_user |>> + if defined(Ceph::Key[$cinder_rbd_user]) { + ensure_resource( + 'file', + "/etc/ceph/ceph.client.${cinder_rbd_user}.keyring", { + owner => 'root', + group => 'cephkeyring', + mode => '0440', + require => Ceph::Key[$cinder_rbd_user], + notify => Service['nova-compute'], + } + ) + } + + Concat::Fragment <<| title == 'ceph-client-os' |>> + } else { + $libvirt_disk_cachemodes_real = [] + } + + class { 'nova::compute::libvirt': + libvirt_type => $libvirt_type, + # Needed to support migration but we still use Spice: + vncserver_listen => '0.0.0.0', + migration_support => true, + libvirt_disk_cachemodes => $libvirt_disk_cachemodes_real, + libvirt_service_name => $::cloud::params::libvirt_service_name, + } + + # Extra config for nova-compute + nova_config { + 'libvirt/inject_key': value => false; + 'libvirt/inject_partition': value => '-2'; + 'libvirt/live_migration_flag': value => 'VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_PERSIST_DEST'; + 'libvirt/block_migration_flag': value => 'VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_NON_SHARED_INC'; + } + + class { 'ceilometer::agent::compute': } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow instances console access': + port => '5900-5999', + extras => $firewall_settings, + } + cloud::firewall::rule{ '100 allow instances migration access': + port => ['16509', '49152-49215'], + extras => $firewall_settings, + } + } + +} diff --git a/cloud/manifests/compute/scheduler.pp b/cloud/manifests/compute/scheduler.pp new file mode 100644 index 000000000..390623aa2 --- /dev/null +++ b/cloud/manifests/compute/scheduler.pp @@ -0,0 +1,36 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Compute Scheduler node +# +# [*scheduler_default_filters*] +# (optional) A comma separated list of filters to be used by default +# Defaults to false +# +class cloud::compute::scheduler( + $scheduler_default_filters = false +){ + + include 'cloud::compute' + + class { 'nova::scheduler': + enabled => true, + } + + class { 'nova::scheduler::filter': + scheduler_default_filters => $scheduler_default_filters, + } + +} diff --git a/cloud/manifests/dashboard.pp b/cloud/manifests/dashboard.pp new file mode 100644 index 000000000..d455f7866 --- /dev/null +++ b/cloud/manifests/dashboard.pp @@ -0,0 +1,217 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::dashboard +# +# Installs the OpenStack Dashboard (Horizon) +# +# === Parameters: +# +# [*ks_keystone_internal_host*] +# (optional) Internal address for endpoint. +# Defaults to '127.0.0.1' +# +# [*secret_key*] +# (optional) Secret key. This is used by Django to provide cryptographic +# signing, and should be set to a unique, unpredictable value. +# Defaults to 'secrete' +# +# [*horizon_port*] +# (optional) Port used to connect to OpenStack Dashboard +# Defaults to '80' +# +# [*horizon_ssl_port*] +# (optional) Port used to connect to OpenStack Dashboard using SSL +# Defaults to '443' +# +# [*api_eth*] +# (optional) Which interface we bind the Horizon server. +# Defaults to '127.0.0.1' +# +# [*servername*] +# (optional) DNS name used to connect to Openstack Dashboard. +# Default value fqdn. +# +# [*listen_ssl*] +# (optional) Enable SSL on OpenStack Dashboard vhost +# It requires SSL files (keys and certificates) +# Defaults false +# +# [*keystone_proto*] +# (optional) Protocol (http or https) of keystone endpoint. +#  Defaults to 'http' +# +# [*keystone_host*] +# (optional) IP / Host of keystone endpoint. +#  Defaults '127.0.0.1' +# +# [*keystone_port*] +# (optional) TCP port of keystone endpoint. +#  Defaults to '5000' +# +# [*debug*] +# (optional) Enable debug or not. +#  Defaults to true +# +# [*horizon_cert*] +# (required with listen_ssl) Certificate to use for SSL support. +# +# [*horizon_key*] +# (required with listen_ssl) Private key to use for SSL support. +# +# [*horizon_ca*] +# (required with listen_ssl) CA certificate to use for SSL support. +# +# [*ssl_forward*] +# (optional) Forward HTTPS proto in the headers +# Useful when activating SSL binding on HAproxy and not in Horizon. +# Defaults to false +# +# [*os_endpoint_type*] +# (optional) endpoint type to use for the endpoints in the Keystone +# service catalog. Defaults to 'undef'. +# +# [*allowed_hosts*] +# (optional) List of hosts which will be set as value of ALLOWED_HOSTS +# parameter in settings_local.py. This is used by Django for +# security reasons. Can be set to * in environments where security is +# deemed unimportant. +# Defaults to ::fqdn. +# +# [*vhost_extra_params*] +# (optionnal) extra parameter to pass to the apache::vhost class +# Defaults to {} +# +# [*neutron_extra_options*] +# (optional) Enable optional services provided by neutron +# Useful when using cisco n1kv plugin, vpnaas or fwaas. +# Default to {} +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::dashboard( + $ks_keystone_internal_host = '127.0.0.1', + $secret_key = 'secrete', + $horizon_port = 80, + $horizon_ssl_port = 443, + $servername = $::fqdn, + $api_eth = '127.0.0.1', + $keystone_host = '127.0.0.1', + $keystone_proto = 'http', + $keystone_port = 5000, + $debug = true, + $listen_ssl = false, + $horizon_cert = undef, + $horizon_key = undef, + $horizon_ca = undef, + $ssl_forward = false, + $os_endpoint_type = undef, + $allowed_hosts = $::fqdn, + $vhost_extra_params = {}, + $neutron_extra_options = {}, + $firewall_settings = {}, +) { + + # We build the param needed for horizon class + $keystone_url = "${keystone_proto}://${keystone_host}:${keystone_port}/v2.0" + + # Apache2 specific configuration + if $ssl_forward { + $setenvif = ['X-Forwarded-Proto https HTTPS=1'] + } else { + $setenvif = [] + } + $extra_params = { + 'add_listen' => true, + 'setenvif' => $setenvif + } + $vhost_extra_params_real = merge ($extra_params, $vhost_extra_params) + + $neutron_options = { + 'enable_lb' => true + } + $neutron_options_real = merge ($neutron_options, $neutron_extra_options) + + ensure_resource('class', 'apache', { + default_vhost => false + }) + + class { 'horizon': + secret_key => $secret_key, + can_set_mount_point => 'False', + servername => $servername, + bind_address => $api_eth, + swift => true, + keystone_url => $keystone_url, + cache_server_ip => false, + django_debug => $debug, + neutron_options => $neutron_options_real, + listen_ssl => $listen_ssl, + horizon_cert => $horizon_cert, + horizon_key => $horizon_key, + horizon_ca => $horizon_ca, + vhost_extra_params => $vhost_extra_params_real, + openstack_endpoint_type => $os_endpoint_type, + allowed_hosts => $allowed_hosts, + } + + if ($::osfamily == 'Debian') { + # TODO(Goneri): HACK to ensure Horizon can cache its files + $horizon_var_dir = ['/var/lib/openstack-dashboard/static/js','/var/lib/openstack-dashboard/static/css'] + file {$horizon_var_dir: + ensure => directory, + owner => 'horizon', + group => 'horizon', + } + } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow horizon access': + port => $horizon_port, + extras => $firewall_settings, + } + } + + @@haproxy::balancermember{"${::fqdn}-horizon": + listening_service => 'horizon_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => $horizon_port, + options => "check inter 2000 rise 2 fall 5 cookie ${::hostname}" + } + + if $listen_ssl { + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow horizon ssl access': + port => $horizon_ssl_port, + extras => $firewall_settings, + } + } + + @@haproxy::balancermember{"${::fqdn}-horizon-ssl": + listening_service => 'horizon_ssl_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => $horizon_ssl_port, + options => "check inter 2000 rise 2 fall 5 cookie ${::hostname}" + } + + } + +} diff --git a/cloud/manifests/database/dbaas.pp b/cloud/manifests/database/dbaas.pp new file mode 100644 index 000000000..2c1ee47be --- /dev/null +++ b/cloud/manifests/database/dbaas.pp @@ -0,0 +1,86 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::database::dbaas +# +# Common class to install OpenStack Database as a Service (Trove) +# +# === Parameters: +# +# [*trove_db_host*] +# (optional) Hostname or IP address to connect to trove database +# Defaults to '127.0.0.1' +# +# [*trove_db_user*] +# (optional) Username to connect to trove database +# Defaults to 'trove' +# +# [*trove_db_password*] +# (optional) Password to connect to trove database +# Defaults to 'trovepassword' +# +# [*rabbit_hosts*] +# (optional) List of RabbitMQ servers. Should be an array. +# Defaults to ['127.0.0.1:5672'] +# +# [*rabbit_password*] +# (optional) Password to connect to nova queues. +# Defaults to 'rabbitpassword' +# +# [*nova_admin_username*] +# (optional) Trove username used to connect to nova. +# Defaults to 'trove' +# +# [*nova_admin_password*] +# (optional) Trove password used to connect to nova. +# Defaults to 'trovepassword' +# +# [*nova_admin_tenant_name*] +# (optional) Trove tenant name used to connect to nova. +# Defaults to 'services' +# +class cloud::database::dbaas( + $trove_db_host = '127.0.0.1', + $trove_db_user = 'trove', + $trove_db_password = 'trovepassword', + $rabbit_hosts = ['127.0.0.1:5672'], + $rabbit_password = 'rabbitpassword', + $nova_admin_username = 'trove', + $nova_admin_tenant_name = 'services', + $nova_admin_password = 'trovepassword', +) { + + $encoded_user = uriescape($trove_db_user) + $encoded_password = uriescape($trove_db_password) + + class { 'trove': + database_connection => "mysql://${encoded_user}:${encoded_password}@${trove_db_host}/trove?charset=utf8", + mysql_module => '2.2', + rabbit_hosts => $rabbit_hosts, + rabbit_password => $rabbit_password, + rabbit_userid => 'trove', + nova_proxy_admin_pass => $nova_admin_password, + nova_proxy_admin_user => $nova_admin_username, + nova_proxy_admin_tenant_name => $nova_admin_tenant_name + } + + exec {'trove_db_sync': + command => 'trove-manage db_sync', + user => 'trove', + path => '/usr/bin', + unless => "/usr/bin/mysql trove -h ${trove_db_host} -u ${encoded_user} -p${encoded_password} -e \"show tables\" | /bin/grep Tables" + } + +} diff --git a/cloud/manifests/database/dbaas/api.pp b/cloud/manifests/database/dbaas/api.pp new file mode 100644 index 000000000..1c7b4b113 --- /dev/null +++ b/cloud/manifests/database/dbaas/api.pp @@ -0,0 +1,104 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::database::dbaas::api +# +# Class to install API service of OpenStack Database as a Service (Trove) +# +# === Parameters: +# +# [*ks_trove_password*] +# (required) Password used by trove for Keystone authentication. +# Default: 'trovepassword' +# +# [*verbose*] +# (optional) Rather to log the trove api service at verbose level. +# Default: true +# +# [*debug*] +# (optional) Rather to log the trove api service at debug level. +# Default: true +# +# [*use_syslog*] +# (optional) Use syslog for logging. +# Defaults to true +# +# [*api_eth*] +# (optional) Hostname or IP to bind Trove API. +# Defaults to '127.0.0.1' +# +# [*ks_trove_public_port*] +# (optional) TCP public port used to connect to Trove API. +# Defaults to '8779' +# +# [*ks_keystone_internal_host*] +# (optional) Internal Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_internal_port*] +# (optional) TCP internal port used to connect to Keystone API. +# Defaults to '5000' +# +# [*ks_keystone_internal_proto*] +# (optional) Protocol used to connect to Keystone API. +# Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::database::dbaas::api( + $ks_trove_password = 'trovepassword', + $verbose = true, + $debug = true, + $use_syslog = true, + $api_eth = '127.0.0.1', + $ks_trove_public_port = '8779', + $ks_keystone_internal_host = '127.0.0.1', + $ks_keystone_internal_port = '5000', + $ks_keystone_internal_proto = 'http', + $firewall_settings = {}, +) { + + include 'cloud::database::dbaas' + + class { 'trove::api': + verbose => $verbose, + debug => $debug, + use_syslog => $use_syslog, + bind_host => $api_eth, + bind_port => $ks_trove_public_port, + auth_url => "${ks_keystone_internal_proto}://${ks_keystone_internal_host}:${ks_keystone_internal_port}/v2.0", + keystone_password => $ks_trove_password, + } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow trove-api access': + port => $ks_trove_public_port, + extras => $firewall_settings, + } + } + + @@haproxy::balancermember{"${::fqdn}-trove_api": + listening_service => 'trove_api_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => $ks_trove_public_port, + options => 'check inter 2000 rise 2 fall 5' + } + +} diff --git a/cloud/manifests/database/dbaas/conductor.pp b/cloud/manifests/database/dbaas/conductor.pp new file mode 100644 index 000000000..6787b0ba1 --- /dev/null +++ b/cloud/manifests/database/dbaas/conductor.pp @@ -0,0 +1,65 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::database::dbaas::conductor +# +# Class to install Conductor service of OpenStack Database as a Service (Trove) +# +# === Parameters: +# +# [*ks_keystone_internal_host*] +# (optional) Internal Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_internal_port*] +# (optional) TCP internal port used to connect to Keystone API. +# Defaults to '5000' +# +# [*ks_keystone_internal_proto*] +# (optional) Protocol used to connect to Keystone API. +# Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*verbose*] +# (optional) Rather to log the trove api service at verbose level. +# Default: true +# +# [*debug*] +# (optional) Rather to log the trove api service at debug level. +# Default: true +# +# [*use_syslog*] +# (optional) Use syslog for logging. +# Defaults to true +# +class cloud::database::dbaas::conductor( + $ks_keystone_internal_host = '127.0.0.1', + $ks_keystone_internal_port = '5000', + $ks_keystone_internal_proto = 'http', + $verbose = true, + $debug = true, + $use_syslog = true, +) { + + include 'cloud::database::dbaas' + + class { 'trove::conductor': + auth_url => "${ks_keystone_internal_proto}://${ks_keystone_internal_host}:${ks_keystone_internal_port}/v2.0", + debug => $debug, + verbose => $verbose, + use_syslog => $use_syslog + } + +} diff --git a/cloud/manifests/database/dbaas/taskmanager.pp b/cloud/manifests/database/dbaas/taskmanager.pp new file mode 100644 index 000000000..47b727850 --- /dev/null +++ b/cloud/manifests/database/dbaas/taskmanager.pp @@ -0,0 +1,65 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::database::dbaas::taskmanager +# +# Class to install Taskmanager service of OpenStack Database as a Service (Trove) +# +# === Parameters: +# +# [*ks_keystone_internal_host*] +# (optional) Internal Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_internal_port*] +# (optional) TCP internal port used to connect to Keystone API. +# Defaults to '5000' +# +# [*ks_keystone_internal_proto*] +# (optional) Protocol used to connect to Keystone API. +# Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*verbose*] +# (optional) Rather to log the trove api service at verbose level. +# Default: true +# +# [*debug*] +# (optional) Rather to log the trove api service at debug level. +# Default: true +# +# [*use_syslog*] +# (optional) Use syslog for logging. +# Defaults to true +# +class cloud::database::dbaas::taskmanager( + $ks_keystone_internal_host = '127.0.0.1', + $ks_keystone_internal_port = '5000', + $ks_keystone_internal_proto = 'http', + $debug = true, + $verbose = true, + $use_syslog = true +) { + + include 'cloud::database::dbaas' + + class { 'trove::taskmanager': + auth_url => "${ks_keystone_internal_proto}://${ks_keystone_internal_host}:${ks_keystone_internal_port}/v2.0", + debug => $debug, + verbose => $verbose, + use_syslog => $use_syslog + } + +} diff --git a/cloud/manifests/database/nosql.pp b/cloud/manifests/database/nosql.pp new file mode 100644 index 000000000..75987a399 --- /dev/null +++ b/cloud/manifests/database/nosql.pp @@ -0,0 +1,99 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::database::nosql +# +# Install a nosql server (MongoDB) +# +# === Parameters: +# +# [*bind_ip*] +# (optional) IP address on which mongod instance should listen +# Defaults to '127.0.0.1' +# +# [*nojournal*] +# (optional) Disable mongodb internal cache. This is not recommended for +# production but results in a much faster boot process. +# http://docs.mongodb.org/manual/reference/configuration-options/#nojournal +# Defaults to false +# +# [*replset_members*] +# (optional) Ceilometer Replica set members hostnames +# Should be an array. Example: ['node1', 'node2', node3'] +# If set to false, the setup won't be HA and no replicaset will be created. +# Defaults to hostname +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::database::nosql( + $bind_ip = '127.0.0.1', + $nojournal = false, + $replset_members = $::hostname, + $firewall_settings = {}, +) { + + # should be an array + $array_bind_ip = any2array($bind_ip) + $array_replset_members = any2array($replset_members) + + # Red Hat & CentOS use packages from RHCL or EPEL to support systemd + # so manage_package_repo should be at false regarding to mongodb module + if $::osfamily == 'RedHat' { + $manage_package_repo = false + } else { + # Debian & Ubuntu are picked from mongodb repo to get recent version + $manage_package_repo = true + } + + class { 'mongodb::globals': + manage_package_repo => $manage_package_repo + }-> + class { 'mongodb': + bind_ip => $array_bind_ip, + nojournal => $nojournal, + replset => 'ceilometer', + logpath => '/var/log/mongodb/mongod.log', + } + + exec {'check_mongodb' : + command => "/usr/bin/mongo ${bind_ip}:27017", + logoutput => false, + tries => 60, + try_sleep => 5, + require => Service['mongodb'], + } + + if $replset_members { + mongodb_replset{'ceilometer': + members => $array_replset_members, + before => Anchor['mongodb setup done'], + } + } + + anchor {'mongodb setup done' : + require => Exec['check_mongodb'], + } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow mongodb access': + port => '27017', + extras => $firewall_settings, + } + } + +} diff --git a/cloud/manifests/database/sql.pp b/cloud/manifests/database/sql.pp new file mode 100644 index 000000000..9ed767160 --- /dev/null +++ b/cloud/manifests/database/sql.pp @@ -0,0 +1,536 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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 optional 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. +# +# MySQL Galera Node +# +# === Parameters +# +# [*api_eth*] +# (optional) Hostname or IP to bind MySQL daemon. +# Defaults to '127.0.0.1' +# +# [*galera_master_name*] +# (optional) Hostname or IP of the Galera master node, databases and users +# resources are created on this node and propagated on the cluster. +# Defaults to 'mgmt001' +# +# [*galera_internal_ips*] +# (optional) Array of internal ip of the galera nodes. +# Defaults to ['127.0.0.1'] +# +# [*galera_gcache*] +# (optional) Size of the Galera gcache +# wsrep_provider_options, for master/slave mode +# Defaults to '1G' +# +# [*keystone_db_host*] +# (optional) Host where user should be allowed all privileges for database. +# Defaults to 127.0.0.1 +# +# [*keystone_db_user*] +# (optional) Name of keystone DB user. +# Defaults to trove +# +# [*keystone_db_password*] +# (optional) Password that will be used for the Keystone db user. +# Defaults to 'keystonepassword' +# +# [*keystone_db_allowed_hosts*] +# (optional) Hosts allowed to use the database +# Defaults to ['127.0.0.1'] +# +# [*cinder_db_host*] +# (optional) Host where user should be allowed all privileges for database. +# Defaults to 127.0.0.1 +# +# [*cinder_db_user*] +# (optional) Name of cinder DB user. +# Defaults to trove +# +# [*cinder_db_password*] +# (optional) Password that will be used for the cinder db user. +# Defaults to 'cinderpassword' +# +# [*cinder_db_allowed_hosts*] +# (optional) Hosts allowed to use the database +# Defaults to ['127.0.0.1'] +# +# [*glance_db_host*] +# (optional) Host where user should be allowed all privileges for database. +# Defaults to 127.0.0.1 +# +# [*glance_db_user*] +# (optional) Name of glance DB user. +# Defaults to trove +# +# [*glance_db_password*] +# (optional) Password that will be used for the glance db user. +# Defaults to 'glancepassword' +# +# [*glance_db_allowed_hosts*] +# (optional) Hosts allowed to use the database +# Defaults to ['127.0.0.1'] +# +# [*heat_db_host*] +# (optional) Host where user should be allowed all privileges for database. +# Defaults to 127.0.0.1 +# +# [*heat_db_user*] +# (optional) Name of heat DB user. +# Defaults to trove +# +# [*heat_db_password*] +# (optional) Password that will be used for the heat db user. +# Defaults to 'heatpassword' +# +# [*heat_db_allowed_hosts*] +# (optional) Hosts allowed to use the database +# Defaults to ['127.0.0.1'] +# +# [*nova_db_host*] +# (optional) Host where user should be allowed all privileges for database. +# Defaults to 127.0.0.1 +# +# [*nova_db_user*] +# (optional) Name of nova DB user. +# Defaults to trove +# +# [*nova_db_password*] +# (optional) Password that will be used for the nova db user. +# Defaults to 'novapassword' +# +# [*nova_db_allowed_hosts*] +# (optional) Hosts allowed to use the database +# Defaults to ['127.0.0.1'] +# +# [*neutron_db_host*] +# (optional) Host where user should be allowed all privileges for database. +# Defaults to 127.0.0.1 +# +# [*neutron_db_user*] +# (optional) Name of neutron DB user. +# Defaults to trove +# +# [*neutron_db_password*] +# (optional) Password that will be used for the neutron db user. +# Defaults to 'neutronpassword' +# +# [*neutron_db_allowed_hosts*] +# (optional) Hosts allowed to use the database +# Defaults to ['127.0.0.1'] +# +# [*trove_db_host*] +# (optional) Host where user should be allowed all privileges for database. +# Defaults to 127.0.0.1 +# +# [*trove_db_user*] +# (optional) Name of trove DB user. +# Defaults to trove +# +# [*trove_db_password*] +# (optional) Password that will be used for the trove db user. +# Defaults to 'trovepassword' +# +# [*trove_db_allowed_hosts*] +# (optional) Hosts allowed to use the database +# Defaults to ['127.0.0.1'] +# +# [*mysql_root_password*] +# (optional) The MySQL root password. +# Puppet will attempt to set the root password and update `/root/.my.cnf` with it. +# Defaults to 'rootpassword' +# +# [*mysql_sys_maint_password*] +# (optional) The MySQL debian-sys-maint password. +# Debian only parameter. +# Defaults to 'sys_maint' +# +# [*galera_clustercheck_dbuser*] +# (optional) The MySQL username for Galera cluster check (using monitoring database) +# Defaults to 'clustercheckdbuser' +# +# [*galera_clustercheck_dbpassword*] +# (optional) The MySQL password for Galera cluster check +# Defaults to 'clustercheckpassword' +# +# [*galera_clustercheck_ipaddress*] +# (optional) The name or ip address of host running monitoring database (clustercheck) +# Defaults to '127.0.0.1' +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +# ==== Deprecated parameters: +# +# [*service_provider*] +# Previously used to choose between sysv and systemd, yes suppressed +# because this subject is potentially a troll :-D +# Defaults to 'sysv' +# +class cloud::database::sql ( + $api_eth = '127.0.0.1', + $galera_master_name = 'mgmt001', + $galera_internal_ips = ['127.0.0.1'], + $galera_gcache = '1G', + $keystone_db_host = '127.0.0.1', + $keystone_db_user = 'keystone', + $keystone_db_password = 'keystonepassword', + $keystone_db_allowed_hosts = ['127.0.0.1'], + $cinder_db_host = '127.0.0.1', + $cinder_db_user = 'cinder', + $cinder_db_password = 'cinderpassword', + $cinder_db_allowed_hosts = ['127.0.0.1'], + $glance_db_host = '127.0.0.1', + $glance_db_user = 'glance', + $glance_db_password = 'glancepassword', + $glance_db_allowed_hosts = ['127.0.0.1'], + $heat_db_host = '127.0.0.1', + $heat_db_user = 'heat', + $heat_db_password = 'heatpassword', + $heat_db_allowed_hosts = ['127.0.0.1'], + $nova_db_host = '127.0.0.1', + $nova_db_user = 'nova', + $nova_db_password = 'novapassword', + $nova_db_allowed_hosts = ['127.0.0.1'], + $neutron_db_host = '127.0.0.1', + $neutron_db_user = 'neutron', + $neutron_db_password = 'neutronpassword', + $neutron_db_allowed_hosts = ['127.0.0.1'], + $trove_db_host = '127.0.0.1', + $trove_db_user = 'trove', + $trove_db_password = 'trovepassword', + $trove_db_allowed_hosts = ['127.0.0.1'], + $mysql_root_password = 'rootpassword', + $mysql_sys_maint_password = 'sys_maint', + $galera_clustercheck_dbuser = 'clustercheckdbuser', + $galera_clustercheck_dbpassword = 'clustercheckpassword', + $galera_clustercheck_ipaddress = '127.0.0.1', + $firewall_settings = {}, + # DEPRECATED PARAMETERS + $service_provider = 'sysv', +) { + + include 'xinetd' + + $gcomm_definition = inline_template('<%= @galera_internal_ips.join(",") + "?pc.wait_prim=no" -%>') + + # Specific to the Galera master node + if $::hostname == $galera_master_name { + + $mysql_root_password_real = $mysql_root_password + + # OpenStack DB + class { 'keystone::db::mysql': + mysql_module => '2.2', + dbname => 'keystone', + user => $keystone_db_user, + password => $keystone_db_password, + host => $keystone_db_host, + allowed_hosts => $keystone_db_allowed_hosts, + } + class { 'glance::db::mysql': + mysql_module => '2.2', + dbname => 'glance', + user => $glance_db_user, + password => $glance_db_password, + host => $glance_db_host, + allowed_hosts => $glance_db_allowed_hosts, + } + class { 'nova::db::mysql': + mysql_module => '2.2', + dbname => 'nova', + user => $nova_db_user, + password => $nova_db_password, + host => $nova_db_host, + allowed_hosts => $nova_db_allowed_hosts, + } + class { 'cinder::db::mysql': + mysql_module => '2.2', + dbname => 'cinder', + user => $cinder_db_user, + password => $cinder_db_password, + host => $cinder_db_host, + allowed_hosts => $cinder_db_allowed_hosts, + } + class { 'neutron::db::mysql': + mysql_module => '2.2', + dbname => 'neutron', + user => $neutron_db_user, + password => $neutron_db_password, + host => $neutron_db_host, + allowed_hosts => $neutron_db_allowed_hosts, + } + class { 'heat::db::mysql': + mysql_module => '2.2', + dbname => 'heat', + user => $heat_db_user, + password => $heat_db_password, + host => $heat_db_host, + allowed_hosts => $heat_db_allowed_hosts, + } + class { 'trove::db::mysql': + mysql_module => '2.2', + dbname => 'trove', + user => $trove_db_user, + password => $trove_db_password, + host => $trove_db_host, + allowed_hosts => $trove_db_allowed_hosts, + } + + # Monitoring DB + mysql_database { 'monitoring': + ensure => 'present', + charset => 'utf8', + collate => 'utf8_unicode_ci', + require => File['/root/.my.cnf'] + } + mysql_user { "${galera_clustercheck_dbuser}@localhost": + ensure => 'present', + # can not change password in clustercheck script + password_hash => mysql_password($galera_clustercheck_dbpassword), + require => File['/root/.my.cnf'] + } + mysql_grant { "${galera_clustercheck_dbuser}@localhost/monitoring": + ensure => 'present', + options => ['GRANT'], + privileges => ['ALL'], + table => 'monitoring.*', + user => "${galera_clustercheck_dbuser}@localhost", + } + + Database_user<<| |>> + } else { + # NOTE(sileht): Only the master must create the password + # into the database, slave nodes must just use the password. + # The one in the database have been retrieved via galera. + file { "${::root_home}/.my.cnf": + content => "[client]\nuser=root\nhost=localhost\npassword=${mysql_root_password}\n", + owner => 'root', + mode => '0600', + } + } + + # Specific to Red Hat or Debian systems: + case $::osfamily { + 'RedHat': { + # Specific to Red Hat + $mysql_server_package_name = 'mariadb-galera-server' + $mysql_client_package_name = 'mariadb' + $wsrep_provider = '/usr/lib64/galera/libgalera_smm.so' + $mysql_server_config_file = '/etc/my.cnf' + $mysql_init_file = '/usr/lib/systemd/system/mysql-bootstrap.service' + + if $::hostname == $galera_master_name { + $mysql_service_name = 'mysql-bootstrap' + } else { + $mysql_service_name = 'mariadb' + } + + $dirs = [ '/var/run/mysqld', '/var/log/mysql' ] + + file { $dirs: + ensure => directory, + mode => '0750', + before => Service['mysqld'], + owner => 'mysql' + } + + # In Red Hat, the package does not perform the mysql db installation. + # We need to do this manually. + # Note: in MariaDB repository, package perform this action in post-install, + # but MariaDB is not packaged for Red Hat / CentOS 7 in MariaDB repository. + exec { 'bootstrap-mysql': + command => '/usr/bin/mysql_install_db --rpm --user=mysql', + unless => 'test -d /var/lib/mysql/mysql', + before => Service['mysqld'], + require => [Package[$mysql_server_package_name], File[$mysql_server_config_file]] + } + + } # RedHat + 'Debian': { + # Specific to Debian / Ubuntu + $mysql_server_package_name = 'mariadb-galera-server' + $mysql_client_package_name = 'mariadb-client' + $wsrep_provider = '/usr/lib/galera/libgalera_smm.so' + $mysql_server_config_file = '/etc/mysql/my.cnf' + $mysql_init_file = '/etc/init.d/mysql-bootstrap' + + if $::hostname == $galera_master_name { + $mysql_service_name = 'mysql-bootstrap' + } else { + $mysql_service_name = 'mysql' + } + + mysql_user { 'debian-sys-maint@localhost': + ensure => 'present', + password_hash => mysql_password($mysql_sys_maint_password), + require => File['/root/.my.cnf'] + } + + file{'/etc/mysql/debian.cnf': + ensure => file, + content => template('cloud/database/debian.cnf.erb'), + owner => 'root', + group => 'root', + mode => '0600', + require => Exec['clean-mysql-binlog'], + } + } # Debian + default: { + err "${::osfamily} not supported yet" + } + } + + # This is due to this bug: https://bugs.launchpad.net/codership-mysql/+bug/1087368 + # The backport to API 23 requires a command line option --wsrep-new-cluster: + # http://bazaar.launchpad.net/~codership/codership-mysql/wsrep-5.5/revision/3844?start_revid=3844 + # and the mysql init script cannot have arguments passed to the daemon + # using /etc/default/mysql standart mechanism. + # To check that the mysqld support the options you can : + # strings `which mysqld` | grep wsrep-new-cluster + # TODO: to be remove as soon as the API 25 is packaged, ie galera 3 ... + file { $mysql_init_file : + content => template("cloud/database/etc_initd_mysql_${::osfamily}"), + owner => 'root', + mode => '0755', + group => 'root', + notify => Service['mysqld'], + before => Package[$mysql_server_package_name], + } + + if($::osfamily == 'Debian'){ + # The startup time can be longer than the default 30s so we take + # care of it there. Until this bug is not resolved + # https://mariadb.atlassian.net/browse/MDEV-5540, we have to do it + # the ugly way. + file_line { 'debian_increase_mysql_startup_time': + line => 'MYSQLD_STARTUP_TIMEOUT=120', + path => '/etc/init.d/mysql', + after => '^CONF=', + require => Package[$mysql_server_package_name], + notify => Service['mysqld'], + } + } + + class { 'mysql::server': + manage_config_file => false, + config_file => $mysql_server_config_file, + package_name => $mysql_server_package_name, + service_name => $mysql_service_name, + override_options => { + 'mysqld' => { + 'bind-address' => $api_eth + } + }, + root_password => $mysql_root_password_real, + notify => Service['xinetd'], + } + + file { $mysql_server_config_file: + content => template('cloud/database/mysql.conf.erb'), + mode => '0644', + owner => 'root', + group => 'root', + notify => [Service['mysqld'],Exec['clean-mysql-binlog']], + require => Package[$mysql_server_package_name], + } + + class { 'mysql::client': + package_name => $mysql_client_package_name, + } + + # Haproxy http monitoring + augeas { 'mysqlchk': + context => '/files/etc/services', + changes => [ + 'ins service-name after service-name[last()]', + 'set service-name[last()] "mysqlchk"', + 'set service-name[. = "mysqlchk"]/port 9200', + 'set service-name[. = "mysqlchk"]/protocol tcp', + ], + onlyif => 'match service-name[. = "mysqlchk"] size == 0', + notify => [ Service['xinetd'], Exec['reload_xinetd'] ] + } + + file { + '/etc/xinetd.d/mysqlchk': + content => template('cloud/database/mysqlchk.erb'), + owner => 'root', + group => 'root', + mode => '0755', + require => File['/usr/bin/clustercheck'], + notify => [ Service['xinetd'], Exec['reload_xinetd'] ]; + '/usr/bin/clustercheck': + ensure => present, + content => template('cloud/database/clustercheck.erb'), + mode => '0755', + owner => 'root', + group => 'root'; + } + + # The puppet-xinetd module do not correctly reload + # the configuration on “notify” + # TODO(Goneri): remove this once https://github.com/puppetlabs/puppetlabs-xinetd/pull/9 + # get merged + exec{ 'reload_xinetd': + command => '/usr/bin/pkill -F /var/run/xinetd.pid --signal HUP', + refreshonly => true, + require => Service['xinetd'], + } + + exec{'clean-mysql-binlog': + # first sync take a long time + command => "/bin/bash -c '/usr/bin/mysqladmin --defaults-file=/root/.my.cnf shutdown ; /bin/rm ${::mysql::params::datadir}/ib_logfile*'", + path => '/usr/bin', + notify => Service['mysqld'], + refreshonly => true, + onlyif => "stat ${::mysql::params::datadir}/ib_logfile0 && test `du -sh ${::mysql::params::datadir}/ib_logfile0 | cut -f1` != '256M'", + } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow galera access': + port => ['3306', '4567', '4568', '4444'], + extras => $firewall_settings, + } + cloud::firewall::rule{ '100 allow mysqlchk access': + port => '9200', + extras => $firewall_settings, + } + cloud::firewall::rule{ '100 allow mysql rsync access': + port => '873', + extras => $firewall_settings, + } + } + + @@haproxy::balancermember{$::fqdn: + listening_service => 'galera_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => '3306', + options => + inline_template('check inter 2000 rise 2 fall 5 port 9200 <% if @hostname != @galera_master_name -%>backup<% end %>') + } + + @@haproxy::balancermember{"${::fqdn}-readonly": + listening_service => 'galera_readonly_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => '3306', + options => + inline_template('check inter 2000 rise 2 fall 5 port 9200 <% if @hostname == @galera_master_name -%>backup<% end %>') + } +} diff --git a/cloud/manifests/firewall/post.pp b/cloud/manifests/firewall/post.pp new file mode 100644 index 000000000..33a80bbec --- /dev/null +++ b/cloud/manifests/firewall/post.pp @@ -0,0 +1,51 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::firewall::post +# +# Firewall rules during 'post' Puppet stage +# +# === Parameters: +# +# [*debug*] +# (optional) Set log output to debug output +# Defaults to false +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::firewall::post( + $debug = false, + $firewall_settings = {}, +){ + + if $debug { + warning('debug is enabled, the traffic is not blocked.') + } else { + firewall { '998 log all': + proto => 'all', + jump => 'LOG', + } + cloud::firewall::rule{ '999 drop all': + proto => 'all', + action => 'drop', + extras => $firewall_settings, + } + notice('At this stage, all network traffic is blocked.') + } + +} diff --git a/cloud/manifests/firewall/pre.pp b/cloud/manifests/firewall/pre.pp new file mode 100644 index 000000000..9d9b73f3e --- /dev/null +++ b/cloud/manifests/firewall/pre.pp @@ -0,0 +1,57 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::firewall::pre +# +# Firewall rules during 'pre' Puppet stage +# +# === Parameters: +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::firewall::pre( + $firewall_settings = {}, +){ + + # ensure the correct packages are installed + include firewall + + # defaults 'pre' rules + cloud::firewall::rule{ '000 accept related established rules': + proto => 'all', + state => ['RELATED', 'ESTABLISHED'], + extras => $firewall_settings, + } + + cloud::firewall::rule{ '001 accept all icmp': + proto => 'icmp', + extras => $firewall_settings, + } + + cloud::firewall::rule{ '002 accept all to lo interface': + proto => 'all', + iniface => 'lo', + extras => $firewall_settings, + } + + cloud::firewall::rule{ '003 accept ssh': + port => '22', + extras => $firewall_settings, + } + +} diff --git a/cloud/manifests/firewall/rule.pp b/cloud/manifests/firewall/rule.pp new file mode 100644 index 000000000..2dc97ef89 --- /dev/null +++ b/cloud/manifests/firewall/rule.pp @@ -0,0 +1,46 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Define:: +# +# cloud::firewall +# +define cloud::firewall::rule ( + $port = undef, + $proto = 'tcp', + $action = 'accept', + $state = ['NEW'], + $source = '0.0.0.0/0', + $iniface = undef, + $chain = 'INPUT', + $extras = {}, +) { + + $basic = { + 'port' => $port, + 'proto' => $proto, + 'action' => $action, + 'state' => $state, + 'source' => $source, + 'iniface' => $iniface, + 'chain' => $chain, + } + + $rule = merge($basic, $extras) + validate_hash($rule) + + create_resources('firewall', { "${title}" => $rule }) + +} diff --git a/cloud/manifests/identity.pp b/cloud/manifests/identity.pp new file mode 100644 index 000000000..cf078c813 --- /dev/null +++ b/cloud/manifests/identity.pp @@ -0,0 +1,746 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::identity +# +# Install Identity Server (Keystone) +# +# === Parameters: +# +# [*identity_roles_addons*] +# (optional) Extra keystone roles to create +# Defaults to ['SwiftOperator', 'ResellerAdmin'] +# +# [*keystone_db_host*] +# (optional) Hostname or IP address to connect to keystone database +# Defaults to '127.0.0.1' +# +# [*keystone_db_user*] +# (optional) Username to connect to keystone database +# Defaults to 'keystone' +# +# [*keystone_db_password*] +# (optional) Password to connect to keystone database +# Defaults to 'keystonepassword' +# +# [*memcache_servers*] +# (optionnal) Memcached servers used by Keystone. Should be an array. +# Defaults to ['127.0.0.1:11211'] +# +# [*ks_admin_email*] +# (optional) Email address of admin user in Keystone +# Defaults to 'no-reply@keystone.openstack' +# +# [*ks_admin_password*] +# (optional) Password of admin user in Keystone +# Defaults to 'adminpassword' +# +# [*ks_admin_tenant*] +# (optional) Admin tenant name in Keystone +# Defaults to 'admin' +# +# [*ks_admin_token*] +# (required) Admin token used by Keystone. +# +# [*ks_glance_internal_host*] +# (optional) Internal Hostname or IP to connect to Glance API +# Defaults to '127.0.0.1' +# +# [*ks_glance_admin_host*] +# (optional) Admin Hostname or IP to connect to Glance API +# Defaults to '127.0.0.1' +# +# [*ks_glance_public_host*] +# (optional) Public Hostname or IP to connect to Glance API +# Defaults to '127.0.0.1' +# +# [*ks_ceilometer_internal_host*] +# (optional) Internal Hostname or IP to connect to Ceilometer API +# Defaults to '127.0.0.1' +# +# [*ks_ceilometer_admin_host*] +# (optional) Admin Hostname or IP to connect to Ceilometer API +# Defaults to '127.0.0.1' +# +# [*ks_ceilometer_public_host*] +# (optional) Public Hostname or IP to connect to Ceilometer API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_internal_host*] +# (optional) Internal Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_admin_host*] +# (optional) Admin Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_public_host*] +# (optional) Public Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_nova_internal_host*] +# (optional) Internal Hostname or IP to connect to Nova API +# Defaults to '127.0.0.1' +# +# [*ks_nova_admin_host*] +# (optional) Admin Hostname or IP to connect to Nova API +# Defaults to '127.0.0.1' +# +# [*ks_nova_public_host*] +# (optional) Public Hostname or IP to connect to Nova API +# Defaults to '127.0.0.1' +# +# [*ks_cinder_internal_host*] +# (optional) Internal Hostname or IP to connect to Cinder API +# Defaults to '127.0.0.1' +# +# [*ks_cinder_admin_host*] +# (optional) Admin Hostname or IP to connect to Cinder API +# Defaults to '127.0.0.1' +# +# [*ks_cinder_public_host*] +# (optional) Public Hostname or IP to connect to Cinder API +# Defaults to '127.0.0.1' +# +# [*ks_trove_internal_host*] +# (optional) Internal Hostname or IP to connect to Trove API +# Defaults to '127.0.0.1' +# +# [*ks_trove_admin_host*] +# (optional) Admin Hostname or IP to connect to Trove API +# Defaults to '127.0.0.1' +# +# [*ks_trove_public_host*] +# (optional) Public Hostname or IP to connect to Trove API +# Defaults to '127.0.0.1' +# +# [*ks_neutron_internal_host*] +# (optional) Internal Hostname or IP to connect to Neutron API +# Defaults to '127.0.0.1' +# +# [*ks_neutron_admin_host*] +# (optional) Admin Hostname or IP to connect to Neutron API +# Defaults to '127.0.0.1' +# +# [*ks_neutron_public_host*] +# (optional) Public Hostname or IP to connect to Neutron API +# Defaults to '127.0.0.1' +# +# [*ks_heat_internal_host*] +# (optional) Internal Hostname or IP to connect to Heat API +# Defaults to '127.0.0.1' +# +# [*ks_heat_admin_host*] +# (optional) Admin Hostname or IP to connect to Heat API +# Defaults to '127.0.0.1' +# +# [*ks_heat_public_host*] +# (optional) Public Hostname or IP to connect to Heat API +# Defaults to '127.0.0.1' +# +# [*ks_swift_internal_host*] +# (optional) Internal Hostname or IP to connect to Swift API +# Defaults to '127.0.0.1' +# +# [*ks_swift_admin_host*] +# (optional) Admin Hostname or IP to connect to Swift API +# Defaults to '127.0.0.1' +# +# [*ks_swift_public_host*] +# (optional) Public Hostname or IP to connect to Swift API +# Defaults to '127.0.0.1' +# +# [*ks_trove_password*] +# (optional) Password used by Trove to connect to Keystone API +# Defaults to 'trovepassword' +# +# [*ks_ceilometer_password*] +# (optional) Password used by Ceilometer to connect to Keystone API +# Defaults to 'ceilometerpassword' +# +# [*ks_swift_password*] +# (optional) Password used by Swift to connect to Keystone API +# Defaults to 'swiftpassword' +# +# [*ks_nova_password*] +# (optional) Password used by Nova to connect to Keystone API +# Defaults to 'novapassword' +# +# [*ks_neutron_password*] +# (optional) Password used by Neutron to connect to Keystone API +# Defaults to 'neutronpassword' +# +# [*ks_heat_password*] +# (optional) Password used by Heat to connect to Keystone API +# Defaults to 'heatpassword' +# +# [*ks_glance_password*] +# (optional) Password used by Glance to connect to Keystone API +# Defaults to 'glancepassword' +# +# [*ks_cinder_password*] +# (optional) Password used by Cinder to connect to Keystone API +# Defaults to 'cinderpassword' +# +# [*ks_swift_public_proto*] +# (optional) Protocol used to connect to API. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_swift_admin_proto*] +# (optional) Protocol for admin endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_swift_internal_proto*] +# (optional) Protocol for public endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_ceilometer_public_proto*] +# (optional) Protocol used to connect to API. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_ceilometer_admin_proto*] +# (optional) Protocol for admin endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_ceilometer_internal_proto*] +# (optional) Protocol for public endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_heat_public_proto*] +# (optional) Protocol used to connect to API. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_heat_admin_proto*] +# (optional) Protocol for admin endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_heat_internal_proto*] +# (optional) Protocol for public endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_keystone_public_proto*] +# (optional) Protocol for public endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_keystone_admin_proto*] +# (optional) Protocol for admin endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_keystone_internal_proto*] +# (optional) Protocol for public endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_nova_public_proto*] +# (optional) Protocol used to connect to API. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_nova_admin_proto*] +# (optional) Protocol for admin endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_nova_internal_proto*] +# (optional) Protocol for public endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_neutron_public_proto*] +# (optional) Protocol used to connect to API. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_neutron_admin_proto*] +# (optional) Protocol for admin endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_neutron_internal_proto*] +# (optional) Protocol for public endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_trove_public_proto*] +# (optional) Protocol used to connect to API. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_trove_admin_proto*] +# (optional) Protocol for admin endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_trove_internal_proto*] +# (optional) Protocol for public endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_glance_public_proto*] +# (optional) Protocol used to connect to API. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_glance_admin_proto*] +# (optional) Protocol for admin endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_glance_internal_proto*] +# (optional) Protocol for public endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_cinder_public_proto*] +# (optional) Protocol used to connect to API. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_cinder_admin_proto*] +# (optional) Protocol for admin endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_cinder_internal_proto*] +# (optional) Protocol for public endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_ceilometer_public_port*] +# (optional) TCP port to connect to Ceilometer API from public network +# Defaults to '8777' +# +# [*ks_keystone_internal_port*] +# (optional) TCP port to connect to Keystone API from internal network +# Defaults to '5000' +# +# [*ks_keystone_public_port*] +# (optional) TCP port to connect to Keystone API from public network +# Defaults to '5000' +# +# [*ks_keystone_admin_port*] +# (optional) TCP port to connect to Keystone API from admin network +# Defaults to '35357' +# +# [*ks_swift_public_port*] +# (optional) TCP port to connect to Swift API from public network +# Defaults to '8080' +# +# [*ks_trove_public_port*] +# (optional) TCP port to connect to Trove API from public network +# Defaults to '8779' +# +# [*ks_nova_public_port*] +# (optional) TCP port to connect to Nova API from public network +# Defaults to '8774' +# +# [*ks_ec2_public_port*] +# (optional) TCP port to connect to EC2 API from public network +# Defaults to '8773' +# +# [*ks_swift_dispersion_password*] +# (optional) Password of the dispersion tenant, used for swift-dispersion-report +# and swift-dispersion-populate tools. +# Defaults to 'dispersion' +# +# [*ks_cinder_public_port*] +# (optional) TCP port to connect to Cinder API from public network +# Defaults to '8776' +# +# [*ks_neutron_public_port*] +# (optional) TCP port to connect to Neutron API from public network +# Defaults to '9696' +# +# [*ks_heat_public_port*] +# (optional) TCP port to connect to Heat API from public network +# Defaults to '8004' +# +# [*ks_heat_cfn_public_port*] +# (optional) TCP port to connect to Heat API from public network +# Defaults to '8000' +# +# [*ks_glance_api_public_port*] +# (optional) TCP port to connect to Glance API from public network +# Defaults to '9292' +# +# [*api_eth*] +# (optional) Which interface we bind the Keystone server. +# Defaults to '127.0.0.1' +# +# [*region*] +# (optional) OpenStack Region Name +# Defaults to 'RegionOne' +# +# [*verbose*] +# (optional) Set log output to verbose output +# Defaults to true +# +# [*debug*] +# (optional) Set log output to debug output +# Defaults to true +# +# [*use_syslog*] +# (optional) Use syslog for logging +# Defaults to true +# +# [*log_facility*] +# (optional) Syslog facility to receive log lines +# Defaults to 'LOG_LOCAL0' +# +# [*token_driver*] +# (optional) Driver to store tokens +# Defaults to 'keystone.token.backends.sql.Token' +# +# [*token_expiration*] +# (optional) Amount of time a token should remain valid (in seconds) +# Defaults to '3600' (1 hour) +# +# [*trove_enabled*] +# (optional) Enable or not Trove (Database as a Service) +# Experimental feature. +# Defaults to false +# +# [*swift_enabled*] +# (optional) Enable or not OpenStack Swift (Stockage as a Service) +# Defaults to true +# +# [*ks_token_expiration*] +# (optional) Amount of time a token should remain valid (seconds). +# Defaults to 3600 (1 hour). +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::identity ( + $swift_enabled = true, + $trove_enabled = false, + $identity_roles_addons = ['SwiftOperator', 'ResellerAdmin'], + $keystone_db_host = '127.0.0.1', + $keystone_db_user = 'keystone', + $keystone_db_password = 'keystonepassword', + $memcache_servers = ['127.0.0.1:11211'], + $ks_admin_email = 'no-reply@keystone.openstack', + $ks_admin_password = 'adminpassword', + $ks_admin_tenant = 'admin', + $ks_admin_token = undef, + $ks_ceilometer_admin_host = '127.0.0.1', + $ks_ceilometer_internal_host = '127.0.0.1', + $ks_ceilometer_password = 'ceilometerpassword', + $ks_ceilometer_public_host = '127.0.0.1', + $ks_ceilometer_public_port = 8777, + $ks_ceilometer_public_proto = 'http', + $ks_ceilometer_admin_proto = 'http', + $ks_ceilometer_internal_proto = 'http', + $ks_cinder_admin_host = '127.0.0.1', + $ks_cinder_internal_host = '127.0.0.1', + $ks_cinder_password = 'cinderpassword', + $ks_cinder_public_host = '127.0.0.1', + $ks_cinder_public_proto = 'http', + $ks_cinder_admin_proto = 'http', + $ks_cinder_internal_proto = 'http', + $ks_cinder_public_port = 8776, + $ks_glance_admin_host = '127.0.0.1', + $ks_glance_internal_host = '127.0.0.1', + $ks_glance_password = 'glancepassword', + $ks_glance_public_host = '127.0.0.1', + $ks_glance_public_proto = 'http', + $ks_glance_internal_proto = 'http', + $ks_glance_admin_proto = 'http', + $ks_glance_api_public_port = 9292, + $ks_heat_admin_host = '127.0.0.1', + $ks_heat_internal_host = '127.0.0.1', + $ks_heat_password = 'heatpassword', + $ks_heat_public_host = '127.0.0.1', + $ks_heat_public_proto = 'http', + $ks_heat_admin_proto = 'http', + $ks_heat_internal_proto = 'http', + $ks_heat_public_port = 8004, + $ks_heat_cfn_public_port = 8000, + $ks_keystone_admin_host = '127.0.0.1', + $ks_keystone_admin_port = 35357, + $ks_keystone_internal_host = '127.0.0.1', + $ks_keystone_internal_port = 5000, + $ks_keystone_public_host = '127.0.0.1', + $ks_keystone_public_port = 5000, + $ks_keystone_public_proto = 'http', + $ks_neutron_admin_host = '127.0.0.1', + $ks_keystone_admin_proto = 'http', + $ks_keystone_internal_proto = 'http', + $ks_neutron_internal_host = '127.0.0.1', + $ks_neutron_password = 'neutronpassword', + $ks_neutron_public_host = '127.0.0.1', + $ks_neutron_public_proto = 'http', + $ks_neutron_admin_proto = 'http', + $ks_neutron_internal_proto = 'http', + $ks_neutron_public_port = 9696, + $ks_nova_admin_host = '127.0.0.1', + $ks_nova_internal_host = '127.0.0.1', + $ks_nova_password = 'novapassword', + $ks_nova_public_host = '127.0.0.1', + $ks_nova_public_proto = 'http', + $ks_nova_internal_proto = 'http', + $ks_nova_admin_proto = 'http', + $ks_nova_public_port = 8774, + $ks_ec2_public_port = 8773, + $ks_swift_dispersion_password = 'dispersion', + $ks_swift_internal_host = '127.0.0.1', + $ks_swift_admin_host = '127.0.0.1', + $ks_swift_password = 'swiftpassword', + $ks_swift_public_host = '127.0.0.1', + $ks_swift_public_port = 8080, + $ks_swift_public_proto = 'http', + $ks_swift_admin_proto = 'http', + $ks_swift_internal_proto = 'http', + $ks_trove_admin_host = '127.0.0.1', + $ks_trove_internal_host = '127.0.0.1', + $ks_trove_password = 'trovepassword', + $ks_trove_public_host = '127.0.0.1', + $ks_trove_public_port = 8779, + $ks_trove_public_proto = 'http', + $ks_trove_admin_proto = 'http', + $ks_trove_internal_proto = 'http', + $api_eth = '127.0.0.1', + $region = 'RegionOne', + $verbose = true, + $debug = true, + $log_facility = 'LOG_LOCAL0', + $use_syslog = true, + $ks_token_expiration = 3600, + $token_driver = 'keystone.token.backends.sql.Token', + $firewall_settings = {}, +){ + + $encoded_user = uriescape($keystone_db_user) + $encoded_password = uriescape($keystone_db_password) + + if $use_syslog { + $log_dir = false + $log_file = false + keystone_config { + 'DEFAULT/logging_context_format_string': value => '%(process)d: %(levelname)s %(name)s [%(request_id)s %(user_identity)s] %(instance)s%(message)s'; + 'DEFAULT/logging_default_format_string': value => '%(process)d: %(levelname)s %(name)s [-] %(instance)s%(message)s'; + 'DEFAULT/logging_debug_format_suffix': value => '%(funcName)s %(pathname)s:%(lineno)d'; + 'DEFAULT/logging_exception_prefix': value => '%(process)d: TRACE %(name)s %(instance)s'; + } + } else { + $log_dir = '/var/log/keystone' + $log_file = 'keystone.log' + } + +# Configure Keystone + class { 'keystone': + enabled => true, + admin_token => $ks_admin_token, + compute_port => $ks_nova_public_port, + debug => $debug, + idle_timeout => 60, + log_facility => $log_facility, + sql_connection => "mysql://${encoded_user}:${encoded_password}@${keystone_db_host}/keystone?charset=utf8", + mysql_module => '2.2', + token_provider => 'keystone.token.providers.uuid.Provider', + use_syslog => $use_syslog, + verbose => $verbose, + bind_host => $api_eth, + log_dir => $log_dir, + log_file => $log_file, + public_port => $ks_keystone_public_port, + admin_port => $ks_keystone_admin_port, + token_driver => $token_driver, + token_expiration => $ks_token_expiration, + admin_endpoint => "${ks_keystone_admin_proto}://${ks_keystone_admin_host}:${ks_keystone_admin_port}/", + public_endpoint => "${ks_keystone_public_proto}://${ks_keystone_public_host}:${ks_keystone_public_port}/", + validate_service => true, + } + + keystone_config { + 'ec2/driver': value => 'keystone.contrib.ec2.backends.sql.Ec2'; + } + + +# Keystone Endpoints + Users + class { 'keystone::roles::admin': + email => $ks_admin_email, + password => $ks_admin_password, + admin_tenant => $ks_admin_tenant, + } + + keystone_role { $identity_roles_addons: ensure => present } + + class {'keystone::endpoint': + public_url => "${ks_keystone_public_proto}://${ks_keystone_public_host}:${ks_keystone_public_port}", + internal_url => "${ks_keystone_internal_proto}://${ks_keystone_internal_host}:${ks_keystone_internal_port}", + admin_url => "${ks_keystone_admin_proto}://${ks_keystone_admin_host}:${ks_keystone_admin_port}", + region => $region, + } + + # TODO(EmilienM) Disable WSGI - bug #98 + #include 'apache' + # class {'keystone::wsgi::apache': + # servername => $::fqdn, + # admin_port => $ks_keystone_admin_port, + # public_port => $ks_keystone_public_port, + # # TODO(EmilienM) not sure workers is useful when using WSGI backend + # workers => $::processorcount, + # ssl => false + # } + + if $swift_enabled { + class {'swift::keystone::auth': + password => $ks_swift_password, + public_address => $ks_swift_public_host, + public_port => $ks_swift_public_port, + public_protocol => $ks_swift_public_proto, + admin_protocol => $ks_swift_admin_proto, + internal_protocol => $ks_swift_internal_proto, + admin_address => $ks_swift_admin_host, + internal_address => $ks_swift_internal_host, + region => $region + } + + class {'swift::keystone::dispersion': + auth_pass => $ks_swift_dispersion_password + } + } + + class {'ceilometer::keystone::auth': + admin_address => $ks_ceilometer_admin_host, + internal_address => $ks_ceilometer_internal_host, + public_address => $ks_ceilometer_public_host, + public_protocol => $ks_ceilometer_public_proto, + admin_protocol => $ks_ceilometer_admin_proto, + internal_protocol => $ks_ceilometer_internal_proto, + port => $ks_ceilometer_public_port, + region => $region, + password => $ks_ceilometer_password + } + + class { 'nova::keystone::auth': + cinder => true, + admin_address => $ks_nova_admin_host, + internal_address => $ks_nova_internal_host, + public_address => $ks_nova_public_host, + compute_port => $ks_nova_public_port, + public_protocol => $ks_nova_public_proto, + admin_protocol => $ks_nova_admin_proto, + internal_protocol => $ks_nova_internal_proto, + ec2_port => $ks_ec2_public_port, + region => $region, + password => $ks_nova_password + } + + class { 'neutron::keystone::auth': + admin_address => $ks_neutron_admin_host, + internal_address => $ks_neutron_internal_host, + public_address => $ks_neutron_public_host, + public_protocol => $ks_neutron_public_proto, + internal_protocol => $ks_neutron_internal_proto, + admin_protocol => $ks_neutron_admin_proto, + port => $ks_neutron_public_port, + region => $region, + password => $ks_neutron_password + } + + class { 'cinder::keystone::auth': + admin_address => $ks_cinder_admin_host, + internal_address => $ks_cinder_internal_host, + public_address => $ks_cinder_public_host, + port => $ks_cinder_public_port, + public_protocol => $ks_cinder_public_proto, + admin_protocol => $ks_cinder_admin_proto, + internal_protocol => $ks_cinder_internal_proto, + region => $region, + password => $ks_cinder_password + } + + class { 'glance::keystone::auth': + admin_address => $ks_glance_admin_host, + internal_address => $ks_glance_internal_host, + public_address => $ks_glance_public_host, + port => $ks_glance_api_public_port, + public_protocol => $ks_glance_public_proto, + internal_protocol => $ks_glance_internal_proto, + admin_protocol => $ks_glance_admin_proto, + region => $region, + password => $ks_glance_password + } + + class { 'heat::keystone::auth': + admin_address => $ks_heat_admin_host, + internal_address => $ks_heat_internal_host, + public_address => $ks_heat_public_host, + port => $ks_heat_public_port, + public_protocol => $ks_heat_public_proto, + internal_protocol => $ks_heat_internal_proto, + admin_protocol => $ks_heat_admin_proto, + region => $region, + password => $ks_heat_password + } + + class { 'heat::keystone::auth_cfn': + admin_address => $ks_heat_admin_host, + internal_address => $ks_heat_internal_host, + public_address => $ks_heat_public_host, + port => $ks_heat_cfn_public_port, + public_protocol => $ks_heat_public_proto, + internal_protocol => $ks_heat_internal_proto, + admin_protocol => $ks_heat_admin_proto, + region => $region, + password => $ks_heat_password + } + + if $trove_enabled { + class {'trove::keystone::auth': + admin_address => $ks_trove_admin_host, + internal_address => $ks_trove_internal_host, + public_address => $ks_trove_public_host, + public_protocol => $ks_trove_public_proto, + admin_protocol => $ks_trove_admin_proto, + internal_protocol => $ks_trove_internal_proto, + port => $ks_trove_public_port, + region => $region, + password => $ks_trove_password + } + } + + # Purge expored tokens every days at midnight + class { 'keystone::cron::token_flush': } + + # Note(EmilienM): + # We check if DB tables are created, if not we populate Keystone DB. + # It's a hack to fit with our setup where we run MySQL/Galera + # TODO(Goneri) + # We have to do this only on the primary node of the galera cluster to avoid race condition + # https://github.com/enovance/puppet-openstack-cloud/issues/156 + exec {'keystone_db_sync': + command => 'keystone-manage db_sync', + path => '/usr/bin', + user => 'keystone', + unless => "/usr/bin/mysql keystone -h ${keystone_db_host} -u ${encoded_user} -p${encoded_password} -e \"show tables\" | /bin/grep Tables" + } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow keystone access': + port => $ks_keystone_public_port, + extras => $firewall_settings, + } + cloud::firewall::rule{ '100 allow keystone admin access': + port => $ks_keystone_admin_port, + extras => $firewall_settings, + } + } + + @@haproxy::balancermember{"${::fqdn}-keystone_api": + listening_service => 'keystone_api_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => $ks_keystone_public_port, + options => 'check inter 2000 rise 2 fall 5' + } + + @@haproxy::balancermember{"${::fqdn}-keystone_api_admin": + listening_service => 'keystone_api_admin_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => $ks_keystone_admin_port, + options => 'check inter 2000 rise 2 fall 5' + } + +} diff --git a/cloud/manifests/image/api.pp b/cloud/manifests/image/api.pp new file mode 100644 index 000000000..1955cf794 --- /dev/null +++ b/cloud/manifests/image/api.pp @@ -0,0 +1,284 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::image::api +# +# Install API Image Server (Glance API) +# +# === Parameters: +# +# [*glance_db_host*] +# (optional) Hostname or IP address to connect to glance database +# Defaults to '127.0.0.1' +# +# [*glance_db_user*] +# (optional) Username to connect to glance database +# Defaults to 'glance' +# +# [*glance_db_password*] +# (optional) Password to connect to glance database +# Defaults to 'glancepassword' +# +# [*ks_keystone_internal_host*] +# (optional) Internal Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_internal_proto*] +# (optional) Protocol used to connect to API. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_glance_internal_host*] +# (optional) Internal Hostname or IP to connect to Glance +# Defaults to '127.0.0.1' +# +# [*ks_glance_api_internal_port*] +# (optional) TCP port to connect to Glance API from internal network +# Defaults to '9292' +# +# [*ks_glance_registry_internal_proto*] +# (optional) Protocol used to connect to API. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_glance_registry_internal_port*] +# (optional) TCP port to connect to Glance Registry from internal network +# Defaults to '9191' +# +# [*ks_glance_password*] +# (optional) Password used by Glance to connect to Keystone API +# Defaults to 'glancepassword' +# +# [*rabbit_host*] +# (optional) IP or Hostname of one RabbitMQ server. +# Defaults to '127.0.0.1' +# +# [*rabbit_password*] +# (optional) Password to connect to glance queue. +# Defaults to 'rabbitpassword' +# +# [*api_eth*] +# (optional) Which interface we bind the Glance API server. +# Defaults to '127.0.0.1' +# +# [*openstack_vip*] +# (optional) Hostname of IP used to connect to Glance registry +# Defaults to '127.0.0.1' +# +# [*glance_rbd_pool*] +# (optional) Name of the Ceph pool which which store the glance images +# Defaults to 'images' +# +# [*glance_rbd_user*] +# (optional) User name used to acces to the glance rbd pool +# Defaults to 'glance' +# +# [*verbose*] +# (optional) Set log output to verbose output +# Defaults to true +# +# [*debug*] +# (optional) Set log output to debug output +# Defaults to true +# +# [*use_syslog*] +# (optional) Use syslog for logging +# Defaults to true +# +# [*log_facility*] +# (optional) Syslog facility to receive log lines +# Defaults to 'LOG_LOCAL0' +# +# [*backend*] +# (optionnal) Backend to use to store images +# Can be 'rbd', 'file', 'nfs' or 'swift' +# Defaults to 'rbd' +# +# [*filesystem_store_datadir*] +# (optional) Full path of data directory to store the images. +# Defaults to '/var/lib/glance/images/' +# +# [*nfs_device*] +# (optionnal) NFS device to mount +# Example: 'nfs.example.com:/vol1' +# Required when running 'nfs' backend. +# Defaults to false +# +# [*nfs_options*] +# (optional) NFS mount options +# Example: 'nfsvers=3,noacl' +# Defaults to 'defaults' +# +# [*pipeline*] +# (optional) Partial name of a pipeline in your paste configuration file with the +# service name removed. +# Defaults to 'keystone'. +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::image::api( + $glance_db_host = '127.0.0.1', + $glance_db_user = 'glance', + $glance_db_password = 'glancepassword', + $ks_keystone_internal_host = '127.0.0.1', + $ks_keystone_internal_proto = 'http', + $ks_glance_internal_host = '127.0.0.1', + $ks_glance_api_internal_port = '9292', + $ks_glance_registry_internal_port = '9191', + $ks_glance_registry_internal_proto = 'http', + $ks_glance_password = 'glancepassword', + $rabbit_password = 'rabbit_password', + $rabbit_host = '127.0.0.1', + $api_eth = '127.0.0.1', + $openstack_vip = '127.0.0.1', + $glance_rbd_pool = 'images', + $glance_rbd_user = 'glance', + $verbose = true, + $debug = true, + $log_facility = 'LOG_LOCAL0', + $use_syslog = true, + $backend = 'rbd', + $filesystem_store_datadir = '/var/lib/glance/images/', + $nfs_device = false, + $nfs_options = 'defaults', + $pipeline = 'keystone', + $firewall_settings = {}, +) { + + # Disable twice logging if syslog is enabled + if $use_syslog { + $log_dir = false + $log_file_api = false + $log_file_registry = false + glance_api_config { + 'DEFAULT/logging_context_format_string': value => '%(process)d: %(levelname)s %(name)s [%(request_id)s %(user_identity)s] %(instance)s%(message)s'; + 'DEFAULT/logging_default_format_string': value => '%(process)d: %(levelname)s %(name)s [-] %(instance)s%(message)s'; + 'DEFAULT/logging_debug_format_suffix': value => '%(funcName)s %(pathname)s:%(lineno)d'; + 'DEFAULT/logging_exception_prefix': value => '%(process)d: TRACE %(name)s %(instance)s'; + } + } else { + $log_dir = '/var/log/glance' + $log_file_api = '/var/log/glance/api.log' + $log_file_registry = '/var/log/glance/registry.log' + } + + $encoded_glance_user = uriescape($glance_db_user) + $encoded_glance_password = uriescape($glance_db_password) + + class { 'glance::api': + database_connection => "mysql://${encoded_glance_user}:${encoded_glance_password}@${glance_db_host}/glance?charset=utf8", + mysql_module => '2.2', + registry_host => $openstack_vip, + registry_port => $ks_glance_registry_internal_port, + verbose => $verbose, + debug => $debug, + auth_host => $ks_keystone_internal_host, + auth_protocol => $ks_keystone_internal_proto, + registry_client_protocol => $ks_glance_registry_internal_proto, + keystone_password => $ks_glance_password, + keystone_tenant => 'services', + keystone_user => 'glance', + show_image_direct_url => true, + log_dir => $log_dir, + log_file => $log_file_api, + log_facility => $log_facility, + bind_host => $api_eth, + bind_port => $ks_glance_api_internal_port, + use_syslog => $use_syslog, + pipeline => 'keystone', + } + + # TODO(EmilienM) Disabled for now + # Follow-up: https://github.com/enovance/puppet-openstack-cloud/issues/160 + # + # class { 'glance::notify::rabbitmq': + # rabbit_password => $rabbit_password, + # rabbit_userid => 'glance', + # rabbit_host => $rabbit_host, + # } + glance_api_config { + 'DEFAULT/notifier_driver': value => 'noop'; + } + + if ($backend == 'rbd') { + class { 'glance::backend::rbd': + rbd_store_user => $glance_rbd_user, + rbd_store_pool => $glance_rbd_pool + } + + Ceph::Key <<| title == $glance_rbd_user |>> -> + file { '/etc/ceph/ceph.client.glance.keyring': + owner => 'glance', + group => 'glance', + mode => '0400', + require => Ceph::Key[$glance_rbd_user], + notify => Service['glance-api','glance-registry'] + } + Concat::Fragment <<| title == 'ceph-client-os' |>> + } elsif ($backend == 'file') { + class { 'glance::backend::file': + filesystem_store_datadir => $filesystem_store_datadir + } + } elsif ($backend == 'swift') { + class { 'glance::backend::swift': + swift_store_user => 'services:glance', + swift_store_key => $ks_glance_password, + swift_store_auth_address => "${ks_keystone_internal_proto}://${ks_keystone_internal_host}:35357/v2.0/", + swift_store_create_container_on_put => true, + } + } elsif ($backend == 'nfs') { + # There is no NFS backend in Glance. + # We mount the NFS share in filesystem_store_datadir to fake the + # backend. + if $nfs_device { + class { 'glance::backend::file': + filesystem_store_datadir => $filesystem_store_datadir + } + $nfs_mount = { + "${filesystem_store_datadir}" => { + 'ensure' => 'mounted', + 'fstype' => 'nfs', + 'device' => $nfs_device, + 'options' => $nfs_options + } + } + ensure_resource('class', 'nfs', {}) + create_resources('types::mount', $nfs_mount) + } else { + fail('When running NFS backend, you need to provide nfs_device parameter.') + } + } else { + fail("${backend} is not a Glance supported backend.") + } + + class { 'glance::cache::cleaner': } + class { 'glance::cache::pruner': } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow glance-api access': + port => $ks_glance_api_internal_port, + extras => $firewall_settings, + } + } + + @@haproxy::balancermember{"${::fqdn}-glance_api": + listening_service => 'glance_api_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => $ks_glance_api_internal_port, + options => 'check inter 2000 rise 2 fall 5' + } +} diff --git a/cloud/manifests/image/registry.pp b/cloud/manifests/image/registry.pp new file mode 100644 index 000000000..6a222a5f6 --- /dev/null +++ b/cloud/manifests/image/registry.pp @@ -0,0 +1,155 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::image::registry +# +# Install Registry Image Server (Glance Registry) +# +# === Parameters: +# +# [*glance_db_host*] +# (optional) Hostname or IP address to connect to glance database +# Defaults to '127.0.0.1' +# +# [*glance_db_user*] +# (optional) Username to connect to glance database +# Defaults to 'glance' +# +# [*glance_db_password*] +# (optional) Password to connect to glance database +# Defaults to 'glancepassword' +# +# [*ks_keystone_internal_host*] +# (optional) Internal Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_internal_proto*] +# (optional) Protocol used to connect to API. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_glance_internal_host*] +# (optional) Internal Hostname or IP to connect to Glance +# Defaults to '127.0.0.1' +# +# [*ks_glance_registry_internal_port*] +# (optional) TCP port to connect to Glance Registry from internal network +# Defaults to '9191' +# +# [*ks_glance_password*] +# (optional) Password used by Glance to connect to Keystone API +# Defaults to 'glancepassword' +# +# [*api_eth*] +# (optional) Which interface we bind the Glance API server. +# Defaults to '127.0.0.1' +# +# [*verbose*] +# (optional) Set log output to verbose output +# Defaults to true +# +# [*debug*] +# (optional) Set log output to debug output +# Defaults to true +# +# [*use_syslog*] +# (optional) Use syslog for logging +# Defaults to true +# +# [*log_facility*] +# (optional) Syslog facility to receive log lines +# Defaults to 'LOG_LOCAL0' +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::image::registry( + $glance_db_host = '127.0.0.1', + $glance_db_user = 'glance', + $glance_db_password = 'glancepassword', + $ks_keystone_internal_host = '127.0.0.1', + $ks_keystone_internal_proto = 'http', + $ks_glance_internal_host = '127.0.0.1', + $ks_glance_registry_internal_port = '9191', + $ks_glance_password = 'glancepassword', + $api_eth = '127.0.0.1', + $verbose = true, + $debug = true, + $log_facility = 'LOG_LOCAL0', + $use_syslog = true, + $firewall_settings = {}, +) { + + # Disable twice logging if syslog is enabled + if $use_syslog { + $log_dir = false + $log_file_api = false + $log_file_registry = false + glance_registry_config { + 'DEFAULT/logging_context_format_string': value => '%(process)d: %(levelname)s %(name)s [%(request_id)s %(user_identity)s] %(instance)s%(message)s'; + 'DEFAULT/logging_default_format_string': value => '%(process)d: %(levelname)s %(name)s [-] %(instance)s%(message)s'; + 'DEFAULT/logging_debug_format_suffix': value => '%(funcName)s %(pathname)s:%(lineno)d'; + 'DEFAULT/logging_exception_prefix': value => '%(process)d: TRACE %(name)s %(instance)s'; + } + } else { + $log_dir = '/var/log/glance' + $log_file_api = '/var/log/glance/api.log' + $log_file_registry = '/var/log/glance/registry.log' + } + + $encoded_glance_user = uriescape($glance_db_user) + $encoded_glance_password = uriescape($glance_db_password) + + class { 'glance::registry': + database_connection => "mysql://${encoded_glance_user}:${encoded_glance_password}@${glance_db_host}/glance?charset=utf8", + mysql_module => '2.2', + verbose => $verbose, + debug => $debug, + auth_host => $ks_keystone_internal_host, + auth_protocol => $ks_keystone_internal_proto, + keystone_password => $ks_glance_password, + keystone_tenant => 'services', + keystone_user => 'glance', + bind_host => $api_eth, + log_dir => $log_dir, + log_file => $log_file_registry, + bind_port => $ks_glance_registry_internal_port, + use_syslog => $use_syslog, + log_facility => $log_facility, + } + + exec {'glance_db_sync': + command => 'glance-manage db_sync', + user => 'glance', + path => '/usr/bin', + unless => "/usr/bin/mysql glance -h ${glance_db_host} -u ${encoded_glance_user} -p${encoded_glance_password} -e \"show tables\" | /bin/grep Tables" + } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow glance-registry access': + port => $ks_glance_registry_internal_port, + extras => $firewall_settings, + } + } + + @@haproxy::balancermember{"${::fqdn}-glance_registry": + listening_service => 'glance_registry_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => $ks_glance_registry_internal_port, + options => 'check inter 2000 rise 2 fall 5' + } +} diff --git a/cloud/manifests/init.pp b/cloud/manifests/init.pp new file mode 100644 index 000000000..945665de5 --- /dev/null +++ b/cloud/manifests/init.pp @@ -0,0 +1,213 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud +# +# Installs the system requirements +# +# === Parameters: +# +# [*rhn_registration*] +# (optional) The RedHat network authentication token +# Defaults to undef +# +# [*root_password*] +# (optional) Unix root password +# Defaults to 'root' +# +# [*dns_ips*] +# (optional) Hostname or IP of the Domain Name Server (dns) used +# Should by an array. +# Defaults to google public dns ['8.8.8.8', '8.8.4.4'] +# +# [*site_domain*] +# (optional) Domain name (used for search and domain fields +# of resolv.conf configuration file +# Defaults to 'mydomain' +# +# [*motd_title*] +# (optional) A string used in the top of the server's motd +# Defaults to 'eNovance IT Operations' +# +# [*selinux_mode*] +# (optional) SELinux mode the system should be in +# Defaults to 'permissive' +# Possible values : disabled, permissive, enforcing +# +# [*selinux_directory*] +# (optional) Path where to find the SELinux modules +# Defaults to '/usr/share/selinux' +# +# [*selinux_booleans*] +# (optional) Set of booleans to persistently enables +# SELinux booleans are the one getsebool -a returns +# Defaults [] +# Example: ['rsync_full_access', 'haproxy_connect_any'] +# +# [*selinux_modules*] +# (optional) Set of modules to load on the system +# Defaults [] +# Example: ['module1', 'module2'] +# Note: Those module should be in the $directory path +# +# [*manage_firewall*] +# (optional) Completely enable or disable firewall settings +# (false means disabled, and true means enabled) +# Defaults to false +# +# [*firewall_rules*] +# (optional) Allow to add custom firewall rules +# Should be an hash. +# Default to {} +# +# [*purge_firewall_rules*] +# (optional) Boolean, purge all firewall resources +# Defaults to false +# +# [*firewall_pre_extras*] +# (optional) Allow to add custom parameters to firewall rules (pre stage) +# Should be an hash. +# Default to {} +# +# [*firewall_post_extras*] +# (optional) Allow to add custom parameters to firewall rules (post stage) +# Should be an hash. +# Default to {} +# +class cloud( + $rhn_registration = undef, + $root_password = 'root', + $dns_ips = ['8.8.8.8', '8.8.4.4'], + $site_domain = 'mydomain', + $motd_title = 'eNovance IT Operations', + $selinux_mode = 'permissive', + $selinux_directory = '/usr/share/selinux', + $selinux_booleans = [], + $selinux_modules = [], + $manage_firewall = false, + $firewall_rules = {}, + $purge_firewall_rules = false, + $firewall_pre_extras = {}, + $firewall_post_extras = {}, +) { + + include ::stdlib + + if ! ($::osfamily in [ 'RedHat', 'Debian' ]) { + fail("OS family unsuppored yet (${::osfamily}), module puppet-openstack-cloud only support RedHat or Debian") + } + + # motd + file + { + '/etc/motd': + ensure => file, + mode => '0644', + content => " +############################################################################ +# ${motd_title} # +############################################################################ +# # +# *** RESTRICTED ACCESS *** # +# Only the authorized users may access this system. # +# Any attempted unauthorized access or any action affecting this computer # +# system is punishable by the law of local country. # +# # +############################################################################ +This node is under the control of Puppet ${::puppetversion}. +"; + } + + # DNS + class { 'dnsclient': + nameservers => $dns_ips, + domain => $site_domain + } + + # NTP + include ::ntp + + # SELinux + if $::osfamily == 'RedHat' { + class {'cloud::selinux' : + mode => $selinux_mode, + booleans => $selinux_booleans, + modules => $selinux_modules, + directory => $selinux_directory, + stage => 'setup', + } + } + + # Strong root password for all servers + user { 'root': + ensure => 'present', + gid => '0', + password => $root_password, + uid => '0', + } + + $cron_service_name = $::osfamily ? { + 'RedHat' => 'crond', + default => 'cron', + } + + service { 'cron': + ensure => running, + name => $cron_service_name, + enable => true + } + + if $::osfamily == 'RedHat' and $rhn_registration { + create_resources('rhn_register', { + "rhn-${::hostname}" => $rhn_registration + } ) + } + + if $manage_firewall { + + # Only purges IPv4 rules + if $purge_firewall_rules { + resources { 'firewall': + purge => true + } + } + + # anyone can add your own rules + # example with Hiera: + # + # cloud::firewall::rules: + # '300 allow custom application 1': + # port: 999 + # proto: udp + # action: accept + # '301 allow custom application 2': + # port: 8081 + # proto: tcp + # action: accept + # + create_resources('cloud::firewall::rule', $firewall_rules) + + ensure_resource('class', 'cloud::firewall::pre', { + 'firewall_settings' => $firewall_pre_extras, + 'stage' => 'setup', + }) + + ensure_resource('class', 'cloud::firewall::post', { + 'stage' => 'runtime', + 'firewall_settings' => $firewall_post_extras, + }) + } + +} diff --git a/cloud/manifests/install/puppetdb.pp b/cloud/manifests/install/puppetdb.pp new file mode 100644 index 000000000..94a0184eb --- /dev/null +++ b/cloud/manifests/install/puppetdb.pp @@ -0,0 +1,30 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::install::puppetdb +# +# Configure the puppetdb server +# +class cloud::install::puppetdb { + + exec { 'puppetdb ssl-setup' : + unless => 'stat /etc/puppetdb/ssl', + path => ['/bin', '/sbin', '/usr/bin', '/usr/sbin'], + before => Class['puppetdb::server'], + } + + include ::puppetdb + +} diff --git a/cloud/manifests/install/puppetmaster.pp b/cloud/manifests/install/puppetmaster.pp new file mode 100644 index 000000000..7be4b3212 --- /dev/null +++ b/cloud/manifests/install/puppetmaster.pp @@ -0,0 +1,143 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::install::puppetmaster +# +# Configure the puppet master on the install-server +# +# == Parameters: +# +# [*puppetmaster_package_name*] +# (optional) Name of the puppetmaster package name +# Default: cloud::params::puppetmaster_package_name +# +# [*puppetmaster_service_name*] +# (optional) Name of the puppetmaster service name +# Default: cloud::params::puppetmaster_service_name +# +# [*main_configuration*] +# (optional) Hash of ini settings to set in the main section of the configuration +# Default: {} +# +# [*agent_configuration*] +# (optional) Hash of ini settings to set in the agent section of the configuration +# Default: {} +# +# [*master_configuration*] +# (optional) Hash of ini settings to set in the master section of the configuration +# Default: {} +# +# [*puppetmaster_vhost_configuration*] +# (optional) Hash of vhost configuration for the puppetmaster vhost +# Default: {} +# +# [*puppetconf_path*] +# (optional) Path to the puppet master configuration file +# Default: /etc/puppet/puppet.conf +# +# [*puppetdb_enable*] +# (optional) Whether the configuration for puppetdb should be enabled +# Default: true +# +# [*autosign_domains*] +# (optional) Array of domain origin to be auto signed +# Default: empty +# +class cloud::install::puppetmaster ( + $puppetmaster_package_name = $cloud::params::puppetmaster_package_name, + $puppetmaster_service_name = $cloud::params::puppetmaster_service_name, + $main_configuration = {}, + $agent_configuration = {}, + $master_configuration = {}, + $puppetmaster_vhost_configuration = {}, + $puppetconf_path = '/etc/puppet/puppet.conf', + $puppetdb_enable = true, + $autosign_domains = [], +) inherits cloud::params { + + package { $puppetmaster_package_name : + ensure => present, + before => File['/usr/share/puppet/rack'], + } -> + service { $puppetmaster_service_name : + ensure => stopped, + hasstatus => true, + hasrestart => true, + } -> + exec { "puppet cert generate ${::fqdn}": + unless => "stat /var/lib/puppet/ssl/certs/${::fqdn}.pem", + path => ['/usr/bin', '/bin'] + } + + # TODO (spredzy): Dirty hack + # to have the package in the catalog + # so puppetlabs/apache won't try to install it + # and fail since it's not present on rhel7 + if $::osfamily == 'RedHat' and $::operatingsystemmajrelease == 7 { + package { 'mod_passenger' : + ensure => absent, + before => Class['apache'], + } + } + + # Create the proper passenger configuration + # Per https://docs.puppetlabs.com/guides/passenger.html + file { + '/usr/share/puppet/rack' : + ensure => directory; + '/usr/share/puppet/rack/puppetmasterd' : + ensure => directory; + '/usr/share/puppet/rack/puppetmasterd/public' : + ensure => directory; + '/usr/share/puppet/rack/puppetmasterd/tmp' : + ensure => directory; + '/usr/share/puppet/rack/puppetmasterd/config.ru' : + ensure => link, + owner => 'puppet', + group => 'puppet', + target => '/usr/share/puppet/ext/rack/config.ru'; + } + + class { 'hiera' : + datadir => '/etc/puppet/data', + hierarchy => [ + '%{::type}/%{::fqdn}', + '%{::type}/common', + 'common', + ] + } + + if $puppetdb_enable { + Class['::puppetdb::master::config'] ~> Service['httpd'] + include ::puppetdb::master::config + } + + include ::apache + create_resources('apache::vhost', $puppetmaster_vhost_configuration, { 'require' => "Exec[puppet cert generate ${::fqdn}]" }) + + create_resources('ini_setting', $main_configuration, { 'section' => 'main', 'path' => $puppetconf_path, 'require' => "Package[${puppetmaster_package_name}]", 'notify' => 'Service[httpd]' }) + create_resources('ini_setting', $agent_configuration, { 'section' => 'agent', 'path' => $puppetconf_path, 'require' => "Package[${puppetmaster_package_name}]", 'notify' => 'Service[httpd]' }) + create_resources('ini_setting', $master_configuration, { 'section' => 'master', 'path' => $puppetconf_path, 'require' => "Package[${puppetmaster_package_name}]", 'notify' => 'Service[httpd]' }) + + file { '/etc/puppet/autosign.conf' : + ensure => present, + owner => 'puppet', + group => 'puppet', + content => template('cloud/installserver/autosign.conf.erb'), + require => Package[$puppetmaster_package_name], + notify => Service['httpd'], + } + +} diff --git a/cloud/manifests/loadbalancer.pp b/cloud/manifests/loadbalancer.pp new file mode 100644 index 000000000..01a731371 --- /dev/null +++ b/cloud/manifests/loadbalancer.pp @@ -0,0 +1,817 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::loadbalancer +# +# Install Load-Balancer node (HAproxy + Keepalived) +# +# === Parameters: +# +# [*keepalived_vrrp_interface*] +# (optional) Networking interface to bind the vrrp traffic. +# Defaults to false (disabled) +# +# [*keepalived_public_interface*] +# (optional) Networking interface to bind the VIP connected to public network. +# Defaults to 'eth0' +# +# [*keepalived_internal_interface*] +# (optional) Networking interface to bind the VIP connected to internal network. +# keepalived_internal_ipvs should be configured to enable the internal VIP. +# Defaults to 'eth1' +# +# [*keepalived_public_ipvs*] +# (optional) IP address of the VIP connected to public network. +# Should be an array. +# Defaults to ['127.0.0.1'] +# +# [*keepalived_internal_ipvs*] +# (optional) IP address of the VIP connected to internal network. +# Should be an array. +# Defaults to false (disabled) +# +# [*keepalived_auth_type*] +# (optional) Authentication method. +# Supported methods are simple Passwd (PASS) or IPSEC AH (AH). +# Defaults to undef +# +# [*keepalived_auth_pass*] +# (optional) Authentication password. +# Password string (up to 8 characters). +# Defaults to undef +# +# [*swift_api*] +# (optional) Enable or not Swift public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false. +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*ceilometer_api*] +# (optional) Enable or not Ceilometer public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false. +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*cinder_api*] +# (optional) Enable or not Cinder public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false. +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*glance_api*] +# (optional) Enable or not Glance API public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false. +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*glance_registry*] +# (optional) Enable or not Glance Registry public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false. +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*neutron_api*] +# (optional) Enable or not Neutron public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false. +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*heat_api*] +# (optional) Enable or not Heat public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false. +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*heat_cfn_api*] +# (optional) Enable or not Heat CFN public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false. +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*heat_cloudwatch_api*] +# (optional) Enable or not Heat Cloudwatch public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false. +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*nova_api*] +# (optional) Enable or not Nova public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false. +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*trove_api*] +# (optional) Enable or not Trove public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false. +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*horizon*] +# (optional) Enable or not Horizon public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false. +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*horizon_ssl*] +# (optional) Enable or not Horizon SSL public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false. +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*ec2_api*] +# (optional) Enable or not EC2 public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false. +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*spice*] +# (optional) Enable or not spice binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false. +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure. +# Defaults to false +# +# [*metadata_api*] +# (optional) Enable or not Metadata public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false. +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*keystone_api*] +# (optional) Enable or not Keystone public binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false. +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*rabbitmq*] +# (optional) Enable or not RabbitMQ binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false. +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure. +# Defaults to false +# +# [*keystone_api_admin*] +# (optional) Enable or not Keystone admin binding. +# If true, both public and internal will attempt to be created except if vip_internal_ip is set to false. +# If set to ['10.0.0.1'], only IP in the array (or in the string) will be configured in the pool. They must be part of keepalived_ip options. +# If set to false, no binding will be configure +# Defaults to true +# +# [*haproxy_auth*] +# (optional) The HTTP sytle basic credentials (using login:password form) +# Defaults to 'admin:changeme' +# +# [*keepalived_state*] +# (optional) TODO +# Defaults to 'BACKUP' +# +# [*keepalived_priority*] +# (optional) TODO +# Defaults to '50' +# +# [*ceilometer_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*cinder_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*ec2_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*glance_api_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*glance_registry_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*heat_cfn_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*heat_cloudwatch_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*heat_api_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*keystone_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*keystone_admin_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*metadata_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*neutron_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*nova_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*trove_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*swift_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*spice_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*horizon_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*horizon_ssl_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*rabbitmq_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*galera_bind_options*] +# (optional) A hash of options that are inserted into the HAproxy listening +# service configuration block. +# Defaults to [] +# +# [*ks_ceilometer_public_port*] +# (optional) TCP port to connect to Ceilometer API from public network +# Defaults to '8777' +# +# [*ks_cinder_public_port*] +# (optional) TCP port to connect to Cinder API from public network +# Defaults to '8776' +# +# [*ks_ec2_public_port*] +# (optional) TCP port to connect to EC2 API from public network +# Defaults to '8773' +# +# [*ks_glance_api_public_port*] +# (optional) TCP port to connect to Glance API from public network +# Defaults to '9292' +# +# [*ks_glance_registry_internal_port*] +# (optional) TCP port to connect to Glance API from public network +# Defaults to '9191' +# +# [*ks_heat_cfn_public_port*] +# (optional) TCP port to connect to Heat API from public network +# Defaults to '8000' +# +# [*ks_heat_cloudwatch_public_port*] +# (optional) TCP port to connect to Heat API from public network +# Defaults to '8003' +# +# [*ks_heat_public_port*] +# (optional) TCP port to connect to Heat API from public network +# Defaults to '8004' +# +# [*ks_keystone_admin_port*] +# (optional) TCP port to connect to Keystone Admin API from public network +# Defaults to '35357' +# +# [*ks_keystone_public_port*] +# (optional) TCP port to connect to Keystone API from public network +# Defaults to '5000' +# +# [*ks_metadata_public_port*] +# (optional) TCP port to connect to Keystone metadata API from public network +# Defaults to '8775' +# +# [*ks_swift_public_port*] +# (optional) TCP port to connect to Swift API from public network +# Defaults to '8080' +# +# [*ks_trove_public_port*] +# (optional) TCP port to connect to Trove API from public network +# Defaults to '8779' +# +# [*ks_nova_public_port*] +# (optional) TCP port to connect to Nova API from public network +# Defaults to '8774' +# +# [*ks_neutron_public_port*] +# (optional) TCP port to connect to Neutron API from public network +# Defaults to '9696' +# +# [*horizon_port*] +# (optional) Port used to connect to OpenStack Dashboard +# Defaults to '80' +# +# [*horizon_ssl_port*] +# (optional) Port used to connect to OpenStack Dashboard using SSL +# Defaults to '443' +# +# [*spice_port*] +# (optional) TCP port to connect to Nova spicehtmlproxy service. +# Defaults to '6082' +# +# [*rabbitmq_port*] +# (optional) Port of RabbitMQ service. +# Defaults to '5672' +# +# [*vip_public_ip*] +# (optional) Array or string for public VIP +# Should be part of keepalived_public_ips +# Defaults to '127.0.0.2' +# +# [*vip_internal_ip*] +# (optional) Array or string for internal VIP +# Should be part of keepalived_internal_ips +# Defaults to false +# +# [*vip_monitor_ip*] +# (optional) Array or string for monitor VIP +# Defaults to false +# +# [*galera_ip*] +# (optional) An array of Galera IP +# Defaults to ['127.0.0.1'] +# +# [*galera_slave*] +# (optional) A boolean to configure galera slave +# Defaults to false +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::loadbalancer( + $swift_api = true, + $ceilometer_api = true, + $cinder_api = true, + $glance_api = true, + $glance_registry = true, + $neutron_api = true, + $heat_api = true, + $heat_cfn_api = true, + $heat_cloudwatch_api = true, + $nova_api = true, + $ec2_api = true, + $metadata_api = true, + $keystone_api = true, + $keystone_api_admin = true, + $trove_api = true, + $horizon = true, + $horizon_ssl = false, + $rabbitmq = false, + $spice = true, + $haproxy_auth = 'admin:changeme', + $keepalived_state = 'BACKUP', + $keepalived_priority = '50', + $keepalived_vrrp_interface = false, + $keepalived_public_interface = 'eth0', + $keepalived_public_ipvs = ['127.0.0.1'], + $keepalived_internal_interface = 'eth1', + $keepalived_internal_ipvs = false, + $keepalived_auth_type = false, + $keepalived_auth_pass = false, + $ceilometer_bind_options = [], + $cinder_bind_options = [], + $ec2_bind_options = [], + $glance_api_bind_options = [], + $glance_registry_bind_options = [], + $heat_cfn_bind_options = [], + $heat_cloudwatch_bind_options = [], + $heat_api_bind_options = [], + $keystone_bind_options = [], + $keystone_admin_bind_options = [], + $metadata_bind_options = [], + $neutron_bind_options = [], + $nova_bind_options = [], + $trove_bind_options = [], + $swift_bind_options = [], + $spice_bind_options = [], + $horizon_bind_options = [], + $horizon_ssl_bind_options = [], + $rabbitmq_bind_options = [], + $galera_bind_options = [], + $ks_ceilometer_public_port = 8777, + $ks_cinder_public_port = 8776, + $ks_ec2_public_port = 8773, + $ks_glance_api_public_port = 9292, + $ks_glance_registry_internal_port = 9191, + $ks_heat_cfn_public_port = 8000, + $ks_heat_cloudwatch_public_port = 8003, + $ks_heat_public_port = 8004, + $ks_keystone_admin_port = 35357, + $ks_keystone_public_port = 5000, + $ks_metadata_public_port = 8775, + $ks_neutron_public_port = 9696, + $ks_nova_public_port = 8774, + $ks_swift_public_port = 8080, + $ks_trove_public_port = 8779, + $rabbitmq_port = 5672, + $horizon_port = 80, + $horizon_ssl_port = 443, + $spice_port = 6082, + $vip_public_ip = ['127.0.0.1'], + $vip_internal_ip = false, + $vip_monitor_ip = false, + $galera_ip = ['127.0.0.1'], + $galera_slave = false, + $firewall_settings = {}, +){ + + include cloud::params + + if $keepalived_vrrp_interface { + $keepalived_vrrp_interface_real = $keepalived_vrrp_interface + } else { + $keepalived_vrrp_interface_real = $keepalived_public_interface + } + + # Fail if OpenStack and Galera VIP are not in the VIP list + if $vip_public_ip and !(member(any2array($keepalived_public_ipvs), $vip_public_ip)) { + fail('vip_public_ip should be part of keepalived_public_ipvs.') + } + if $vip_internal_ip and !(member(any2array($keepalived_internal_ipvs),$vip_internal_ip)) { + fail('vip_internal_ip should be part of keepalived_internal_ipvs.') + } + if $galera_ip and !((member(any2array($keepalived_public_ipvs),$galera_ip)) or (member(any2array($keepalived_internal_ipvs),$galera_ip))) { + fail('galera_ip should be part of keepalived_public_ipvs or keepalived_internal_ipvs.') + } + + # Ensure Keepalived is started before HAproxy to avoid binding errors. + class { 'keepalived': } -> + class { 'haproxy': + service_manage => true + } + + keepalived::vrrp_script { 'haproxy': + name_is_process => $::cloud::params::keepalived_name_is_process, + script => $::cloud::params::keepalived_vrrp_script, + } + + keepalived::instance { '1': + interface => $keepalived_vrrp_interface_real, + virtual_ips => unique(split(join(flatten([$keepalived_public_ipvs, ['']]), " dev ${keepalived_public_interface},"), ',')), + state => $keepalived_state, + track_script => ['haproxy'], + priority => $keepalived_priority, + auth_type => $keepalived_auth_type, + auth_pass => $keepalived_auth_pass, + notify_master => $::cloud::params::start_haproxy_service, + notify_backup => $::cloud::params::stop_haproxy_service, + } + + + # If using an internal VIP, allow to use a dedicated interface for VRRP traffic. + # First we check if internal binding is enabled + if $keepalived_internal_ipvs { + # Then we validate this is not the same as public binding + if !empty(difference(any2array($keepalived_internal_ipvs), any2array($keepalived_public_ipvs))) { + if ! $keepalived_vrrp_interface { + $keepalived_vrrp_interface_internal = $keepalived_internal_interface + } else { + $keepalived_vrrp_interface_internal = $keepalived_vrrp_interface + } + keepalived::instance { '2': + interface => $keepalived_vrrp_interface_internal, + virtual_ips => unique(split(join(flatten([$keepalived_internal_ipvs, ['']]), " dev ${keepalived_internal_interface},"), ',')), + state => $keepalived_state, + track_script => ['haproxy'], + priority => $keepalived_priority, + auth_type => $keepalived_auth_type, + auth_pass => $keepalived_auth_pass, + notify_master => $::cloud::params::start_haproxy_service, + notify_backup => $::cloud::params::stop_haproxy_service, + } + } + } + + file { '/etc/logrotate.d/haproxy': + ensure => file, + source => 'puppet:///modules/cloud/logrotate/haproxy', + owner => root, + group => root, + mode => '0644'; + } + + if $vip_monitor_ip { + $vip_monitor_ip_real = $vip_monitor_ip + } else { + $vip_monitor_ip_real = $vip_public_ip + } + + haproxy::listen { 'monitor': + ipaddress => $vip_monitor_ip_real, + ports => '9300', + options => { + 'mode' => 'http', + 'monitor-uri' => '/status', + 'stats' => ['enable','uri /admin','realm Haproxy\ Statistics',"auth ${haproxy_auth}", 'refresh 5s' ], + '' => template('cloud/loadbalancer/monitor.erb'), + } + } + + # Instanciate HAproxy binding + cloud::loadbalancer::binding { 'keystone_api_cluster': + ip => $keystone_api, + port => $ks_keystone_public_port, + bind_options => $keystone_bind_options, + firewall_settings => $firewall_settings, + } + cloud::loadbalancer::binding { 'keystone_api_admin_cluster': + ip => $keystone_api_admin, + port => $ks_keystone_admin_port, + bind_options => $keystone_admin_bind_options, + firewall_settings => $firewall_settings, + } + cloud::loadbalancer::binding { 'swift_api_cluster': + ip => $swift_api, + port => $ks_swift_public_port, + bind_options => $swift_bind_options, + httpchk => 'httpchk /healthcheck', + firewall_settings => $firewall_settings, + } + cloud::loadbalancer::binding { 'nova_api_cluster': + ip => $nova_api, + port => $ks_nova_public_port, + bind_options => $nova_bind_options, + firewall_settings => $firewall_settings, + } + cloud::loadbalancer::binding { 'ec2_api_cluster': + ip => $ec2_api, + port => $ks_ec2_public_port, + bind_options => $ec2_bind_options, + firewall_settings => $firewall_settings, + } + cloud::loadbalancer::binding { 'metadata_api_cluster': + ip => $metadata_api, + port => $ks_metadata_public_port, + bind_options => $metadata_bind_options, + firewall_settings => $firewall_settings, + } + cloud::loadbalancer::binding { 'spice_cluster': + ip => $spice, + port => $spice_port, + options => { + 'mode' => 'tcp', + 'option' => ['tcpka', 'tcplog', 'forwardfor'], + 'balance' => 'source', + 'timeout server' => '120m', + 'timeout client' => '120m', + }, + bind_options => $spice_bind_options, + firewall_settings => $firewall_settings, + } + cloud::loadbalancer::binding { 'rabbitmq_cluster': + ip => $rabbitmq, + port => $rabbitmq_port, + options => { + 'mode' => 'tcp', + 'option' => ['tcpka', 'tcplog', 'forwardfor'], + 'balance' => 'roundrobin', + }, + bind_options => $rabbitmq_bind_options, + firewall_settings => $firewall_settings, + } + cloud::loadbalancer::binding { 'trove_api_cluster': + ip => $trove_api, + port => $ks_trove_public_port, + bind_options => $trove_bind_options, + firewall_settings => $firewall_settings, + } + cloud::loadbalancer::binding { 'glance_api_cluster': + ip => $glance_api, + options => { + 'mode' => 'tcp', + 'balance' => 'source', + 'option' => ['tcpka', 'tcplog', 'forwardfor'], + 'timeout server' => '120m', + 'timeout client' => '120m', + }, + port => $ks_glance_api_public_port, + bind_options => $glance_api_bind_options, + firewall_settings => $firewall_settings, + } + cloud::loadbalancer::binding { 'glance_registry_cluster': + ip => $glance_registry, + port => $ks_glance_registry_internal_port, + bind_options => $glance_registry_bind_options, + firewall_settings => $firewall_settings, + } + cloud::loadbalancer::binding { 'neutron_api_cluster': + ip => $neutron_api, + port => $ks_neutron_public_port, + bind_options => $neutron_bind_options, + firewall_settings => $firewall_settings, + } + cloud::loadbalancer::binding { 'cinder_api_cluster': + ip => $cinder_api, + port => $ks_cinder_public_port, + bind_options => $cinder_bind_options, + firewall_settings => $firewall_settings, + } + cloud::loadbalancer::binding { 'ceilometer_api_cluster': + ip => $ceilometer_api, + port => $ks_ceilometer_public_port, + bind_options => $ceilometer_bind_options, + firewall_settings => $firewall_settings, + } + if 'ssl' in $heat_api_bind_options { + $heat_api_options = { + 'reqadd' => 'X-Forwarded-Proto:\ https if { ssl_fc }' } + } else { + $heat_api_options = {} + } + cloud::loadbalancer::binding { 'heat_api_cluster': + ip => $heat_api, + port => $ks_heat_public_port, + bind_options => $heat_api_bind_options, + options => $heat_api_options, + firewall_settings => $firewall_settings, + } + if 'ssl' in $heat_cfn_bind_options { + $heat_cfn_options = { + 'reqadd' => 'X-Forwarded-Proto:\ https if { ssl_fc }' } + } else { + $heat_cfn_options = { } + } + cloud::loadbalancer::binding { 'heat_cfn_api_cluster': + ip => $heat_cfn_api, + port => $ks_heat_cfn_public_port, + bind_options => $heat_cfn_bind_options, + options => $heat_cfn_options, + firewall_settings => $firewall_settings, + } + if 'ssl' in $heat_cloudwatch_bind_options { + $heat_cloudwatch_options = { + 'reqadd' => 'X-Forwarded-Proto:\ https if { ssl_fc }' } + } else { + $heat_cloudwatch_options = { } + } + cloud::loadbalancer::binding { 'heat_cloudwatch_api_cluster': + ip => $heat_cloudwatch_api, + port => $ks_heat_cloudwatch_public_port, + bind_options => $heat_cloudwatch_bind_options, + options => $heat_cloudwatch_options, + firewall_settings => $firewall_settings, + } + + $horizon_ssl_options = { + 'mode' => 'tcp', + 'cookie' => 'sessionid prefix', + 'balance' => 'leastconn' + } + + if 'ssl' in $horizon_bind_options { + $horizon_options = { + 'cookie' => 'sessionid prefix', + 'reqadd' => 'X-Forwarded-Proto:\ https if { ssl_fc }', + 'balance' => 'leastconn' + } + } else { + $horizon_options = { + 'cookie' => 'sessionid prefix', + 'balance' => 'leastconn' + } + } + + cloud::loadbalancer::binding { 'horizon_cluster': + ip => $horizon, + port => $horizon_port, + httpchk => "httpchk GET /${::cloud::params::horizon_auth_url} \"HTTP/1.0\\r\\nUser-Agent: HAproxy-${::hostname}\"", + options => $horizon_options, + bind_options => $horizon_bind_options, + firewall_settings => $firewall_settings, + } + + cloud::loadbalancer::binding { 'horizon_ssl_cluster': + ip => $horizon_ssl, + port => $horizon_ssl_port, + httpchk => 'ssl-hello-chk', + options => $horizon_ssl_options, + bind_options => $horizon_ssl_bind_options, + firewall_settings => $firewall_settings, + } + + if (member(any2array($keepalived_public_ipvs), $galera_ip)) { + warning('Exposing Galera cluster to public network is a security issue.') + } + haproxy::listen { 'galera_cluster': + ipaddress => $galera_ip, + ports => 3306, + options => { + 'maxconn' => '1000', + 'mode' => 'tcp', + 'balance' => 'roundrobin', + 'option' => ['tcpka', 'tcplog', 'httpchk'], #httpchk mandatory expect 200 on port 9000 + 'timeout client' => '400s', + 'timeout server' => '400s', + }, + bind_options => $galera_bind_options, + } + + if $galera_slave { + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow galera-slave binding access': + port => '3307', + extras => $firewall_settings, + } + } + + haproxy::listen { 'galera_readonly_cluster': + ipaddress => $galera_ip, + ports => 3307, + options => { + 'maxconn' => '1000', + 'mode' => 'tcp', + 'balance' => 'roundrobin', + 'option' => ['tcpka', 'tcplog', 'httpchk'], #httpchk mandatory expect 200 on port 9000 + 'timeout client' => '400s', + 'timeout server' => '400s', + }, + bind_options => $galera_bind_options, + } + } + + # Allow HAProxy to bind to a non-local IP address + $haproxy_sysctl_settings = { + 'net.ipv4.ip_nonlocal_bind' => { value => 1 } + } + create_resources(sysctl::value,$haproxy_sysctl_settings) + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow galera binding access': + port => '3306', + extras => $firewall_settings, + } + cloud::firewall::rule{ '100 allow haproxy monitor access': + port => '9300', + extras => $firewall_settings, + } + cloud::firewall::rule{ '100 allow keepalived access': + port => undef, + proto => 'vrrp', + extras => $firewall_settings, + } + } + +} diff --git a/cloud/manifests/loadbalancer/binding.pp b/cloud/manifests/loadbalancer/binding.pp new file mode 100644 index 000000000..5fb957f14 --- /dev/null +++ b/cloud/manifests/loadbalancer/binding.pp @@ -0,0 +1,76 @@ +# +# Copyright (C) 2014 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. +define cloud::loadbalancer::binding ( + $ip, + $port, + $httpchk = undef, + $options = undef, + $bind_options = undef, + $firewall_settings = {}, +){ + + include cloud::loadbalancer + + # join all VIP together + $vip_public_ip_array = any2array($::cloud::loadbalancer::vip_public_ip) + $vip_internal_ip_array = any2array($::cloud::loadbalancer::vip_internal_ip) + if $::cloud::loadbalancer::vip_public_ip and $::cloud::loadbalancer::vip_internal_ip { + $all_vip_array = union($vip_public_ip_array, $vip_internal_ip_array) + } + if $::cloud::loadbalancer::vip_public_ip and ! $::cloud::loadbalancer::vip_internal_ip { + $all_vip_array = $vip_public_ip_array + } + if ! $::cloud::loadbalancer::vip_public_ip and $::cloud::loadbalancer::vip_internal_ip { + $all_vip_array = $vip_internal_ip_array + } + if ! $::cloud::loadbalancer::vip_internal_ip and ! $::cloud::loadbalancer::vip_public_ip { + fail('vip_public_ip and vip_internal_ip are both set to false, no binding is possible.') + } + + # when we do not want binding + if ($ip == false) { + notice("no HAproxy binding for ${name} has been enabled.") + } else { + # when we want both internal & public binding + if ($ip == true) { + $listen_ip_real = $all_vip_array + } else { + # when binding is specified in parameter + if (member($all_vip_array, $ip)) { + $listen_ip_real = $ip + } else { + fail("${ip} is not part of VIP pools.") + } + } + cloud::loadbalancer::listen_http { $name : + ports => $port, + httpchk => $httpchk, + options => $options, + listen_ip => $listen_ip_real, + bind_options => $bind_options; + } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ "100 allow ${name} binding access": + port => $port, + extras => $firewall_settings, + } + } + + } + +} diff --git a/cloud/manifests/loadbalancer/listen_http.pp b/cloud/manifests/loadbalancer/listen_http.pp new file mode 100644 index 000000000..bb1a26da7 --- /dev/null +++ b/cloud/manifests/loadbalancer/listen_http.pp @@ -0,0 +1,45 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Define:: +# +# cloud::loadbalancer::listen_http +# +define cloud::loadbalancer::listen_http( + $ports = 'unset', + $httpchk = 'httpchk', + $options = {}, + $bind_options = [], + $listen_ip = ['0.0.0.0']) { + + $options_basic = {'mode' => 'http', + 'balance' => 'roundrobin', + 'option' => ['tcpka', 'forwardfor', 'tcplog', $httpchk] } + + $options_custom = merge($options_basic, $options) + + if $options_custom['mode'] == 'http' { + $final_options = merge($options_custom, { 'http-check' => 'expect ! rstatus ^5' }) + } else { + $final_options = $options_custom + } + + haproxy::listen { $name: + ipaddress => $listen_ip, + ports => $ports, + options => $final_options, + bind_options => $bind_options, + } +} diff --git a/cloud/manifests/logging.pp b/cloud/manifests/logging.pp new file mode 100644 index 000000000..2af0c11e0 --- /dev/null +++ b/cloud/manifests/logging.pp @@ -0,0 +1,24 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::logging +# +# Configure common logging +# +class cloud::logging{ + + include ::fluentd + +} diff --git a/cloud/manifests/logging/agent.pp b/cloud/manifests/logging/agent.pp new file mode 100644 index 000000000..167caf7cb --- /dev/null +++ b/cloud/manifests/logging/agent.pp @@ -0,0 +1,73 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::logging::agent +# +# Configure logging agent +# +# === Parameters: +# +# [*syslog_enable*] +# (optional) Enable the configuration of rsyslog +# Defaults to false +# +# [*sources*] +# (optional) Fluentd sources +# Defaults to empty hash +# +# [*matches*] +# (optional) Fluentd matches +# Defaults to empty hash +# +# [*plugins*] +# (optional) Fluentd plugins to install +# Defaults to empty hash +# +# [*logrotate_rule*] +# (optional) A log rotate rule for the logging agent +# Defaults to empty hash +# +class cloud::logging::agent( + $syslog_enable = false, + $sources = {}, + $matches = {}, + $plugins = {}, + $logrotate_rule = $cloud::params::logging_agent_logrotate_rule, +) inherits cloud::params { + + include cloud::logging + + if $syslog_enable { + include rsyslog::client + } + + file { '/var/db': + ensure => directory, + } -> + file { '/var/db/td-agent': + ensure => 'directory', + owner => 'td-agent', + group => 'td-agent', + require => Class['fluentd'], + } + + ensure_resource('fluentd::configfile', keys($sources)) + ensure_resource('fluentd::configfile', keys($matches)) + create_resources('fluentd::source', $sources, {'require' => 'File[/var/db/td-agent]', 'notify' => 'Service[td-agent]'}) + create_resources('fluentd::match', $matches, {'notify' => 'Service[td-agent]'}) + create_resources('fluentd::install_plugin', $plugins) + create_resources('logrotate::rule', $logrotate_rule) + +} diff --git a/cloud/manifests/logging/server.pp b/cloud/manifests/logging/server.pp new file mode 100644 index 000000000..766678755 --- /dev/null +++ b/cloud/manifests/logging/server.pp @@ -0,0 +1,25 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::logging::server +# +class cloud::logging::server{ + + include ::elasticsearch + include ::kibana3 + include cloud::logging::agent + elasticsearch::instance {'fluentd' : } + +} diff --git a/cloud/manifests/messaging.pp b/cloud/manifests/messaging.pp new file mode 100644 index 000000000..6a9931608 --- /dev/null +++ b/cloud/manifests/messaging.pp @@ -0,0 +1,139 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::messaging +# +# Install Messsaging Server (RabbitMQ) +# +# === Parameters: +# +# [*rabbit_names*] +# (optional) List of RabbitMQ servers. Should be an array. +# Defaults to $::hostname +# +# [*rabbit_password*] +# (optional) Password to connect to OpenStack queues. +# Defaults to 'rabbitpassword' +# +# [*cluster_node_type*] +# (optional) Store the queues on the disc or in the RAM. +# Could be set to 'disk' or 'ram'. +# Defaults to 'disc' +# +# [*haproxy_binding*] +# (optional) Enable or not HAproxy binding for load-balancing. +# Defaults to false +# +# [*rabbitmq_ip*] +# (optional) IP address of RabbitMQ interface. +# Required when using HAproxy binding. +# Defaults to $::ipaddress +# +# [*rabbitmq_port*] +# (optional) Port of RabbitMQ service. +# Defaults to '5672' +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::messaging( + $cluster_node_type = 'disc', + $rabbit_names = $::hostname, + $rabbit_password = 'rabbitpassword', + $haproxy_binding = false, + $rabbitmq_ip = $::ipaddress, + $rabbitmq_port = '5672', + $firewall_settings = {}, +){ + + # we ensure having an array + $array_rabbit_names = any2array($rabbit_names) + + Class['rabbitmq'] -> Rabbitmq_vhost <<| |>> + Class['rabbitmq'] -> Rabbitmq_user <<| |>> + Class['rabbitmq'] -> Rabbitmq_user_permissions <<| |>> + + # Packaging issue: https://bugzilla.redhat.com/show_bug.cgi?id=1033305 + if $::osfamily == 'RedHat' { + file {'/usr/sbin/rabbitmq-plugins': + ensure => link, + target => '/usr/lib/rabbitmq/bin/rabbitmq-plugins' + } + + file {'/usr/sbin/rabbitmq-env': + ensure => link, + target => '/usr/lib/rabbitmq/bin/rabbitmq-env' + } + } + + class { 'rabbitmq': + delete_guest_user => true, + config_cluster => true, + cluster_nodes => $array_rabbit_names, + wipe_db_on_cookie_change => true, + cluster_node_type => $cluster_node_type, + node_ip_address => $rabbitmq_ip, + port => $rabbitmq_port, + } + + rabbitmq_vhost { '/': + provider => 'rabbitmqctl', + require => Class['rabbitmq'], + } + rabbitmq_user { ['nova','glance','neutron','cinder','ceilometer','heat','trove']: + admin => true, + password => $rabbit_password, + provider => 'rabbitmqctl', + require => Class['rabbitmq'] + } + rabbitmq_user_permissions {[ + 'nova@/', + 'glance@/', + 'neutron@/', + 'cinder@/', + 'ceilometer@/', + 'heat@/', + 'trove@/', + ]: + configure_permission => '.*', + write_permission => '.*', + read_permission => '.*', + provider => 'rabbitmqctl', + } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow rabbitmq access': + port => $rabbitmq_port, + extras => $firewall_settings, + } + cloud::firewall::rule{ '100 allow rabbitmq management access': + port => '55672', + extras => $firewall_settings, + } + } + + if $haproxy_binding { + @@haproxy::balancermember{"${::fqdn}-rabbitmq": + listening_service => 'rabbitmq_cluster', + server_names => $::hostname, + ipaddresses => $rabbitmq_ip, + ports => $rabbitmq_port, + options => 'check inter 5s rise 2 fall 3' + } + } + +} diff --git a/cloud/manifests/monitoring/agent/sensu.pp b/cloud/manifests/monitoring/agent/sensu.pp new file mode 100644 index 000000000..743a0e86b --- /dev/null +++ b/cloud/manifests/monitoring/agent/sensu.pp @@ -0,0 +1,21 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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 cloud::monitoring::agent::sensu { + + Package['sensu'] -> Sensu::Plugin <<| |>> + + include ::sensu +} diff --git a/cloud/manifests/monitoring/server/sensu.pp b/cloud/manifests/monitoring/server/sensu.pp new file mode 100644 index 000000000..1b2af830b --- /dev/null +++ b/cloud/manifests/monitoring/server/sensu.pp @@ -0,0 +1,105 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# [*checks*] +# (optionnal) Hash of checks and their respective options +# Defaults to {}. +# Example : +# $checks = { +# 'ntp' => { +# 'command' => '/etc/sensu/plugins/check-ntp.sh'}, +# 'http' => { +# 'command' => '/etc/sensu/plugins/check-http.sh'}, +# } +# +# [*handlers*] +# (optionnal) Hash of handlers and their respective options +# Defaults to {}. +# Example : +# $handlers = { +# 'mail' => { +# 'command' => 'mail -s "Sensu Alert" contact@example.com'}, +# } +# +# [*plugins*] +# (optionnal) Hash of handlers and their respective options +# Defaults to {}. +# Example : +# $plugins = { +# 'http://www.example.com/ntp.sh' => { +# 'type' => 'url', +# 'install_path' => '/etc/sensu/plugins', +# } +# } +# +# [*rabbitmq_user*] +# (optionnal) Rabbitmq user +# Defaults to 'sensu' +# +# [*rabbitmq_password*] +# (optionnal) Rabbitmq_password +# Defaults to 'rabbitpassword' +# +# [*rabbitmq_vhost*] +# (optionnal) Rabbitmq vhost +# Defaults to '/sensu' +# +# [*uchiwa_ip*] +# (optionnal) IP address to bind uchiwa to +# Defaults to '%{::ipaddress}' +class cloud::monitoring::server::sensu ( + $checks = {}, + $handlers = {}, + $plugins = {}, + $rabbitmq_user = 'sensu', + $rabbitmq_password = 'rabbitpassword', + $rabbitmq_vhost = '/sensu', + $uchiwa_ip = $::ipaddress, +) { + + @@rabbitmq_user { $rabbitmq_user : + password => $rabbitmq_password, + } + @@rabbitmq_vhost { $rabbitmq_vhost : + ensure => present, + } + @@rabbitmq_user_permissions { "${rabbitmq_user}@${rabbitmq_vhost}" : + configure_permission => '.*', + read_permission => '.*', + write_permission => '.*', + } + + $rabbitmq_user_realized = query_nodes("Rabbitmq_user['${rabbitmq_user}']") + + if size($rabbitmq_user_realized) >= 1 { + + Service['redis-6379'] -> Service['sensu-api'] -> Service['sensu-server'] -> Service['uchiwa'] + Service['sensu-server'] -> Sensu::Plugin <<| |>> + + + include cloud::monitoring::agent::sensu + include redis + + create_resources('sensu::check', $checks) + create_resources('sensu::handler', $handlers) + create_resources('@@sensu::plugin', $plugins) + + include ::uchiwa + uchiwa::api { 'OpenStack' : + host => $uchiwa_ip, + } + } + +} diff --git a/cloud/manifests/network.pp b/cloud/manifests/network.pp new file mode 100644 index 000000000..0b16f7bce --- /dev/null +++ b/cloud/manifests/network.pp @@ -0,0 +1,115 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::network +# +# Common class for network nodes +# +# === Parameters: +# +# [*rabbit_hosts*] +# (optional) List of RabbitMQ servers. Should be an array. +# Defaults to ['127.0.0.1:5672'] +# +# [*rabbit_password*] +# (optional) Password to connect to nova queues. +# Defaults to 'rabbitpassword' +# +# [*verbose*] +# (optional) Set log output to verbose output +# Defaults to true +# +# [*debug*] +# (optional) Set log output to debug output +# Defaults to true +# +# [*api_eth*] +# (optional) Which interface we bind the Neutron API server. +# Defaults to '127.0.0.1' +# +# [*use_syslog*] +# (optional) Use syslog for logging +# Defaults to true +# +# [*log_facility*] +# (optional) Syslog facility to receive log lines +# Defaults to 'LOG_LOCAL0' +# +# [*dhcp_lease_duration*] +# (optional) DHCP Lease duration (in seconds) +# Defaults to '120' +# +# [*plugin*] +# (optional) Neutron plugin name +# Supported values: 'ml2', 'n1kv'. +# Defaults to 'ml2' +# +class cloud::network( + $verbose = true, + $debug = true, + $rabbit_hosts = ['127.0.0.1:5672'], + $rabbit_password = 'rabbitpassword', + $api_eth = '127.0.0.1', + $use_syslog = true, + $log_facility = 'LOG_LOCAL0', + $dhcp_lease_duration = '120', + $plugin = 'ml2', +) { + + # Disable twice logging if syslog is enabled + if $use_syslog { + $log_dir = false + neutron_config { + 'DEFAULT/logging_context_format_string': value => '%(process)d: %(levelname)s %(name)s [%(request_id)s %(user_identity)s] %(instance)s%(message)s'; + 'DEFAULT/logging_default_format_string': value => '%(process)d: %(levelname)s %(name)s [-] %(instance)s%(message)s'; + 'DEFAULT/logging_debug_format_suffix': value => '%(funcName)s %(pathname)s:%(lineno)d'; + 'DEFAULT/logging_exception_prefix': value => '%(process)d: TRACE %(name)s %(instance)s'; + } + } else { + $log_dir = '/var/log/neutron' + } + + case $plugin { + 'ml2': { + $core_plugin = 'neutron.plugins.ml2.plugin.Ml2Plugin' + } + 'n1kv': { + $core_plugin = 'neutron.plugins.cisco.network_plugin.PluginV2' + } + default: { + err "${plugin} plugin is not supported." + } + } + + class { 'neutron': + allow_overlapping_ips => true, + verbose => $verbose, + debug => $debug, + rabbit_user => 'neutron', + rabbit_hosts => $rabbit_hosts, + rabbit_password => $rabbit_password, + rabbit_virtual_host => '/', + bind_host => $api_eth, + log_facility => $log_facility, + use_syslog => $use_syslog, + dhcp_agents_per_network => '2', + core_plugin => $core_plugin, + service_plugins => ['neutron.services.loadbalancer.plugin.LoadBalancerPlugin','neutron.services.metering.metering_plugin.MeteringPlugin','neutron.services.l3_router.l3_router_plugin.L3RouterPlugin'], + log_dir => $log_dir, + dhcp_lease_duration => $dhcp_lease_duration, + report_interval => '30', + } + +} diff --git a/cloud/manifests/network/controller.pp b/cloud/manifests/network/controller.pp new file mode 100644 index 000000000..ebfbb1615 --- /dev/null +++ b/cloud/manifests/network/controller.pp @@ -0,0 +1,278 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Network Controller node (API + Scheduler) +# +# === Parameters: +# +# [*neutron_db_host*] +# (optional) Host where user should be allowed all privileges for database. +# Defaults to 127.0.0.1 +# +# [*neutron_db_user*] +# (optional) Name of neutron DB user. +# Defaults to trove +# +# [*neutron_db_password*] +# (optional) Password that will be used for the neutron db user. +# Defaults to 'neutronpassword' +# +# [*ks_neutron_password*] +# (optional) Password used by Neutron to connect to Keystone API +# Defaults to 'neutronpassword' +# +# [*ks_keystone_admin_host*] +# (optional) Admin Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_admin_proto*] +# (optional) Protocol for admin endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_keystone_public_port*] +# (optional) TCP port to connect to Keystone API from public network +# Defaults to '5000' +# +# [*ks_neutron_public_port*] +# (optional) TCP port to connect to Neutron API from public network +# Defaults to '9696' +# +# [*api_eth*] +# (optional) Which interface we bind the Neutron server. +# Defaults to '127.0.0.1' +# +# [*ks_admin_tenant*] +# (optional) Admin tenant name in Keystone +# Defaults to 'admin' +# +# +# [*nova_url*] +# (optional) URL for connection to nova (Only supports one nova region +# currently). +# Defaults to 'http://127.0.0.1:8774/v2' +# +# [*nova_admin_auth_url*] +# (optional) Authorization URL for connection to nova in admin context. +# Defaults to 'http://127.0.0.1:5000/v2.0' +# +# [*nova_admin_username*] +# (optional) Username for connection to nova in admin context +# Defaults to 'nova' +# +# [*nova_admin_tenant_name*] +# (optional) The name of the admin nova tenant +# Defaults to 'services' +# +# [*nova_admin_password*] +# (optional) Password for connection to nova in admin context. +# Defaults to 'novapassword' +# +# [*nova_region_name*] +# (optional) Name of nova region to use. Useful if keystone manages more than +# one region. +# Defaults to 'RegionOne' +# +# [*manage_ext_network*] +# (optionnal) Manage or not external network with provider network API +# Defaults to false. +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +# [*tenant_network_types*] +# (optional) Handled tenant network types +# Defaults to ['gre'] +# Possible value ['local', 'flat', 'vlan', 'gre', 'vxlan'] +# +# [*type_drivers*] +# (optional) Drivers to load +# Defaults to ['gre', 'vlan', 'flat'] +# Possible value ['local', 'flat', 'vlan', 'gre', 'vxlan'] +# +# [*plugin*] +# (optional) Neutron plugin name +# Supported values: 'ml2', 'n1kv'. +# Defaults to 'ml2' +# +# [*ks_keystone_admin_port*] +# (optional) TCP port to connect to Keystone API from admin network +# Defaults to '35357' +# +# [*provider_vlan_ranges*] +# (optionnal) VLAN range for provider networks +# Defaults to ['physnet1:1000:2999'] +# +# [*flat_networks*] +# (optionnal) List of physical_network names with which flat networks +# can be created. Use * to allow flat networks with arbitrary +# physical_network names. +# Should be an array. +# Default to ['public']. +# +# [*n1kv_vsm_ip*] +# (required) N1KV VSM (Virtual Supervisor Module) VM's IP. +# Defaults to 127.0.0.1 +# +# [*n1kv_vsm_password*] +# (required) N1KV VSM (Virtual Supervisor Module) password. +# Defaults to secrete +# +# [*tunnel_id_ranges*] +# (optional) GRE tunnel id ranges. used by he ml2 plugin +# List of colon-separated id ranges +# Defaults to ['1:10000'] +# +# [*vni_ranges*] +# (optional) VxLan Network ID range. used by the ml2 plugin +# List of colon-separated id ranges +# Defautls to ['1:10000'] +# +class cloud::network::controller( + $neutron_db_host = '127.0.0.1', + $neutron_db_user = 'neutron', + $neutron_db_password = 'neutronpassword', + $ks_neutron_password = 'neutronpassword', + $ks_keystone_admin_host = '127.0.0.1', + $ks_keystone_admin_proto = 'http', + $ks_keystone_public_port = 5000, + $ks_neutron_public_port = 9696, + $api_eth = '127.0.0.1', + $ks_admin_tenant = 'admin', + $nova_url = 'http://127.0.0.1:8774/v2', + $nova_admin_auth_url = 'http://127.0.0.1:5000/v2.0', + $nova_admin_username = 'nova', + $nova_admin_tenant_name = 'services', + $nova_admin_password = 'novapassword', + $nova_region_name = 'RegionOne', + $manage_ext_network = false, + $firewall_settings = {}, + $flat_networks = ['public'], + $tenant_network_types = ['gre'], + $type_drivers = ['gre', 'vlan', 'flat'], + $provider_vlan_ranges = ['physnet1:1000:2999'], + $plugin = 'ml2', + # only needed by cisco n1kv plugin + $n1kv_vsm_ip = '127.0.0.1', + $n1kv_vsm_password = 'secrete', + $ks_keystone_admin_port = 35357, + # only needed by ml2 plugin + $tunnel_id_ranges = ['1:10000'], + $vni_ranges = ['1:10000'], +) { + + include 'cloud::network' + + $encoded_user = uriescape($neutron_db_user) + $encoded_password = uriescape($neutron_db_password) + + class { 'neutron::server': + auth_password => $ks_neutron_password, + auth_host => $ks_keystone_admin_host, + auth_protocol => $ks_keystone_admin_proto, + auth_port => $ks_keystone_public_port, + database_connection => "mysql://${encoded_user}:${encoded_password}@${neutron_db_host}/neutron?charset=utf8", + mysql_module => '2.2', + api_workers => $::processorcount, + agent_down_time => '60', + } + + case $plugin { + 'ml2': { + $core_plugin = 'neutron.plugins.ml2.plugin.Ml2Plugin' + class { 'neutron::plugins::ml2': + type_drivers => $type_drivers, + tenant_network_types => $tenant_network_types, + network_vlan_ranges => $provider_vlan_ranges, + tunnel_id_ranges => $tunnel_id_ranges, + vni_ranges => $vni_ranges, + flat_networks => $flat_networks, + mechanism_drivers => ['linuxbridge', 'openvswitch','l2population'], + enable_security_group => true + } + } + + 'n1kv': { + $core_plugin = 'neutron.plugins.cisco.network_plugin.PluginV2' + class { 'neuton::plugins::cisco': + database_user => $neutron_db_user, + database_password => $neutron_db_password, + database_host => $neutron_db_host, + keystone_auth_url => "${ks_keystone_admin_proto}://${ks_keystone_admin_host}:${ks_keystone_admin_port}/v2.0/", + keystone_password => $ks_neutron_password, + vswitch_plugin => 'neutron.plugins.cisco.n1kv.n1kv_neutron_plugin.N1kvNeutronPluginV2', + } + neutron_plugin_cisco { + 'securitygroup/firewall_driver': value => 'neutron.agent.firewall.NoopFirewallDriver'; + "N1KV:${n1kv_vsm_ip}/username": value => 'admin'; + "N1KV:${n1kv_vsm_ip}/password": value => $n1kv_vsm_password; + # TODO (EmilienM) not sure about this one: + 'database/connection': value => "mysql://${neutron_db_user}:${neutron_db_password}@${neutron_db_host}/neutron"; + } + } + + default: { + err "${plugin} plugin is not supported." + } + } + + class { 'neutron::server::notifications': + nova_url => $nova_url, + nova_admin_auth_url => $nova_admin_auth_url, + nova_admin_username => $nova_admin_username, + nova_admin_tenant_name => $nova_admin_tenant_name, + nova_admin_password => $nova_admin_password, + nova_region_name => $nova_region_name + } + + if $manage_ext_network { + neutron_network {'public': + provider_network_type => 'flat', + provider_physical_network => 'public', + shared => true, + router_external => true + } + } + + # Note(EmilienM): + # We check if DB tables are created, if not we populate Neutron DB. + # It's a hack to fit with our setup where we run MySQL/Galera + Neutron_config<| |> -> + exec {'neutron_db_sync': + command => 'neutron-db-manage --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/plugin.ini upgrade head', + path => '/usr/bin', + user => 'neutron', + unless => "/usr/bin/mysql neutron -h ${neutron_db_host} -u ${encoded_user} -p${encoded_password} -e \"show tables\" | /bin/grep Tables", + require => 'Neutron_config[DEFAULT/service_plugins]', + notify => Service['neutron-server'] + } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow neutron-server access': + port => $ks_neutron_public_port, + extras => $firewall_settings, + } + } + + @@haproxy::balancermember{"${::fqdn}-neutron_api": + listening_service => 'neutron_api_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => $ks_neutron_public_port, + options => 'check inter 2000 rise 2 fall 5' + } + +} diff --git a/cloud/manifests/network/dhcp.pp b/cloud/manifests/network/dhcp.pp new file mode 100644 index 000000000..45da0dd18 --- /dev/null +++ b/cloud/manifests/network/dhcp.pp @@ -0,0 +1,87 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: +# +# Network DHCP node +# +# === Parameters: +# +# [*veth_mtu*] +# (optional) Enforce the default virtual interface MTU (option 26) +# Defaults to 1500 +# +# [*debug*] +# (optional) Set log output to debug output +# Defaults to true +# +# [*dnsmasq_dns_servers*] +# (optional) An array of DNS IP used to configure Virtual server resolver +# Defaults to false +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::network::dhcp( + $veth_mtu = 1500, + $debug = true, + $dnsmasq_dns_servers = false, + $firewall_settings = {}, +) { + + include 'cloud::network' + include 'cloud::network::vswitch' + + class { 'neutron::agents::dhcp': + debug => $debug, + dnsmasq_config_file => '/etc/neutron/dnsmasq-neutron.conf', + enable_isolated_metadata => true + } + + if $dnsmasq_dns_servers { + neutron_dhcp_agent_config { 'DEFAULT/dnsmasq_dns_servers': + value => join($dnsmasq_dns_servers, ',') + } + } else { + neutron_dhcp_agent_config { 'DEFAULT/dnsmasq_dns_servers': + ensure => absent + } + } + + file { '/etc/neutron/dnsmasq-neutron.conf': + content => template('cloud/network/dnsmasq-neutron.conf.erb'), + owner => 'root', + mode => '0755', + group => 'root', + notify => Service['neutron-dhcp-agent'] + } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow dhcp in access': + port => '67', + proto => 'udp', + extras => $firewall_settings, + } + cloud::firewall::rule{ '100 allow dhcp out access': + port => '68', + proto => 'udp', + chain => 'OUTPUT', + extras => $firewall_settings, + } + } + +} diff --git a/cloud/manifests/network/l3.pp b/cloud/manifests/network/l3.pp new file mode 100644 index 000000000..0b7f407e2 --- /dev/null +++ b/cloud/manifests/network/l3.pp @@ -0,0 +1,92 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: +# +# Network L3 node +# +# === Parameters: +# +# [*debug*] +# (optional) Set log output to debug output +# Defaults to true +# +# [*ext_provider_net*] +# (optional) Manage L3 with another provider +# Defaults to false +# +# [*external_int*] +# (optional) The name of the external nic +# Defaults to eth1 +# +# [*manage_tso*] +# (optional) Disable TSO on Neutron interfaces +# Defaults to true +# +class cloud::network::l3( + $external_int = 'eth1', + $ext_provider_net = false, + $debug = true, + $manage_tso = true, +) { + + include 'cloud::network' + include 'cloud::network::vswitch' + + if ! $ext_provider_net { + vs_bridge{'br-ex': + external_ids => 'bridge-id=br-ex', + } -> + vs_port{$external_int: + ensure => present, + bridge => 'br-ex' + } + $external_network_bridge_real = 'br-ex' + } else { + $external_network_bridge_real = '' + } + + class { 'neutron::agents::l3': + debug => $debug, + external_network_bridge => $external_network_bridge_real + } + + class { 'neutron::agents::metering': + debug => $debug, + } + + # Disabling TSO/GSO/GRO + if $manage_tso { + if $::osfamily == 'Debian' { + ensure_resource ('exec','enable-tso-script', { + 'command' => '/usr/sbin/update-rc.d disable-tso defaults', + 'unless' => '/bin/ls /etc/rc*.d | /bin/grep disable-tso', + 'onlyif' => '/usr/bin/test -f /etc/init.d/disable-tso' + }) + } elsif $::osfamily == 'RedHat' { + ensure_resource ('exec','enable-tso-script', { + 'command' => '/usr/sbin/chkconfig disable-tso on', + 'unless' => '/bin/ls /etc/rc*.d | /bin/grep disable-tso', + 'onlyif' => '/usr/bin/test -f /etc/init.d/disable-tso' + }) + } + ensure_resource ('exec','start-tso-script', { + 'command' => '/etc/init.d/disable-tso start', + 'unless' => '/usr/bin/test -f /var/run/disable-tso.pid', + 'onlyif' => '/usr/bin/test -f /etc/init.d/disable-tso' + }) + } + +} diff --git a/cloud/manifests/network/lbaas.pp b/cloud/manifests/network/lbaas.pp new file mode 100644 index 000000000..c65a4f235 --- /dev/null +++ b/cloud/manifests/network/lbaas.pp @@ -0,0 +1,43 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: +# +# Network LBaaS node +# +# === Parameters: +# +# [*debug*] +# (optional) Set log output to debug output +# Defaults to true +# +# [*manage_haproxy_pkg*] +# (optional) Manage or not HAproxy package +# Defaults to true +# +class cloud::network::lbaas( + $debug = true, + $manage_haproxy_pkg = true +) { + + include 'cloud::network' + include 'cloud::network::vswitch' + + class { 'neutron::agents::lbaas': + manage_haproxy_package => $manage_haproxy_pkg, + debug => $debug, + } + +} diff --git a/cloud/manifests/network/metadata.pp b/cloud/manifests/network/metadata.pp new file mode 100644 index 000000000..929f9b37e --- /dev/null +++ b/cloud/manifests/network/metadata.pp @@ -0,0 +1,92 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Network Metadata node (need to be run once) +# Could be managed by spof_node manifest +# +# === Parameters: +# +# [*enabled*] +# (optional) State of the metadata service. +# Defaults to true +# +# [*debug*] +# (optional) Set log output to debug output +# Defaults to true +# +# [*ks_neutron_password*] +# (optional) Password used by Neutron to connect to Keystone API +# Defaults to 'neutronpassword' +# +# [*neutron_metadata_proxy_shared_secret*] +# (optional) Shared secret to validate proxies Neutron metadata requests +# Defaults to 'metadatapassword' +# +# [*nova_metadata_server*] +# (optional) Hostname or IP of the Nova metadata server +# Defaults to '127.0.0.1' +# +# [*ks_keystone_admin_host*] +# (optional) Admin Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_admin_proto*] +# (optional) Protocol for admin endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_keystone_admin_port*] +# (optional) TCP port to connect to Keystone API from admin network +# Defaults to '35357' +# +# [*ks_nova_internal_proto*] +# (optional) Protocol for public endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*auth_region*] +# (optional) OpenStack Region Name +# Defaults to 'RegionOne' +# +class cloud::network::metadata( + $enabled = true, + $debug = true, + $ks_neutron_password = 'neutronpassword', + $neutron_metadata_proxy_shared_secret = 'asecreteaboutneutron', + $nova_metadata_server = '127.0.0.1', + $ks_keystone_admin_proto = 'http', + $ks_keystone_admin_port = 35357, + $ks_keystone_admin_host = '127.0.0.1', + $auth_region = 'RegionOne', + $ks_nova_internal_proto = 'http' +) { + + include 'cloud::network' + include 'cloud::network::vswitch' + + class { 'neutron::agents::metadata': + enabled => $enabled, + shared_secret => $neutron_metadata_proxy_shared_secret, + debug => $debug, + metadata_ip => $nova_metadata_server, + auth_url => "${ks_keystone_admin_proto}://${ks_keystone_admin_host}:${ks_keystone_admin_port}/v2.0", + auth_password => $ks_neutron_password, + auth_region => $auth_region, + metadata_workers => $::processorcount + } + + neutron_metadata_agent_config { + 'DEFAULT/nova_metadata_protocol': value => $ks_nova_internal_proto; + } + +} diff --git a/cloud/manifests/network/vpn.pp b/cloud/manifests/network/vpn.pp new file mode 100644 index 000000000..f351f5920 --- /dev/null +++ b/cloud/manifests/network/vpn.pp @@ -0,0 +1,25 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Network VPNaaS node +# +class cloud::network::vpn{ + + include 'cloud::network' + include 'cloud::network::vswitch' + + class { 'neutron::agents::vpnaas': } + +} diff --git a/cloud/manifests/network/vswitch.pp b/cloud/manifests/network/vswitch.pp new file mode 100644 index 000000000..6e637cfef --- /dev/null +++ b/cloud/manifests/network/vswitch.pp @@ -0,0 +1,240 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Network vswitch class +# +# === Parameters: +# +# [*driver*] +# (optional) Neutron vswitch driver +# Supported values: 'ml2_ovs', 'ml2_lb', 'n1kv_vem'. +# Note: 'n1kv_vem' currently works only on Red Hat systems. +# Defaults to 'ml2_ovs' +# +# [*external_int*] +# (optionnal) Network interface to bind the external provider network +# Defaults to 'eth1'. +# +# [*external_bridge*] +# (optionnal) OVS bridge used to bind external provider network +# Defaults to 'br-pub'. +# +# [*manage_ext_network*] +# (optionnal) Manage or not external network with provider network API +# Defaults to false. +# +# [*tunnel_eth*] +# (optional) Interface IP used to build the tunnels +# Defaults to '127.0.0.1' +# +# [*tunnel_typeis] +# (optional) List of types of tunnels to use when utilizing tunnels +# Defaults to ['gre'] +# +# [*provider_bridge_mappings*] +# (optional) List of : +# +# [*n1kv_vsm_ip*] +# (required) N1KV VSM (Virtual Supervisor Module) VM's IP. +# Defaults to 127.0.0.1 +# +# [*n1kv_vsm_domainid*] +# (required) N1KV VSM DomainID. +# Defaults to 1000 +# +# [*host_mgmt_intf*] +# (required) Management Interface of node where VEM will be installed. +# Defaults to eth1 +# +# [*uplink_profile*] +# (optional) Uplink Interfaces that will be managed by VEM. The uplink +# port-profile that configures these interfaces should also be specified. +# (format) +# $uplink_profile = { 'eth1' => 'profile1', +# 'eth2' => 'profile2' +# }, +# Defaults to empty +# +# [*vtep_config*] +# (optional) Virtual tunnel interface configuration. +# Eg:VxLAN tunnel end-points. +# (format) +# $vtep_config = { 'vtep1' => { 'profile' => 'virtprof1', +# 'ipmode' => 'dhcp' +# }, +# 'vtep2' => { 'profile' => 'virtprof2', +# 'ipmode' => 'static', +# 'ipaddress' => '192.168.1.1', +# 'netmask' => '255.255.255.0' +# } +# }, +# Defaults to empty +# +# [*node_type*] +# (optional). Specify the type of node: 'compute' (or) 'network'. +# Defaults to 'compute' +# +# All the above parameter values will be used in the config file: n1kv.conf +# +# [*vteps_in_same_subnet*] +# (optional) +# The VXLAN tunnel interfaces created on VEM can belong to same IP-subnet. +# In such case, set this parameter to true. This results in below +# 'sysctl:ipv4' values to be modified. +# rp_filter (reverse path filtering) set to 2(Loose).Default is 1(Strict) +# arp_ignore (arp reply mode) set to 1:reply only if target ip matches +# that of incoming interface. Default is 0 +# Please refer Linux Documentation for detailed description +# http://lxr.free-electrons.com/source/Documentation/networking/ip-sysctl.txt +# +# If the tunnel interfaces are not in same subnet set this parameter to false. +# Note that setting to false causes no change in the sysctl settings and does +# not revert the changes made if it was originally set to true on a previous +# catalog run. +# +# Defaults to false +# +# [*n1kv_source*] +# (optional) +# n1kv_source ==> VEM package location. One of below +# A)URL of yum repository that hosts VEM package. +# B)VEM RPM/DPKG file name, If present locally in 'files' folder +# C)If not specified, assumes that VEM image is available in +# default enabled repositories. +# Defaults to empty +# +# [*n1kv_version*] +# (optional). Specify VEM package version to be installed. +# Not applicable if 'n1kv_source' is a file. (Option-B above) +# Defaults to 'present' +# +# [*tunnel_types*] +# (optional) List of types of tunnels to use when utilizing tunnels. +# Supported tunnel types are: vxlan. +# Defaults to ['gre'] +# +# [*n1kv_vsm_domain_id*] +# (optional) N1000 KV Domain ID (does nothing?) +# Defaults to 1000 +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::network::vswitch( + # common + $driver = 'ml2_ovs', + $manage_ext_network = false, + $external_int = 'eth1', + $external_bridge = 'br-pub', + $firewall_settings = {}, + # common to ml2 + $tunnel_types = ['gre'], + $tunnel_eth = '127.0.0.1', + # ml2_ovs + $provider_bridge_mappings = ['public:br-pub'], + # n1kv_vem + $n1kv_vsm_ip = '127.0.0.1', + $n1kv_vsm_domain_id = 1000, + $host_mgmt_intf = 'eth1', + $uplink_profile = {}, + $vtep_config = {}, + $node_type = 'compute', + $vteps_in_same_subnet = false, + $n1kv_source = '', + $n1kv_version = 'present', +) { + + include 'cloud::network' + + case $driver { + 'ml2_ovs': { + class { 'neutron::agents::ml2::ovs': + enable_tunneling => true, + l2_population => true, + polling_interval => '15', + tunnel_types => $tunnel_types, + bridge_mappings => $provider_bridge_mappings, + local_ip => $tunnel_eth + } + + if $::osfamily == 'RedHat' { + kmod::load { 'ip_gre': } + } + } + + 'ml2_lb': { + class { 'neutron::agents::ml2::linuxbridge': + l2_population => true, + polling_interval => '15', + tunnel_types => $tunnel_types, + local_ip => $tunnel_eth + } + + if $::osfamily == 'RedHat' { + kmod::load { 'ip_gre': } + } + } + + 'n1kv_vem': { + # We don't check if we are on Red Hat system + # (already done by puppet-neutron) + class { 'neutron::agents::n1kv_vem': + n1kv_vsm_ip => $n1kv_vsm_ip, + n1kv_vsm_domain_id => $n1kv_vsm_domain_id, + host_mgmt_intf => $host_mgmt_intf, + uplink_profile => $uplink_profile, + vtep_config => $vtep_config, + node_type => $node_type, + vteps_in_same_subnet => $vteps_in_same_subnet, + n1kv_source => $n1kv_source, + n1kv_version => $n1kv_version, + } + ensure_resource('package', 'nexus1000v', { + ensure => present + }) + } + + default: { + err "${driver} driver is not supported." + } + } + + if $manage_ext_network { + vs_port {$external_int: + ensure => present, + bridge => $external_bridge + } + } + + if $::cloud::manage_firewall { + if ('gre' in $tunnel_types) { + cloud::firewall::rule{ '100 allow gre access': + port => undef, + proto => 'gre', + extras => $firewall_settings, + } + } + if ('vxlan' in $tunnel_types) { + cloud::firewall::rule{ '100 allow vxlan access': + port => '4789', + proto => 'udp', + extras => $firewall_settings, + } + } + } + +} diff --git a/cloud/manifests/object.pp b/cloud/manifests/object.pp new file mode 100644 index 000000000..0ad00e70b --- /dev/null +++ b/cloud/manifests/object.pp @@ -0,0 +1,35 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::object +# +# Common class for object storage nodes +# +# === Parameters: +# +# [*swift_hash_suffix*] +# (required) String of text to be used as a salt when hashing to determine mappings in the ring. +# +class cloud::object( + $swift_hash_suffix = undef +) { + + class { 'swift': + swift_hash_suffix => $swift_hash_suffix, + } + + class {'cloud::object::tweaking': } + +} diff --git a/cloud/manifests/object/controller.pp b/cloud/manifests/object/controller.pp new file mode 100644 index 000000000..1e05007c7 --- /dev/null +++ b/cloud/manifests/object/controller.pp @@ -0,0 +1,194 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::object::controller +# +# Swift Proxy node +# +# === Parameters: +# +# [*ks_keystone_admin_host*] +# (optional) Admin Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_admin_port*] +# (optional) TCP port to connect to Keystone API from admin network +# Defaults to '35357' +# +# [*ks_keystone_internal_host*] +# (optional) Internal Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_internal_port*] +# (optional) TCP port to connect to Keystone API from internal network +# Defaults to '5000' +# +# [*ks_keystone_internal_proto*] +# (optional) Protocol for public endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_keystone_admin_proto*] +# (optional) Protocol for admin endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_swift_internal_port*] +# (optional) TCP port to connect to Swift from internal network +# Defaults to '8080' +# +# [*ks_swift_password*] +# (optional) Password used by Swift to connect to Keystone API +# Defaults to 'swiftpassword' +# +# [*ks_swift_dispersion_password*] +# (optional) Password of the dispersion tenant, used for swift-dispersion-report +# and swift-dispersion-populate tools. +# Defaults to 'dispersion' +# +# [*api_eth*] +# (optional) Which interface we bind the Swift proxy server. +# Defaults to '127.0.0.1' +# +# [*memcache_servers*] +# (optionnal) Memcached servers used by Keystone. Should be an array. +# Defaults to ['127.0.0.1:11211'] +# +# [*statsd_host*] +# (optional) Hostname or IP of the statd server. +# Defaults to '127.0.0.1' +# +# [*statsd_port*] +# (optional) TCP port of the statd server +# Defaults to '4125' +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::object::controller( + $ks_keystone_admin_host = '127.0.0.1', + $ks_keystone_admin_port = 35357, + $ks_keystone_internal_host = '127.0.0.1', + $ks_keystone_internal_port = 5000, + $ks_swift_dispersion_password = 'dispersion', + $ks_swift_internal_port = 8080, + $ks_keystone_internal_proto = 'http', + $ks_keystone_admin_proto = 'http', + $ks_swift_password = 'swiftpassword', + $statsd_host = '127.0.0.1', + $statsd_port = 4125, + $memcache_servers = ['127.0.0.1:11211'], + $api_eth = '127.0.0.1', + $firewall_settings = {}, +) { + + include 'cloud::object' + + class { 'swift::proxy': + proxy_local_net_ip => $api_eth, + port => $ks_swift_internal_port, + pipeline => [ + #'catch_errors', 'healthcheck', 'cache', 'bulk', 'ratelimit', + 'catch_errors', 'healthcheck', 'cache', 'ratelimit', + #'swift3', 's3token', 'container_quotas', 'account_quotas', 'tempurl', + 'swift3', 's3token', 'tempurl', + 'formpost', 'staticweb', + # TODO: (spredzy) re enable ceilometer middleware after the current bug as been fixed + # https://review.openstack.org/#/c/97702 + # 'ceilometer', + 'authtoken', 'keystone', + 'proxy-logging', 'proxy-server'], + account_autocreate => true, + log_level => 'DEBUG', + workers => inline_template('<%= @processorcount.to_i * 2 %> +cors_allow_origin = <%= scope.lookupvar("swift_cors_allow_origin") %> +log_statsd_host = <%= scope.lookupvar("statsd_host") %> +log_statsd_port = <%= scope.lookupvar("statsd_port") %> +log_statsd_default_sample_rate = 1 +'), + } + + class{'swift::proxy::cache': + memcache_servers => inline_template( + '<%= scope.lookupvar("memcache_servers").join(",") %>'), + } + class { 'swift::proxy::proxy-logging': } + class { 'swift::proxy::healthcheck': } + class { 'swift::proxy::catch_errors': } + class { 'swift::proxy::ratelimit': } + #class { 'swift::proxy::account_quotas': } + #class { 'swift::proxy::container_quotas': } + #class { 'swift::proxy::bulk': } + class { 'swift::proxy::staticweb': } + # TODO: (spredzy) re enable ceilometer middleware after the current bug as been fixed + # https://review.openstack.org/#/c/97702 + #class { 'swift::proxy::ceilometer': } + class { 'swift::proxy::keystone': + operator_roles => ['admin', 'SwiftOperator', 'ResellerAdmin'], + } + + class { 'swift::proxy::tempurl': } + class { 'swift::proxy::formpost': } + class { 'swift::proxy::authtoken': + admin_password => $ks_swift_password, + auth_host => $ks_keystone_admin_host, + auth_port => $ks_keystone_admin_port, + auth_protocol => $ks_keystone_admin_proto, + delay_auth_decision => inline_template('1 +cache = swift.cache') + } + class { 'swift::proxy::swift3': + ensure => 'latest', + } + class { 'swift::proxy::s3token': + auth_host => $ks_keystone_admin_host, + auth_port => $ks_keystone_admin_port, + auth_protocol => $ks_keystone_internal_proto + } + + class { 'swift::dispersion': + auth_url => "${ks_keystone_internal_proto}://${ks_keystone_internal_host}:${ks_keystone_internal_port}/v2.0", + swift_dir => '/etc/swift', + auth_pass => $ks_swift_dispersion_password, + endpoint_type => 'internalURL' + } + + # Note(sileht): log file should exists to swift proxy to write to + # the ceilometer directory + file{'/var/log/ceilometer/swift-proxy-server.log': + ensure => present, + owner => 'swift', + group => 'swift', + notify => Service['swift-proxy'] + } + + Swift::Ringsync<<| |>> #~> Service["swift-proxy"] + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow swift-proxy access': + port => $ks_swift_internal_port, + extras => $firewall_settings, + } + } + + @@haproxy::balancermember{"${::fqdn}-swift_api": + listening_service => 'swift_api_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => $ks_swift_internal_port, + options => 'check inter 2000 rise 2 fall 5' + } + +} diff --git a/cloud/manifests/object/ringbuilder.pp b/cloud/manifests/object/ringbuilder.pp new file mode 100644 index 000000000..6519dbd2b --- /dev/null +++ b/cloud/manifests/object/ringbuilder.pp @@ -0,0 +1,70 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::object::ringbuilder +# +# Swift ring builder node +# +# === Parameters: +# +# [*enabled*] +# (optional) Enable or not the Swift ringbuilder rsync server +# Defaults to false +# +# [*rsyncd_ipaddress*] +# (optional) Hostname or IP of the swift ringbuilder rsync daemon +# Defaults to '127.0.0.1' +# +# [*replicas*] +# (optional) Number of replicas to kept +# Defaults to '3' +# +# [*swift_rsync_max_connections*] +# (optional) Max number of connections to the rsync daemon +# Defaults to '5' +# +class cloud::object::ringbuilder( + $enabled = false, + $rsyncd_ipaddress = '127.0.0.1', + $replicas = 3, + $swift_rsync_max_connections = 5, +) { + + include cloud::object + + if $enabled { + Ring_object_device <<| |>> + Ring_container_device <<| |>> + Ring_account_device <<| |>> + + class {'swift::ringbuilder' : + part_power => 15, + replicas => $replicas, + min_part_hours => 24, + } + + class {'swift::ringserver' : + local_net_ip => $rsyncd_ipaddress, + max_connections => $swift_rsync_max_connections, + } + + # exports rsync gets that can be used to sync the ring files + @@swift::ringsync { ['account', 'object', 'container']: + ring_server => $rsyncd_ipaddress, + } + } + +} + diff --git a/cloud/manifests/object/set_io_scheduler.pp b/cloud/manifests/object/set_io_scheduler.pp new file mode 100644 index 000000000..8f0d0a7a5 --- /dev/null +++ b/cloud/manifests/object/set_io_scheduler.pp @@ -0,0 +1,26 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Swift set_io_scheduler +# +define cloud::object::set_io_scheduler(){ + # TODO: Add it on server boot + exec{"/bin/echo deadline > /sys/block/${name}/queue/scheduler": + onlyif => [ + "/usr/bin/test '-e /sys/block/${name}/queue/scheduler'", + "/bin/grep -v -F '[deadline]' /sys/block/${name}/queue/scheduler" + ], + } +} diff --git a/cloud/manifests/object/storage.pp b/cloud/manifests/object/storage.pp new file mode 100644 index 000000000..5d755943d --- /dev/null +++ b/cloud/manifests/object/storage.pp @@ -0,0 +1,170 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::object::storage +# +# Swift Storage node +# +# === Parameters: +# +# [*storage_eth*] +# (optional) IP or hostname of the Swift storage node +# Defaults to '127.0.0.1' +# +# [*swift_zone*] +# (optional) Name of the swift zone +# Defaults to undef +# +# [*object_port*] +# (optional) TCP port number of the Object middleware +# Defaults to '6000' +# +# [*container_port*] +# (optional) TCP port number of the container middleware +# Defaults to '6001' +# +# [*account_port*] +# (optional) TCP port number of the account middleware +# Defaults to '6002' +# +# [*fstype*] +# (optional) Name of the File-System type +# Defaults to 'xfs' +# +# [*device_config_hash*] +# (optional) A hash of options to pass to io scheduler +# Defaults to {} +# +# [*ring_container_device*] +# (optional) The name of the container device +# Defaults to 'sdb' +# +# [*ring_account_device*] +# (optional) The name of the account device +# Defaults to 'sdb' +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::object::storage( + $storage_eth = '127.0.0.1', + $swift_zone = undef, + $object_port = '6000', + $container_port = '6001', + $account_port = '6002', + $fstype = 'xfs', + $device_config_hash = {}, + $ring_container_device = 'sdb', + $ring_account_device = 'sdb', + $firewall_settings = {}, +) { + + include 'cloud::object' + + class { 'swift::storage': + storage_local_net_ip => $storage_eth, + } + + Rsync::Server::Module { + incoming_chmod => 'u=rwX,go=rX', + outgoing_chmod => 'u=rwX,go=rX', + } + + Swift::Storage::Server { + #devices => $devices, + storage_local_net_ip => $storage_eth, + workers => inline_template('<%= @processorcount.to_i / 2 %>'), + replicator_concurrency => 2, + updater_concurrency => 1, + reaper_concurrency => 1, + require => Class['swift'], + mount_check => true, + } + # concurrency at 2 and 1 seems better see + # http://docs.openstack.org/trunk/openstack-object-storage/admin/content/general-service-tuning.html + + swift::storage::server { $account_port: + type => 'account', + config_file_path => 'account-server.conf', + pipeline => ['healthcheck', 'account-server'], + log_facility => 'LOG_LOCAL2', + } + + swift::storage::server { $container_port: + type => 'container', + config_file_path => 'container-server.conf', + workers => inline_template("<%= @processorcount.to_i / 2 %> +db_preallocation = on +allow_versions = on +"), # great hack :( + pipeline => ['healthcheck', 'container-server'], + log_facility => 'LOG_LOCAL4', + } + + swift::storage::server { $object_port: + type => 'object', + config_file_path => 'object-server.conf', + pipeline => ['healthcheck', 'recon', 'object-server'], + log_facility => 'LOG_LOCAL6', + } + + $swift_components = ['account', 'container', 'object'] + swift::storage::filter::recon { $swift_components : } + swift::storage::filter::healthcheck { $swift_components : } + + create_resources("swift::storage::${fstype}", $device_config_hash) + ensure_resource('cloud::object::set_io_scheduler', keys($device_config_hash)) + + @@ring_container_device { "${storage_eth}:${container_port}/${ring_container_device}": + zone => $swift_zone, + weight => '100.0', + } + @@ring_account_device { "${storage_eth}:${account_port}/${ring_account_device}": + zone => $swift_zone, + weight => '100.0', + } + $object_urls = prefix(keys($device_config_hash), "${storage_eth}:${object_port}/") + @@ring_object_device {$object_urls: + zone => $swift_zone, + weight => '100.0', + } + + Swift::Ringsync<<| |>> -> + Swift::Storage::Server[$container_port] -> + Swift::Storage::Server[$account_port] -> + Swift::Storage::Server[$object_port] + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow swift-container access': + port => $container_port, + extras => $firewall_settings, + } + cloud::firewall::rule{ '100 allow swift-account access': + port => $account_port, + extras => $firewall_settings, + } + cloud::firewall::rule{ '100 allow swift-object access': + port => $object_port, + extras => $firewall_settings, + } + cloud::firewall::rule{ '100 allow swift rsync access': + port => '873', + extras => $firewall_settings, + } + } + +} diff --git a/cloud/manifests/object/tweaking.pp b/cloud/manifests/object/tweaking.pp new file mode 100644 index 000000000..9e3c2d7c6 --- /dev/null +++ b/cloud/manifests/object/tweaking.pp @@ -0,0 +1,83 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Swift tweaking +# +class cloud::object::tweaking { + file {'/etc/sysctl.d/swift-tuning.conf': + content => " +# disable TIME_WAIT.. wait.. +net.ipv4.tcp_tw_recycle=1 +net.ipv4.tcp_tw_reuse=1 + +# disable syn cookies +net.ipv4.tcp_syncookies = 0 + +# double amount of allowed conntrack +net.ipv4.netfilter.ip_conntrack_max = 524288 +net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait = 2 +net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait = 2 + +net.ipv4.ip_local_port_range = 1024 65000 + +## 10Gb Tuning +net.core.netdev_max_backlog = 300000 +net.ipv4.tcp_sack = 0 + +", + owner => 'root', + group => 'root', + } + + exec{'update-etc-modules-with-ip_conntrack': + command => '/bin/echo ip_conntrack >> /etc/modules', + unless => '/bin/grep -qFx "ip_conntrack" /etc/modules', + } + + # Load sysctl and module only the first time + exec{'load-ip_conntrack': + command => '/sbin/modprobe ip_conntrack', + unless => '/bin/grep -qFx "ip_conntrack" /etc/modules', + require => File['/etc/sysctl.d/swift-tuning.conf'] + } + exec{'reload-sysctl-swift-tunning': + command => '/sbin/sysctl -p /etc/sysctl.d/swift-tuning.conf', + unless => '/bin/grep -qFx "ip_conntrack" /etc/modules', + require => File['/etc/sysctl.d/swift-tuning.conf'] + } + + file{'/var/log/swift': + ensure => directory, + owner => swift, + group => swift, + } + + file{'/etc/logrotate.d/swift': + content => " + /var/log/swift/proxy.log /var/log/swift/proxy.error.log /var/log/swift/account-server.log /var/log/swift/account-server.error.log /var/log/swift/container-server.log /var/log/swift/container-server.error.log /var/log/swift/object-server.log /var/log/swift/object-server.error.log +{ + rotate 7 + daily + missingok + notifempty + delaycompress + compress + postrotate + endscript +} +" + } + +} diff --git a/cloud/manifests/orchestration.pp b/cloud/manifests/orchestration.pp new file mode 100644 index 000000000..1435b11a2 --- /dev/null +++ b/cloud/manifests/orchestration.pp @@ -0,0 +1,171 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::orchestration +# +# Orchestration common node +# +# === Parameters: +# +# [*ks_keystone_internal_host*] +# (optional) Internal Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_admin_host*] +# (optional) Admin Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_internal_port*] +# (optional) TCP port to connect to Keystone API from internal network +# Defaults to '5000' +# +# [*ks_keystone_admin_port*] +# (optional) TCP port to connect to Keystone API from admin network +# Defaults to '35357' +# +# [*ks_keystone_internal_proto*] +# (optional) Protocol used to connect to API. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_keystone_admin_proto*] +# (optional) Protocol used to connect to API. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_heat_public_host*] +# (optional) Public Hostname or IP to connect to Heat API +# Defaults to '127.0.0.1' +# +# [*ks_heat_public_proto*] +# (optional) Protocol used to connect to API. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_heat_password*] +# (optional) Password used by Heat to connect to Keystone API +# Defaults to 'heatpassword' +# +# [*heat_db_host*] +# (optional) Hostname or IP address to connect to heat database +# Defaults to '127.0.0.1' +# +# [*heat_db_user*] +# (optional) Username to connect to heat database +# Defaults to 'heat' +# +# [*heat_db_password*] +# (optional) Password to connect to heat database +# Defaults to 'heatpassword' +# +# [*rabbit_hosts*] +# (optional) List of RabbitMQ servers. Should be an array. +# Defaults to ['127.0.0.1:5672'] +# +# [*rabbit_password*] +# (optional) Password to connect to heat queues. +# Defaults to 'rabbitpassword' +# +# [*verbose*] +# (optional) Set log output to verbose output +# Defaults to true +# +# [*debug*] +# (optional) Set log output to debug output +# Defaults to true +# +# [*use_syslog*] +# (optional) Use syslog for logging +# Defaults to true +# +# [*log_facility*] +# (optional) Syslog facility to receive log lines +# Defaults to 'LOG_LOCAL0' +# +# [*os_endpoint_type*] +# (optional) The type of the OpenStack endpoint (public/internal/admin) URL +# Defaults to 'publicURL' +# +class cloud::orchestration( + $ks_keystone_internal_host = '127.0.0.1', + $ks_keystone_internal_port = '5000', + $ks_keystone_internal_proto = 'http', + $ks_keystone_admin_host = '127.0.0.1', + $ks_keystone_admin_port = '35357', + $ks_keystone_admin_proto = 'http', + $ks_heat_public_host = '127.0.0.1', + $ks_heat_public_proto = 'http', + $ks_heat_password = 'heatpassword', + $heat_db_host = '127.0.0.1', + $heat_db_user = 'heat', + $heat_db_password = 'heatpassword', + $rabbit_hosts = ['127.0.0.1:5672'], + $rabbit_password = 'rabbitpassword', + $verbose = true, + $debug = true, + $use_syslog = true, + $log_facility = 'LOG_LOCAL0', + $os_endpoint_type = 'publicURL' +) { + + # Disable twice logging if syslog is enabled + if $use_syslog { + $log_dir = false + heat_config { + 'DEFAULT/logging_context_format_string': value => '%(process)d: %(levelname)s %(name)s [%(request_id)s %(user_identity)s] %(instance)s%(message)s'; + 'DEFAULT/logging_default_format_string': value => '%(process)d: %(levelname)s %(name)s [-] %(instance)s%(message)s'; + 'DEFAULT/logging_debug_format_suffix': value => '%(funcName)s %(pathname)s:%(lineno)d'; + 'DEFAULT/logging_exception_prefix': value => '%(process)d: TRACE %(name)s %(instance)s'; + } + } else { + $log_dir = '/var/log/heat' + } + + $encoded_user = uriescape($heat_db_user) + $encoded_password = uriescape($heat_db_password) + + class { 'heat': + keystone_host => $ks_keystone_admin_host, + keystone_port => $ks_keystone_admin_port, + keystone_protocol => $ks_keystone_admin_proto, + keystone_password => $ks_heat_password, + auth_uri => "${ks_keystone_internal_proto}://${ks_keystone_internal_host}:${ks_keystone_internal_port}/v2.0", + keystone_ec2_uri => "${ks_keystone_internal_proto}://${ks_keystone_internal_host}:${ks_keystone_internal_port}/v2.0/ec2tokens", + sql_connection => "mysql://${encoded_user}:${encoded_password}@${heat_db_host}/heat?charset=utf8", + mysql_module => '2.2', + rabbit_hosts => $rabbit_hosts, + rabbit_password => $rabbit_password, + rabbit_userid => 'heat', + verbose => $verbose, + debug => $debug, + log_facility => $log_facility, + use_syslog => $use_syslog, + log_dir => $log_dir, + } + + # Note(EmilienM): + # We check if DB tables are created, if not we populate Heat DB. + # It's a hack to fit with our setup where we run MySQL/Galera + # TODO(Goneri) + # We have to do this only on the primary node of the galera cluster to avoid race condition + # https://github.com/enovance/puppet-openstack-cloud/issues/156 + exec {'heat_db_sync': + command => 'heat-manage --config-file /etc/heat/heat.conf db_sync', + path => '/usr/bin', + user => 'heat', + unless => "/usr/bin/mysql heat -h ${heat_db_host} -u ${encoded_user} -p${encoded_password} -e \"show tables\" | /bin/grep Tables" + } + + heat_config { + 'clients/endpoint_type': value => $os_endpoint_type; + } +} diff --git a/cloud/manifests/orchestration/api.pp b/cloud/manifests/orchestration/api.pp new file mode 100644 index 000000000..31df0154e --- /dev/null +++ b/cloud/manifests/orchestration/api.pp @@ -0,0 +1,115 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::image::api +# +# Orchestration APIs node +# +# === Parameters: +# +# [*ks_heat_internal_port*] +# (optional) TCP port to connect to Heat API from public network +# Defaults to '8004' +# +# [*ks_heat_cfn_internal_port*] +# (optional) TCP port to connect to Heat API from public network +# Defaults to '8000' +# +# [*ks_heat_cloudwatch_internal_port*] +# (optional) TCP port to connect to Heat API from public network +# Defaults to '8003' +# +# [*api_eth*] +# (optional) Which interface we bind the Heat server. +# Defaults to '127.0.0.1' +# +# [*workers*] +# (optional) The number of Heat API workers +# Defaults to $::processorcount +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::orchestration::api( + $ks_heat_internal_port = 8004, + $ks_heat_cfn_internal_port = 8000, + $ks_heat_cloudwatch_internal_port = 8003, + $api_eth = '127.0.0.1', + $workers = $::processorcount, + $firewall_settings = {}, +) { + + include 'cloud::orchestration' + + class { 'heat::api': + bind_host => $api_eth, + bind_port => $ks_heat_internal_port, + workers => $workers + } + + class { 'heat::api_cfn': + bind_host => $api_eth, + bind_port => $ks_heat_cfn_internal_port, + workers => $workers + } + + class { 'heat::api_cloudwatch': + bind_host => $api_eth, + bind_port => $ks_heat_cloudwatch_internal_port, + workers => $workers + } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow heat-api access': + port => $ks_heat_internal_port, + extras => $firewall_settings, + } + cloud::firewall::rule{ '100 allow heat-cfn access': + port => $ks_heat_cfn_internal_port, + extras => $firewall_settings, + } + cloud::firewall::rule{ '100 allow heat-cloudwatch access': + port => $ks_heat_cloudwatch_internal_port, + extras => $firewall_settings, + } + } + + @@haproxy::balancermember{"${::fqdn}-heat_api": + listening_service => 'heat_api_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => $ks_heat_internal_port, + options => 'check inter 2000 rise 2 fall 5' + } + + @@haproxy::balancermember{"${::fqdn}-heat_cfn_api": + listening_service => 'heat_cfn_api_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => $ks_heat_cfn_internal_port, + options => 'check inter 2000 rise 2 fall 5' + } + + @@haproxy::balancermember{"${::fqdn}-heat_cloudwatch_api": + listening_service => 'heat_cloudwatch_api_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => $ks_heat_cloudwatch_internal_port, + options => 'check inter 2000 rise 2 fall 5' + } + +} diff --git a/cloud/manifests/orchestration/engine.pp b/cloud/manifests/orchestration/engine.pp new file mode 100644 index 000000000..d79723b09 --- /dev/null +++ b/cloud/manifests/orchestration/engine.pp @@ -0,0 +1,76 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::orchestration::engine +# +# Orchestration engine node (should be run once) +# Could be managed by spof node as Active / Passive. +# +# === Parameters: +# +# [*enabled*] +# (optional) State of the orchestration engine service. +# Defaults to true +# +# [*ks_heat_public_host*] +# (optional) Public Hostname or IP to connect to Heat API +# Defaults to '127.0.0.1' +# +# [*ks_heat_public_proto*] +# (optional) Protocol used to connect to API. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_heat_password*] +# (optional) Password used by Heat to connect to Keystone API +# Defaults to 'heatpassword' +# +# [*ks_heat_cfn_public_port*] +# (optional) TCP port to connect to Heat API from public network +# Defaults to '8000' +# +# [*ks_heat_cloudwatch_public_port*] +# (optional) TCP port to connect to Heat API from public network +# Defaults to '8003' +# +# [*auth_encryption_key*] +# (optional) Encryption key used for authentication info in database +# Defaults to 'secrete' +# +class cloud::orchestration::engine( + $enabled = true, + $ks_heat_public_host = '127.0.0.1', + $ks_heat_public_proto = 'http', + $ks_heat_password = 'heatpassword', + $ks_heat_cfn_public_port = 8000, + $ks_heat_cloudwatch_public_port = 8003, + $auth_encryption_key = 'secrete' +) { + + include 'cloud::orchestration' + + class { 'heat::engine': + enabled => $enabled, + auth_encryption_key => $auth_encryption_key, + heat_metadata_server_url => "${ks_heat_public_proto}://${ks_heat_public_host}:${ks_heat_cfn_public_port}", + heat_waitcondition_server_url => "${ks_heat_public_proto}://${ks_heat_public_host}:${ks_heat_cfn_public_port}/v1/waitcondition", + heat_watch_server_url => "${ks_heat_public_proto}://${ks_heat_public_host}:${ks_heat_cloudwatch_public_port}", + # TODO (EmilienM): Need to be updated in Juno + # The default deferred_auth_method of password is deprecated as of Icehouse, so although it is still the default, deployers are + # strongly encouraged to move to using deferred_auth_method=trusts, which is planned to become the default for Juno. + # 'trusts' requires Keystone API v3 enabled, otherwise we have to use 'password'. + deferred_auth_method => 'password', + } + +} diff --git a/cloud/manifests/params.pp b/cloud/manifests/params.pp new file mode 100644 index 000000000..a482b0746 --- /dev/null +++ b/cloud/manifests/params.pp @@ -0,0 +1,74 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::params +# +# Configure set of default parameters +# +class cloud::params { + + # cloud::logging::agent + $logging_agent_logrotate_rule = { + 'td-agent' => { + 'path' => '/var/log/td-agent/td-agent.log', + 'rotate' => 30, + 'compress' => true, + 'delaycompress' => true, + 'ifempty' => false, + 'create' => true, + 'create_mode' => '640', + 'create_owner' => 'td-agent', + 'create_group' => 'td-agent', + 'sharedscripts' => true, + 'postrotate' => ['pid=/var/run/td-agent/td-agent.pid', 'test -s $pid && kill -USR1 "$(cat $pid)"'], + } + } + + $puppetmaster_service_name = 'puppetmaster' + + case $::osfamily { + 'RedHat': { + # Specific to Red Hat + $start_haproxy_service = '"/usr/bin/systemctl start haproxy"' + $stop_haproxy_service = '"/usr/bin/systemctl stop haproxy"' + $horizon_auth_url = 'dashboard' + $libvirt_service_name = 'libvirtd' + $keepalived_name_is_process = false + $keepalived_vrrp_script = 'systemctl status haproxy.service' + $puppetmaster_package_name = 'puppet-server' + } # RedHat + 'Debian': { + # Specific to Debian / Ubuntu + $start_haproxy_service = '"/etc/init.d/haproxy start"' + $stop_haproxy_service = '"/etc/init.d/haproxy stop"' + $horizon_auth_url = 'horizon' + $keepalived_name_is_process = true + $keepalived_vrrp_script = undef + $puppetmaster_package_name = 'puppetmaster' + case $::operatingsystem { + 'Ubuntu': { + $libvirt_service_name = 'libvirt-bin' + } + default: { + $libvirt_service_name = 'libvirtd' + } + } + } # Debian + default: { + fail("Unsupported osfamily (${::osfamily})") + } + } + +} diff --git a/cloud/manifests/selinux.pp b/cloud/manifests/selinux.pp new file mode 100644 index 000000000..0e478cf02 --- /dev/null +++ b/cloud/manifests/selinux.pp @@ -0,0 +1,96 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::selinux +# +# Helper class to configure SELinux on nodes +# +# === Parameters: +# +# [*mode*] +# (optional) SELinux mode the system should be in +# Defaults to 'permissive' +# Possible values : disabled, permissive, enforcing +# +# [*directory*] +# (optional) Path where to find the SELinux modules +# Defaults to '/usr/share/selinux' +# +# [*booleans*] +# (optional) Set of booleans to persistently enables +# SELinux booleans are the one getsebool -a returns +# Defaults [] +# Example: ['rsync_full_access', 'haproxy_connect_any'] +# +# [*modules*] +# (optional) Set of modules to load on the system +# Defaults [] +# Example: ['module1', 'module2'] +# Note: Those module should be in the $directory path +# +class cloud::selinux ( + $mode = 'permissive', + $directory = '/usr/share/selinux/', + $booleans = [], + $modules = [], +) { + + if $::osfamily != 'RedHat' { + fail("OS family unsuppored yet (${::osfamily}), SELinux support is only limited to RedHat family OS") + } + + Selboolean { + persistent => true, + value => 'on', + } + + Selmodule { + ensure => present, + selmoduledir => $directory, + } + + file { '/etc/selinux/config': + ensure => present, + mode => '0444', + content => template('cloud/selinux/sysconfig_selinux.erb') + } + + $current_mode = $::selinux? { + 'false' => 'disabled', + false => 'disabled', + default => $::selinux_current_mode, + } + + if $current_mode != $mode { + case $mode { + /^(disabled|permissive)$/: { + if $current_mode == 'enforcing' { + exec { 'setenforce 0': } + } + } + 'enforcing': { + exec { 'setenforce 1': } + } + default: { + fail('You must specify a mode (enforcing, permissive, or disabled)') + } + } + } + + selboolean { $booleans : } + selmodule { $modules: } + +} + diff --git a/cloud/manifests/spof.pp b/cloud/manifests/spof.pp new file mode 100644 index 000000000..f77605a77 --- /dev/null +++ b/cloud/manifests/spof.pp @@ -0,0 +1,155 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::spof +# +# Install all SPOF services in active / passive with Pacemaker / Corosync +# +# === Parameters: +# +# [*cluster_ip*] +# (optional) Interface used by Corosync to send multicast traffic +# Defaults to '127.0.0.1' +# [*cluster_members*] +# (required on Red Hat) A space-separted list of cluster IP's or names +# Defaults to false +# +# [*multicast_address*] +# (optionnal) IP address used to send multicast traffic +# Defaults to '239.1.1.2' +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +# [*cluster_password*] +# (optionnal) Password of the pacemaker cluster +# Defaults to 'secrete' +# +class cloud::spof( + $cluster_ip = '127.0.0.1', + $cluster_members = false, + $multicast_address = '239.1.1.2', + $cluster_password = 'secrete', + $firewall_settings = {}, +) { + + if $::osfamily == 'RedHat' { + if ! $cluster_members { + fail('cluster_members is a required parameter.') + } + + class { 'pacemaker': + hacluster_pwd => $cluster_password + } + class { 'pacemaker::corosync': + cluster_name => 'openstack', + cluster_members => $cluster_members, + settle_timeout => 10, + settle_tries => 2, + settle_try_sleep => 5, + manage_fw => false + } + class {'pacemaker::stonith': + disable => true + } + file { '/usr/lib/ocf/resource.d/heartbeat/ceilometer-agent-central': + source => 'puppet:///modules/cloud/heartbeat/ceilometer-agent-central', + mode => '0755', + owner => 'root', + group => 'root', + } -> + exec {'pcmk_ceilometer_agent_central': + command => 'pcs resource create ceilometer-agent-central ocf:heartbeat:ceilometer-agent-central', + path => ['/usr/bin','/usr/sbin','/sbin/','/bin'], + user => 'root', + unless => '/usr/sbin/pcs resource | /bin/grep ceilometer-agent-central | /bin/grep Started' + } + } else { + + class { 'corosync': + enable_secauth => false, + authkey => '/var/lib/puppet/ssl/certs/ca.pem', + bind_address => $cluster_ip, + multicast_address => $multicast_address + } + + corosync::service { 'pacemaker': + version => '0', + } + + Package['corosync'] -> + cs_property { + 'no-quorum-policy': value => 'ignore'; + 'stonith-enabled': value => 'false'; + 'pe-warn-series-max': value => 1000; + 'pe-input-series-max': value => 1000; + 'cluster-recheck-interval': value => '5min'; + } -> + file { '/usr/lib/ocf/resource.d/heartbeat/ceilometer-agent-central': + source => 'puppet:///modules/cloud/heartbeat/ceilometer-agent-central', + mode => '0755', + owner => 'root', + group => 'root', + } -> + cs_primitive { 'ceilometer-agent-central': + primitive_class => 'ocf', + primitive_type => 'ceilometer-agent-central', + provided_by => 'heartbeat', + operations => { + 'monitor' => { + interval => '10s', + timeout => '30s' + }, + 'start' => { + interval => '0', + timeout => '30s', + on-fail => 'restart' + } + } + } -> + exec { 'cleanup_ceilometer_agent_central': + command => 'crm resource cleanup ceilometer-agent-central', + unless => 'crm resource show ceilometer-agent-central | grep Started', + user => 'root', + path => ['/usr/sbin', '/bin'], + } + } + + + # Run OpenStack SPOF service and disable them since they will be managed by Corosync. + class { 'cloud::telemetry::centralagent': + enabled => false, + } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow vrrp access': + port => undef, + proto => 'vrrp', + extras => $firewall_settings, + } + cloud::firewall::rule{ '100 allow corosync tcp access': + port => ['2224','3121','21064'], + extras => $firewall_settings, + } + cloud::firewall::rule{ '100 allow corosync udp access': + port => ['5404','5405'], + proto => 'udp', + extras => $firewall_settings, + } + } + +} diff --git a/cloud/manifests/storage/rbd.pp b/cloud/manifests/storage/rbd.pp new file mode 100644 index 000000000..48ce025dd --- /dev/null +++ b/cloud/manifests/storage/rbd.pp @@ -0,0 +1,49 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::storage::rbd +# +# === Parameters: +# +# [*fsid*] The cluster's fsid. +# Mandatory. Get one with `uuidgen -r`. +# +# [*cluster_network*] +# (optional) The cluster internal network +# Defaults to '127.0.0.1/24' +# +# [*public_network*] +# (optional) The cluster public (where clients are) network +# Defaults to '127.0.0.1/24' +# +class cloud::storage::rbd ( + $fsid = undef, + $cluster_network = '127.0.0.1/24', + $public_network = '127.0.0.1/24' +) { + + class { 'ceph::conf': + fsid => $fsid, + auth_type => 'cephx', + cluster_network => $cluster_network, + public_network => $public_network, + enable_service => true + } + + Exec { + path => '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin' + } + +} diff --git a/cloud/manifests/storage/rbd/key.pp b/cloud/manifests/storage/rbd/key.pp new file mode 100644 index 000000000..9fe8a19c2 --- /dev/null +++ b/cloud/manifests/storage/rbd/key.pp @@ -0,0 +1,37 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::storage::rbd::key +# +# === Parameters: +# +# [*enabled*] +# (optional) Configure or not the ceph admin keyring +# Defaults to true +# +class cloud::storage::rbd::key ( + $enabled = false +) { + + if $enabled { + if !empty($::ceph_admin_key) { + @@ceph::key { 'admin': + secret => $::ceph_admin_key, + keyring_path => '/etc/ceph/keyring', + } + } + } + +} diff --git a/cloud/manifests/storage/rbd/monitor.pp b/cloud/manifests/storage/rbd/monitor.pp new file mode 100644 index 000000000..5bf7e1cbb --- /dev/null +++ b/cloud/manifests/storage/rbd/monitor.pp @@ -0,0 +1,61 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::storage::rbd::monitor +# +# Ceph monitor +# +# === Parameters: +# +# [*id*] +# (optional) Then uuid of the cluster +# Defaults to $::uniqueid +# +# [*mon_addr*] +# (optional) Which interface we bind the Ceph monitor +# Defaults to '127.0.0.1' +# +# [*monitor_secret*]] +# (optional) Password of the Ceph monitor +# Defaults to 'cephsecret' +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::storage::rbd::monitor ( + $id = $::uniqueid, + $mon_addr = '127.0.0.1', + $monitor_secret = 'cephmonsecret', + $firewall_settings = {}, +) { + + include 'cloud::storage::rbd' + + ceph::mon { $id: + monitor_secret => $monitor_secret, + mon_port => 6789, + mon_addr => $mon_addr, + } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow ceph-mon access': + port => '6789', + extras => $firewall_settings, + } + } + +} diff --git a/cloud/manifests/storage/rbd/osd.pp b/cloud/manifests/storage/rbd/osd.pp new file mode 100644 index 000000000..2105f6a5d --- /dev/null +++ b/cloud/manifests/storage/rbd/osd.pp @@ -0,0 +1,74 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::storage::rbd::osd +# +# Ceph OSD +# +# === Parameters: +# +# [*public_address*] +# (optional) Which interface we bind the Ceph OSD +# Defaults to '127.0.0.1' +# +# [*cluster_address*] +# (optional) Which interface we bind internal the Ceph OSD +# Defaults to '127.0.0.1' +# +# [*devices*]] +# (optional) An array of device, should be full-qualified or short. +# Defaults to ['sdb','/dev/sdc'] +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# + +class cloud::storage::rbd::osd ( + $public_address = '127.0.0.1', + $cluster_address = '127.0.0.1', + $devices = ['sdb','/dev/sdc'], + $firewall_settings = {}, +) { + + include 'cloud::storage::rbd' + + class { 'ceph::osd' : + public_address => $public_address, + cluster_address => $cluster_address, + } + + if is_array($devices) { + if '/dev/' in $devices { + ceph::osd::device { $devices: } + } + else { + $osd_ceph = prefix($devices,'/dev/') + ceph::osd::device { $osd_ceph: } + } + } + elsif is_hash($devices) { + create_resources('ceph::osd::device', $devices) + } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow ceph-osd access': + port => '6800-6810', + extras => $firewall_settings, + } + } + +} diff --git a/cloud/manifests/storage/rbd/pools.pp b/cloud/manifests/storage/rbd/pools.pp new file mode 100644 index 000000000..2a99da4a7 --- /dev/null +++ b/cloud/manifests/storage/rbd/pools.pp @@ -0,0 +1,153 @@ +# +# Copyright (C) 2013 eNovance SAS +# +# 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: cloud::storage::rbd::pools +# +# Configure Ceph RBD pools (images,volumes,backup,nova) +# +# === Parameters: +# +# [*setup_pools*] +# (optional) Create or not Ceph pools +# Defaults to false +# +# [*glance_rbd_pool*] +# (optional) Name of the Ceph pool which which store the glance images +# Defaults to 'images' +# +# [*glance_rbd_user*] +# (optional) User name used to acces to the glance rbd pool +# Defaults to 'glance' +# +# [*ceph_fsid*] The cluster's fsid. +# Mandatory. Get one with `uuidgen -r`. +# +# [*cinder_backup_pool*] +# (optional) Name of the Ceph pool which which store the cinder backups +# Defaults to 'volumes' +# +# [*cinder_backup_user*] +# (optional) User name used to acces to the backup rbd pool +# Defaults to 'cinder' +# +# [*cinder_rbd_pool*] +# (optional) Name of the Ceph pool which which store the cinder images +# Defaults to 'volumes' +# +# [*cinder_rbd_user*] +# (optional) User name used to acces to the cinder rbd pool +# Defaults to 'cinder' +# +# [*nova_rbd_pool*] +# (optional) The RADOS pool in which rbd volumes are stored. +# Defaults to 'vms' +# +class cloud::storage::rbd::pools( + $setup_pools = false, + $glance_rbd_user = 'glance', + $glance_rbd_pool = 'images', + $cinder_rbd_user = 'cinder', + $cinder_rbd_pool = 'volumes', + $nova_rbd_pool = 'vms', + $cinder_backup_user = 'cinder', + $cinder_backup_pool = 'cinder_backup', + $ceph_fsid = undef +) { + + if $setup_pools { + if !empty($::ceph_admin_key) { + + exec { "create_${glance_rbd_pool}_pool": + command => "rados mkpool ${glance_rbd_pool}", + unless => "rados lspools | grep -sq ${glance_rbd_pool}", + } + + exec { "create_${glance_rbd_pool}_user_and_key": + command => "ceph auth get-or-create client.${glance_rbd_user} mon 'allow r' osd 'allow class-read object_prefix rbd_children, allow rwx pool=${glance_rbd_pool}'", + unless => "ceph auth list 2> /dev/null | egrep -sq '^client.${glance_rbd_user}$'", + require => Exec["create_${glance_rbd_pool}_pool"]; + } + + exec { "create_${cinder_rbd_pool}_pool": + command => "rados mkpool ${cinder_rbd_pool}", + unless => "/usr/bin/rados lspools | grep -sq ${cinder_rbd_pool}", + } + + exec { "create_${cinder_rbd_pool}_user_and_key": + # TODO: point PG num with a cluster variable + command => "ceph auth get-or-create client.${cinder_rbd_user} mon 'allow r' osd 'allow class-read object_prefix rbd_children, allow rx pool=${glance_rbd_pool}, allow rwx pool=${cinder_rbd_pool}, allow rwx pool=${nova_rbd_pool}'", + unless => "ceph auth list 2> /dev/null | egrep -sq '^client.${cinder_rbd_user}$'", + require => Exec["create_${cinder_rbd_pool}_pool"]; + } + + # Note(EmilienM): We use the same keyring for Nova and Cinder. + exec { "create_${nova_rbd_pool}_pool": + command => "rados mkpool ${nova_rbd_pool}", + unless => "/usr/bin/rados lspools | grep -sq ${nova_rbd_pool}", + } + + if $::ceph_keyring_glance { + # NOTE(fc): Puppet needs to run a second time to enter this + @@ceph::key { $glance_rbd_user: + secret => $::ceph_keyring_glance, + keyring_path => "/etc/ceph/ceph.client.${glance_rbd_user}.keyring" + } + Ceph::Key <<| title == $glance_rbd_user |>> + } + + if $::ceph_keyring_cinder { + # NOTE(fc): Puppet needs to run a second time to enter this + @@ceph::key { $cinder_rbd_user: + secret => $::ceph_keyring_cinder, + keyring_path => "/etc/ceph/ceph.client.${cinder_rbd_user}.keyring" + } + Ceph::Key <<| title == $cinder_rbd_user |>> + } + + $clients = [$glance_rbd_user, $cinder_rbd_user] + @@concat::fragment { 'ceph-clients-os': + target => '/etc/ceph/ceph.conf', + order => '95', + content => template('cloud/storage/ceph/ceph-client.conf.erb') + } + + @@file { '/etc/ceph/secret.xml': + content => template('cloud/storage/ceph/secret-compute.xml.erb'), + tag => 'ceph_compute_secret_file', + } + + if $::osfamily == 'RedHat' { + $libvirt_package_name = 'libvirt' + } else { + $libvirt_package_name = 'libvirt-bin' + } + + @@exec { 'get_or_set_virsh_secret': + command => 'virsh secret-define --file /etc/ceph/secret.xml', + unless => "virsh secret-list | tail -n +3 | cut -f1 -d' ' | grep -sq ${ceph_fsid}", + tag => 'ceph_compute_get_secret', + require => [Package[$libvirt_package_name],File['/etc/ceph/secret.xml']], + notify => Exec['set_secret_value_virsh'], + } + + @@exec { 'set_secret_value_virsh': + command => "virsh secret-set-value --secret ${ceph_fsid} --base64 ${::ceph_keyring_cinder}", + tag => 'ceph_compute_set_secret', + refreshonly => true, + } + + } # !empty($::ceph_admin_key) + } # if setup pools +} # class diff --git a/cloud/manifests/telemetry.pp b/cloud/manifests/telemetry.pp new file mode 100644 index 000000000..7048f7408 --- /dev/null +++ b/cloud/manifests/telemetry.pp @@ -0,0 +1,134 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::telemetry +# +# Common telemetry class, used by Controller, Storage, +# Network and Compute nodes +# +# === Parameters: +# +# [*ceilometer_secret*] +# Secret key for signing messages. +# Defaults to 'ceilometersecret' +# +# [*rabbit_hosts*] +# (optional) List of RabbitMQ servers. Should be an array. +# Defaults to ['127.0.0.1:5672'] +# +# [*rabbit_password*] +# (optional) Password to connect to nova queues. +# Defaults to 'rabbitpassword' +# +# [*ks_keystone_internal_host*] +# (optional) Internal Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_internal_proto*] +# (optional) Protocol for public endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_keystone_internal_port*] +# (optional) TCP port to connect to Keystone API from internal network +# Defaults to '5000' +# +# [*ks_keystone_admin_host*] +# (optional) Admin Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_public_host*] +# (optional) Public Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_ceilometer_password*] +# (optional) Password used by Ceilometer to connect to Keystone API +# Defaults to 'ceilometerpassword' +# +# [*verbose*] +# (optional) Set log output to verbose output +# Defaults to true +# +# [*debug*] +# (optional) Set log output to debug output +# Defaults to true +# +# [*use_syslog*] +# (optional) Use syslog for logging +# Defaults to true +# +# [*log_facility*] +# (optional) Syslog facility to receive log lines +# Defaults to 'LOG_LOCAL0' +# +# [*region*] +# (optional) the keystone region of this node +# Defaults to 'RegionOne' +# +# [*os_endpoint_type*] +# (optional) The type of the OpenStack endpoint (public/internal/admin) URL +# Defaults to 'publicURL' +# +class cloud::telemetry( + $ceilometer_secret = 'ceilometersecret', + $rabbit_hosts = ['127.0.0.1:5672'], + $rabbit_password = 'rabbitpassword' , + $ks_keystone_internal_host = '127.0.0.1', + $ks_keystone_internal_port = '5000', + $ks_keystone_internal_proto = 'http', + $ks_ceilometer_password = 'ceilometerpassword', + $region = 'RegionOne', + $verbose = true, + $debug = true, + $log_facility = 'LOG_LOCAL0', + $use_syslog = true, + $os_endpoint_type = 'publicURL' +){ + + # Disable twice logging if syslog is enabled + if $use_syslog { + $log_dir = false + ceilometer_config { + 'DEFAULT/logging_context_format_string': value => '%(process)d: %(levelname)s %(name)s [%(request_id)s %(user_identity)s] %(instance)s%(message)s'; + 'DEFAULT/logging_default_format_string': value => '%(process)d: %(levelname)s %(name)s [-] %(instance)s%(message)s'; + 'DEFAULT/logging_debug_format_suffix': value => '%(funcName)s %(pathname)s:%(lineno)d'; + 'DEFAULT/logging_exception_prefix': value => '%(process)d: TRACE %(name)s %(instance)s'; + } + } else { + $log_dir = '/var/log/ceilometer' + } + + class { 'ceilometer': + metering_secret => $ceilometer_secret, + rabbit_hosts => $rabbit_hosts, + rabbit_password => $rabbit_password, + rabbit_userid => 'ceilometer', + verbose => $verbose, + debug => $debug, + log_dir => $log_dir, + use_syslog => $use_syslog, + log_facility => $log_facility + } + + ceilometer_config { + 'service_credentials/os_endpoint_type': value => $os_endpoint_type; + } + + class { 'ceilometer::agent::auth': + auth_url => "${ks_keystone_internal_proto}://${ks_keystone_internal_host}:${ks_keystone_internal_port}/v2.0", + auth_password => $ks_ceilometer_password, + auth_region => $region + } + +} diff --git a/cloud/manifests/telemetry/alarmevaluator.pp b/cloud/manifests/telemetry/alarmevaluator.pp new file mode 100644 index 000000000..9f435f75c --- /dev/null +++ b/cloud/manifests/telemetry/alarmevaluator.pp @@ -0,0 +1,25 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Telemetry Alarm Evaluator nodes +# +class cloud::telemetry::alarmevaluator( +){ + + include 'cloud::telemetry' + + class { 'ceilometer::alarm::evaluator': } + +} diff --git a/cloud/manifests/telemetry/alarmnotifier.pp b/cloud/manifests/telemetry/alarmnotifier.pp new file mode 100644 index 000000000..59bcd6e1e --- /dev/null +++ b/cloud/manifests/telemetry/alarmnotifier.pp @@ -0,0 +1,25 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Telemetry Alarm Notifier nodes +# +class cloud::telemetry::alarmnotifier( +){ + + include 'cloud::telemetry' + + class { 'ceilometer::alarm::notifier': } + +} diff --git a/cloud/manifests/telemetry/api.pp b/cloud/manifests/telemetry/api.pp new file mode 100644 index 000000000..7963918ae --- /dev/null +++ b/cloud/manifests/telemetry/api.pp @@ -0,0 +1,92 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::telemetry::api +# +# Telemetry API nodes +# +# === Parameters: +# +# [*ks_keystone_internal_host*] +# (optional) Internal Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_internal_proto*] +# (optional) Protocol for public endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_ceilometer_password*] +# (optional) Password used by Ceilometer to connect to Keystone API +# Defaults to 'ceilometerpassword' +# +# [*ks_ceilometer_internal_port*] +# (optional) TCP port to connect to Ceilometer API from public network +# Defaults to '8777' +# +# [*api_eth*] +# (optional) Which interface we bind the Ceilometer API server. +# Defaults to '127.0.0.1' +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::telemetry::api( + $ks_keystone_internal_host = '127.0.0.1', + $ks_keystone_internal_proto = 'http', + $ks_ceilometer_internal_port = '8777', + $ks_ceilometer_password = 'ceilometerpassword', + $api_eth = '127.0.0.1', + $firewall_settings = {}, +){ + + include 'cloud::telemetry' + + class { 'ceilometer::api': + keystone_password => $ks_ceilometer_password, + keystone_host => $ks_keystone_internal_host, + keystone_protocol => $ks_keystone_internal_proto, + host => $api_eth + } + +# Configure TTL for samples +# Purge datas older than one month +# Run the script once a day but with a random time to avoid +# issues with MongoDB access + class { 'ceilometer::expirer': + time_to_live => '2592000', + minute => '0', + hour => '0', + } + + Cron <<| title == 'ceilometer-expirer' |>> { command => "sleep $((\$RANDOM % 86400)) && ${::ceilometer::params::expirer_command}" } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow ceilometer-api access': + port => $ks_ceilometer_internal_port, + extras => $firewall_settings, + } + } + + @@haproxy::balancermember{"${::fqdn}-ceilometer_api": + listening_service => 'ceilometer_api_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => $ks_ceilometer_internal_port, + options => 'check inter 2000 rise 2 fall 5' + } + +} diff --git a/cloud/manifests/telemetry/centralagent.pp b/cloud/manifests/telemetry/centralagent.pp new file mode 100644 index 000000000..cb16069ba --- /dev/null +++ b/cloud/manifests/telemetry/centralagent.pp @@ -0,0 +1,38 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::telemetry::centralagent +# +# Telemetry Central Agent node (should be run once) +# Could be managed by spof node as Active / Passive. +# +# === Parameters: +# +# [*enabled*] +# (optional) State of the telemetry central agent service. +# Defaults to true +# +class cloud::telemetry::centralagent( + $enabled = true, +){ + + include 'cloud::telemetry' + + class { 'ceilometer::agent::central': + enabled => $enabled, + } + +} diff --git a/cloud/manifests/telemetry/collector.pp b/cloud/manifests/telemetry/collector.pp new file mode 100644 index 000000000..fa59e0177 --- /dev/null +++ b/cloud/manifests/telemetry/collector.pp @@ -0,0 +1,53 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::telemetry::collector +# +# Telemetry Collector nodes +# +# === Parameters: +# +# [*mongo_nodes*] +# (optional) An array of mongo db nodes +# Defaults to ['127.0.0.1:27017'] +# +# [*replicaset_enabled*] +# (optional) Enable or not mongo replicat (using ceilometer name) +# Defaults to true +# +class cloud::telemetry::collector( + $mongo_nodes = ['127.0.0.1:27017'], + $replicaset_enabled = true, +){ + + include 'cloud::telemetry' + + $s_mongo_nodes = join($mongo_nodes, ',') + + if $replicaset_enabled { + $db_conn = "mongodb://${s_mongo_nodes}/ceilometer?replicaSet=ceilometer" + } else { + $db_conn = "mongodb://${s_mongo_nodes}/ceilometer" + } + + class { 'ceilometer::db': + database_connection => $db_conn, + sync_db => true, + require => Anchor['mongodb setup done'], + } + class { 'ceilometer::collector': } + +} diff --git a/cloud/manifests/telemetry/notification.pp b/cloud/manifests/telemetry/notification.pp new file mode 100644 index 000000000..e6281d47f --- /dev/null +++ b/cloud/manifests/telemetry/notification.pp @@ -0,0 +1,24 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Telemetry Notifications nodes +# +class cloud::telemetry::notification { + + include 'cloud::telemetry' + + class { 'ceilometer::agent::notification': } + +} diff --git a/cloud/manifests/volume.pp b/cloud/manifests/volume.pp new file mode 100644 index 000000000..45bb58e0f --- /dev/null +++ b/cloud/manifests/volume.pp @@ -0,0 +1,132 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::volume +# +# Common class for volume nodes +# +# === Parameters: +# +# [*cinder_db_host*] +# (optional) Cinder database host +# Defaults to '127.0.0.1' +# +# [*cinder_db_user*] +# (optional) Cinder database user +# Defaults to 'cinder' +# +# [*cinder_db_password*] +# (optional) Cinder database password +# Defaults to 'cinderpassword' +# +# [*rabbit_hosts*] +# (optional) List of RabbitMQ servers. Should be an array. +# Defaults to ['127.0.0.1:5672'] +# +# [*rabbit_password*] +# (optional) Password to connect to cinder queues. +# Defaults to 'rabbitpassword' +# +# [*verbose*] +# (optional) Set log output to verbose output +# Defaults to true +# +# [*debug*] +# (optional) Set log output to debug output +# Defaults to true +# +# [*use_syslog*] +# (optional) Use syslog for logging +# Defaults to true +# +# [*log_facility*] +# (optional) Syslog facility to receive log lines +# Defaults to 'LOG_LOCAL0' +# +# [*storage_availability_zone*] +# (optional) The storage availability zone +# Defaults to 'nova' +# +# [*nova_endpoint_type*] +# (optional) The type of the OpenStack endpoint (public/internal/admin) URL +# Defaults to 'publicURL' +# +class cloud::volume( + $cinder_db_host = '127.0.0.1', + $cinder_db_user = 'cinder', + $cinder_db_password = 'cinderpassword', + $rabbit_hosts = ['127.0.0.1:5672'], + $rabbit_password = 'rabbitpassword', + $verbose = true, + $debug = true, + $log_facility = 'LOG_LOCAL0', + $storage_availability_zone = 'nova', + $use_syslog = true, + $nova_endpoint_type = 'publicURL' +) { + + # Disable twice logging if syslog is enabled + if $use_syslog { + $log_dir = false + cinder_config { + 'DEFAULT/logging_context_format_string': value => '%(process)d: %(levelname)s %(name)s [%(request_id)s %(user_identity)s] %(instance)s%(message)s'; + 'DEFAULT/logging_default_format_string': value => '%(process)d: %(levelname)s %(name)s [-] %(instance)s%(message)s'; + 'DEFAULT/logging_debug_format_suffix': value => '%(funcName)s %(pathname)s:%(lineno)d'; + 'DEFAULT/logging_exception_prefix': value => '%(process)d: TRACE %(name)s %(instance)s'; + } + } else { + $log_dir = '/var/log/cinder' + } + + $encoded_user = uriescape($cinder_db_user) + $encoded_password = uriescape($cinder_db_password) + + + class { 'cinder': + sql_connection => "mysql://${encoded_user}:${encoded_password}@${cinder_db_host}/cinder?charset=utf8", + mysql_module => '2.2', + rabbit_userid => 'cinder', + rabbit_hosts => $rabbit_hosts, + rabbit_password => $rabbit_password, + rabbit_virtual_host => '/', + verbose => $verbose, + debug => $debug, + log_dir => $log_dir, + log_facility => $log_facility, + use_syslog => $use_syslog, + storage_availability_zone => $storage_availability_zone + } + + cinder_config { + 'DEFAULT/nova_catalog_info': value => "compute:nova:${nova_endpoint_type}"; + } + + class { 'cinder::ceilometer': } + + # Note(EmilienM): + # We check if DB tables are created, if not we populate Cinder DB. + # It's a hack to fit with our setup where we run MySQL/Galera + # TODO(Goneri) + # We have to do this only on the primary node of the galera cluster to avoid race condition + # https://github.com/enovance/puppet-openstack-cloud/issues/156 + exec {'cinder_db_sync': + command => 'cinder-manage db sync', + path => '/usr/bin', + user => 'cinder', + unless => "/usr/bin/mysql cinder -h ${cinder_db_host} -u ${encoded_user} -p${encoded_password} -e \"show tables\" | /bin/grep Tables" + } + +} diff --git a/cloud/manifests/volume/api.pp b/cloud/manifests/volume/api.pp new file mode 100644 index 000000000..41e9f03bf --- /dev/null +++ b/cloud/manifests/volume/api.pp @@ -0,0 +1,113 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: cloud::volume::api +# +# Volume API node +# +# === Parameters: +# +# [*default_volume_type*] +# (required) default volume type to use. +# This should contain the name of the default volume type to use. +# If not configured, it produces an error when creating a volume +# without specifying a type. +# +# [*ks_cinder_internal_port*] +# (optional) TCP port to connect to Cinder API from public network +# Defaults to '8776' +# +# [*ks_keystone_internal_host*] +# (optional) Internal Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_internal_proto*] +# (optional) Protocol for public endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_glance_internal_host*] +# (optional) Internal Hostname or IP to connect to Glance API +# Defaults to '127.0.0.1' +# +# [*ks_cinder_password*] +# (optional) Password used by Cinder to connect to Keystone API +# Defaults to 'cinderpassword' +# +# [*ks_glance_api_internal_port*] +# (optional) TCP port to connect to Glance API from public network +# Defaults to '9292' +# +# [*api_eth*] +# (optional) Which interface we bind the Cinder API server. +# Defaults to '127.0.0.1' +# +# [*ks_glance_internal_proto*] +# (optional) Protocol for public endpoint. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*firewall_settings*] +# (optional) Allow to add custom parameters to firewall rules +# Should be an hash. +# Default to {} +# +class cloud::volume::api( + $default_volume_type, + $ks_cinder_internal_port = 8776, + $ks_cinder_password = 'cinderpassword', + $ks_keystone_internal_host = '127.0.0.1', + $ks_keystone_internal_proto = 'http', + $ks_glance_internal_host = '127.0.0.1', + $ks_glance_api_internal_port = 9292, + $api_eth = '127.0.0.1', + $ks_glance_internal_proto = 'http', + $firewall_settings = {}, +) { + + include 'cloud::volume' + + if ! $default_volume_type { + fail('default_volume_type should be defined when running Cinder Multi-Backend.') + } + + class { 'cinder::api': + keystone_password => $ks_cinder_password, + keystone_auth_host => $ks_keystone_internal_host, + keystone_auth_protocol => $ks_keystone_internal_proto, + bind_host => $api_eth, + default_volume_type => $default_volume_type + } + + class { 'cinder::glance': + glance_api_servers => "${ks_glance_internal_proto}://${ks_glance_internal_host}:${ks_glance_api_internal_port}", + glance_request_timeout => '10', + glance_num_retries => '10' + } + + if $::cloud::manage_firewall { + cloud::firewall::rule{ '100 allow cinder-api access': + port => $ks_cinder_internal_port, + extras => $firewall_settings, + } + } + + @@haproxy::balancermember{"${::fqdn}-cinder_api": + listening_service => 'cinder_api_cluster', + server_names => $::hostname, + ipaddresses => $api_eth, + ports => $ks_cinder_internal_port, + options => 'check inter 2000 rise 2 fall 5' + } + +} diff --git a/cloud/manifests/volume/backend/emc_vnx.pp b/cloud/manifests/volume/backend/emc_vnx.pp new file mode 100644 index 000000000..4251d8970 --- /dev/null +++ b/cloud/manifests/volume/backend/emc_vnx.pp @@ -0,0 +1,71 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Configure EMC VNX backend for Cinder +# +# +# === Parameters +# +# [*volume_backend_name*] +# (optional) Allows for the volume_backend_name to be separate of $name. +# Defaults to: $name +# +# [*san_ip*] +# (required) IP address of SAN controller. +# +# [*san_password*] +# (required) Password of SAN controller. +# +# [*san_login*] +# (optional) Login of SAN controller. +# Defaults to : 'admin' +# +# [*storage_vnx_pool_name*] +# (required) Storage pool name. +# +# [*default_timeout*] +# (optional) Default timeout for CLI operations in minutes. +# Defaults to: '10' +# +# [*max_luns_per_storage_group*] +# (optional) Default max number of LUNs in a storage group. +# Defaults to: '256' +# +define cloud::volume::backend::emc_vnx ( + $iscsi_ip_address, + $san_ip, + $san_password, + $storage_vnx_pool_name, + $default_timeout = '10', + $max_luns_per_storage_group = '256', + $san_login = 'admin', + $volume_backend_name = $name, +) { + cinder::backend::emc_vnx { $name: + default_timeout => $default_timeout, + iscsi_ip_address => $iscsi_ip_address, + max_luns_per_storage_group => $max_luns_per_storage_group, + san_ip => $san_ip, + san_login => $san_login, + san_password => $san_password, + storage_vnx_pool_name => $storage_vnx_pool_name, + } + + @cinder::type { $volume_backend_name: + set_key => 'volume_backend_name', + set_value => $volume_backend_name, + notify => Service['cinder-volume'] + } +} diff --git a/cloud/manifests/volume/backend/eqlx.pp b/cloud/manifests/volume/backend/eqlx.pp new file mode 100644 index 000000000..39694708b --- /dev/null +++ b/cloud/manifests/volume/backend/eqlx.pp @@ -0,0 +1,100 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Configure Dell EqualLogic backend for Cinder +# +# +# === Parameters +# +# [*san_ip*] +# (required) The IP address of the Dell EqualLogic array. +# +# [*san_login*] +# (required) The account to use for issuing SSH commands. +# +# [*san_password*] +# (required) The password for the specified SSH account. +# +# [*san_thin_provision*] +# (optional) Whether or not to use thin provisioning for volumes. +# Defaults to true +# +# [*volume_backend_name*] +# (optional) The backend name. +# Defaults to the name of the resource +# +# [*eqlx_group_name*] +# (optional) The CLI prompt message without '>'. +# Defaults to 'group-0' +# +# [*eqlx_pool*] +# (optional) The pool in which volumes will be created. +# Defaults to 'default' +# +# [*eqlx_use_chap*] +# (optional) Use CHAP authentification for targets? +# Defaults to false +# +# [*eqlx_chap_login*] +# (optional) An existing CHAP account name. +# Defaults to 'chapadmin' +# +# [*eqlx_chap_password*] +# (optional) The password for the specified CHAP account name. +# Defaults to '12345' +# +# [*eqlx_cli_timeout*] +# (optional) The timeout for the Group Manager cli command execution. +# Defaults to 30 seconds +# +# [*eqlx_cli_max_retries*] +# (optional) The maximum retry count for reconnection. +# Defaults to 5 +# +define cloud::volume::backend::eqlx ( + $san_ip, + $san_login, + $san_password, + $san_thin_provision = true, + $volume_backend_name = $name, + $eqlx_group_name = 'group-0', + $eqlx_pool = 'default', + $eqlx_use_chap = false, + $eqlx_chap_login = 'chapadmin', + $eqlx_chap_password = '12345', + $eqlx_cli_timeout = 30, + $eqlx_cli_max_retries = 5, +) { + + cinder::backend::eqlx { $name: + san_ip => $san_ip, + san_login => $san_login, + san_password => $san_password, + san_thin_provision => $san_thin_provision, + eqlx_group_name => $eqlx_group_name, + eqlx_pool => $eqlx_pool, + eqlx_use_chap => $eqlx_use_chap, + eqlx_chap_login => $eqlx_chap_login, + eqlx_chap_password => $eqlx_chap_password, + eqlx_cli_timeout => $eqlx_cli_timeout, + eqlx_cli_max_retries => $eqlx_cli_max_retries, + } + + @cinder::type { $volume_backend_name: + set_key => 'volume_backend_name', + set_value => $volume_backend_name, + notify => Service['cinder-volume'] + } +} diff --git a/cloud/manifests/volume/backend/glusterfs.pp b/cloud/manifests/volume/backend/glusterfs.pp new file mode 100644 index 000000000..9bdfa8b9f --- /dev/null +++ b/cloud/manifests/volume/backend/glusterfs.pp @@ -0,0 +1,60 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Configure GlusterFS backend for Cinder +# +# === Parameters +# +# [*glusterfs_shares*] +# (required) An array of GlusterFS volume locations. +# Must be an array even if there is only one volume. +# +# [*volume_backend_name*] +# (optional) Allows for the volume_backend_name to be separate of $name. +# Defaults to: $name +# +# [*glusterfs_sparsed_volumes*] +# (optional) Whether or not to use sparse (thin) volumes. +# Defaults to undef which uses the driver's default of "true". +# +# [*glusterfs_mount_point_base*] +# (optional) Where to mount the Gluster volumes. +# Defaults to undef which uses the driver's default of "$state_path/mnt". +# +# [*glusterfs_shares_config*] +# (optional) The config file to store the given $glusterfs_shares. +# Defaults to '/etc/cinder/shares.conf' +# +define cloud::volume::backend::glusterfs ( + $glusterfs_shares, + $volume_backend_name = $name, + $glusterfs_sparsed_volumes = undef, + $glusterfs_mount_point_base = undef, + $glusterfs_shares_config = '/etc/cinder/shares.conf' +) { + + cinder::backend::glusterfs { $name: + glusterfs_shares => $glusterfs_shares, + glusterfs_sparsed_volumes => $glusterfs_sparsed_volumes, + glusterfs_mount_point_base => $glusterfs_mount_point_base, + glusterfs_shares_config => $glusterfs_shares_config, + } + + @cinder::type { $volume_backend_name: + set_key => 'volume_backend_name', + set_value => $volume_backend_name, + notify => Service['cinder-volume'] + } +} diff --git a/cloud/manifests/volume/backend/iscsi.pp b/cloud/manifests/volume/backend/iscsi.pp new file mode 100644 index 000000000..9e8da91d3 --- /dev/null +++ b/cloud/manifests/volume/backend/iscsi.pp @@ -0,0 +1,45 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Configure iSCSI backend for Cinder +# +# +# === Parameters +# +# [*iscsi_ip_address*] +# (required) IP address of iSCSI target. +# +# [*volume_group*] +# (optional) Cinder volume group name. +# Defaults to 'cinder-volumes'. +# +define cloud::volume::backend::iscsi ( + $iscsi_ip_address, + $volume_group = 'cinder-volumes', + $volume_backend_name = $name, +) { + + + cinder::backend::iscsi { $name: + iscsi_ip_address => $iscsi_ip_address, + volume_group => $volume_group, + } + + @cinder::type { $volume_backend_name: + set_key => 'volume_backend_name', + set_value => $volume_backend_name, + notify => Service['cinder-volume'] + } +} diff --git a/cloud/manifests/volume/backend/netapp.pp b/cloud/manifests/volume/backend/netapp.pp new file mode 100644 index 000000000..273f84f1a --- /dev/null +++ b/cloud/manifests/volume/backend/netapp.pp @@ -0,0 +1,157 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Configure NetApp backend for Cinder +# +# +# === Parameters +# +# [*netapp_login*] +# (required) Administrative user account name used to access the storage +# system or proxy server. +# +# [*netapp_password*] +# (required) Password for the administrative user account specified in the +# netapp_login parameter. +# +# [*netapp_server_hostname*] +# (required) The hostname (or IP address) for the storage system or proxy +# server. +# +# [*netapp_server_port*] +# (optional) The TCP port to use for communication with ONTAPI on the +# storage system. Traditionally, port 80 is used for HTTP and port 443 is +# used for HTTPS; however, this value should be changed if an alternate +# port has been configured on the storage system or proxy server. +# Defaults to 80 +# +# [*netapp_size_multiplier*] +# (optional) The quantity to be multiplied by the requested volume size to +# ensure enough space is available on the virtual storage server (Vserver) to +# fulfill the volume creation request. +# Defaults to 1.2 +# +# [*netapp_storage_family*] +# (optional) The storage family type used on the storage system; valid values +# are ontap_7mode for using Data ONTAP operating in 7-Mode or ontap_cluster +# for using clustered Data ONTAP, or eseries for NetApp E-Series. +# Defaults to ontap_cluster +# +# [*netapp_storage_protocol*] +# (optional) The storage protocol to be used on the data path with the storage +# system; valid values are iscsi or nfs. +# Defaults to nfs +# +# [*netapp_transport_type*] +# (optional) The transport protocol used when communicating with ONTAPI on the +# storage system or proxy server. Valid values are http or https. +# Defaults to http +# +# [*netapp_vfiler*] +# (optional) The vFiler unit on which provisioning of block storage volumes +# will be done. This parameter is only used by the driver when connecting to +# an instance with a storage family of Data ONTAP operating in 7-Mode and the +# storage protocol selected is iSCSI. Only use this parameter when utilizing +# the MultiStore feature on the NetApp storage system. +# Defaults to '' +# +# [*netapp_volume_list*] +# (optional) This parameter is only utilized when the storage protocol is +# configured to use iSCSI. This parameter is used to restrict provisioning to +# the specified controller volumes. Specify the value of this parameter to be +# a comma separated list of NetApp controller volume names to be used for +# provisioning. +# Defaults to '' +# +# [*netapp_vserver*] +# (optional) This parameter specifies the virtual storage server (Vserver) +# name on the storage cluster on which provisioning of block storage volumes +# should occur. If using the NFS storage protocol, this parameter is mandatory +# for storage service catalog support (utilized by Cinder volume type +# extra_specs support). If this parameter is specified, the exports belonging +# to the Vserver will only be used for provisioning in the future. Block +# storage volumes on exports not belonging to the Vserver specified by +# this parameter will continue to function normally. +# Defaults to '' +# +# [*expiry_thres_minutes*] +# (optional) This parameter specifies the threshold for last access time for +# images in the NFS image cache. When a cache cleaning cycle begins, images +# in the cache that have not been accessed in the last M minutes, where M is +# the value of this parameter, will be deleted from the cache to create free +# space on the NFS share. +# Defaults to 720 +# +# [*thres_avl_size_perc_start*] +# (optional) If the percentage of available space for an NFS share has +# dropped below the value specified by this parameter, the NFS image cache +# will be cleaned. +# Defaults to 20 +# +# [*thres_avl_size_perc_stop*] +# (optional) When the percentage of available space on an NFS share has +# reached the percentage specified by this parameter, the driver will stop +# clearing files from the NFS image cache that have not been accessed in the +# last M minutes, where M is the value of the expiry_thres_minutes parameter. +# Defaults to 60 +# +# [*nfs_shares_config*] +# (optional) File with the list of available NFS shares +# Defaults to '' +# +define cloud::volume::backend::netapp ( + $netapp_login, + $netapp_password, + $netapp_server_hostname, + $volume_backend_name = $name, + $netapp_server_port = '80', + $netapp_size_multiplier = '1.2', + $netapp_storage_family = 'ontap_cluster', + $netapp_storage_protocol = 'nfs', + $netapp_transport_type = 'http', + $netapp_vfiler = '', + $netapp_volume_list = '', + $netapp_vserver = '', + $expiry_thres_minutes = '720', + $thres_avl_size_perc_start = '20', + $thres_avl_size_perc_stop = '60', + $nfs_shares_config = '', +) { + + + cinder::backend::netapp { $name: + netapp_server_hostname => $netapp_server_hostname, + netapp_login => $netapp_login, + netapp_password => $netapp_password, + netapp_server_port => $netapp_server_port, + netapp_size_multiplier => $netapp_size_multiplier, + netapp_storage_family => $netapp_storage_family, + netapp_storage_protocol => $netapp_storage_protocol, + netapp_transport_type => $netapp_transport_type, + netapp_vfiler => $netapp_vfiler, + netapp_volume_list => $netapp_volume_list, + netapp_vserver => $netapp_vserver, + expiry_thres_minutes => $expiry_thres_minutes, + thres_avl_size_perc_start => $thres_avl_size_perc_start, + thres_avl_size_perc_stop => $thres_avl_size_perc_stop, + nfs_shares_config => $nfs_shares_config, + } + + @cinder::type { $volume_backend_name: + set_key => 'volume_backend_name', + set_value => $volume_backend_name, + notify => Service['cinder-volume'] + } +} diff --git a/cloud/manifests/volume/backend/nfs.pp b/cloud/manifests/volume/backend/nfs.pp new file mode 100644 index 000000000..0007dddd2 --- /dev/null +++ b/cloud/manifests/volume/backend/nfs.pp @@ -0,0 +1,87 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Configure NFS backend for Cinder +# +# +# === Parameters +# +# [*nfs_servers*] +# (required) Array of NFS servers in the form 'ipaddress:/share' +# +# [*nfs_mount_options*] +# (optional) Mount options passed to the nfs client. See section +# of the nfs man page for details. +# Defaults to undef +# +# [*nfs_disk_util*] +# (optional) Use du or df for free space calculation +# Defaults to undef +# +# [*nfs_sparsed_volumes*] +# (optional) Create volumes as sparsed files which take no space. +# If set to 'false' volume is created as regular file. +# In such case volume creation takes a lot of time. +# Defaults to undef +# +# [*nfs_mount_point_base*] +# (optional) Base dir containing mount points for nfs shares. +# Defaults to undef +# +# [*nfs_shares_config*] +# (optional) File with the list of available NFS shares. +# Defaults to '/etc/cinder/shares.conf' +# +# [*nfs_used_ratio*] +# (optional) Percent of ACTUAL usage of the underlying volume +# before no new volumes can be allocated to the volume destination. +# Defaults to 0.95 +# +# [*nfs_oversub_ratio*] +# (optional) This will compare the allocated to available space on +# the volume destination. If the ratio exceeds this number, the +# destination will no longer be valid. +# Defaults to 1.0 +# +define cloud::volume::backend::nfs( + $volume_backend_name = $name, + $nfs_servers = [], + $nfs_mount_options = undef, + $nfs_disk_util = undef, + $nfs_sparsed_volumes = undef, + $nfs_mount_point_base = undef, + $nfs_shares_config = '/etc/cinder/shares.conf', + $nfs_used_ratio = '0.95', + $nfs_oversub_ratio = '1.0', +) { + + cinder::backend::nfs { $name: + volume_backend_name => $volume_backend_name, + nfs_servers => $nfs_servers, + nfs_mount_options => $nfs_mount_options, + nfs_disk_util => $nfs_disk_util, + nfs_sparsed_volumes => $nfs_sparsed_volumes, + nfs_mount_point_base => $nfs_mount_point_base, + nfs_shares_config => $nfs_shares_config, + nfs_used_ratio => $nfs_used_ratio, + nfs_oversub_ratio => $nfs_oversub_ratio, + } + + @cinder::type { $volume_backend_name: + set_key => 'volume_backend_name', + set_value => $volume_backend_name, + notify => Service['cinder-volume'] + } +} diff --git a/cloud/manifests/volume/backend/rbd.pp b/cloud/manifests/volume/backend/rbd.pp new file mode 100644 index 000000000..cf33d08b9 --- /dev/null +++ b/cloud/manifests/volume/backend/rbd.pp @@ -0,0 +1,103 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Configure RBD backend for Cinder +# +# +# === Parameters +# +# [*rbd_pool*] +# (required) Specifies the pool name for the block device driver. +# +# [*rbd_user*] +# (required) A required parameter to configure OS init scripts and cephx. +# +# [*volume_backend_name*] +# Allows for the volume_backend_name to be separate of $name. +# +# [*rbd_ceph_conf*] +# (optional) Path to the ceph configuration file to use +# Defaults to '/etc/ceph/ceph.conf' +# +# [*rbd_flatten_volume_from_snapshot*] +# (optional) Enable flatten volumes created from snapshots. +# Defaults to false +# +# [*rbd_secret_uuid*] +# (optional) A required parameter to use cephx. +# Defaults to false +# +# [*volume_tmp_dir*] +# (optional) Location to store temporary image files if the volume +# driver does not write them directly to the volume +# Defaults to false +# +# [*rbd_max_clone_depth*] +# (optional) Maximum number of nested clones that can be taken of a +# volume before enforcing a flatten prior to next clone. +# A value of zero disables cloning +# Defaults to '5' +# +define cloud::volume::backend::rbd ( + $rbd_pool, + $rbd_user, + $volume_backend_name = $name, + $rbd_ceph_conf = '/etc/ceph/ceph.conf', + $rbd_flatten_volume_from_snapshot = false, + $rbd_secret_uuid = false, + $rbd_max_clone_depth = '5', +) { + + cinder::backend::rbd { $volume_backend_name: + rbd_pool => $rbd_pool, + rbd_user => $rbd_user, + rbd_secret_uuid => $rbd_secret_uuid, + rbd_ceph_conf => $rbd_ceph_conf, + rbd_flatten_volume_from_snapshot => $rbd_flatten_volume_from_snapshot, + rbd_max_clone_depth => $rbd_max_clone_depth, + volume_tmp_dir => '/tmp' + } + + # If Cinder & Nova reside on the same node, we need a group + # where nova & cinder users have read permissions. + ensure_resource('group', 'cephkeyring', { + ensure => 'present' + }) + + ensure_resource ('exec','add-cinder-to-group', { + 'command' => 'usermod -a -G cephkeyring cinder', + 'path' => ['/usr/sbin', '/usr/bin', '/bin', '/sbin'], + 'unless' => 'groups cinder | grep cephkeyring' + }) + + # Configure Ceph keyring + Ceph::Key <<| title == $rbd_user |>> + if defined(Ceph::Key[$rbd_user]) { + ensure_resource('file', "/etc/ceph/ceph.client.${rbd_user}.keyring", { + owner => 'root', + group => 'cephkeyring', + mode => '0440', + require => Ceph::Key[$rbd_user], + }) + } + + Concat::Fragment <<| title == 'ceph-client-os' |>> + + @cinder::type { $volume_backend_name: + set_key => 'volume_backend_name', + set_value => $volume_backend_name, + notify => Service['cinder-volume'] + } +} diff --git a/cloud/manifests/volume/backup.pp b/cloud/manifests/volume/backup.pp new file mode 100644 index 000000000..26e701810 --- /dev/null +++ b/cloud/manifests/volume/backup.pp @@ -0,0 +1,45 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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: +# +# Volume Backup node +# +# === Parameters +# +# [*backup_ceph_pool*] +# (optional) Name of the Ceph pool which which store the cinder backups +# Defaults to 'backup' +# +# [*backup_ceph_user*] +# (optional) User name used to acces to the backup rbd pool +# Defaults to 'cinder' +# +class cloud::volume::backup( + $backup_ceph_pool = 'backup', + $backup_ceph_user = 'cinder' +) { + + include 'cloud::volume' + + class { 'cinder::backup': } + + # TODO(EmilienM) Disabled for now: http://git.io/kfTmcA + # class { 'cinder::backup::ceph': + # backup_ceph_user => $backup_ceph_user, + # backup_ceph_pool => $backup_ceph_pool + # } + +} diff --git a/cloud/manifests/volume/scheduler.pp b/cloud/manifests/volume/scheduler.pp new file mode 100644 index 000000000..79ba66a98 --- /dev/null +++ b/cloud/manifests/volume/scheduler.pp @@ -0,0 +1,26 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Volume Scheduler node +# +class cloud::volume::scheduler{ + + include 'cloud::volume' + + class { 'cinder::scheduler': + scheduler_driver => 'cinder.scheduler.filter_scheduler.FilterScheduler' + } + +} diff --git a/cloud/manifests/volume/storage.pp b/cloud/manifests/volume/storage.pp new file mode 100644 index 000000000..bc6b642bc --- /dev/null +++ b/cloud/manifests/volume/storage.pp @@ -0,0 +1,167 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Volume storage +# +# === Parameters +# +# [*ks_keystone_internal_proto*] +# (optional) Protocol used to connect to API. Could be 'http' or 'https'. +# Defaults to 'http' +# +# [*ks_keystone_internal_host*] +# (optional) Internal Hostname or IP to connect to Keystone API +# Defaults to '127.0.0.1' +# +# [*ks_keystone_internal_port*] +# (optional) TCP port to connect to Keystone API from admin network +# Default to '5000' +# +# [*ks_cinder_password*] +# (optional) Password used by Cinder to connect to Keystone API +# Defaults to 'secrete' +# +# [*cinder_backends*] +# (optionnal) Hash of the Cinder backends to enable +# Example: +# cinder_backends = { +# 'rbd' => { +# 'lowcost' => { 'rbd_pool' => 'slow', 'rbd_user' => 'cinder', 'rbd_secret_uuid' => '123' }, +# 'standard' => { 'rbd_pool' => 'normal', 'rbd_user' => 'cinder', 'rbd_secret_uuid' => '123' } +# }, +# 'netapp' => { +# 'premium' => { 'netapp_server_hostname' => 'netapp.host', 'netapp_login' => 'joe', 'netapp_password' => 'secret' } +# } +# } +# Defaults to undef +# +# [*cinder_rbd_pool*] +# (optional) Name of the Ceph pool which which store the cinder images +# Defaults to 'volumes' +# +# [*cinder_rbd_user*] +# (optional) User name used to acces to the cinder rbd pool +# Defaults to 'cinder' +# +# [*cinder_rbd_secret_uuid*] +# (optional) A required parameter to use cephx. +# Defaults to false +# +# [*cinder_rbd_conf*] +# (optional) Path to the ceph configuration file to use +# Defaults to '/etc/ceph/ceph.conf' +# +# [*cinder_rbd_flatten_volume_from_snapshot*] +# (optional) Enable flatten volumes created from snapshots. +# Defaults to false +# +# [*cinder_rbd_max_clone_depth*] +# (optional) Maximum number of nested clones that can be taken of a +# volume before enforcing a flatten prior to next clone. +# A value of zero disables cloning +# Defaults to '5' +# +class cloud::volume::storage( + $cinder_backends = undef, + $ks_keystone_internal_proto = 'http', + $ks_keystone_internal_port = '5000', + $ks_keystone_internal_host = '127.0.0.1', + $ks_cinder_password = 'secrete', + $cinder_rbd_pool = 'volumes', + $cinder_rbd_user = 'cinder', + $cinder_rbd_secret_uuid = undef, + $cinder_rbd_conf = '/etc/ceph/ceph.conf', + $cinder_rbd_flatten_volume_from_snapshot = false, + $cinder_rbd_max_clone_depth = '5', +) { + + include 'cloud::volume' + + include 'cinder::volume' + + if $cinder_backends { + + if has_key($cinder_backends, 'rbd') { + $rbd_backends = $cinder_backends['rbd'] + create_resources('cloud::volume::backend::rbd', $rbd_backends) + } + else { + $rbd_backends = { } + } + + if has_key($cinder_backends, 'netapp') { + $netapp_backends = $cinder_backends['netapp'] + create_resources('cloud::volume::backend::netapp', $netapp_backends) + } + else { + $netapp_backends = { } + } + + if has_key($cinder_backends, 'iscsi') { + $iscsi_backends = $cinder_backends['iscsi'] + create_resources('cloud::volume::backend::iscsi', $iscsi_backends) + } + else { + $iscsi_backends = { } + } + + if has_key($cinder_backends, 'emc_vnx') { + $emc_vnx_backends = $cinder_backends['emc_vnx'] + create_resources('cloud::volume::backend::emc_vnx', $emc_vnx_backends) + } + else { + $emc_vnx_backends = { } + } + + if has_key($cinder_backends, 'eqlx') { + $eqlx_backends = $cinder_backends['eqlx'] + create_resources('cloud::volume::backend::eqlx', $eqlx_backends) + } + else { + $eqlx_backends = { } + } + + if has_key($cinder_backends, 'glusterfs') { + $glusterfs_backends = $cinder_backends['glusterfs'] + create_resources('cloud::volume::backend::glusterfs', $glusterfs_backends) + } + else { + $glusterfs_backends = { } + } + + if has_key($cinder_backends, 'nfs') { + $nfs_backends = $cinder_backends['nfs'] + create_resources('cloud::volume::backend::nfs', $nfs_backends) + } + else { + $nfs_backends = { } + } + + class { 'cinder::backends': + enabled_backends => keys(merge($rbd_backends, $netapp_backends, $iscsi_backends, $emc_vnx_backends, $eqlx_backends, $nfs_backends, $glusterfs_backends)) + } + + # Manage Volume types. + # It allows to the end-user to choose from which backend he would like to provision a volume. + # Cinder::Type requires keystone credentials + Cinder::Type <| |> { + os_tenant_name => 'services', + os_username => 'cinder', + os_password => $ks_cinder_password, + os_auth_url => "${ks_keystone_internal_proto}://${ks_keystone_internal_host}:${ks_keystone_internal_port}/v2.0" + } + } + +} diff --git a/cloud/metadata.json b/cloud/metadata.json new file mode 100644 index 000000000..40352cf53 --- /dev/null +++ b/cloud/metadata.json @@ -0,0 +1,33 @@ +{ + "name": "eNovance-cloud", + "version": "2.2.0", + "author": "eNovance", + "summary": "eNovance Openstack Module", + "license": "Apache License 2.0", + "source": "https://github.com/enovance/puppet-openstack-cloud", + "project_page": "https://github.com/enovance/puppet-openstack-cloud", + "issues_url": "https://github.com/enovance/puppet-openstack-cloud/issues", + "operatingsystem_support": [ + {"operatingsystem": "Debian"}, + {"operatingsystem": "Ubuntu"}, + {"operatingsystem": "CentOS"}, + {"operatingsystem": "RedHat"} + ], + "requirements": [ + {"name": "pe","version_requirement": "3.x"}, + {"name": "puppet","version_requirement": "3.x"} + ], + "description": "Puppet module that pulls together all the individual components of Openstack, resulting is a complete and functional stack", + "dependencies": [ + {"name":"puppetlabs/glance","version_requirement":">=4.0.0"}, + {"name":"puppetlabs/horizon","version_requirement":">=4.0.0"}, + {"name":"puppetlabs/keystone","version_requirement":">=4.0.0"}, + {"name":"puppetlabs/nova","version_requirement":">=4.0.0"}, + {"name":"puppetlabs/cinder","version_requirement":">=4.0.0"}, + {"name":"puppetlabs/swift","version_requirement":">=4.0.0"}, + {"name":"puppetlabs/neutron","version_requirement":">=4.0.0"}, + {"name":"puppetlabs/ceilometer","version_requirement":">=4.0.0"}, + {"name":"puppetlabs/heat","version_requirement":">=4.0.0"}, + {"name":"eNovance/ceph", "version_requirement":">=1.1.0"} + ] +} diff --git a/cloud/scripts/bootstrap.pp b/cloud/scripts/bootstrap.pp new file mode 100644 index 000000000..ba3db2422 --- /dev/null +++ b/cloud/scripts/bootstrap.pp @@ -0,0 +1,78 @@ +case $::osfamily { + 'RedHat': { + augeas {'httpd-lang' : + context => '/files/etc/sysconfig/httpd/', + changes => 'set LANG en_US.UTF-8', + notify => Service['httpd'], + require => Package['httpd'], + } + } + 'Debian': { + # Bug Puppet: https://tickets.puppetlabs.com/browse/PUP-1386 + exec { 'echo \'. /etc/default/locale\' >> /etc/apache2/envvars' : + path => ['/bin', '/usr/bin'], + unless => 'grep \'^. /etc/default/locale$\' /etc/apache2/envvars', + notify => Service['httpd'], + require => Package['httpd'], + } + # Bug Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=736849 + exec { 'echo \'umask 022\' >> /etc/apache2/envvars' : + path => ['/bin', '/usr/bin'], + unless => 'grep \'umask 022\' /etc/apache2/envvars', + notify => Service['httpd'], + require => Package['httpd'], + } + } + default: { + fail("Unsupported osfamily (${::osfamily})") + } +} + +class { 'cloud::install::puppetmaster' : + puppetdb_enable => false, + autosign_domains => ['*'], + agent_configuration => { + 'agent-ssl_client_header' => { + 'setting' => 'ssl_client_header', + 'value' => 'SSL_CLIENT_S_DN' + }, + 'agent-ssl_client_verify_header' => { + 'setting' => 'ssl_client_verify_header', + 'value' => 'SSL_CLIENT_VERIFY' + }, + 'agent-certname' => { + 'setting' => 'certname', + 'value' => $::fqdn + }, + 'agent-server' => { + 'setting' => 'server', + 'value' => $::fqdn + }, + }, + main_configuration => { + 'main-configtimeout' => { + 'setting' => 'configtimeout', + 'value' => '10m' + }, + }, + puppetmaster_vhost_configuration => { + 'puppetmasterd' => { + 'docroot' => '/usr/share/puppet/rack/puppetmasterd/public', + 'port' => 8140, + 'ssl' => true, + 'ssl_protocol' => 'ALL -SSLv2 -SSLv3', + 'ssl_cipher' => 'ALL:!aNULL:!eNULL:!DES:!3DES:!IDEA:!SEED:!DSS:!PSK:!RC4:!MD5:+HIGH:+MEDIUM:!LOW:!SSLv2:!EXP', + 'ssl_honorcipherorder' => 'On', + 'ssl_cert' => "/var/lib/puppet/ssl/certs/${::fqdn}.pem", + 'ssl_key' => "/var/lib/puppet/ssl/private_keys/${::fqdn}.pem", + 'ssl_chain' => '/var/lib/puppet/ssl/certs/ca.pem', + 'ssl_ca' => '/var/lib/puppet/ssl/certs/ca.pem', + 'ssl_verify_client' => 'optional', + 'ssl_verify_depth' => 1, + 'ssl_options' => ['+StdEnvVars', '+ExportCertData'], + 'request_headers' => ['unset X-Forwarded-For', 'set X-SSL-Subject %{SSL_CLIENT_S_DN}e', 'set X-Client-DN %{SSL_CLIENT_S_DN}e', 'set X-Client-Verify %{SSL_CLIENT_VERIFY}e'], + 'rack_base_uris' => '/', + 'add_default_charset' => 'UTF-8', + } + } +} diff --git a/cloud/scripts/bootstrap_post_puppetdb.pp b/cloud/scripts/bootstrap_post_puppetdb.pp new file mode 100644 index 000000000..141c11ed3 --- /dev/null +++ b/cloud/scripts/bootstrap_post_puppetdb.pp @@ -0,0 +1,48 @@ +class { 'cloud::install::puppetmaster' : + puppetdb_enable => true, + autosign_domains => ['*'], + agent_configuration => { + 'agent-ssl_client_header' => { + 'setting' => 'ssl_client_header', + 'value' => 'SSL_CLIENT_S_DN' + }, + 'agent-ssl_client_verify_header' => { + 'setting' => 'ssl_client_verify_header', + 'value' => 'SSL_CLIENT_VERIFY' + }, + 'agent-certname' => { + 'setting' => 'certname', + 'value' => $::fqdn + }, + 'agent-server' => { + 'setting' => 'server', + 'value' => $::fqdn + }, + }, + main_configuration => { + 'main-configtimeout' => { + 'setting' => 'configtimeout', + 'value' => '10m' + }, + }, + puppetmaster_vhost_configuration => { + 'puppetmasterd' => { + 'docroot' => '/usr/share/puppet/rack/puppetmasterd/public', + 'port' => 8140, + 'ssl' => true, + 'ssl_protocol' => 'ALL -SSLv2 -SSLv3', + 'ssl_cipher' => 'ALL:!aNULL:!eNULL:!DES:!3DES:!IDEA:!SEED:!DSS:!PSK:!RC4:!MD5:+HIGH:+MEDIUM:!LOW:!SSLv2:!EXP', + 'ssl_honorcipherorder' => 'On', + 'ssl_cert' => "/var/lib/puppet/ssl/certs/${::fqdn}.pem", + 'ssl_key' => "/var/lib/puppet/ssl/private_keys/${::fqdn}.pem", + 'ssl_chain' => '/var/lib/puppet/ssl/certs/ca.pem', + 'ssl_ca' => '/var/lib/puppet/ssl/certs/ca.pem', + 'ssl_verify_client' => 'optional', + 'ssl_verify_depth' => 1, + 'ssl_options' => ['+StdEnvVars', '+ExportCertData'], + 'request_headers' => ['unset X-Forwarded-For', 'set X-SSL-Subject %{SSL_CLIENT_S_DN}e', 'set X-Client-DN %{SSL_CLIENT_S_DN}e', 'set X-Client-Verify %{SSL_CLIENT_VERIFY}e'], + 'rack_base_uris' => '/', + 'add_default_charset' => 'UTF-8', + } + } +} diff --git a/cloud/spec/classes/cloud_cache_spec.rb b/cloud/spec/classes/cloud_cache_spec.rb new file mode 100644 index 000000000..ea4f476ee --- /dev/null +++ b/cloud/spec/classes/cloud_cache_spec.rb @@ -0,0 +1,84 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::cache +# + +require 'spec_helper' + +describe 'cloud::cache' do + + shared_examples_for 'cache server' do + + let :params do + { :listen_ip => '10.0.0.1' } + end + + it 'configure memcached with some params' do + is_expected.to contain_class('memcached').with( + :listen_ip => '10.0.0.1', + :max_memory => '60%' + ) + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure memcached firewall rules' do + is_expected.to contain_firewall('100 allow memcached access').with( + :port => '11211', + :proto => 'tcp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure memcached firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow memcached access').with( + :port => '11211', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'cache server' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'cache server' + end + +end diff --git a/cloud/spec/classes/cloud_compute_api_spec.rb b/cloud/spec/classes/cloud_compute_api_spec.rb new file mode 100644 index 000000000..8c2aede11 --- /dev/null +++ b/cloud/spec/classes/cloud_compute_api_spec.rb @@ -0,0 +1,197 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::compute::api class +# + +require 'spec_helper' + +describe 'cloud::compute::api' do + + shared_examples_for 'openstack compute api' do + + let :pre_condition do + "class { 'cloud::compute': + availability_zone => 'MyZone', + nova_db_host => '10.0.0.1', + nova_db_use_slave => false, + nova_db_user => 'nova', + nova_db_password => 'secrete', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + ks_glance_internal_host => '10.0.0.1', + glance_api_port => '9292', + verbose => true, + debug => true, + use_syslog => true, + neutron_protocol => 'http', + neutron_endpoint => '10.0.0.1', + neutron_region_name => 'MyRegion', + neutron_password => 'secrete', + memcache_servers => ['10.0.0.1','10.0.0.2'], + log_facility => 'LOG_LOCAL0' }" + end + + let :params do + { :ks_keystone_internal_host => '127.0.0.1', + :ks_keystone_internal_proto => 'https', + :ks_nova_password => 'novapassword', + :api_eth => '127.0.0.1', + :ks_ec2_public_port => '8773', + :ks_nova_public_port => '8774', + :ks_metadata_public_port => '8775', + :neutron_metadata_proxy_shared_secret => 'metadatapassword' } + end + + it 'configure nova common' do + is_expected.to contain_class('nova').with( + :verbose => true, + :debug => true, + :use_syslog => true, + :log_facility => 'LOG_LOCAL0', + :rabbit_userid => 'nova', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :memcached_servers => ['10.0.0.1','10.0.0.2'], + :database_connection => 'mysql://nova:secrete@10.0.0.1/nova?charset=utf8', + :glance_api_servers => 'http://10.0.0.1:9292', + :log_dir => false + ) + is_expected.to contain_nova_config('DEFAULT/resume_guests_state_on_host_boot').with('value' => true) + is_expected.to contain_nova_config('DEFAULT/default_availability_zone').with('value' => 'MyZone') + is_expected.to contain_nova_config('DEFAULT/servicegroup_driver').with_value('mc') + is_expected.to contain_nova_config('DEFAULT/glance_num_retries').with_value('10') + end + + it 'does not configure nova db slave' do + is_expected.to contain_nova_config('database/slave_connection').with('ensure' => 'absent') + end + + context "when enabling nova db slave" do + let :pre_condition do + "class { 'cloud::compute': + nova_db_host => '10.0.0.1', + nova_db_use_slave => true, + nova_db_user => 'nova', + nova_db_password => 'secrete' }" + end + it 'configure nova db slave' do + is_expected.to contain_nova_config('database/slave_connection').with( + 'value' => 'mysql://nova:secrete@10.0.0.1:3307/nova?charset=utf8') + end + end + + it 'configure neutron on compute node' do + is_expected.to contain_class('nova::network::neutron').with( + :neutron_admin_password => 'secrete', + :neutron_admin_auth_url => 'http://10.0.0.1:35357/v2.0', + :neutron_region_name => 'MyRegion', + :neutron_url => 'http://10.0.0.1:9696' + ) + end + + it 'checks if Nova DB is populated' do + is_expected.to contain_exec('nova_db_sync').with( + :command => 'nova-manage db sync', + :user => 'nova', + :path => '/usr/bin', + :unless => '/usr/bin/mysql nova -h 10.0.0.1 -u nova -psecrete -e "show tables" | /bin/grep Tables' + ) + end + + it 'configure nova-api' do + is_expected.to contain_class('nova::api').with( + :enabled => true, + :auth_host => '127.0.0.1', + :auth_protocol => 'https', + :admin_password => 'novapassword', + :api_bind_address => '127.0.0.1', + :metadata_listen => '127.0.0.1', + :neutron_metadata_proxy_shared_secret => 'metadatapassword', + :osapi_v3 => true + ) + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure nova firewall rules' do + is_expected.to contain_firewall('100 allow nova-api access').with( + :port => '8774', + :proto => 'tcp', + :action => 'accept', + ) + is_expected.to contain_firewall('100 allow nova-ec2 access').with( + :port => '8773', + :proto => 'tcp', + :action => 'accept', + ) + is_expected.to contain_firewall('100 allow nova-metadata access').with( + :port => '8775', + :proto => 'tcp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure nova firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow nova-api access').with( + :port => '8774', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + is_expected.to contain_firewall('100 allow nova-ec2 access').with( + :port => '8773', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + is_expected.to contain_firewall('100 allow nova-metadata access').with( + :port => '8775', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack compute api' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + it_configures 'openstack compute api' + end + +end diff --git a/cloud/spec/classes/cloud_compute_cert_spec.rb b/cloud/spec/classes/cloud_compute_cert_spec.rb new file mode 100644 index 000000000..ba1b469a6 --- /dev/null +++ b/cloud/spec/classes/cloud_compute_cert_spec.rb @@ -0,0 +1,106 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::compute::cert class +# + +require 'spec_helper' + +describe 'cloud::compute::cert' do + + shared_examples_for 'openstack compute cert' do + + let :pre_condition do + "class { 'cloud::compute': + availability_zone => 'MyZone', + nova_db_host => '10.0.0.1', + nova_db_user => 'nova', + nova_db_password => 'secrete', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + ks_glance_internal_host => '10.0.0.1', + glance_api_port => '9292', + verbose => true, + debug => true, + use_syslog => true, + neutron_protocol => 'http', + neutron_endpoint => '10.0.0.1', + neutron_region_name => 'MyRegion', + neutron_password => 'secrete', + memcache_servers => ['10.0.0.1','10.0.0.2'], + log_facility => 'LOG_LOCAL0' }" + end + + it 'configure nova common' do + is_expected.to contain_class('nova').with( + :verbose => true, + :debug => true, + :use_syslog => true, + :log_facility => 'LOG_LOCAL0', + :rabbit_userid => 'nova', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :memcached_servers => ['10.0.0.1','10.0.0.2'], + :database_connection => 'mysql://nova:secrete@10.0.0.1/nova?charset=utf8', + :glance_api_servers => 'http://10.0.0.1:9292', + :log_dir => false + ) + is_expected.to contain_nova_config('DEFAULT/resume_guests_state_on_host_boot').with('value' => true) + is_expected.to contain_nova_config('DEFAULT/default_availability_zone').with('value' => 'MyZone') + is_expected.to contain_nova_config('DEFAULT/servicegroup_driver').with_value('mc') + is_expected.to contain_nova_config('DEFAULT/glance_num_retries').with_value('10') + end + + it 'configure neutron on compute node' do + is_expected.to contain_class('nova::network::neutron').with( + :neutron_admin_password => 'secrete', + :neutron_admin_auth_url => 'http://10.0.0.1:35357/v2.0', + :neutron_region_name => 'MyRegion', + :neutron_url => 'http://10.0.0.1:9696' + ) + end + + it 'checks if Nova DB is populated' do + is_expected.to contain_exec('nova_db_sync').with( + :command => 'nova-manage db sync', + :user => 'nova', + :path => '/usr/bin', + :unless => '/usr/bin/mysql nova -h 10.0.0.1 -u nova -psecrete -e "show tables" | /bin/grep Tables' + ) + end + + it 'configure nova-cert' do + is_expected.to contain_class('nova::cert').with(:enabled => true) + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack compute cert' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + it_configures 'openstack compute cert' + end + +end diff --git a/cloud/spec/classes/cloud_compute_conductor_spec.rb b/cloud/spec/classes/cloud_compute_conductor_spec.rb new file mode 100644 index 000000000..44840f9d0 --- /dev/null +++ b/cloud/spec/classes/cloud_compute_conductor_spec.rb @@ -0,0 +1,106 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::compute::conductor class +# + +require 'spec_helper' + +describe 'cloud::compute::conductor' do + + shared_examples_for 'openstack compute conductor' do + + let :pre_condition do + "class { 'cloud::compute': + availability_zone => 'MyZone', + nova_db_host => '10.0.0.1', + nova_db_user => 'nova', + nova_db_password => 'secrete', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + ks_glance_internal_host => '10.0.0.1', + glance_api_port => '9292', + verbose => true, + debug => true, + use_syslog => true, + neutron_protocol => 'http', + neutron_endpoint => '10.0.0.1', + neutron_region_name => 'MyRegion', + neutron_password => 'secrete', + memcache_servers => ['10.0.0.1','10.0.0.2'], + log_facility => 'LOG_LOCAL0' }" + end + + it 'configure nova common' do + is_expected.to contain_class('nova').with( + :verbose => true, + :debug => true, + :use_syslog => true, + :log_facility => 'LOG_LOCAL0', + :rabbit_userid => 'nova', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :memcached_servers => ['10.0.0.1','10.0.0.2'], + :database_connection => 'mysql://nova:secrete@10.0.0.1/nova?charset=utf8', + :glance_api_servers => 'http://10.0.0.1:9292', + :log_dir => false + ) + is_expected.to contain_nova_config('DEFAULT/resume_guests_state_on_host_boot').with('value' => true) + is_expected.to contain_nova_config('DEFAULT/default_availability_zone').with('value' => 'MyZone') + is_expected.to contain_nova_config('DEFAULT/servicegroup_driver').with_value('mc') + is_expected.to contain_nova_config('DEFAULT/glance_num_retries').with_value('10') + end + + it 'configure neutron on compute node' do + is_expected.to contain_class('nova::network::neutron').with( + :neutron_admin_password => 'secrete', + :neutron_admin_auth_url => 'http://10.0.0.1:35357/v2.0', + :neutron_region_name => 'MyRegion', + :neutron_url => 'http://10.0.0.1:9696' + ) + end + + it 'checks if Nova DB is populated' do + is_expected.to contain_exec('nova_db_sync').with( + :command => 'nova-manage db sync', + :user => 'nova', + :path => '/usr/bin', + :unless => '/usr/bin/mysql nova -h 10.0.0.1 -u nova -psecrete -e "show tables" | /bin/grep Tables' + ) + end + + it 'configure nova-conductor' do + is_expected.to contain_class('nova::conductor').with(:enabled => true) + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack compute conductor' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + it_configures 'openstack compute conductor' + end + +end diff --git a/cloud/spec/classes/cloud_compute_consoleauth_spec.rb b/cloud/spec/classes/cloud_compute_consoleauth_spec.rb new file mode 100644 index 000000000..bb63c0fe9 --- /dev/null +++ b/cloud/spec/classes/cloud_compute_consoleauth_spec.rb @@ -0,0 +1,106 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::compute::consoleauth class +# + +require 'spec_helper' + +describe 'cloud::compute::consoleauth' do + + shared_examples_for 'openstack compute consoleauth' do + + let :pre_condition do + "class { 'cloud::compute': + availability_zone => 'MyZone', + nova_db_host => '10.0.0.1', + nova_db_user => 'nova', + nova_db_password => 'secrete', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + ks_glance_internal_host => '10.0.0.1', + glance_api_port => '9292', + verbose => true, + debug => true, + use_syslog => true, + neutron_protocol => 'http', + neutron_endpoint => '10.0.0.1', + neutron_region_name => 'MyRegion', + neutron_password => 'secrete', + memcache_servers => ['10.0.0.1','10.0.0.2'], + log_facility => 'LOG_LOCAL0' }" + end + + it 'configure nova common' do + is_expected.to contain_class('nova').with( + :verbose => true, + :debug => true, + :use_syslog => true, + :log_facility => 'LOG_LOCAL0', + :rabbit_userid => 'nova', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :memcached_servers => ['10.0.0.1','10.0.0.2'], + :database_connection => 'mysql://nova:secrete@10.0.0.1/nova?charset=utf8', + :glance_api_servers => 'http://10.0.0.1:9292', + :log_dir => false + ) + is_expected.to contain_nova_config('DEFAULT/resume_guests_state_on_host_boot').with('value' => true) + is_expected.to contain_nova_config('DEFAULT/default_availability_zone').with('value' => 'MyZone') + is_expected.to contain_nova_config('DEFAULT/servicegroup_driver').with_value('mc') + is_expected.to contain_nova_config('DEFAULT/glance_num_retries').with_value('10') + end + + it 'configure neutron on compute node' do + is_expected.to contain_class('nova::network::neutron').with( + :neutron_admin_password => 'secrete', + :neutron_admin_auth_url => 'http://10.0.0.1:35357/v2.0', + :neutron_region_name => 'MyRegion', + :neutron_url => 'http://10.0.0.1:9696' + ) + end + + it 'checks if Nova DB is populated' do + is_expected.to contain_exec('nova_db_sync').with( + :command => 'nova-manage db sync', + :user => 'nova', + :path => '/usr/bin', + :unless => '/usr/bin/mysql nova -h 10.0.0.1 -u nova -psecrete -e "show tables" | /bin/grep Tables' + ) + end + + it 'configure nova-consoleauth' do + is_expected.to contain_class('nova::consoleauth').with(:enabled => true) + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack compute consoleauth' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + it_configures 'openstack compute consoleauth' + end + +end diff --git a/cloud/spec/classes/cloud_compute_consoleproxy_spec.rb b/cloud/spec/classes/cloud_compute_consoleproxy_spec.rb new file mode 100644 index 000000000..ae2ddab1b --- /dev/null +++ b/cloud/spec/classes/cloud_compute_consoleproxy_spec.rb @@ -0,0 +1,144 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::compute::consoleproxy class +# + +require 'spec_helper' + +describe 'cloud::compute::consoleproxy' do + + shared_examples_for 'openstack compute consoleproxy' do + + let :pre_condition do + "class { 'cloud::compute': + availability_zone => 'MyZone', + nova_db_host => '10.0.0.1', + nova_db_user => 'nova', + nova_db_password => 'secrete', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + ks_glance_internal_host => '10.0.0.1', + glance_api_port => '9292', + verbose => true, + debug => true, + use_syslog => true, + neutron_protocol => 'http', + neutron_endpoint => '10.0.0.1', + neutron_region_name => 'MyRegion', + neutron_password => 'secrete', + memcache_servers => ['10.0.0.1','10.0.0.2'], + log_facility => 'LOG_LOCAL0' }" + end + + let :params do + { :api_eth => '10.0.0.1', + :spice_port => '6082' } + end + + it 'configure nova common' do + is_expected.to contain_class('nova').with( + :verbose => true, + :debug => true, + :use_syslog => true, + :log_facility => 'LOG_LOCAL0', + :rabbit_userid => 'nova', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :memcached_servers => ['10.0.0.1','10.0.0.2'], + :database_connection => 'mysql://nova:secrete@10.0.0.1/nova?charset=utf8', + :glance_api_servers => 'http://10.0.0.1:9292', + :log_dir => false + ) + is_expected.to contain_nova_config('DEFAULT/resume_guests_state_on_host_boot').with('value' => true) + is_expected.to contain_nova_config('DEFAULT/default_availability_zone').with('value' => 'MyZone') + is_expected.to contain_nova_config('DEFAULT/servicegroup_driver').with_value('mc') + is_expected.to contain_nova_config('DEFAULT/glance_num_retries').with_value('10') + end + + it 'configure neutron on compute node' do + is_expected.to contain_class('nova::network::neutron').with( + :neutron_admin_password => 'secrete', + :neutron_admin_auth_url => 'http://10.0.0.1:35357/v2.0', + :neutron_region_name => 'MyRegion', + :neutron_url => 'http://10.0.0.1:9696' + ) + end + + it 'checks if Nova DB is populated' do + is_expected.to contain_exec('nova_db_sync').with( + :command => 'nova-manage db sync', + :user => 'nova', + :path => '/usr/bin', + :unless => '/usr/bin/mysql nova -h 10.0.0.1 -u nova -psecrete -e "show tables" | /bin/grep Tables' + ) + end + + it 'configure nova-spicehtml5proxy' do + is_expected.to contain_class('nova::spicehtml5proxy').with( + :enabled => true, + :host => '10.0.0.1' + ) + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure spice firewall rules' do + is_expected.to contain_firewall('100 allow spice access').with( + :port => '6082', + :proto => 'tcp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure spice firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow spice access').with( + :port => '6082', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack compute consoleproxy' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + it_configures 'openstack compute consoleproxy' + end + +end diff --git a/cloud/spec/classes/cloud_compute_hypervisor_spec.rb b/cloud/spec/classes/cloud_compute_hypervisor_spec.rb new file mode 100644 index 000000000..f13e31919 --- /dev/null +++ b/cloud/spec/classes/cloud_compute_hypervisor_spec.rb @@ -0,0 +1,519 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::compute::hypervisor class +# + +require 'spec_helper' + +describe 'cloud::compute::hypervisor' do + + shared_examples_for 'openstack compute hypervisor' do + + let :pre_condition do + "class { 'cloud::compute': + availability_zone => 'MyZone', + nova_db_host => '10.0.0.1', + nova_db_user => 'nova', + nova_db_password => 'secrete', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + ks_glance_internal_host => '10.0.0.1', + glance_api_port => '9292', + verbose => true, + debug => true, + use_syslog => true, + neutron_protocol => 'http', + neutron_endpoint => '10.0.0.1', + neutron_region_name => 'MyRegion', + neutron_password => 'secrete', + memcache_servers => ['10.0.0.1','10.0.0.2'], + log_facility => 'LOG_LOCAL0' } + class { 'cloud::telemetry': + ceilometer_secret => 'secrete', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + ks_keystone_internal_host => '10.0.0.1', + ks_keystone_internal_port => '5000', + ks_keystone_internal_proto => 'http', + ks_ceilometer_password => 'secrete', + log_facility => 'LOG_LOCAL0', + use_syslog => true, + verbose => true, + debug => true } + class { 'cloud::network': + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + api_eth => '10.0.0.1', + verbose => true, + debug => true, + use_syslog => true, + dhcp_lease_duration => '10', + log_facility => 'LOG_LOCAL0' }" + end + + let :params do + { :libvirt_type => 'kvm', + :server_proxyclient_address => '7.0.0.1', + :spice_port => '6082', + :nova_ssh_private_key => 'secrete', + :nova_ssh_public_key => 'public', + :ks_nova_public_proto => 'http', + :ks_spice_public_proto => 'https', + :ks_spice_public_host => '10.0.0.2', + :vm_rbd => false, + :volume_rbd => false, + :nova_shell => false, + :ks_nova_public_host => '10.0.0.1' } + end + + it 'configure nova common' do + is_expected.to contain_class('nova').with( + :verbose => true, + :debug => true, + :use_syslog => true, + :log_facility => 'LOG_LOCAL0', + :rabbit_userid => 'nova', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :memcached_servers => ['10.0.0.1','10.0.0.2'], + :database_connection => 'mysql://nova:secrete@10.0.0.1/nova?charset=utf8', + :glance_api_servers => 'http://10.0.0.1:9292', + :log_dir => false, + :nova_shell => '/bin/bash' + ) + is_expected.to contain_nova_config('DEFAULT/resume_guests_state_on_host_boot').with('value' => true) + is_expected.to contain_nova_config('DEFAULT/default_availability_zone').with('value' => 'MyZone') + is_expected.to contain_nova_config('DEFAULT/servicegroup_driver').with_value('mc') + is_expected.to contain_nova_config('DEFAULT/glance_num_retries').with_value('10') + end + + it 'configure neutron on compute node' do + is_expected.to contain_class('nova::network::neutron').with( + :neutron_admin_password => 'secrete', + :neutron_admin_auth_url => 'http://10.0.0.1:35357/v2.0', + :neutron_region_name => 'MyRegion', + :neutron_url => 'http://10.0.0.1:9696' + ) + end + + it 'configure ceilometer common' do + is_expected.to contain_class('ceilometer').with( + :verbose => true, + :debug => true, + :rabbit_userid => 'ceilometer', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :metering_secret => 'secrete', + :use_syslog => true, + :log_facility => 'LOG_LOCAL0' + ) + is_expected.to contain_class('ceilometer::agent::auth').with( + :auth_password => 'secrete', + :auth_url => 'http://10.0.0.1:5000/v2.0' + ) + end + + it 'configure neutron common' do + is_expected.to contain_class('neutron').with( + :allow_overlapping_ips => true, + :dhcp_agents_per_network => '2', + :verbose => true, + :debug => true, + :log_facility => 'LOG_LOCAL0', + :use_syslog => true, + :rabbit_user => 'neutron', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :bind_host => '10.0.0.1', + :core_plugin => 'neutron.plugins.ml2.plugin.Ml2Plugin', + :service_plugins => ['neutron.services.loadbalancer.plugin.LoadBalancerPlugin','neutron.services.metering.metering_plugin.MeteringPlugin','neutron.services.l3_router.l3_router_plugin.L3RouterPlugin'], + :log_dir => false, + :report_interval => '30' + ) + end + + it 'configure neutron on compute node' do + is_expected.to contain_class('nova::network::neutron').with( + :neutron_admin_password => 'secrete', + :neutron_admin_auth_url => 'http://10.0.0.1:35357/v2.0', + :neutron_region_name => 'MyRegion', + :neutron_url => 'http://10.0.0.1:9696' + ) + end + + it 'configure ceilometer common' do + is_expected.to contain_class('ceilometer').with( + :verbose => true, + :debug => true, + :rabbit_userid => 'ceilometer', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :metering_secret => 'secrete', + :use_syslog => true, + :log_facility => 'LOG_LOCAL0' + ) + is_expected.to contain_class('ceilometer::agent::auth').with( + :auth_password => 'secrete', + :auth_url => 'http://10.0.0.1:5000/v2.0' + ) + end + + it 'checks if Nova DB is populated' do + is_expected.to contain_exec('nova_db_sync').with( + :command => 'nova-manage db sync', + :path => '/usr/bin', + :user => 'nova', + :unless => '/usr/bin/mysql nova -h 10.0.0.1 -u nova -psecrete -e "show tables" | /bin/grep Tables' + ) + end + + it 'configure nova-compute' do + is_expected.to contain_class('nova::compute').with( + :enabled => true, + :vnc_enabled => false, + :virtio_nic => false, + :neutron_enabled => true + ) + end + + it 'configure spice console' do + is_expected.to contain_class('nova::compute::spice').with( + :server_listen => '0.0.0.0', + :server_proxyclient_address => '7.0.0.1', + :proxy_host => '10.0.0.2', + :proxy_protocol => 'https', + :proxy_port => '6082' + ) + end + + it 'configure nova compute with neutron' do + is_expected.to contain_class('nova::compute::neutron') + end + + it 'configure ceilometer agent compute' do + is_expected.to contain_class('ceilometer::agent::compute') + end + + it 'do not configure nova shell' do + is_expected.not_to contain_user('nova') + end + + it 'should not configure nova-compute for RBD backend' do + is_expected.not_to contain_nova_config('libvirt/rbd_user').with('value' => 'cinder') + is_expected.not_to contain_nova_config('libvirt/images_type').with('value' => 'rbd') + end + + it 'configure libvirt driver without disk cachemodes' do + is_expected.to contain_class('nova::compute::libvirt').with( + :libvirt_type => 'kvm', + :vncserver_listen => '0.0.0.0', + :migration_support => true, + :libvirt_disk_cachemodes => [] + ) + end + + it 'configure nova-compute with extra parameters' do + is_expected.to contain_nova_config('DEFAULT/default_availability_zone').with('value' => 'MyZone') + is_expected.to contain_nova_config('libvirt/inject_key').with('value' => false) + is_expected.to contain_nova_config('libvirt/inject_partition').with('value' => '-2') + is_expected.to contain_nova_config('libvirt/live_migration_flag').with('value' => 'VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_PERSIST_DEST') + is_expected.to contain_nova_config('libvirt/block_migration_flag').with('value' => 'VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_NON_SHARED_INC') + end + + context 'with dbus on Ubuntu' do + let :facts do + { :osfamily => 'Debian', + :operatingsystem => 'Ubuntu', + :vtx => true, + } + end + + it 'ensure dbus is running and started at boot' do + is_expected.to contain_service('dbus').with( + :ensure => 'running', + :enable => 'true' + ) + end + end + + context 'without TSO/GSO/GRO on Debian systems' do + before :each do + facts.merge!( :osfamily => 'Debian', + :operatingsystem => 'Debian', + :vtx => true ) + end + it 'ensure TSO script is enabled at boot' do + is_expected.to contain_exec('enable-tso-script').with( + :command => '/usr/sbin/update-rc.d disable-tso defaults', + :unless => '/bin/ls /etc/rc*.d | /bin/grep disable-tso', + :onlyif => '/usr/bin/test -f /etc/init.d/disable-tso' + ) + end + it 'start TSO script' do + is_expected.to contain_exec('start-tso-script').with( + :command => '/etc/init.d/disable-tso start', + :unless => '/usr/bin/test -f /var/run/disable-tso.pid', + :onlyif => '/usr/bin/test -f /etc/init.d/disable-tso' + ) + end + end + + context 'without TSO/GSO/GRO on Red Hat systems' do + before :each do + facts.merge!( :osfamily => 'RedHat', + :vtx => true ) + end + it 'ensure TSO script is enabled at boot' do + is_expected.to contain_exec('enable-tso-script').with( + :command => '/usr/sbin/chkconfig disable-tso on', + :unless => '/bin/ls /etc/rc*.d | /bin/grep disable-tso', + :onlyif => '/usr/bin/test -f /etc/init.d/disable-tso' + ) + end + it 'start TSO script' do + is_expected.to contain_exec('start-tso-script').with( + :command => '/etc/init.d/disable-tso start', + :unless => '/usr/bin/test -f /var/run/disable-tso.pid', + :onlyif => '/usr/bin/test -f /etc/init.d/disable-tso' + ) + end + end + + context 'when not managing TSO/GSO/GRO' do + before :each do + params.merge!( :manage_tso => false) + end + it 'ensure TSO script is not managed at boot' do + is_expected.not_to contain_exec('enable-tso-script') + end + it 'do not start TSO script' do + is_expected.not_to contain_exec('start-tso-script') + end + end + + context 'when managing nova shell' do + before :each do + params.merge!( :nova_shell => '/bin/bash') + end + it 'ensure nova shell is configured by Puppet' do + is_expected.to contain_user('nova').with( + :ensure => 'present', + :system => true, + :home => '/var/lib/nova', + :managehome => false, + :shell => '/bin/bash' + ) + end + end + + context 'with RBD backend for instances and volumes' do + before :each do + facts.merge!( :vtx => true ) + params.merge!( + :vm_rbd => true, + :volume_rbd => true, + :cinder_rbd_user => 'cinder', + :nova_rbd_pool => 'nova', + :nova_rbd_secret_uuid => 'secrete' ) + end + + it 'configure nova-compute to support RBD backend' do + is_expected.to contain_nova_config('libvirt/images_type').with('value' => 'rbd') + is_expected.to contain_nova_config('libvirt/images_rbd_pool').with('value' => 'nova') + is_expected.to contain_nova_config('libvirt/images_rbd_ceph_conf').with('value' => '/etc/ceph/ceph.conf') + is_expected.to contain_nova_config('libvirt/rbd_user').with('value' => 'cinder') + is_expected.to contain_nova_config('libvirt/rbd_secret_uuid').with('value' => 'secrete') + is_expected.to contain_group('cephkeyring').with(:ensure => 'present') + is_expected.to contain_exec('add-nova-to-group').with( + :command => 'usermod -a -G cephkeyring nova', + :unless => 'groups nova | grep cephkeyring' + ) + end + + it 'configure libvirt driver' do + is_expected.to contain_class('nova::compute::libvirt').with( + :libvirt_type => 'kvm', + :vncserver_listen => '0.0.0.0', + :migration_support => true, + :libvirt_disk_cachemodes => ['network=writeback'] + ) + end + end + + context 'with RBD support only for volumes' do + before :each do + facts.merge!( :vtx => true ) + params.merge!( + :vm_rbd => false, + :volume_rbd => true, + :cinder_rbd_user => 'cinder', + :nova_rbd_secret_uuid => 'secrete' ) + end + + it 'configure nova-compute to support RBD backend' do + is_expected.not_to contain_nova_config('libvirt/images_type').with('value' => 'rbd') + is_expected.not_to contain_nova_config('libvirt/images_rbd_pool').with('value' => 'nova') + is_expected.to contain_nova_config('libvirt/rbd_user').with('value' => 'cinder') + is_expected.to contain_nova_config('libvirt/rbd_secret_uuid').with('value' => 'secrete') + is_expected.to contain_group('cephkeyring').with(:ensure => 'present') + is_expected.to contain_exec('add-nova-to-group').with( + :command => 'usermod -a -G cephkeyring nova', + :unless => 'groups nova | grep cephkeyring' + ) + end + + it 'configure libvirt driver' do + is_expected.to contain_class('nova::compute::libvirt').with( + :libvirt_type => 'kvm', + :vncserver_listen => '0.0.0.0', + :migration_support => true, + :libvirt_disk_cachemodes => ['network=writeback'] + ) + end + end + + context 'when trying to enable RBD backend on RedHat OSP < 7 plaforms' do + before :each do + facts.merge!( :osfamily => 'RedHat', + :operatingsystemmajrelease => '6' ) + params.merge!( + :vm_rbd => true, + :cinder_rbd_user => 'cinder', + :nova_rbd_pool => 'nova', + :nova_rbd_secret_uuid => 'secrete' ) + end + it_raises 'a Puppet::Error', /RBD image backend in Nova is not supported in RHEL 6./ + end + + context 'when running KVM libvirt driver without VTX enabled' do + before :each do + facts.merge!( :vtx => false ) + end + it_raises 'a Puppet::Error', /libvirt_type is set to KVM and VTX seems to be disabled on this node./ + end + + context 'when storing instances on a NFS share' do + before :each do + params.merge!( + :nfs_enabled => true, + :nfs_device => 'nfs.example.com:/vol1', + :nfs_options => 'noacl,fsid=123' ) + end + it 'configure nova instances path and NFS mount' do + is_expected.to contain_nova_config('DEFAULT/instances_path').with('value' => '/var/lib/nova/instances') + is_expected.to contain_mount('/var/lib/nova/instances').with({ + 'ensure' => 'mounted', + 'fstype' => 'nfs', + 'device' => 'nfs.example.com:/vol1', + 'options' => 'noacl,fsid=123' + }) + end + end + + context 'when storing instances on a NFS share without nfs_device' do + before :each do + params.merge!( + :nfs_enabled => true, + :nfs_device => false ) + end + it_raises 'a Puppet::Error', /When running NFS backend, you need to provide nfs_device parameter./ + end + + context 'when storing instances on a NFS share with vm_rbd enabled' do + before :each do + params.merge!( + :nfs_enabled => true, + :vm_rbd => true, + :nfs_device => 'nfs.example.com:/vol1' ) + end + it_raises 'a Puppet::Error', /When running NFS backend, vm_rbd parameter cannot be set to true./ + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure compute firewall rules' do + is_expected.to contain_firewall('100 allow instances console access').with( + :port => '5900-5999', + :proto => 'tcp', + :action => 'accept', + ) + is_expected.to contain_firewall('100 allow instances migration access').with( + :port => ['16509', '49152-49215'], + :proto => 'tcp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure compute firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow instances console access').with( + :port => '5900-5999', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + is_expected.to contain_firewall('100 allow instances migration access').with( + :port => ['16509', '49152-49215'], + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian', + :operatingsystem => 'Debian', + :vtx => true, + # required for rpcbind module + :lsbdistid => 'Debian' + } + end + + it_configures 'openstack compute hypervisor' + it { should contain_file_line('/etc/default/libvirtd libvirtd opts').with(:line => 'libvirtd_opts="-d -l"') } + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat', + :vtx => true, + # required for rbd support check + :operatingsystemmajrelease => '7', + # required for nfs module + :lsbmajdistrelease => '7' + } + end + + it_configures 'openstack compute hypervisor' + it { should contain_file_line('/etc/sysconfig/libvirtd libvirtd args').with(:line => 'LIBVIRTD_ARGS="--listen"') } + end + +end diff --git a/cloud/spec/classes/cloud_compute_scheduler_spec.rb b/cloud/spec/classes/cloud_compute_scheduler_spec.rb new file mode 100644 index 000000000..5a096c118 --- /dev/null +++ b/cloud/spec/classes/cloud_compute_scheduler_spec.rb @@ -0,0 +1,123 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::compute::scheduler class +# + +require 'spec_helper' + +describe 'cloud::compute::scheduler' do + + shared_examples_for 'openstack compute scheduler' do + + let :pre_condition do + "class { 'cloud::compute': + availability_zone => 'MyZone', + nova_db_host => '10.0.0.1', + nova_db_user => 'nova', + nova_db_password => 'secrete', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + ks_glance_internal_host => '10.0.0.1', + glance_api_port => '9292', + verbose => true, + debug => true, + use_syslog => true, + neutron_protocol => 'http', + neutron_endpoint => '10.0.0.1', + neutron_region_name => 'MyRegion', + neutron_password => 'secrete', + memcache_servers => ['10.0.0.1','10.0.0.2'], + log_facility => 'LOG_LOCAL0' }" + end + + let :params do + { + :scheduler_default_filters => false + } + end + + it 'configure nova common' do + is_expected.to contain_class('nova').with( + :verbose => true, + :debug => true, + :use_syslog => true, + :log_facility => 'LOG_LOCAL0', + :rabbit_userid => 'nova', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :memcached_servers => ['10.0.0.1','10.0.0.2'], + :database_connection => 'mysql://nova:secrete@10.0.0.1/nova?charset=utf8', + :glance_api_servers => 'http://10.0.0.1:9292', + :log_dir => false + ) + is_expected.to contain_nova_config('DEFAULT/resume_guests_state_on_host_boot').with('value' => true) + is_expected.to contain_nova_config('DEFAULT/default_availability_zone').with('value' => 'MyZone') + is_expected.to contain_nova_config('DEFAULT/servicegroup_driver').with_value('mc') + is_expected.to contain_nova_config('DEFAULT/glance_num_retries').with_value('10') + end + + it 'configure neutron on compute node' do + is_expected.to contain_class('nova::network::neutron').with( + :neutron_admin_password => 'secrete', + :neutron_admin_auth_url => 'http://10.0.0.1:35357/v2.0', + :neutron_region_name => 'MyRegion', + :neutron_url => 'http://10.0.0.1:9696' + ) + end + + it 'checks if Nova DB is populated' do + is_expected.to contain_exec('nova_db_sync').with( + :command => 'nova-manage db sync', + :user => 'nova', + :path => '/usr/bin', + :unless => '/usr/bin/mysql nova -h 10.0.0.1 -u nova -psecrete -e "show tables" | /bin/grep Tables' + ) + end + + it 'configure nova-scheduler' do + is_expected.to contain_class('nova::scheduler').with(:enabled => true) + end + + context 'openstack compute scheduler with nova-scheduler filters' do + before do + params.merge!( + :scheduler_default_filters => ['RamFilter', 'ComputeFilter'] + ) + end + it { is_expected.to contain_nova_config('DEFAULT/scheduler_default_filters').with( + 'value' => "RamFilter,ComputeFilter" + )} + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack compute scheduler' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + it_configures 'openstack compute scheduler' + end + +end diff --git a/cloud/spec/classes/cloud_dashboard_spec.rb b/cloud/spec/classes/cloud_dashboard_spec.rb new file mode 100644 index 000000000..f8bbf725b --- /dev/null +++ b/cloud/spec/classes/cloud_dashboard_spec.rb @@ -0,0 +1,178 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::dashboard class +# + +require 'spec_helper' + +describe 'cloud::dashboard' do + + shared_examples_for 'openstack dashboard' do + + let :params do + { :listen_ssl => false, + :ks_keystone_internal_host => 'localhost', + :ks_keystone_internal_host => 'localhost', + :secret_key => '/etc/ssl/secret', + :keystone_host => 'keystone.openstack.org', + :keystone_proto => 'http', + :keystone_port => '5000', + :debug => true, + :api_eth => '10.0.0.1', + :ssl_forward => true, + :servername => 'horizon.openstack.org', + :os_endpoint_type => 'internalURL', + :allowed_hosts => 'horizon.openstack.org'} + end + + it 'configure horizon' do + is_expected.to contain_class('horizon').with( + :listen_ssl => false, + :secret_key => '/etc/ssl/secret', + :can_set_mount_point => 'False', + :bind_address => '10.0.0.1', + :servername => 'horizon.openstack.org', + :swift => true, + :cache_server_ip => false, + :keystone_url => 'http://keystone.openstack.org:5000/v2.0', + :django_debug => true, + :neutron_options => { 'enable_lb' => true }, + :vhost_extra_params => { + 'add_listen' => true , + 'setenvif' => ['X-Forwarded-Proto https HTTPS=1'] + }, + :openstack_endpoint_type => 'internalURL', + :allowed_hosts => 'horizon.openstack.org' + ) + is_expected.to contain_class('apache').with(:default_vhost => false) + end + + context 'with custom apache2 vhost parameters' do + before do + params.merge!( + :vhost_extra_params => { + 'ssl_protocol' => 'all -SSLv3 -SSLv2' + }) + end + + it 'configure horizon with custom vhost configuration' do + is_expected.to contain_class('horizon').with( + :vhost_extra_params => { + 'add_listen' => true , + 'setenvif' => ['X-Forwarded-Proto https HTTPS=1'], + 'ssl_protocol' => 'all -SSLv3 -SSLv2' + }, + ) + end + end + + context 'with cisco plugin enabled' do + before do + params.merge!( + :neutron_extra_options => { + 'profile_support' => 'cisco' + }) + end + + it 'configure horizon with cisco support' do + is_expected.to contain_class('horizon').with( + :neutron_options => { + 'enable_lb' => true, + 'profile_support' => 'cisco' + }, + ) + end + end + + context 'with multiple allowed_hosts' do + before do + params.merge!(:allowed_hosts => ['horizon.openstack.org', 'vip.openstack.org']) + end + + it 'configure horizon with multiple allowed hosts' do + is_expected.to contain_class('horizon').with( + :listen_ssl => false, + :secret_key => '/etc/ssl/secret', + :can_set_mount_point => 'False', + :bind_address => '10.0.0.1', + :servername => 'horizon.openstack.org', + :swift => true, + :cache_server_ip => false, + :keystone_url => 'http://keystone.openstack.org:5000/v2.0', + :django_debug => true, + :neutron_options => { 'enable_lb' => true }, + :vhost_extra_params => { + 'add_listen' => true , + 'setenvif' => ['X-Forwarded-Proto https HTTPS=1'] + }, + :openstack_endpoint_type => 'internalURL', + :allowed_hosts => ['horizon.openstack.org', 'vip.openstack.org'] + ) + end + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure horizon firewall rules' do + is_expected.to contain_firewall('100 allow horizon access').with( + :port => '80', + :proto => 'tcp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure horizon firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow horizon access').with( + :port => '80', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian', + :operatingsystem => 'Ubuntu', + :operatingsystemrelease => '12.04' } + end + + it_configures 'openstack dashboard' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat', + :operatingsystemrelease => '6' } + end + + it_configures 'openstack dashboard' + end + +end diff --git a/cloud/spec/classes/cloud_database_dbaas_api_spec.rb b/cloud/spec/classes/cloud_database_dbaas_api_spec.rb new file mode 100644 index 000000000..66c27a572 --- /dev/null +++ b/cloud/spec/classes/cloud_database_dbaas_api_spec.rb @@ -0,0 +1,129 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::database::dbaas::api class +# + +require 'spec_helper' + +describe 'cloud::database::dbaas::api' do + + shared_examples_for 'openstack database dbaas api' do + + let :pre_condition do + "class { 'cloud::database::dbaas': + trove_db_host => '10.0.0.1', + trove_db_user => 'trove', + trove_db_password => 'secrete', + nova_admin_username => 'trove', + nova_admin_password => 'trovepassword', + nova_admin_tenant_name => 'services', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete' }" + end + + let :params do + { :ks_keystone_internal_host => '10.0.0.1', + :ks_keystone_internal_proto => 'https', + :ks_trove_password => 'trovepassword', + :api_eth => '10.0.0.1', + :debug => true, + :verbose => true, + :use_syslog => true, + :ks_trove_public_port => '8779' } + end + + it 'configure trove common' do + is_expected.to contain_class('trove').with( + :rabbit_userid => 'trove', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :nova_proxy_admin_pass => 'trovepassword', + :nova_proxy_admin_user => 'trove', + :nova_proxy_admin_tenant_name => 'services', + :database_connection => 'mysql://trove:secrete@10.0.0.1/trove?charset=utf8', + ) + end + + it 'configure trove api' do + is_expected.to contain_class('trove::api').with( + :verbose => true, + :debug => true, + :use_syslog => true, + :bind_host => '10.0.0.1', + :bind_port => '8779', + :auth_url => 'https://10.0.0.1:5000/v2.0', + :keystone_password => 'trovepassword' + ) + end + + it 'checks if Trove DB is populated' do + is_expected.to contain_exec('trove_db_sync').with( + :command => 'trove-manage db_sync', + :user => 'trove', + :path => '/usr/bin', + :unless => '/usr/bin/mysql trove -h 10.0.0.1 -u trove -psecrete -e "show tables" | /bin/grep Tables' + ) + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure trove-api firewall rules' do + is_expected.to contain_firewall('100 allow trove-api access').with( + :port => '8779', + :proto => 'tcp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure trove-api firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow trove-api access').with( + :port => '8779', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack database dbaas api' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + it_configures 'openstack database dbaas api' + end + +end diff --git a/cloud/spec/classes/cloud_database_dbaas_conductor_spec.rb b/cloud/spec/classes/cloud_database_dbaas_conductor_spec.rb new file mode 100644 index 000000000..374542b28 --- /dev/null +++ b/cloud/spec/classes/cloud_database_dbaas_conductor_spec.rb @@ -0,0 +1,85 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::database::dbaas::conductor class +# + +require 'spec_helper' + +describe 'cloud::database::dbaas::conductor' do + + shared_examples_for 'openstack database dbaas conductor' do + + let :pre_condition do + "class { 'cloud::database::dbaas': + trove_db_host => '10.0.0.1', + trove_db_user => 'trove', + trove_db_password => 'secrete', + nova_admin_username => 'trove', + nova_admin_password => 'trovepassword', + nova_admin_tenant_name => 'services', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete' }" + end + + let :params do + { :ks_keystone_internal_host => '10.0.0.1', + :ks_keystone_internal_port => '5000', + :ks_keystone_internal_proto => 'https', + :debug => true, + :verbose => true, + :use_syslog => true } + end + + it 'configure trove common' do + is_expected.to contain_class('trove').with( + :rabbit_userid => 'trove', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :nova_proxy_admin_pass => 'trovepassword', + :nova_proxy_admin_user => 'trove', + :nova_proxy_admin_tenant_name => 'services', + :database_connection => 'mysql://trove:secrete@10.0.0.1/trove?charset=utf8', + ) + end + + it 'configure trove conductor' do + is_expected.to contain_class('trove::conductor').with( + :verbose => true, + :debug => true, + :use_syslog => true, + :auth_url => 'https://10.0.0.1:5000/v2.0', + ) + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack database dbaas conductor' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + it_configures 'openstack database dbaas conductor' + end + +end diff --git a/cloud/spec/classes/cloud_database_dbaas_taskmanager_spec.rb b/cloud/spec/classes/cloud_database_dbaas_taskmanager_spec.rb new file mode 100644 index 000000000..cc0256338 --- /dev/null +++ b/cloud/spec/classes/cloud_database_dbaas_taskmanager_spec.rb @@ -0,0 +1,85 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::database::dbaas::taskmanager class +# + +require 'spec_helper' + +describe 'cloud::database::dbaas::taskmanager' do + + shared_examples_for 'openstack database dbaas taskmanager' do + + let :pre_condition do + "class { 'cloud::database::dbaas': + trove_db_host => '10.0.0.1', + trove_db_user => 'trove', + trove_db_password => 'secrete', + nova_admin_username => 'trove', + nova_admin_password => 'trovepassword', + nova_admin_tenant_name => 'services', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete' }" + end + + let :params do + { :ks_keystone_internal_host => '10.0.0.1', + :ks_keystone_internal_port => '5000', + :ks_keystone_internal_proto => 'https', + :debug => true, + :verbose => true, + :use_syslog => true } + end + + it 'configure trove common' do + is_expected.to contain_class('trove').with( + :rabbit_userid => 'trove', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :nova_proxy_admin_pass => 'trovepassword', + :nova_proxy_admin_user => 'trove', + :nova_proxy_admin_tenant_name => 'services', + :database_connection => 'mysql://trove:secrete@10.0.0.1/trove?charset=utf8', + ) + end + + it 'configure trove taskmanager' do + is_expected.to contain_class('trove::taskmanager').with( + :verbose => true, + :debug => true, + :use_syslog => true, + :auth_url => 'https://10.0.0.1:5000/v2.0', + ) + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack database dbaas taskmanager' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + it_configures 'openstack database dbaas taskmanager' + end + +end diff --git a/cloud/spec/classes/cloud_database_nosql_spec.rb b/cloud/spec/classes/cloud_database_nosql_spec.rb new file mode 100644 index 000000000..59f5654ec --- /dev/null +++ b/cloud/spec/classes/cloud_database_nosql_spec.rb @@ -0,0 +1,120 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::database:nosql class +# + +require 'spec_helper' + +describe 'cloud::database::nosql' do + + shared_examples_for 'openstack database nosql' do + + let :params do + { :bind_ip => '10.0.0.1', + :nojournal => false, + :replset_members => ['node1', 'node2', 'node3'] } + end + + it 'configure mongodb server' do + is_expected.to contain_class('mongodb::globals').with( :manage_package_repo => platform_params[:manage_package_repo]) + is_expected.to contain_class('mongodb::globals').with_before('Class[Mongodb]') + is_expected.to contain_class('mongodb').with( + :bind_ip => ['10.0.0.1'], + :nojournal => false, + :logpath => '/var/log/mongodb/mongod.log', + ) + end + + it 'configure mongodb replicasets' do + is_expected.to contain_exec('check_mongodb').with( + :command => "/usr/bin/mongo 10.0.0.1:27017", + :logoutput => false, + :tries => 60, + :try_sleep => 5 + ) + is_expected.to contain_mongodb_replset('ceilometer').with( + :members => ['node1', 'node2', 'node3'] + ) + is_expected.to contain_anchor('mongodb setup done') + end + + context 'without replica set' do + before :each do + params.merge!( :replset_members => false) + end + it 'do not configure mongodb replicasets' do + is_expected.not_to contain_mongodb_replset('ceilometer') + end + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure mongodb firewall rules' do + is_expected.to contain_firewall('100 allow mongodb access').with( + :port => '27017', + :proto => 'tcp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure mongodb firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow mongodb access').with( + :port => '27017', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian', + :lsbdistid => 'Debian' } + end + + let :platform_params do + { :manage_package_repo => true } + end + + it_configures 'openstack database nosql' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + let :platform_params do + { :manage_package_repo => false } + end + + it_configures 'openstack database nosql' + end + +end + diff --git a/cloud/spec/classes/cloud_database_sql_spec.rb b/cloud/spec/classes/cloud_database_sql_spec.rb new file mode 100644 index 000000000..2346ccac7 --- /dev/null +++ b/cloud/spec/classes/cloud_database_sql_spec.rb @@ -0,0 +1,294 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::database::sql class +# + +require 'spec_helper' + +describe 'cloud::database::sql' do + + shared_examples_for 'openstack database sql' do + + let :pre_condition do + "include xinetd" + end + + let :params do + { + :api_eth => '10.0.0.1', + :galera_master_name => 'os-ci-test1', + :galera_internal_ips => ['10.0.0.1','10.0.0.2','10.0.0.3'], + :galera_gcache => '1G', + :keystone_db_host => '10.0.0.1', + :keystone_db_user => 'keystone', + :keystone_db_password => 'secrete', + :keystone_db_allowed_hosts => ['10.0.0.1','10.0.0.2','10.0.0.3'], + :cinder_db_host => '10.0.0.1', + :cinder_db_user => 'cinder', + :cinder_db_password => 'secrete', + :cinder_db_allowed_hosts => ['10.0.0.1','10.0.0.2','10.0.0.3'], + :glance_db_host => '10.0.0.1', + :glance_db_user => 'glance', + :glance_db_password => 'secrete', + :glance_db_allowed_hosts => ['10.0.0.1','10.0.0.2','10.0.0.3'], + :heat_db_host => '10.0.0.1', + :heat_db_user => 'heat', + :heat_db_password => 'secrete', + :heat_db_allowed_hosts => ['10.0.0.1','10.0.0.2','10.0.0.3'], + :nova_db_host => '10.0.0.1', + :nova_db_user => 'nova', + :nova_db_password => 'secrete', + :nova_db_allowed_hosts => ['10.0.0.1','10.0.0.2','10.0.0.3'], + :neutron_db_host => '10.0.0.1', + :neutron_db_user => 'neutron', + :neutron_db_password => 'secrete', + :neutron_db_allowed_hosts => ['10.0.0.1','10.0.0.2','10.0.0.3'], + :trove_db_host => '10.0.0.1', + :trove_db_user => 'trove', + :trove_db_password => 'secrete', + :trove_db_allowed_hosts => ['10.0.0.1','10.0.0.2','10.0.0.3'], + :mysql_root_password => 'secrete', + :mysql_sys_maint_password => 'sys', + :galera_clustercheck_dbuser => 'clustercheckuser', + :galera_clustercheck_dbpassword => 'clustercheckpassword!', + :galera_clustercheck_ipaddress => '10.0.0.1' + } + end + + it 'configure mysql galera server' do + is_expected.to contain_class('mysql::client').with( + :package_name => platform_params[:mysql_client_package_name] + ) + + is_expected.to contain_class('mysql::server').with( + :package_name => platform_params[:mysql_server_package_name], + :override_options => { 'mysqld' => { 'bind-address' => '10.0.0.1' } }, + :notify => 'Service[xinetd]' + ) + + is_expected.to contain_file(platform_params[:mysql_server_config_file]).with_content(/^wsrep_cluster_name\s*= "galera_cluster"$/) + is_expected.to contain_file(platform_params[:mysql_server_config_file]).with_content(/^wsrep_node_address\s*= "#{params[:api_eth]}"$/) + is_expected.to contain_file(platform_params[:mysql_server_config_file]).with_content(/^wsrep_node_incoming_address\s*= "#{params[:api_eth]}"$/) + + end # configure mysql galera server + + context 'configure mysqlchk http replication' do + it { is_expected.to contain_file('/etc/xinetd.d/mysqlchk').with_mode('0755') } + it { is_expected.to contain_file('/usr/bin/clustercheck').with_mode('0755') } + it { is_expected.to contain_file('/usr/bin/clustercheck').with_content(/MYSQL_USERNAME='#{params[:galera_clustercheck_dbuser]}'/)} + it { is_expected.to contain_file('/usr/bin/clustercheck').with_content(/MYSQL_PASSWORD='#{params[:galera_clustercheck_dbpassword]}'/)} + it { is_expected.to contain_file('/etc/xinetd.d/mysqlchk').with_content(/bind = #{params[:galera_clustercheck_ipaddress]}/)} + + end # configure mysqlchk http replication + + context 'configure databases on the galera master server' do + + before :each do + facts.merge!( :hostname => 'os-ci-test1' ) + end + + it 'configure mysql server' do + is_expected.to contain_class('mysql::server').with( + :package_name => platform_params[:mysql_server_package_name], + :root_password => 'secrete', + :override_options => { 'mysqld' => { 'bind-address' => '10.0.0.1' } }, + :notify => 'Service[xinetd]' + ) + end + + it 'configure keystone database' do + is_expected.to contain_class('keystone::db::mysql').with( + :mysql_module => '2.2', + :dbname => 'keystone', + :user => 'keystone', + :password => 'secrete', + :host => '10.0.0.1', + :allowed_hosts => ['10.0.0.1','10.0.0.2','10.0.0.3'] ) + end + + it 'configure glance database' do + is_expected.to contain_class('glance::db::mysql').with( + :mysql_module => '2.2', + :dbname => 'glance', + :user => 'glance', + :password => 'secrete', + :host => '10.0.0.1', + :allowed_hosts => ['10.0.0.1','10.0.0.2','10.0.0.3'] ) + end + + it 'configure nova database' do + is_expected.to contain_class('nova::db::mysql').with( + :mysql_module => '2.2', + :dbname => 'nova', + :user => 'nova', + :password => 'secrete', + :host => '10.0.0.1', + :allowed_hosts => ['10.0.0.1','10.0.0.2','10.0.0.3'] ) + end + + it 'configure cinder database' do + is_expected.to contain_class('cinder::db::mysql').with( + :mysql_module => '2.2', + :dbname => 'cinder', + :user => 'cinder', + :password => 'secrete', + :host => '10.0.0.1', + :allowed_hosts => ['10.0.0.1','10.0.0.2','10.0.0.3'] ) + end + + it 'configure neutron database' do + is_expected.to contain_class('neutron::db::mysql').with( + :mysql_module => '2.2', + :dbname => 'neutron', + :user => 'neutron', + :password => 'secrete', + :host => '10.0.0.1', + :allowed_hosts => ['10.0.0.1','10.0.0.2','10.0.0.3'] ) + end + + it 'configure heat database' do + is_expected.to contain_class('heat::db::mysql').with( + :mysql_module => '2.2', + :dbname => 'heat', + :user => 'heat', + :password => 'secrete', + :host => '10.0.0.1', + :allowed_hosts => ['10.0.0.1','10.0.0.2','10.0.0.3'] ) + end + + it 'configure trove database' do + is_expected.to contain_class('trove::db::mysql').with( + :mysql_module => '2.2', + :dbname => 'trove', + :user => 'trove', + :password => 'secrete', + :host => '10.0.0.1', + :allowed_hosts => ['10.0.0.1','10.0.0.2','10.0.0.3'] ) + end + + it 'configure monitoring database' do + is_expected.to contain_mysql_database('monitoring').with( + :ensure => 'present', + :charset => 'utf8' + ) + is_expected.to contain_mysql_user("#{params[:galera_clustercheck_dbuser]}@localhost").with( + :ensure => 'present', + :password_hash => '*FDC68394456829A7344C2E9D4CDFD43DCE2EFD8F' + ) + is_expected.to contain_mysql_grant("#{params[:galera_clustercheck_dbuser]}@localhost/monitoring").with( + :privileges => 'ALL' + ) + end # configure monitoring database + end # configure databases on the galera master server + + context 'Bootstrap MySQL database on RedHat plaforms' do + before :each do + facts.merge!( :osfamily => 'RedHat' ) + end + it 'configure mysql database' do + is_expected.to contain_exec('bootstrap-mysql').with( + :command => '/usr/bin/mysql_install_db --rpm --user=mysql', + :unless => "test -d /var/lib/mysql/mysql", + :before => 'Service[mysqld]' + ) + end + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure mysql firewall rules' do + is_expected.to contain_firewall('100 allow galera access').with( + :port => ['3306', '4567', '4568', '4444'], + :proto => 'tcp', + :action => 'accept', + ) + is_expected.to contain_firewall('100 allow mysqlchk access').with( + :port => '9200', + :proto => 'tcp', + :action => 'accept', + ) + is_expected.to contain_firewall('100 allow mysql rsync access').with( + :port => '873', + :proto => 'tcp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure mysql firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow galera access').with( + :port => ['3306', '4567', '4568', '4444'], + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + is_expected.to contain_firewall('100 allow mysqlchk access').with( + :port => '9200', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + is_expected.to contain_firewall('100 allow mysql rsync access').with( + :port => '873', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end # openstack database sql + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + let :platform_params do + { :mysql_server_package_name => 'mariadb-galera-server', + :mysql_client_package_name => 'mariadb-client', + :mysql_server_config_file => '/etc/mysql/my.cnf', + :wsrep_provider => '/usr/lib/galera/libgalera_smm.so' } + end + + it_configures 'openstack database sql' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + let :platform_params do + { :mysql_server_package_name => 'mariadb-galera-server', + :mysql_client_package_name => 'mariadb', + :mysql_server_config_file => '/etc/my.cnf', + :wsrep_provider => '/usr/lib64/galera/libgalera_smm.so' } + end + + it_configures 'openstack database sql' + end + +end diff --git a/cloud/spec/classes/cloud_identity_spec.rb b/cloud/spec/classes/cloud_identity_spec.rb new file mode 100644 index 000000000..46eff32cc --- /dev/null +++ b/cloud/spec/classes/cloud_identity_spec.rb @@ -0,0 +1,414 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::identity class +# + +require 'spec_helper' + +describe 'cloud::identity' do + + shared_examples_for 'openstack identity' do + + let :params do + { :identity_roles_addons => ['SwiftOperator', 'ResellerAdmin'], + :swift_enabled => true, + :keystone_db_host => '10.0.0.1', + :keystone_db_user => 'keystone', + :keystone_db_password => 'secrete', + :ks_admin_email => 'admin@openstack.org', + :ks_admin_password => 'secrete', + :ks_admin_tenant => 'admin', + :ks_admin_token => 'SECRETE', + :ks_ceilometer_admin_host => '10.0.0.1', + :ks_ceilometer_internal_host => '10.0.0.1', + :ks_ceilometer_password => 'secrete', + :ks_ceilometer_public_host => '10.0.0.1', + :ks_ceilometer_public_port => '8777', + :ks_ceilometer_public_proto => 'https', + :ks_ceilometer_admin_proto => 'https', + :ks_ceilometer_internal_proto => 'https', + :ks_cinder_admin_host => '10.0.0.1', + :ks_cinder_internal_host => '10.0.0.1', + :ks_cinder_password => 'secrete', + :ks_cinder_public_host => '10.0.0.1', + :ks_cinder_public_proto => 'https', + :ks_cinder_public_proto => 'https', + :ks_cinder_admin_proto => 'https', + :ks_glance_admin_host => '10.0.0.1', + :ks_glance_internal_host => '10.0.0.1', + :ks_glance_password => 'secrete', + :ks_glance_public_host => '10.0.0.1', + :ks_glance_public_proto => 'https', + :ks_glance_admin_proto => 'https', + :ks_glance_internal_proto => 'https', + :ks_heat_admin_host => '10.0.0.1', + :ks_heat_internal_host => '10.0.0.1', + :ks_heat_password => 'secrete', + :ks_heat_public_host => '10.0.0.1', + :ks_heat_public_proto => 'https', + :ks_heat_admin_proto => 'https', + :ks_heat_internal_proto => 'https', + :ks_heat_public_port => '8004', + :ks_heat_cfn_public_port => '8000', + :ks_keystone_admin_host => '10.0.0.1', + :ks_keystone_admin_port => '35357', + :ks_keystone_internal_host => '10.0.0.1', + :ks_keystone_internal_port => '5000', + :ks_keystone_public_host => '10.0.0.1', + :ks_keystone_public_port => '5000', + :ks_keystone_public_proto => 'https', + :ks_keystone_admin_proto => 'https', + :ks_keystone_internal_proto => 'https', + :ks_neutron_admin_host => '10.0.0.1', + :ks_neutron_internal_host => '10.0.0.1', + :ks_neutron_password => 'secrete', + :ks_neutron_public_host => '10.0.0.1', + :ks_neutron_admin_proto => 'https', + :ks_neutron_internal_proto => 'https', + :ks_neutron_public_proto => 'https', + :ks_neutron_public_port => '9696', + :ks_nova_admin_host => '10.0.0.1', + :ks_nova_internal_host => '10.0.0.1', + :ks_nova_password => 'secrete', + :ks_nova_public_host => '10.0.0.1', + :ks_nova_public_proto => 'https', + :ks_nova_internal_proto => 'https', + :ks_nova_admin_proto => 'https', + :ks_nova_public_port => '8774', + :ks_ec2_public_port => '8773', + :ks_swift_dispersion_password => 'secrete', + :ks_swift_internal_host => '10.0.0.1', + :ks_swift_password => 'secrete', + :ks_swift_public_host => '10.0.0.1', + :ks_swift_public_port => '8080', + :ks_swift_public_proto => 'https', + :ks_swift_admin_proto => 'https', + :ks_swift_internal_proto => 'https', + :ks_swift_admin_host => '10.0.0.1', + :ks_trove_admin_host => '10.0.0.1', + :ks_trove_internal_host => '10.0.0.1', + :ks_trove_password => 'secrete', + :ks_trove_public_host => '10.0.0.1', + :ks_trove_public_port => '8779', + :ks_trove_public_proto => 'https', + :ks_trove_admin_proto => 'https', + :ks_trove_internal_proto => 'https', + :region => 'BigCloud', + :verbose => true, + :debug => true, + :log_facility => 'LOG_LOCAL0', + :use_syslog => true, + :token_driver => 'keystone.token.backends.sql.Token', + :ks_token_expiration => '3600', + :api_eth => '10.0.0.1' } + end + + it 'configure keystone server' do + is_expected.to contain_class('keystone').with( + :enabled => true, + :admin_token => 'SECRETE', + :compute_port => '8774', + :debug => true, + :verbose => true, + :idle_timeout => '60', + :log_facility => 'LOG_LOCAL0', + :sql_connection => 'mysql://keystone:secrete@10.0.0.1/keystone?charset=utf8', + :token_driver => 'keystone.token.backends.sql.Token', + :token_provider => 'keystone.token.providers.uuid.Provider', + :use_syslog => true, + :bind_host => '10.0.0.1', + :public_port => '5000', + :admin_port => '35357', + :token_expiration => '3600', + :log_dir => false, + :log_file => false, + :admin_endpoint => 'https://10.0.0.1:35357/', + :public_endpoint => 'https://10.0.0.1:5000/' + ) + is_expected.to contain_exec('validate_keystone_connection') + is_expected.to contain_keystone_config('ec2/driver').with('value' => 'keystone.contrib.ec2.backends.sql.Ec2') + is_expected.to contain_keystone_config('DEFAULT/log_file').with_ensure('absent') + is_expected.to contain_keystone_config('DEFAULT/log_dir').with_ensure('absent') + end + + it 'checks if Keystone DB is populated' do + is_expected.to contain_exec('keystone_db_sync').with( + :command => 'keystone-manage db_sync', + :path => '/usr/bin', + :user => 'keystone', + :unless => '/usr/bin/mysql keystone -h 10.0.0.1 -u keystone -psecrete -e "show tables" | /bin/grep Tables' + ) + end + + it 'configure keystone admin role' do + is_expected.to contain_class('keystone::roles::admin').with( + :email => 'admin@openstack.org', + :password => 'secrete', + :admin_tenant => 'admin' + ) + end + + # TODO(EmilienM) Disable WSGI - bug #98 + # it 'configure apache to run keystone with wsgi' do + # should contain_class('keystone::wsgi::apache').with( + # :servername => 'keystone.openstack.org', + # :admin_port => '35357', + # :public_port => '5000', + # :workers => '2', + # :ssl => false + # ) + # end + + it 'configure keystone endpoint' do + is_expected.to contain_class('keystone::endpoint').with( + :public_url => 'https://10.0.0.1:5000', + :admin_url => 'https://10.0.0.1:35357', + :internal_url => 'https://10.0.0.1:5000', + :region => 'BigCloud' + ) + end + + it 'configure swift endpoints' do + is_expected.to contain_class('swift::keystone::auth').with( + :password => 'secrete', + :public_address => '10.0.0.1', + :public_port => '8080', + :public_protocol => 'https', + :admin_protocol => 'https', + :internal_protocol => 'https', + :admin_address => '10.0.0.1', + :internal_address => '10.0.0.1', + :region => 'BigCloud' + ) + end + + it 'configure swift dispersion' do + is_expected.to contain_class('swift::keystone::dispersion').with( :auth_pass => 'secrete' ) + end + + it 'configure ceilometer endpoints' do + is_expected.to contain_class('ceilometer::keystone::auth').with( + :admin_address => '10.0.0.1', + :internal_address => '10.0.0.1', + :password => 'secrete', + :port => '8777', + :public_address => '10.0.0.1', + :public_protocol => 'https', + :admin_protocol => 'https', + :internal_protocol => 'https', + :region => 'BigCloud' + ) + end + + it 'should not configure trove endpoint by default' do + is_expected.not_to contain_class('trove::keystone::auth') + end + + it 'configure nova endpoints' do + is_expected.to contain_class('nova::keystone::auth').with( + :admin_address => '10.0.0.1', + :cinder => true, + :internal_address => '10.0.0.1', + :password => 'secrete', + :public_address => '10.0.0.1', + :public_protocol => 'https', + :admin_protocol => 'https', + :internal_protocol => 'https', + :compute_port => '8774', + :ec2_port => '8773', + :region => 'BigCloud' + ) + end + + it 'configure neutron endpoints' do + is_expected.to contain_class('neutron::keystone::auth').with( + :admin_address => '10.0.0.1', + :internal_address => '10.0.0.1', + :password => 'secrete', + :public_address => '10.0.0.1', + :public_protocol => 'https', + :internal_protocol => 'https', + :admin_protocol => 'https', + :port => '9696', + :region => 'BigCloud' + ) + end + + it 'configure cinder endpoints' do + is_expected.to contain_class('cinder::keystone::auth').with( + :admin_address => '10.0.0.1', + :internal_address => '10.0.0.1', + :password => 'secrete', + :public_address => '10.0.0.1', + :public_protocol => 'https', + :region => 'BigCloud' + ) + end + + it 'configure glance endpoints' do + is_expected.to contain_class('glance::keystone::auth').with( + :admin_address => '10.0.0.1', + :internal_address => '10.0.0.1', + :password => 'secrete', + :public_address => '10.0.0.1', + :public_protocol => 'https', + :admin_protocol => 'https', + :internal_protocol => 'https', + :port => '9292', + :region => 'BigCloud' + ) + end + + it 'configure heat endpoints' do + is_expected.to contain_class('heat::keystone::auth').with( + :admin_address => '10.0.0.1', + :internal_address => '10.0.0.1', + :password => 'secrete', + :public_address => '10.0.0.1', + :public_protocol => 'https', + :admin_protocol => 'https', + :internal_protocol => 'https', + :port => '8004', + :region => 'BigCloud' + ) + end + + it 'configure heat cloudformation endpoints' do + is_expected.to contain_class('heat::keystone::auth_cfn').with( + :admin_address => '10.0.0.1', + :internal_address => '10.0.0.1', + :password => 'secrete', + :public_address => '10.0.0.1', + :public_protocol => 'https', + :admin_protocol => 'https', + :internal_protocol => 'https', + :port => '8000', + :region => 'BigCloud' + ) + end + + it 'configure a crontab to purge tokens every days at midnight' do + is_expected.to contain_class('keystone::cron::token_flush') + end + + context 'without syslog' do + before :each do + params.merge!(:use_syslog => false) + end + it 'configure keystone server' do + is_expected.to contain_class('keystone').with( + :use_syslog => false, + :log_dir => '/var/log/keystone', + :log_file => 'keystone.log' + ) + end + end + + context 'without Swift' do + before :each do + params.merge!(:swift_enabled => false) + end + it 'should not configure swift endpoints and users' do + is_expected.not_to contain_class('swift::keystone::auth') + is_expected.not_to contain_class('swift::keystone::dispersion') + end + end + + context 'with Trove' do + before :each do + params.merge!(:trove_enabled => true) + end + it 'configure trove endpoints' do + is_expected.to contain_class('trove::keystone::auth').with( + :admin_address => '10.0.0.1', + :internal_address => '10.0.0.1', + :password => 'secrete', + :port => '8779', + :public_address => '10.0.0.1', + :public_protocol => 'https', + :admin_protocol => 'https', + :internal_protocol => 'https', + :region => 'BigCloud' + ) + end + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure keystone firewall rules' do + is_expected.to contain_firewall('100 allow keystone access').with( + :port => '5000', + :proto => 'tcp', + :action => 'accept', + ) + is_expected.to contain_firewall('100 allow keystone admin access').with( + :port => '35357', + :proto => 'tcp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure keystone firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow keystone access').with( + :port => '5000', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + is_expected.to contain_firewall('100 allow keystone admin access').with( + :port => '35357', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian', + :operatingsystemrelease => '12.04', + :processorcount => '2', + :fqdn => 'keystone.openstack.org' } + end + + it_configures 'openstack identity' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :processorcount => '2', + :fqdn => 'keystone.openstack.org' } + end + + it_configures 'openstack identity' + end + +end diff --git a/cloud/spec/classes/cloud_image_api_spec.rb b/cloud/spec/classes/cloud_image_api_spec.rb new file mode 100644 index 000000000..e50940a75 --- /dev/null +++ b/cloud/spec/classes/cloud_image_api_spec.rb @@ -0,0 +1,219 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::image class +# +require 'spec_helper' + +describe 'cloud::image::api' do + + let :params do + { :glance_db_host => '10.0.0.1', + :glance_db_user => 'glance', + :glance_db_password => 'secrete', + :ks_keystone_internal_host => '10.0.0.1', + :ks_keystone_internal_proto => 'https', + :ks_glance_internal_host => '10.0.0.1', + :openstack_vip => '10.0.0.42', + :ks_glance_api_internal_port => '9292', + :ks_glance_registry_internal_port => '9191', + :ks_glance_registry_internal_proto => 'https', + :ks_glance_password => 'secrete', + :rabbit_host => '10.0.0.1', + :rabbit_password => 'secrete', + :glance_rbd_user => 'glance', + :glance_rbd_pool => 'images', + :backend => 'rbd', + :debug => true, + :verbose => true, + :use_syslog => true, + :log_facility => 'LOG_LOCAL0', + :api_eth => '10.0.0.1' + } + end + + shared_examples_for 'openstack image api' do + + it 'should not configure firewall rule' do + is_expected.not_to contain_firewall('100 allow glance api access') + end + + it 'configure glance-api' do + is_expected.to contain_class('glance::api').with( + :database_connection => 'mysql://glance:secrete@10.0.0.1/glance?charset=utf8', + :keystone_password => 'secrete', + :registry_host => '10.0.0.42', + :registry_port => '9191', + :registry_client_protocol => 'https', + :keystone_tenant => 'services', + :keystone_user => 'glance', + :show_image_direct_url => true, + :verbose => true, + :debug => true, + :auth_host => '10.0.0.1', + :auth_protocol => 'https', + :log_facility => 'LOG_LOCAL0', + :bind_host => '10.0.0.1', + :bind_port => '9292', + :use_syslog => true, + :pipeline => 'keystone', + :log_dir => false, + :log_file => false + ) + end + + # TODO(EmilienM) Disabled for now + # Follow-up https://github.com/enovance/puppet-openstack-cloud/issues/160 + # + # it 'configure glance notifications with rabbitmq backend' do + # should contain_class('glance::notify::rabbitmq').with( + # :rabbit_password => 'secrete', + # :rabbit_userid => 'glance', + # :rabbit_host => '10.0.0.1' + # ) + # end + it { is_expected.to contain_glance_api_config('DEFAULT/notifier_driver').with_value('noop') } + + it 'configure glance rbd backend' do + is_expected.to contain_class('glance::backend::rbd').with( + :rbd_store_pool => 'images', + :rbd_store_user => 'glance' + ) + end + + it 'configure crontab to clean glance cache' do + is_expected.to contain_class('glance::cache::cleaner') + is_expected.to contain_class('glance::cache::pruner') + end + + context 'with file Glance backend' do + before :each do + params.merge!(:backend => 'file') + end + + it 'configure Glance with file backend' do + is_expected.to contain_class('glance::backend::file') + is_expected.not_to contain_class('glance::backend::rbd') + is_expected.to contain_glance_api_config('DEFAULT/filesystem_store_datadir').with('value' => '/var/lib/glance/images/') + is_expected.to contain_glance_api_config('DEFAULT/default_store').with('value' => 'file') + end + end + + context 'with NFS Glance backend' do + before :each do + params.merge!(:backend => 'nfs', + :filesystem_store_datadir => '/srv/images/', + :nfs_device => 'nfs.example.com:/vol1', + :nfs_options => 'noacl,fsid=123' ) + end + + it 'configure Glance with NFS backend' do + is_expected.to contain_class('glance::backend::file') + is_expected.not_to contain_class('glance::backend::rbd') + is_expected.to contain_glance_api_config('DEFAULT/filesystem_store_datadir').with('value' => '/srv/images/') + is_expected.to contain_glance_api_config('DEFAULT/default_store').with('value' => 'file') + is_expected.to contain_mount('/srv/images/').with({ + 'ensure' => 'mounted', + 'fstype' => 'nfs', + 'device' => 'nfs.example.com:/vol1', + 'options' => 'noacl,fsid=123', + }) + end + end + + context 'with Swift backend' do + before :each do + params.merge!(:backend => 'swift') + end + + it 'configure Glance with Glance backend' do + is_expected.not_to contain_class('glance::backend::file') + is_expected.not_to contain_class('glance::backend::rbd') + is_expected.to contain_glance_api_config('DEFAULT/default_store').with('value' => 'swift') + is_expected.to contain_glance_api_config('DEFAULT/swift_store_user').with('value' => 'services:glance') + is_expected.to contain_glance_api_config('DEFAULT/swift_store_key').with('value' => 'secrete') + is_expected.to contain_glance_api_config('DEFAULT/swift_store_auth_address').with('value' => 'https://10.0.0.1:35357/v2.0/') + is_expected.to contain_glance_api_config('DEFAULT/swift_store_create_container_on_put').with('value' => true) + end + end + + context 'with missing parameter when using Glance NFS backend' do + before :each do + params.merge!(:backend => 'nfs', + :nfs_device => false ) + end + it { is_expected.to compile.and_raise_error(/When running NFS backend, you need to provide nfs_device parameter./) } + end + + context 'with wrong Glance backend' do + before :each do + params.merge!(:backend => 'Something') + end + it { is_expected.to compile.and_raise_error(/Something is not a Glance supported backend./) } + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure Glance API firewall rules' do + is_expected.to contain_firewall('100 allow glance-api access').with( + :port => '9292', + :proto => 'tcp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure Glance API firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow glance-api access').with( + :port => '9292', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian', + # required for rpcbind module + :lsbdistid => 'Debian' } + end + + it_configures 'openstack image api' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat', + # required for nfs module + :lsbmajdistrelease => '7' } + end + + it_configures 'openstack image api' + end + +end diff --git a/cloud/spec/classes/cloud_image_registry_spec.rb b/cloud/spec/classes/cloud_image_registry_spec.rb new file mode 100644 index 000000000..dc103174d --- /dev/null +++ b/cloud/spec/classes/cloud_image_registry_spec.rb @@ -0,0 +1,117 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::image class +# +require 'spec_helper' + +describe 'cloud::image::registry' do + + let :params do + { :glance_db_host => '10.0.0.1', + :glance_db_user => 'glance', + :glance_db_password => 'secrete', + :ks_keystone_internal_host => '10.0.0.1', + :ks_keystone_internal_proto => 'https', + :ks_glance_internal_host => '10.0.0.1', + :ks_glance_registry_internal_port => '9191', + :ks_glance_password => 'secrete', + :debug => true, + :verbose => true, + :use_syslog => true, + :log_facility => 'LOG_LOCAL0', + :api_eth => '10.0.0.1' + } + end + + shared_examples_for 'openstack image registry' do + + it 'configure glance-registry' do + is_expected.to contain_class('glance::registry').with( + :database_connection => 'mysql://glance:secrete@10.0.0.1/glance?charset=utf8', + :keystone_password => 'secrete', + :keystone_tenant => 'services', + :keystone_user => 'glance', + :verbose => true, + :debug => true, + :auth_host => '10.0.0.1', + :auth_protocol => 'https', + :log_facility => 'LOG_LOCAL0', + :bind_host => '10.0.0.1', + :bind_port => '9191', + :use_syslog => true, + :log_dir => false, + :log_file => false + ) + end + + it 'checks if Glance DB is populated' do + is_expected.to contain_exec('glance_db_sync').with( + :command => 'glance-manage db_sync', + :user => 'glance', + :path => '/usr/bin', + :unless => '/usr/bin/mysql glance -h 10.0.0.1 -u glance -psecrete -e "show tables" | /bin/grep Tables' + ) + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure Glance Registry firewall rules' do + is_expected.to contain_firewall('100 allow glance-registry access').with( + :port => '9191', + :proto => 'tcp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure Glance API firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow glance-registry access').with( + :port => '9191', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack image registry' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'openstack image registry' + end + +end diff --git a/cloud/spec/classes/cloud_init_spec.rb b/cloud/spec/classes/cloud_init_spec.rb new file mode 100644 index 000000000..895e8f0b9 --- /dev/null +++ b/cloud/spec/classes/cloud_init_spec.rb @@ -0,0 +1,181 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud +# + +require 'spec_helper' + +describe 'cloud' do + + let :params do + { } + end + + shared_examples_for 'cloud node' do + + let :pre_condition do + ' + include concat::setup + ' + end + + let :file_defaults do + { + :mode => '0644' + } + end + + it {is_expected.to contain_class('ntp')} + + it {is_expected.to contain_file('/etc/motd').with( + {:ensure => 'file'}.merge(file_defaults) + )} + + it { is_expected.to contain_service('cron').with({ + :name => platform_params[:cron_service_name], + :ensure => 'running', + :enable => true + }) } + + context 'with firewall enabled' do + before :each do + params.merge!( + :manage_firewall => true, + ) + end + + it 'configure basic pre firewall rules' do + is_expected.to contain_firewall('000 accept related established rules').with( + :proto => 'all', + :state => ['RELATED', 'ESTABLISHED'], + :action => 'accept', + ) + is_expected.to contain_firewall('001 accept all icmp').with( + :proto => 'icmp', + :action => 'accept', + :state => ['NEW'], + ) + is_expected.to contain_firewall('002 accept all to lo interface').with( + :proto => 'all', + :iniface => 'lo', + :action => 'accept', + :state => ['NEW'], + ) + is_expected.to contain_firewall('003 accept ssh').with( + :port => '22', + :proto => 'tcp', + :action => 'accept', + :state => ['NEW'], + ) + end + + it 'configure basic post firewall rules' do + is_expected.to contain_firewall('999 drop all').with( + :proto => 'all', + :action => 'drop', + :source => '0.0.0.0/0', + ) + end + end + + context 'with custom firewall rules' do + before :each do + params.merge!( + :manage_firewall => true, + :firewall_rules => { + '300 add custom application 1' => {'port' => '999', 'proto' => 'udp', 'action' => 'accept'}, + '301 add custom application 2' => {'port' => '8081', 'proto' => 'tcp', 'action' => 'accept'} + } + ) + end + it 'configure custom firewall rules' do + is_expected.to contain_firewall('300 add custom application 1').with( + :port => '999', + :proto => 'udp', + :action => 'accept', + :state => ['NEW'], + ) + is_expected.to contain_firewall('301 add custom application 2').with( + :port => '8081', + :proto => 'tcp', + :action => 'accept', + :state => ['NEW'], + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + let :platform_params do + { :cron_service_name => 'cron'} + end + + it_configures 'cloud node' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat', + :hostname => 'redhat1' } + end + + let :platform_params do + { :cron_service_name => 'crond'} + end + + let :params do + { :rhn_registration => { "username" => "rhn", "password" => "pass" } } + end + + it_configures 'cloud node' + + xit { is_expected.to contain_rhn_register('rhn-redhat1') } + + context 'with SELinux set to enforcing' do + let :params do + { :selinux_mode => 'enforcing', + :selinux_modules => ['module1', 'module2'], + :selinux_booleans => ['foo', 'bar'], + :selinux_directory => '/path/to/modules'} + end + + it 'set SELINUX=enforcing' do + is_expected.to contain_class('cloud::selinux').with( + :mode => params[:selinux_mode], + :booleans => params[:selinux_booleans], + :modules => params[:selinux_modules], + :directory => params[:selinux_directory], + :stage => 'setup', + ) + end + end + + end + + context 'on other platforms' do + let :facts do + { :osfamily => 'Solaris' } + end + + it { is_expected.to compile.and_raise_error(/module puppet-openstack-cloud only support/) } + + end +end diff --git a/cloud/spec/classes/cloud_install_puppetdb_server.rb b/cloud/spec/classes/cloud_install_puppetdb_server.rb new file mode 100644 index 000000000..a3cdd8494 --- /dev/null +++ b/cloud/spec/classes/cloud_install_puppetdb_server.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe 'cloud::install::puppetdb_server' do + + shared_examples_for 'puppetdb' do + + it 'install puppetdb' do + is_exptected.to contain_class('puppetdb::server') + end + + end + + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'puppetdb' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + it_configures 'puppetdb' + end +end diff --git a/cloud/spec/classes/cloud_install_puppetmaster.rb b/cloud/spec/classes/cloud_install_puppetmaster.rb new file mode 100644 index 000000000..9ce612fd7 --- /dev/null +++ b/cloud/spec/classes/cloud_install_puppetmaster.rb @@ -0,0 +1,112 @@ +require 'spec_helper' + +describe 'cloud::install::puppetmaster' do + + shared_examples_for 'puppetmaster' do + + let :params do + { :puppetconf_path => '/etc/puppet/puppet.conf', + :main_configuration => {}, + :agent_configuration => { + 'certname' => { 'setting' => 'certname', 'value' => 'foo.bar' } + }, + :master_configuration => { + 'timeout' => { 'setting' => 'timeout', 'value' => '0' } + }} + end + + it 'install puppetmaster package' do + is_expected.to contain_package(platform_params[:puppetmaster_package_name]).with({ + :ensure => 'present', + }) + end + + it 'ensure puppetmaster is stopped' do + is_exptected.to contain_server(platform_params[:puppetmaster_service_name]).with({ + :ensure => 'stopped', + :hasstatus => true, + :hasrestart => true, + }) + end + + it 'generate certificate if necessary' do + is_expected.to contain_exec('puppet cert generate node.example.com') + end + + it 'install hiera' do + is_expected.to contain_class('hiera') + end + + it 'configure the puppetdb settings of puppetmaster' do + is_exptected.to contain_class('puppetdb::master::config') + end + + it 'configure the puppet master configuration file' do + is_expected.to contain_init_setting('certname').with( + :setting => 'certname', + :value => 'foo.bar', + :section => 'agent', + :path => '/etc/puppet/puppet.conf', + ) + is_expected.to contain_init_setting('timeout').with( + :setting => 'timeout', + :value => '0', + :section => 'master', + :path => '/etc/puppet/puppet.conf', + ) + end + + it 'configure the autosign.conf' do + is_expected.to contain_file('/etc/puppet/autosign/conf').with({ + :ensure => 'present', + :owner => 'puppet', + :group => 'puppet', + :conent => 'template(\'cloud/installserver/autosign.conf.erb\')', + }) + end + + end + + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian', + :fqdn => 'node.example.com' + } + end + + let :platform_params do + { :puppetmaster_package_name => 'puppet-server', + :puppetmaster_service_name => 'puppetmaster', + } + end + + it_configures 'puppetmaster' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat', + :fqdn => 'node.example.com' + } + end + + let :platform_params do + { :puppetmaster_package_name => 'puppetmaster', + :puppetmaster_service_name => 'puppetmaster', + } + end + + it_configures 'puppetmaster' + + context 'on Maj Release 7' do + facts.merge!(:operatingsystemmajrelease => '7') + + it 'ensure package mod_passenger is not installed' do + is_expected.to contain_package('mod_passenger').with({ + :ensure => 'absent', + }) + end + end + end +end diff --git a/cloud/spec/classes/cloud_loadbalancer_spec.rb b/cloud/spec/classes/cloud_loadbalancer_spec.rb new file mode 100644 index 000000000..e8b2edc46 --- /dev/null +++ b/cloud/spec/classes/cloud_loadbalancer_spec.rb @@ -0,0 +1,609 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::loadbalancer class +# + +require 'spec_helper' + +describe 'cloud::loadbalancer' do + + shared_examples_for 'openstack loadbalancer' do + + let :params do + { :ceilometer_api => true, + :cinder_api => true, + :glance_api => true, + :neutron_api => true, + :heat_api => true, + :heat_cfn_api => true, + :heat_cloudwatch_api => true, + :nova_api => true, + :ec2_api => true, + :metadata_api => true, + :swift_api => true, + :keystone_api_admin => true, + :keystone_api => true, + :trove_api => true, + :horizon => true, + :spice => true, + :ceilometer_bind_options => [], + :cinder_bind_options => [], + :ec2_bind_options => [], + :glance_api_bind_options => [], + :glance_registry_bind_options => [], + :heat_cfn_bind_options => [], + :heat_cloudwatch_bind_options => [], + :heat_api_bind_options => [], + :keystone_bind_options => [], + :keystone_admin_bind_options => [], + :metadata_bind_options => [], + :neutron_bind_options => [], + :trove_bind_options => [], + :swift_bind_options => [], + :spice_bind_options => [], + :horizon_bind_options => [], + :galera_bind_options => [], + :haproxy_auth => 'root:secrete', + :keepalived_state => 'BACKUP', + :keepalived_priority => 50, + :keepalived_vrrp_interface => false, + :keepalived_public_interface => 'eth0', + :keepalived_public_ipvs => ['10.0.0.1', '10.0.0.2'], + :keepalived_internal_ipvs => false, + :keepalived_auth_type => 'PASS', + :keepalived_auth_pass => 'secret', + :horizon_port => '80', + :spice_port => '6082', + :vip_public_ip => '10.0.0.1', + :galera_ip => '10.0.0.2', + :galera_slave => false, + :horizon_ssl => false, + :horizon_ssl_port => false, + :ks_ceilometer_public_port => '8777', + :ks_nova_public_port => '8774', + :ks_ec2_public_port => '8773', + :ks_metadata_public_port => '8777', + :ks_glance_api_public_port => '9292', + :ks_glance_registry_internal_port => '9191', + :ks_swift_public_port => '8080', + :ks_keystone_public_port => '5000', + :ks_keystone_admin_port => '35357', + :ks_cinder_public_port => '8776', + :ks_neutron_public_port => '9696', + :ks_trove_public_port => '8779', + :ks_heat_public_port => '8004', + :ks_heat_cfn_public_port => '8000', + :ks_heat_cloudwatch_public_port => '8003' } + end + + it 'configure haproxy server' do + is_expected.to contain_class('haproxy') + end # configure haproxy server + + it 'configure keepalived server' do + is_expected.to contain_class('keepalived') + end # configure keepalived server + + it 'configure sysctl to allow HAproxy to bind to a non-local IP address' do + is_expected.to contain_exec('exec_sysctl_net.ipv4.ip_nonlocal_bind').with_command( + 'sysctl -w net.ipv4.ip_nonlocal_bind=1' + ) + end + + it 'do not configure an internal VRRP instance by default' do + is_expected.not_to contain_keepalived__instance('2') + end + + context 'configure an internal VIP with the same VIP as public network' do + before do + params.merge!(:keepalived_internal_ipvs => ['10.0.0.1', '10.0.0.2']) + end + it 'shoult not configure an internal VRRP instance' do + is_expected.not_to contain_keepalived__instance('2') + end + end + + context 'configure an internal VIP' do + before do + params.merge!(:keepalived_internal_ipvs => ['192.168.0.1']) + end + it 'configure an internal VRRP instance' do + is_expected.to contain_keepalived__instance('2').with({ + 'interface' => 'eth1', + 'virtual_ips' => ['192.168.0.1 dev eth1'], + 'track_script' => ['haproxy'], + 'state' => 'BACKUP', + 'priority' => params[:keepalived_priority], + 'auth_type' => 'PASS', + 'auth_pass' => 'secret', + 'notify_master' => "#{platform_params[:start_haproxy_service]}", + 'notify_backup' => "#{platform_params[:stop_haproxy_service]}", + }) + end + end + + context 'configure keepalived vrrp on dedicated interface' do + before do + params.merge!(:keepalived_vrrp_interface => 'eth2') + end + it 'configure keepalived with a dedicated interface for vrrp' do + is_expected.to contain_keepalived__instance('1').with({ + 'interface' => 'eth2', + }) + end + end + + context 'configure keepalived with proper haproxy track script' do + it 'configure keepalived with a proper haproxy track script' do + is_expected.to contain_keepalived__vrrp_script('haproxy').with({ + 'name_is_process' => platform_params[:keepalived_name_is_process], + 'script' => platform_params[:keepalived_vrrp_script], + }) + end + end + + context 'when keepalived and HAproxy are in backup' do + it 'configure vrrp_instance with BACKUP state' do + is_expected.to contain_keepalived__instance('1').with({ + 'interface' => params[:keepalived_public_interface], + 'virtual_ips' => ['10.0.0.1 dev eth0', '10.0.0.2 dev eth0'], + 'track_script' => ['haproxy'], + 'state' => params[:keepalived_state], + 'priority' => params[:keepalived_priority], + 'auth_type' => 'PASS', + 'auth_pass' => 'secret', + 'notify_master' => "#{platform_params[:start_haproxy_service]}", + 'notify_backup' => "#{platform_params[:stop_haproxy_service]}", + }) + end # configure vrrp_instance with BACKUP state + it 'configure haproxy server without service managed' do + is_expected.to contain_class('haproxy').with(:service_manage => true) + end # configure haproxy server + end # configure keepalived in backup + + context 'configure keepalived in master' do + before do + params.merge!( :keepalived_state => 'MASTER' ) + end + it 'configure vrrp_instance with MASTER state' do + is_expected.to contain_keepalived__instance('1').with({ + 'interface' => params[:keepalived_public_interface], + 'track_script' => ['haproxy'], + 'state' => 'MASTER', + 'priority' => params[:keepalived_priority], + 'auth_type' => 'PASS', + 'auth_pass' => 'secret', + 'notify_master' => "#{platform_params[:start_haproxy_service]}", + 'notify_backup' => "#{platform_params[:stop_haproxy_service]}", + }) + end + it 'configure haproxy server with service managed' do + is_expected.to contain_class('haproxy').with(:service_manage => true) + end # configure haproxy server + end # configure keepalived in master + + context 'configure logrotate file' do + it { is_expected.to contain_file('/etc/logrotate.d/haproxy').with( + :source => 'puppet:///modules/cloud/logrotate/haproxy', + :mode => '0644', + :owner => 'root', + :group => 'root' + )} + end # configure logrotate file + + context 'configure monitor haproxy listen' do + it { is_expected.to contain_haproxy__listen('monitor').with( + :ipaddress => params[:vip_public_ip], + :ports => '9300' + )} + end # configure monitor haproxy listen + + context 'configure monitor haproxy listen with another vip' do + before do + params.merge!( :vip_monitor_ip => ['192.168.0.1'] ) + end + it { is_expected.to contain_haproxy__listen('monitor').with( + :ipaddress => ['192.168.0.1'], + :ports => '9300' + )} + end # configure monitor haproxy listen + + context 'configure galera haproxy listen' do + it { is_expected.to contain_haproxy__listen('galera_cluster').with( + :ipaddress => params[:galera_ip], + :ports => '3306', + :options => { + 'maxconn' => '1000', + 'mode' => 'tcp', + 'balance' => 'roundrobin', + 'option' => ['tcpka','tcplog','httpchk'], + 'timeout client' => '400s', + 'timeout server' => '400s' + } + )} + end # configure monitor haproxy listen + + context 'not configure galera slave haproxy listen' do + it { is_expected.not_to contain_haproxy__listen('galera_readonly_cluster') } + end # configure monitor haproxy listen + + context 'configure galera slave haproxy listen' do + before do + params.merge!( :galera_slave => true ) + end + it { is_expected.to contain_haproxy__listen('galera_readonly_cluster').with( + :ipaddress => params[:galera_ip], + :ports => '3307', + :options => { + 'maxconn' => '1000', + 'mode' => 'tcp', + 'balance' => 'roundrobin', + 'option' => ['tcpka','tcplog','httpchk'], + 'timeout client' => '400s', + 'timeout server' => '400s' + } + )} + end # configure monitor haproxy listen + + # test backward compatibility + context 'configure OpenStack binding on public network only' do + it { is_expected.to contain_haproxy__listen('spice_cluster').with( + :ipaddress => [params[:vip_public_ip]], + :ports => '6082', + :options => { + 'mode' => 'tcp', + 'balance' => 'source', + 'option' => ['tcpka', 'tcplog', 'forwardfor'], + 'timeout server' => '120m', + 'timeout client' => '120m' + } + )} + end + + context 'configure OpenStack binding on both public and internal networks' do + before do + params.merge!( + :nova_api => true, + :galera_ip => '172.16.0.1', + :vip_public_ip => '172.16.0.1', + :vip_internal_ip => '192.168.0.1', + :keepalived_public_ipvs => ['172.16.0.1', '172.16.0.2'], + :keepalived_internal_ipvs => ['192.168.0.1', '192.168.0.2'] + ) + end + it { is_expected.to contain_haproxy__listen('nova_api_cluster').with( + :ipaddress => ['172.16.0.1', '192.168.0.1'], + :ports => '8774' + )} + end + + context 'configure OpenStack binding on IPv4 and IPv6 public ip' do + before do + params.merge!( + :nova_api => true, + :galera_ip => '172.16.0.1', + :vip_public_ip => ['172.16.0.1', '2001:0db8:85a3:0000:0000:8a2e:0370:7334'], + :vip_internal_ip => '192.168.0.1', + :keepalived_public_ipvs => ['172.16.0.1', '172.16.0.2', '2001:0db8:85a3:0000:0000:8a2e:0370:7334'], + :keepalived_internal_ipvs => ['192.168.0.1', '192.168.0.2'] + ) + end + it { is_expected.to contain_haproxy__listen('nova_api_cluster').with( + :ipaddress => ['172.16.0.1', '2001:0db8:85a3:0000:0000:8a2e:0370:7334', '192.168.0.1'], + :ports => '8774' + )} + end + + context 'disable an OpenStack service binding' do + before do + params.merge!(:metadata_api => false) + end + it { is_expected.not_to contain_haproxy__listen('metadata_api_cluster') } + end + + context 'should fail to configure OpenStack binding when vip_public_ip and vip_internal_ip are missing' do + before do + params.merge!( + :nova_api => true, + :galera_ip => '172.16.0.1', + :vip_public_ip => false, + :vip_internal_ip => false, + :keepalived_public_ipvs => ['172.16.0.1', '172.16.0.2'] + ) + end + it_raises 'a Puppet::Error', /vip_public_ip and vip_internal_ip are both set to false, no binding is possible./ + end + + context 'should fail to configure OpenStack binding when given VIP is not in the VIP pool list' do + before do + params.merge!( + :nova_api => '10.0.0.1', + :galera_ip => '172.16.0.1', + :vip_public_ip => '172.16.0.1', + :vip_internal_ip => false, + :keepalived_public_ipvs => ['172.16.0.1', '172.16.0.2'] + ) + end + it_raises 'a Puppet::Error', /10.0.0.1 is not part of VIP pools./ + end + + context 'with a public OpenStack VIP not in the keepalived VIP list' do + before do + params.merge!( + :vip_public_ip => '172.16.0.1', + :keepalived_public_ipvs => ['192.168.0.1', '192.168.0.2'] + ) + end + it_raises 'a Puppet::Error', /vip_public_ip should be part of keepalived_public_ipvs./ + end + + context 'with an internal OpenStack VIP not in the keepalived VIP list' do + before do + params.merge!( + :vip_internal_ip => '172.16.0.1', + :keepalived_internal_ipvs => ['192.168.0.1', '192.168.0.2'] + ) + end + it_raises 'a Puppet::Error', /vip_internal_ip should be part of keepalived_internal_ipvs./ + end + + context 'with a Galera VIP not in the keepalived VIP list' do + before do + params.merge!( + :galera_ip => '172.16.0.1', + :vip_public_ip => '192.168.0.1', + :keepalived_public_ipvs => ['192.168.0.1', '192.168.0.2'], + :keepalived_internal_ipvs => ['192.168.1.1', '192.168.1.2'] + ) + end + it_raises 'a Puppet::Error', /galera_ip should be part of keepalived_public_ipvs or keepalived_internal_ipvs./ + end + + context 'configure OpenStack binding with HTTPS and SSL offloading' do + before do + params.merge!( + :nova_bind_options => ['ssl', 'crt'] + ) + end + it { is_expected.to contain_haproxy__listen('nova_api_cluster').with( + :ipaddress => [params[:vip_public_ip]], + :ports => '8774', + :options => { + 'mode' => 'http', + 'option' => ['tcpka','forwardfor','tcplog','httpchk'], + 'http-check' => 'expect ! rstatus ^5', + 'balance' => 'roundrobin', + }, + :bind_options => ['ssl', 'crt'] + )} + end + + context 'configure OpenStack binding with HTTP options' do + before do + params.merge!( + :cinder_bind_options => 'something not secure', + ) + end + it { is_expected.to contain_haproxy__listen('cinder_api_cluster').with( + :ipaddress => [params[:vip_public_ip]], + :ports => '8776', + :options => { + 'mode' => 'http', + 'option' => ['tcpka','forwardfor','tcplog', 'httpchk'], + 'http-check' => 'expect ! rstatus ^5', + 'balance' => 'roundrobin', + }, + :bind_options => ['something not secure'] + )} + end + + context 'configure OpenStack Horizon' do + it { is_expected.to contain_haproxy__listen('horizon_cluster').with( + :ipaddress => [params[:vip_public_ip]], + :ports => '80', + :options => { + 'mode' => 'http', + 'http-check' => 'expect ! rstatus ^5', + 'option' => ["tcpka", "forwardfor", "tcplog", "httpchk GET /#{platform_params[:auth_url]} \"HTTP/1.0\\r\\nUser-Agent: HAproxy-myhost\""], + 'cookie' => 'sessionid prefix', + 'balance' => 'leastconn', + } + )} + end + + context 'configure OpenStack Horizon with SSL termination on HAProxy' do + before do + params.merge!( + :horizon_port => '443', + :horizon_ssl => false, + :horizon_ssl_port => false, + :horizon_bind_options => ['ssl', 'crt'] + ) + end + it { is_expected.to contain_haproxy__listen('horizon_cluster').with( + :ipaddress => [params[:vip_public_ip]], + :ports => '443', + :options => { + 'mode' => 'http', + 'http-check' => 'expect ! rstatus ^5', + 'option' => ["tcpka", "forwardfor", "tcplog", "httpchk GET /#{platform_params[:auth_url]} \"HTTP/1.0\\r\\nUser-Agent: HAproxy-myhost\""], + 'cookie' => 'sessionid prefix', + 'balance' => 'leastconn', + 'reqadd' => 'X-Forwarded-Proto:\ https if { ssl_fc }' + }, + :bind_options => ['ssl', 'crt'] + )} + end + + context 'configure OpenStack Horizon SSL with termination on the webserver' do + before do + params.merge!( + :horizon_ssl => true, + :horizon_ssl_port => '443' + ) + end + it { is_expected.to contain_haproxy__listen('horizon_ssl_cluster').with( + :ipaddress => [params[:vip_public_ip]], + :ports => '443', + :options => { + 'mode' => 'tcp', + 'option' => ["tcpka", "forwardfor", "tcplog", "ssl-hello-chk"], + 'cookie' => 'sessionid prefix', + 'balance' => 'leastconn', + } + )} + end + + context 'configure OpenStack Heat API SSL binding' do + before do + params.merge!( + :heat_api_bind_options => ['ssl', 'crt'] + ) + end + it { is_expected.to contain_haproxy__listen('heat_api_cluster').with( + :ipaddress => [params[:vip_public_ip]], + :ports => '8004', + :options => { + 'reqadd' => 'X-Forwarded-Proto:\ https if { ssl_fc }', + 'mode' => 'http', + 'option' => ['tcpka','forwardfor','tcplog', 'httpchk'], + 'http-check' => 'expect ! rstatus ^5', + 'balance' => 'roundrobin' + }, + :bind_options => ['ssl', 'crt'] + )} + end + context 'configure RabbitMQ binding' do + before do + params.merge!( :rabbitmq => true ) + end + it { is_expected.to contain_haproxy__listen('rabbitmq_cluster').with( + :ipaddress => [params[:vip_public_ip]], + :ports => '5672', + :options => { + 'mode' => 'tcp', + 'balance' => 'roundrobin', + 'option' => ['tcpka', 'tcplog', 'forwardfor'], + } + )} + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure haproxy firewall rules' do + # test the firewall rule in cloud::loadbalancer::binding + is_expected.to contain_firewall('100 allow horizon_cluster binding access').with( + :port => '80', + :proto => 'tcp', + :action => 'accept', + ) + # test the firewall rules in cloud::loadbalancer + is_expected.to contain_firewall('100 allow galera binding access').with( + :port => '3306', + :proto => 'tcp', + :action => 'accept', + ) + is_expected.to contain_firewall('100 allow haproxy monitor access').with( + :port => '9300', + :proto => 'tcp', + :action => 'accept', + ) + is_expected.to contain_firewall('100 allow keepalived access').with( + :port => nil, + :proto => 'vrrp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure haproxy firewall rules with custom parameter' do + # test the firewall rule in cloud::loadbalancer::binding + is_expected.to contain_firewall('100 allow horizon_cluster binding access').with( + :port => '80', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + # test the firewall rules in cloud::loadbalancer + is_expected.to contain_firewall('100 allow galera binding access').with( + :port => '3306', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + is_expected.to contain_firewall('100 allow haproxy monitor access').with( + :port => '9300', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + is_expected.to contain_firewall('100 allow keepalived access').with( + :port => nil, + :proto => 'vrrp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end # shared:: openstack loadbalancer + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian', + :hostname => 'myhost' } + end + + let :platform_params do + { :auth_url => 'horizon', + :start_haproxy_service => '"/etc/init.d/haproxy start"', + :stop_haproxy_service => '"/etc/init.d/haproxy stop"', + :keepalived_name_is_process => 'true', + :keepalived_vrrp_script => nil, + } + end + + it_configures 'openstack loadbalancer' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat', + :hostname => 'myhost' } + end + + let :platform_params do + { :auth_url => 'dashboard', + :start_haproxy_service => '"/usr/bin/systemctl start haproxy"', + :stop_haproxy_service => '"/usr/bin/systemctl stop haproxy"', + :keepalived_name_is_process => 'false', + :keepalived_vrrp_script => 'systemctl status haproxy.service', + } + end + + it_configures 'openstack loadbalancer' + end + +end diff --git a/cloud/spec/classes/cloud_logging_agent.rb b/cloud/spec/classes/cloud_logging_agent.rb new file mode 100644 index 000000000..fe2c93b61 --- /dev/null +++ b/cloud/spec/classes/cloud_logging_agent.rb @@ -0,0 +1,147 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::logging::agent class +# + +require 'spec_helper' + +describe 'cloud::logging::agent' do + + shared_examples_for 'openstack logging agent' do + + let :pre_condition do + "class { 'cloud::logging': } + include ::fluentd" + end + + let :common_params do { + :plugins => {}, + :matches => {}, + :sources => { + 'apache' => {'type' => 'tail', 'configfile' => 'apache'}, + 'syslog' => {'type' => 'tail', 'configfile' => 'syslog'} + }, + :logrotate_rule => { + 'td-agent' => { + 'path' => '/var/log/td-agent/td-agent.log', + 'rotate' => '30', + 'compress' => 'true', + } + }, + } + end + + + context 'rsyslog is enabled' do + let :params do + common_params.merge( {:syslog_enable => 'true' } ) + end + + it 'include cloud::loging' do + it is_expected.to contain_class('cloud::logging') + end + + it 'include rsyslog::client' do + it is_expected.to contain_class('rsyglog::client') + end + + it 'create /var/db/td-agent' do + it is_expected.to contain_file('/var/db/td-agent').with({ + :ensure => 'directory', + :owner => 'td-agent', + :group => 'td-agent', + }) + end + + end + + context 'rsyslog is disabled' do + let :params do + common_params.merge( {:syslog_enable => 'false' } ) + end + + it 'include cloud::loging' do + it is_expected.to contain_class('cloud::logging') + end + + it 'include rsyslog::client' do + it is_expected.not_to contain_class('rsyglog::client') + end + + it 'create /var/db/td-agent' do + it is_expected.to contain_file('/var/db/td-agent').with({ + :ensure => 'directory', + :owner => 'td-agent', + :group => 'td-agent', + }) + end + + it 'has a logrotate rule for td-agent.log' do + it is_expected.to contain_logrotate__rule('td-agent').with({ + :path => '/var/log/td-agent/td-agent.log', + :rotate => '30', + :compress => 'true', + }) + end + + end + + context 'logrotate rule with default parameters' do + + it 'has a logrotate rule for td-agent.log' do + it is_expected.to contain_logrotate__rule('td-agent').with({ + :path => '/var/log/td-agent/td-agent.log', + :rotate => '30', + :compress => 'true', + }) + end + + end + + context 'logrotate rule with custom parameters' do + let :params do + common_params.merge!( {:logrotate_rule => { 'td-agent' => { 'path' => '/foo/bar', 'rotate' => '5', 'compress' => 'false'} }} ) + end + + it 'has a logrotate rule for td-agent.log' do + it is_expected.to contain_logrotate__rule('td-agent').with({ + :path => '/foo/bar', + :rotate => '5', + :compress => 'false', + }) + end + + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack logging agent' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'openstack logging agent' + end + +end diff --git a/cloud/spec/classes/cloud_logging_server.rb b/cloud/spec/classes/cloud_logging_server.rb new file mode 100644 index 000000000..1eceeabec --- /dev/null +++ b/cloud/spec/classes/cloud_logging_server.rb @@ -0,0 +1,59 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::logging::server class +# + +require 'spec_helper' + +describe 'cloud::logging::server' do + + shared_examples_for 'openstack logging server' do + + let :pre_condition do + "class { 'cloud::logging': } + include ::fluentd" + end + + it 'configure logging common' do + it is_expected.to contain_concat("/etc/td-agent/config.d/forward.conf") + end + + it 'configure kibana' do + is_expected.to contain_class('kibana3').with(:ws_port => '8001') + end + + it 'configure an elasticsearch instance' do + is_expected.to contain_elasticsearch__instance('fluentd') + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack logging server' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'openstack logging server' + end + +end diff --git a/cloud/spec/classes/cloud_messaging_spec.rb b/cloud/spec/classes/cloud_messaging_spec.rb new file mode 100644 index 000000000..ff20621bf --- /dev/null +++ b/cloud/spec/classes/cloud_messaging_spec.rb @@ -0,0 +1,119 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::messaging class +# + +require 'spec_helper' + +describe 'cloud::messaging' do + + shared_examples_for 'openstack messaging' do + + let :params do + { + :cluster_node_type => 'disc', + :rabbit_names => ['foo','boo','zoo'], + :rabbit_password => 'secrete', + :rabbitmq_ip => '10.0.0.1', + } + end + + it 'configure rabbitmq-server with default values' do + is_expected.to contain_class('rabbitmq').with( + :delete_guest_user => true, + :config_cluster => true, + :cluster_nodes => params[:rabbit_names], + :wipe_db_on_cookie_change => true, + :cluster_node_type => params[:cluster_node_type], + :node_ip_address => params[:rabbitmq_ip], + :port => '5672', + ) + end + + context 'with RAM mode' do + before :each do + params.merge!( :cluster_node_type => 'ram') + end + + it 'configure rabbitmq-server in RAM mode' do + is_expected.to contain_class('rabbitmq').with( :cluster_node_type => 'ram' ) + end + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure rabbitmq firewall rules' do + is_expected.to contain_firewall('100 allow rabbitmq access').with( + :port => '5672', + :proto => 'tcp', + :action => 'accept', + ) + is_expected.to contain_firewall('100 allow rabbitmq management access').with( + :port => '55672', + :proto => 'tcp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure rabbitmq firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow rabbitmq management access').with( + :port => '55672', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack messaging' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'openstack messaging' + + it 'should create rabbitmq binaries symbolic links' do + is_expected.to contain_file('/usr/sbin/rabbitmq-plugins').with( + :ensure => 'link', + :target => '/usr/lib/rabbitmq/bin/rabbitmq-plugins' + ) + is_expected.to contain_file('/usr/sbin/rabbitmq-env').with( + :ensure => 'link', + :target => '/usr/lib/rabbitmq/bin/rabbitmq-env' + ) + end + end + +end diff --git a/cloud/spec/classes/cloud_monitoring_agent_sensu_spec.rb b/cloud/spec/classes/cloud_monitoring_agent_sensu_spec.rb new file mode 100644 index 000000000..e36b144a4 --- /dev/null +++ b/cloud/spec/classes/cloud_monitoring_agent_sensu_spec.rb @@ -0,0 +1,47 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::monitoring::agent::sensu class +# + +require 'spec_helper' + +describe 'cloud::monitoring::agent::sensu' do + + shared_examples_for 'openstack sensu monitoring agent' do + + it 'include ::sensu' do + is_expected.to contain_class('sensu') + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack sensu monitoring agent' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'openstack sensu monitoring agent' + end + +end diff --git a/cloud/spec/classes/cloud_monitoring_server_sensu_spec.rb b/cloud/spec/classes/cloud_monitoring_server_sensu_spec.rb new file mode 100644 index 000000000..4d16c89ab --- /dev/null +++ b/cloud/spec/classes/cloud_monitoring_server_sensu_spec.rb @@ -0,0 +1,43 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::monitoring::server::sensu class +# + +require 'spec_helper' + +describe 'cloud::monitoring::server::sensu' do + + shared_examples_for 'openstack sensu monitoring server' do + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack sensu monitoring server' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'openstack sensu monitoring server' + end + +end diff --git a/cloud/spec/classes/cloud_network_controller_spec.rb b/cloud/spec/classes/cloud_network_controller_spec.rb new file mode 100644 index 000000000..787e3b442 --- /dev/null +++ b/cloud/spec/classes/cloud_network_controller_spec.rb @@ -0,0 +1,195 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::network::controller class +# +require 'spec_helper' + +describe 'cloud::network::controller' do + + shared_examples_for 'openstack network controller' do + + let :pre_condition do + "class { 'cloud::network': + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + api_eth => '10.0.0.1', + verbose => true, + debug => true, + use_syslog => true, + dhcp_lease_duration => '10', + log_facility => 'LOG_LOCAL0' }" + end + + let :params do + { :neutron_db_host => '10.0.0.1', + :neutron_db_user => 'neutron', + :neutron_db_password => 'secrete', + :ks_neutron_password => 'secrete', + :ks_keystone_admin_host => '10.0.0.1', + :ks_keystone_admin_proto => 'https', + :ks_keystone_public_port => '5000', + :nova_url => 'http://127.0.0.1:8774/v2', + :nova_admin_auth_url => 'http://127.0.0.1:5000/v2.0', + :nova_admin_username => 'nova', + :nova_admin_tenant_name => 'services', + :nova_admin_password => 'novapassword', + :nova_region_name => 'RegionOne', + :manage_ext_network => false, + :api_eth => '10.0.0.1' } + end + + it 'configure neutron common' do + is_expected.to contain_class('neutron').with( + :allow_overlapping_ips => true, + :dhcp_agents_per_network => '2', + :verbose => true, + :debug => true, + :log_facility => 'LOG_LOCAL0', + :use_syslog => true, + :rabbit_user => 'neutron', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :bind_host => '10.0.0.1', + :core_plugin => 'neutron.plugins.ml2.plugin.Ml2Plugin', + :service_plugins => ['neutron.services.loadbalancer.plugin.LoadBalancerPlugin','neutron.services.metering.metering_plugin.MeteringPlugin','neutron.services.l3_router.l3_router_plugin.L3RouterPlugin'], + :log_dir => false, + :dhcp_lease_duration => '10', + :report_interval => '30' + ) + is_expected.to contain_class('neutron::plugins::ml2').with( + :type_drivers => ['gre', 'vlan', 'flat'], + :tenant_network_types => ['gre'], + :mechanism_drivers => ['linuxbridge','openvswitch','l2population'], + :tunnel_id_ranges => ['1:10000'], + :vni_ranges => ['1:10000'], + :network_vlan_ranges => ['physnet1:1000:2999'], + :flat_networks => ['public'], + :enable_security_group => true + ) + end + + it 'configure neutron server' do + is_expected.to contain_class('neutron::server').with( + :auth_password => 'secrete', + :auth_host => '10.0.0.1', + :auth_port => '5000', + :auth_protocol => 'https', + :database_connection => 'mysql://neutron:secrete@10.0.0.1/neutron?charset=utf8', + :api_workers => '2', + :agent_down_time => '60' + ) + end + + it 'configure neutron server notifications to nova' do + is_expected.to contain_class('neutron::server::notifications').with( + :nova_url => 'http://127.0.0.1:8774/v2', + :nova_admin_auth_url => 'http://127.0.0.1:5000/v2.0', + :nova_admin_username => 'nova', + :nova_admin_tenant_name => 'services', + :nova_admin_password => 'novapassword', + :nova_region_name => 'RegionOne' + ) + end + it 'checks if Neutron DB is populated' do + is_expected.to contain_exec('neutron_db_sync').with( + :command => 'neutron-db-manage --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/plugin.ini upgrade head', + :path => '/usr/bin', + :user => 'neutron', + :unless => '/usr/bin/mysql neutron -h 10.0.0.1 -u neutron -psecrete -e "show tables" | /bin/grep Tables', + :require => 'Neutron_config[DEFAULT/service_plugins]', + :notify => 'Service[neutron-server]' + ) + end + + it 'should not configure provider external network' do + is_expected.not_to contain__neutron_network('public') + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure neutron-server firewall rules' do + is_expected.to contain_firewall('100 allow neutron-server access').with( + :port => '9696', + :proto => 'tcp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure neutrons-server firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow neutron-server access').with( + :port => '9696', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + context 'with custom ml2 parameters' do + before :each do + params.merge!( + :tenant_network_types => ['vxlan'], + :type_drivers => ['gre', 'vlan', 'flat', 'vxlan'], + :tunnel_id_ranges => ['100:300'], + :vni_ranges => ['42:51','53:69'], + ) + end + it 'contains correct parameters' do + is_expected.to contain_class('neutron::plugins::ml2').with( + :type_drivers => ['gre', 'vlan', 'flat', 'vxlan'], + :tenant_network_types => ['vxlan'], + :mechanism_drivers => ['linuxbridge', 'openvswitch','l2population'], + :tunnel_id_ranges => ['100:300'], + :vni_ranges => ['42:51','53:69'], + :network_vlan_ranges => ['physnet1:1000:2999'], + :flat_networks => ['public'], + :enable_security_group => true + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian', + :processorcount => '2' } + end + + it_configures 'openstack network controller' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat', + :processorcount => '2' } + end + + it_configures 'openstack network controller' + end + +end diff --git a/cloud/spec/classes/cloud_network_dhcp_spec.rb b/cloud/spec/classes/cloud_network_dhcp_spec.rb new file mode 100644 index 000000000..9f0cfe984 --- /dev/null +++ b/cloud/spec/classes/cloud_network_dhcp_spec.rb @@ -0,0 +1,188 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::network::dhcp class +# +require 'spec_helper' + +describe 'cloud::network::dhcp' do + + shared_examples_for 'openstack network dhcp' do + + let :pre_condition do + "class { 'cloud::network': + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + api_eth => '10.0.0.1', + verbose => true, + debug => true, + use_syslog => true, + dhcp_lease_duration => '10', + log_facility => 'LOG_LOCAL0' }" + end + + let :params do + { :veth_mtu => '1400', + :debug => true } + end + + it 'configure neutron common' do + is_expected.to contain_class('neutron').with( + :allow_overlapping_ips => true, + :dhcp_agents_per_network => '2', + :verbose => true, + :debug => true, + :log_facility => 'LOG_LOCAL0', + :use_syslog => true, + :rabbit_user => 'neutron', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :bind_host => '10.0.0.1', + :core_plugin => 'neutron.plugins.ml2.plugin.Ml2Plugin', + :service_plugins => ['neutron.services.loadbalancer.plugin.LoadBalancerPlugin','neutron.services.metering.metering_plugin.MeteringPlugin','neutron.services.l3_router.l3_router_plugin.L3RouterPlugin'], + :log_dir => false, + :dhcp_lease_duration => '10', + :report_interval => '30' + ) + end + + it 'configure neutron dhcp' do + is_expected.to contain_class('neutron::agents::dhcp').with( + :debug => true, + :dnsmasq_config_file => '/etc/neutron/dnsmasq-neutron.conf', + :enable_isolated_metadata => true + ) + + is_expected.to contain_neutron_dhcp_agent_config('DEFAULT/dnsmasq_dns_servers').with_ensure('absent') + + is_expected.to contain_file('/etc/neutron/dnsmasq-neutron.conf').with( + :mode => '0755', + :owner => 'root', + :group => 'root' + ) + is_expected.to contain_file('/etc/neutron/dnsmasq-neutron.conf').with_content(/^dhcp-option-force=26,1400$/) + end + end + + shared_examples_for 'openstack network dhcp with custom nameserver' do + + let :pre_condition do + "class { 'cloud::network': + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + api_eth => '10.0.0.1', + verbose => true, + debug => true, + use_syslog => true, + dhcp_lease_duration => '10', + log_facility => 'LOG_LOCAL0' }" + end + + let :params do + { :veth_mtu => '1400', + :debug => true, + :dnsmasq_dns_servers => ['1.2.3.4'] } + end + + it 'configure neutron dhcp' do + is_expected.to contain_class('neutron::agents::dhcp').with( + :debug => true + ) + + is_expected.to contain_neutron_dhcp_agent_config('DEFAULT/dnsmasq_config_file').with_value('/etc/neutron/dnsmasq-neutron.conf') + is_expected.to contain_neutron_dhcp_agent_config('DEFAULT/enable_isolated_metadata').with_value(true) + is_expected.to contain_neutron_dhcp_agent_config('DEFAULT/dnsmasq_dns_servers').with_value('1.2.3.4') + + is_expected.to contain_file('/etc/neutron/dnsmasq-neutron.conf').with( + :mode => '0755', + :owner => 'root', + :group => 'root' + ) + is_expected.to contain_file('/etc/neutron/dnsmasq-neutron.conf').with_content(/^dhcp-option-force=26,1400$/) + + end + + context 'with more than one dns server' do + before { params.merge!(:dnsmasq_dns_servers => ['1.2.3.4','4.3.2.1','2.2.2.2']) } + it { is_expected.to contain_neutron_dhcp_agent_config('DEFAULT/dnsmasq_dns_servers').with_value('1.2.3.4,4.3.2.1,2.2.2.2') } + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure neutron-server firewall rules' do + is_expected.to contain_firewall('100 allow dhcp in access').with( + :port => '67', + :proto => 'udp', + :chain => 'INPUT', + :action => 'accept', + ) + is_expected.to contain_firewall('100 allow dhcp out access').with( + :port => '68', + :proto => 'udp', + :chain => 'OUTPUT', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure neutrons-server firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow dhcp in access').with( + :port => '67', + :proto => 'udp', + :chain => 'INPUT', + :action => 'accept', + :limit => '50/sec', + ) + is_expected.to contain_firewall('100 allow dhcp out access').with( + :port => '68', + :proto => 'udp', + :chain => 'OUTPUT', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack network dhcp' + it_configures 'openstack network dhcp with custom nameserver' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'openstack network dhcp' + it_configures 'openstack network dhcp with custom nameserver' + end + +end diff --git a/cloud/spec/classes/cloud_network_l3_spec.rb b/cloud/spec/classes/cloud_network_l3_spec.rb new file mode 100644 index 000000000..090470524 --- /dev/null +++ b/cloud/spec/classes/cloud_network_l3_spec.rb @@ -0,0 +1,149 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::network::l3 class +# +require 'spec_helper' + +describe 'cloud::network::l3' do + + shared_examples_for 'openstack network l3' do + + let :pre_condition do + "class { 'cloud::network': + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + api_eth => '10.0.0.1', + verbose => true, + debug => true, + use_syslog => true, + dhcp_lease_duration => '10', + log_facility => 'LOG_LOCAL0' }" + end + + let :params do + { :debug => true, + :external_int => 'eth1' } + end + + it 'configure neutron common' do + is_expected.to contain_class('neutron').with( + :allow_overlapping_ips => true, + :dhcp_agents_per_network => '2', + :verbose => true, + :debug => true, + :log_facility => 'LOG_LOCAL0', + :use_syslog => true, + :rabbit_user => 'neutron', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :bind_host => '10.0.0.1', + :core_plugin => 'neutron.plugins.ml2.plugin.Ml2Plugin', + :service_plugins => ['neutron.services.loadbalancer.plugin.LoadBalancerPlugin','neutron.services.metering.metering_plugin.MeteringPlugin','neutron.services.l3_router.l3_router_plugin.L3RouterPlugin'], + :log_dir => false, + :dhcp_lease_duration => '10', + :report_interval => '30' + ) + end + + it 'configure neutron l3' do + is_expected.to contain_class('neutron::agents::l3').with( + :debug => true, + :external_network_bridge => 'br-ex' + ) + end + it 'configure br-ex bridge' do + is_expected.not_to contain__vs_bridge('br-ex') + end + + it 'configure neutron metering agent' do + is_expected.to contain_class('neutron::agents::metering').with( + :debug => true + ) + end + + context 'without TSO/GSO/GRO on Red Hat systems' do + before :each do + facts.merge!( :osfamily => 'RedHat') + end + + it 'ensure TSO script is enabled at boot' do + is_expected.to contain_exec('enable-tso-script').with( + :command => '/usr/sbin/chkconfig disable-tso on', + :unless => '/bin/ls /etc/rc*.d | /bin/grep disable-tso', + :onlyif => '/usr/bin/test -f /etc/init.d/disable-tso' + ) + end + it 'start TSO script' do + is_expected.to contain_exec('start-tso-script').with( + :command => '/etc/init.d/disable-tso start', + :unless => '/usr/bin/test -f /var/run/disable-tso.pid', + :onlyif => '/usr/bin/test -f /etc/init.d/disable-tso' + ) + end + end + + context 'without TSO/GSO/GRO on Debian systems' do + before :each do + facts.merge!( :osfamily => 'Debian') + end + + it 'ensure TSO script is enabled at boot' do + is_expected.to contain_exec('enable-tso-script').with( + :command => '/usr/sbin/update-rc.d disable-tso defaults', + :unless => '/bin/ls /etc/rc*.d | /bin/grep disable-tso', + :onlyif => '/usr/bin/test -f /etc/init.d/disable-tso' + ) + end + it 'start TSO script' do + is_expected.to contain_exec('start-tso-script').with( + :command => '/etc/init.d/disable-tso start', + :unless => '/usr/bin/test -f /var/run/disable-tso.pid', + :onlyif => '/usr/bin/test -f /etc/init.d/disable-tso' + ) + end + end + + context 'when not managing TSO/GSO/GRO' do + before :each do + params.merge!( :manage_tso => false) + end + it 'ensure TSO script is not enabled at boot' do + is_expected.not_to contain_exec('enable-tso-script') + end + it 'do not start TSO script' do + is_expected.not_to contain_exec('start-tso-script') + end + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack network l3' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'openstack network l3' + end + +end diff --git a/cloud/spec/classes/cloud_network_lbaas_spec.rb b/cloud/spec/classes/cloud_network_lbaas_spec.rb new file mode 100644 index 000000000..869a8cd85 --- /dev/null +++ b/cloud/spec/classes/cloud_network_lbaas_spec.rb @@ -0,0 +1,108 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::network::lbaas class +# +require 'spec_helper' + +describe 'cloud::network::lbaas' do + + shared_examples_for 'openstack network lbaas' do + + let :pre_condition do + "class { 'cloud::network': + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + api_eth => '10.0.0.1', + verbose => true, + debug => true, + use_syslog => true, + dhcp_lease_duration => '10', + log_facility => 'LOG_LOCAL0' }" + end + + let :params do + { :debug => true, + :manage_haproxy_pkg => true } + end + + it 'configure neutron common' do + is_expected.to contain_class('neutron').with( + :allow_overlapping_ips => true, + :dhcp_agents_per_network => '2', + :verbose => true, + :debug => true, + :log_facility => 'LOG_LOCAL0', + :use_syslog => true, + :rabbit_user => 'neutron', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :bind_host => '10.0.0.1', + :core_plugin => 'neutron.plugins.ml2.plugin.Ml2Plugin', + :service_plugins => ['neutron.services.loadbalancer.plugin.LoadBalancerPlugin','neutron.services.metering.metering_plugin.MeteringPlugin','neutron.services.l3_router.l3_router_plugin.L3RouterPlugin'], + :log_dir => false, + :dhcp_lease_duration => '10', + :report_interval => '30' + ) + end + + it 'configure neutron lbaas' do + is_expected.to contain_class('neutron::agents::lbaas').with( + :debug => true, + :manage_haproxy_package => true + ) + end + + context 'when not managing HAproxy package' do + let :pre_condition do + "package {'haproxy': ensure => 'present'} + class { 'cloud::network': + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + api_eth => '10.0.0.1', + verbose => true, + debug => true, + use_syslog => true, + dhcp_lease_duration => '10', + log_facility => 'LOG_LOCAL0' }" + end + before :each do + params.merge!(:manage_haproxy_pkg => false) + end + it 'configure neutron lbaas agent without managing haproxy package' do + is_expected.to contain_class('neutron::agents::lbaas').with(:manage_haproxy_package => false) + is_expected.to contain_package('haproxy').with(:ensure => 'present') + end + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack network lbaas' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'openstack network lbaas' + end + +end diff --git a/cloud/spec/classes/cloud_network_metadata_spec.rb b/cloud/spec/classes/cloud_network_metadata_spec.rb new file mode 100644 index 000000000..0c9beeac4 --- /dev/null +++ b/cloud/spec/classes/cloud_network_metadata_spec.rb @@ -0,0 +1,103 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::network::metadata class +# +require 'spec_helper' + +describe 'cloud::network::metadata' do + + shared_examples_for 'openstack network metadata' do + + let :pre_condition do + "class { 'cloud::network': + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + api_eth => '10.0.0.1', + verbose => true, + debug => true, + use_syslog => true, + dhcp_lease_duration => '10', + log_facility => 'LOG_LOCAL0' }" + end + + let :params do + { :debug => true, + :enabled => true, + :neutron_metadata_proxy_shared_secret => 'secrete', + :auth_region => 'MyRegion', + :ks_neutron_password => 'secrete', + :nova_metadata_server => '10.0.0.1', + :ks_keystone_admin_proto => 'http', + :ks_keystone_admin_port => '35357', + :ks_nova_internal_proto => 'https', + :ks_keystone_admin_host => '10.0.0.1' } + end + + it 'configure neutron common' do + is_expected.to contain_class('neutron').with( + :allow_overlapping_ips => true, + :dhcp_agents_per_network => '2', + :verbose => true, + :debug => true, + :log_facility => 'LOG_LOCAL0', + :use_syslog => true, + :rabbit_user => 'neutron', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :bind_host => '10.0.0.1', + :core_plugin => 'neutron.plugins.ml2.plugin.Ml2Plugin', + :service_plugins => ['neutron.services.loadbalancer.plugin.LoadBalancerPlugin','neutron.services.metering.metering_plugin.MeteringPlugin','neutron.services.l3_router.l3_router_plugin.L3RouterPlugin'], + :log_dir => false, + :dhcp_lease_duration => '10', + :report_interval => '30' + ) + end + + it 'configure neutron metadata' do + is_expected.to contain_class('neutron::agents::metadata').with( + :debug => true, + :enabled => true, + :shared_secret => 'secrete', + :metadata_ip => '10.0.0.1', + :auth_url => 'http://10.0.0.1:35357/v2.0', + :auth_password => 'secrete', + :auth_region => 'MyRegion', + :metadata_workers => '8' + ) + is_expected.to contain_neutron_metadata_agent_config('DEFAULT/nova_metadata_protocol').with(:value => 'https') + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian', + :processorcount => '8' } + end + + it_configures 'openstack network metadata' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat', + :processorcount => '8' } + end + + it_configures 'openstack network metadata' + end + +end diff --git a/cloud/spec/classes/cloud_network_vpn_spec.rb b/cloud/spec/classes/cloud_network_vpn_spec.rb new file mode 100644 index 000000000..c3076947d --- /dev/null +++ b/cloud/spec/classes/cloud_network_vpn_spec.rb @@ -0,0 +1,78 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::network::vpnaas class +# +require 'spec_helper' + +describe 'cloud::network::vpn' do + + shared_examples_for 'openstack network vpnaas' do + + let :pre_condition do + "class { 'cloud::network': + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + api_eth => '10.0.0.1', + verbose => true, + debug => true, + use_syslog => true, + dhcp_lease_duration => '10', + log_facility => 'LOG_LOCAL0' }" + end + + it 'configure neutron common' do + is_expected.to contain_class('neutron').with( + :allow_overlapping_ips => true, + :dhcp_agents_per_network => '2', + :verbose => true, + :debug => true, + :log_facility => 'LOG_LOCAL0', + :use_syslog => true, + :rabbit_user => 'neutron', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :bind_host => '10.0.0.1', + :core_plugin => 'neutron.plugins.ml2.plugin.Ml2Plugin', + :service_plugins => ['neutron.services.loadbalancer.plugin.LoadBalancerPlugin','neutron.services.metering.metering_plugin.MeteringPlugin','neutron.services.l3_router.l3_router_plugin.L3RouterPlugin'], + :log_dir => false, + :dhcp_lease_duration => '10', + :report_interval => '30' + ) + end + + it 'configure neutron vpnaas' do + is_expected.to contain_class('neutron::agents::vpnaas') + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack network vpnaas' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'openstack network vpnaas' + end + +end diff --git a/cloud/spec/classes/cloud_network_vswitch_spec.rb b/cloud/spec/classes/cloud_network_vswitch_spec.rb new file mode 100644 index 000000000..362d85026 --- /dev/null +++ b/cloud/spec/classes/cloud_network_vswitch_spec.rb @@ -0,0 +1,171 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::network::vswitch class +# +require 'spec_helper' + +describe 'cloud::network::vswitch' do + + shared_examples_for 'openstack network vswitch' do + + let :pre_condition do + "class { 'cloud::network': + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + api_eth => '10.0.0.1', + verbose => true, + debug => true, + use_syslog => true, + dhcp_lease_duration => '10', + log_facility => 'LOG_LOCAL0' }" + end + + let :params do + { + :tunnel_eth => '10.0.1.1' + } + end + + it 'configure neutron common' do + is_expected.to contain_class('neutron').with( + :allow_overlapping_ips => true, + :dhcp_agents_per_network => '2', + :verbose => true, + :debug => true, + :log_facility => 'LOG_LOCAL0', + :use_syslog => true, + :rabbit_user => 'neutron', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :bind_host => '10.0.0.1', + :core_plugin => 'neutron.plugins.ml2.plugin.Ml2Plugin', + :service_plugins => ['neutron.services.loadbalancer.plugin.LoadBalancerPlugin','neutron.services.metering.metering_plugin.MeteringPlugin','neutron.services.l3_router.l3_router_plugin.L3RouterPlugin'], + :log_dir => false, + :dhcp_lease_duration => '10', + :report_interval => '30' + ) + end + + context 'when running ML2 plugin with OVS driver' do + it 'configure neutron vswitch' do + is_expected.to contain_class('neutron::agents::ml2::ovs').with( + :enable_tunneling => true, + :tunnel_types => ['gre'], + :bridge_mappings => ['public:br-pub'], + :local_ip => '10.0.1.1' + ) + end + end + + context 'when running Cisco N1KV plugin with VEM driver' do + before do + facts.merge!( :osfamily => 'RedHat' ) + params.merge!( + :driver => 'n1kv_vem', + :n1kv_vsm_ip => '10.0.1.1' + ) + end + it 'configure neutron n1kv agent' do + should contain_class('neutron::agents::n1kv_vem').with( + :n1kv_vsm_ip => '10.0.1.1', + :n1kv_vsm_domain_id => '1000', + :host_mgmt_intf => 'eth1', + :node_type => 'compute' + ) + end + it 'ensure cisco VEM package is present' do + should contain_package('nexus1000v').with( + :ensure => 'present' + ) + end + end + + context 'when using provider external network' do + before do + params.merge!( + :manage_ext_network => true, + ) + end + + it 'configure br-pub bridge' do + is_expected.to contain_vs_bridge('br-pub') + end + it 'configure eth1 in br-pub' do + is_expected.to contain_vs_port('eth1').with( + :ensure => 'present', + :bridge => 'br-pub' + ) + end + + end + + context 'with unsupported Neutron driver' do + before :each do + params.merge!(:driver => 'Something') + end + it { should compile.and_raise_error(/Something plugin is not supported./) } + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure gre firewall rules' do + is_expected.to contain_firewall('100 allow gre access').with( + :port => nil, + :proto => 'gre', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure gre firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow gre access').with( + :port => nil, + :proto => 'gre', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack network vswitch' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'openstack network vswitch' + end + +end diff --git a/cloud/spec/classes/cloud_object_ringbuilder_spec.rb b/cloud/spec/classes/cloud_object_ringbuilder_spec.rb new file mode 100644 index 000000000..9356f73a8 --- /dev/null +++ b/cloud/spec/classes/cloud_object_ringbuilder_spec.rb @@ -0,0 +1,58 @@ +require 'spec_helper' + +describe 'cloud::object::ringbuilder' do + + shared_examples_for 'openstack swift ringbuilder' do + + let :params do + { + :rsyncd_ipaddress => '127.0.0.1', + :replicas => 3, + :swift_rsync_max_connections => 5, + :enabled => true + } + end + + it 'create the three rings' do + is_expected.to contain_class('swift::ringbuilder').with({ + 'part_power' => '15', + 'replicas' => '3', + 'min_part_hours' => '24', + }) + end + + it 'create the ring rsync server' do + is_expected.to contain_class('swift::ringserver').with({ + 'local_net_ip' => '127.0.0.1', + 'max_connections' => '5', + }) + end + + context 'when ringbuilder is not enabled' do + before do + params.merge!( + :enabled => false + ) + end + it 'should not configure swift ring builder' do + is_expected.not_to contain_class('swift::ringbuilder') + end + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack swift ringbuilder' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + it_configures 'openstack swift ringbuilder' + end + +end diff --git a/cloud/spec/classes/cloud_object_storage_spec.rb b/cloud/spec/classes/cloud_object_storage_spec.rb new file mode 100644 index 000000000..766e21218 --- /dev/null +++ b/cloud/spec/classes/cloud_object_storage_spec.rb @@ -0,0 +1,180 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::object::storage class +# + +require 'spec_helper' + +describe 'cloud::object::storage' do + + shared_examples_for 'openstack storage configuration' do + let :params do + { :storage_eth => '127.0.0.1', + :swift_zone => 'undef', + :object_port => '6000', + :container_port => '6001', + :account_port => '6002', + :fstype => 'xfs', + :device_config_hash => {'sdc' => {}, 'sdd' => {}}, + :ring_container_device => 'sdb', + :ring_account_device => 'sdb' } + end + + it 'create and configure storage server' do + + is_expected.to contain_class('swift::storage').with({ + 'storage_local_net_ip' => '127.0.0.1', + }) + + is_expected.to contain_swift__storage__server('6000').with({ + 'type' => 'object', + 'config_file_path' => 'object-server.conf', + 'log_facility' => 'LOG_LOCAL6', + 'pipeline' => ['healthcheck', 'recon', 'object-server'], + 'storage_local_net_ip' => '127.0.0.1', + 'replicator_concurrency' => '2', + 'updater_concurrency' => '1', + 'reaper_concurrency' => '1', + 'mount_check' => 'true', + 'require' => 'Class[Swift]', + }) + + is_expected.to contain_swift__storage__server('6001').with({ + 'type' => 'container', + 'config_file_path' => 'container-server.conf', + 'log_facility' => 'LOG_LOCAL4', + 'pipeline' => ['healthcheck', 'container-server'], + 'storage_local_net_ip' => '127.0.0.1', + 'replicator_concurrency' => '2', + 'updater_concurrency' => '1', + 'reaper_concurrency' => '1', + 'mount_check' => 'true', + 'require' => 'Class[Swift]', + }) + + is_expected.to contain_swift__storage__server('6002').with({ + 'type' => 'account', + 'config_file_path' => 'account-server.conf', + 'log_facility' => 'LOG_LOCAL2', + 'pipeline' => ['healthcheck', 'account-server'], + 'storage_local_net_ip' => '127.0.0.1', + 'replicator_concurrency' => '2', + 'updater_concurrency' => '1', + 'reaper_concurrency' => '1', + 'mount_check' => 'true', + 'require' => 'Class[Swift]', + }) + + end + + it 'create and configure the hard drive' do + is_expected.to contain_swift__storage__xfs('sdc') + is_expected.to contain_swift__storage__xfs('sdd') + is_expected.to contain_cloud__object__set_io_scheduler('sdc') + is_expected.to contain_cloud__object__set_io_scheduler('sdd') + end + + ['account', 'container', 'object'].each do |swift_component| + it "configures #{swift_component} filter" do + is_expected.to contain_swift__storage__filter__recon(swift_component) + is_expected.to contain_swift__storage__filter__healthcheck(swift_component) + end + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure swift-storage firewall rules' do + is_expected.to contain_firewall('100 allow swift-container access').with( + :port => '6001', + :proto => 'tcp', + :action => 'accept', + ) + is_expected.to contain_firewall('100 allow swift-account access').with( + :port => '6002', + :proto => 'tcp', + :action => 'accept', + ) + is_expected.to contain_firewall('100 allow swift-object access').with( + :port => '6000', + :proto => 'tcp', + :action => 'accept', + ) + is_expected.to contain_firewall('100 allow swift rsync access').with( + :port => '873', + :proto => 'tcp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure swift-storage firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow swift-container access').with( + :port => '6001', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + is_expected.to contain_firewall('100 allow swift-account access').with( + :port => '6002', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + is_expected.to contain_firewall('100 allow swift-object access').with( + :port => '6000', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + is_expected.to contain_firewall('100 allow swift rsync access').with( + :port => '873', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { + :osfamily => 'Debian' , + } + end + + it_configures 'openstack storage configuration' + end + + context 'on RedHat platforms' do + let :facts do + { + :osfamily => 'RedHat' + } + end + it_configures 'openstack storage configuration' + end +end diff --git a/cloud/spec/classes/cloud_orchestration_api_spec.rb b/cloud/spec/classes/cloud_orchestration_api_spec.rb new file mode 100644 index 000000000..cf6ffa777 --- /dev/null +++ b/cloud/spec/classes/cloud_orchestration_api_spec.rb @@ -0,0 +1,173 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::orchestration::api class +# + +require 'spec_helper' + +describe 'cloud::orchestration::api' do + + shared_examples_for 'openstack orchestration api' do + + let :pre_condition do + "class { 'cloud::orchestration': + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + ks_keystone_internal_host => '10.0.0.1', + ks_keystone_internal_port => '5000', + ks_keystone_internal_proto => 'http', + ks_keystone_admin_host => '10.0.0.1', + ks_keystone_admin_port => '5000', + ks_keystone_admin_proto => 'http', + ks_heat_public_host => '10.0.0.1', + ks_heat_public_proto => 'http', + ks_heat_password => 'secrete', + heat_db_host => '10.0.0.1', + heat_db_user => 'heat', + heat_db_password => 'secrete', + verbose => true, + log_facility => 'LOG_LOCAL0', + use_syslog => true, + debug => true }" + end + + let :params do + { :ks_heat_internal_port => '8004', + :ks_heat_cfn_internal_port => '8000', + :ks_heat_cloudwatch_internal_port => '8003', + :api_eth => '10.0.0.1' } + end + + it 'configure heat common' do + is_expected.to contain_class('heat').with( + :verbose => true, + :debug => true, + :log_facility => 'LOG_LOCAL0', + :use_syslog => true, + :rabbit_userid => 'heat', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :keystone_host => '10.0.0.1', + :keystone_port => '5000', + :keystone_protocol => 'http', + :keystone_password => 'secrete', + :auth_uri => 'http://10.0.0.1:5000/v2.0', + :keystone_ec2_uri => 'http://10.0.0.1:5000/v2.0/ec2tokens', + :sql_connection => 'mysql://heat:secrete@10.0.0.1/heat?charset=utf8', + :log_dir => false + ) + end + + it 'configure heat api' do + is_expected.to contain_class('heat::api').with( + :bind_host => '10.0.0.1', + :bind_port => '8004', + :workers => '8' + ) + is_expected.to contain_class('heat::api_cfn').with( + :bind_host => '10.0.0.1', + :bind_port => '8000', + :workers => '8' + ) + is_expected.to contain_class('heat::api_cloudwatch').with( + :bind_host => '10.0.0.1', + :bind_port => '8003', + :workers => '8' + ) + end + + it 'checks if Heat DB is populated' do + is_expected.to contain_exec('heat_db_sync').with( + :command => 'heat-manage --config-file /etc/heat/heat.conf db_sync', + :user => 'heat', + :path => '/usr/bin', + :unless => '/usr/bin/mysql heat -h 10.0.0.1 -u heat -psecrete -e "show tables" | /bin/grep Tables' + ) + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure heat api firewall rules' do + is_expected.to contain_firewall('100 allow heat-api access').with( + :port => '8004', + :proto => 'tcp', + :action => 'accept', + ) + is_expected.to contain_firewall('100 allow heat-cfn access').with( + :port => '8000', + :proto => 'tcp', + :action => 'accept', + ) + is_expected.to contain_firewall('100 allow heat-cloudwatch access').with( + :port => '8003', + :proto => 'tcp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure heat firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow heat-api access').with( + :port => '8004', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + is_expected.to contain_firewall('100 allow heat-cfn access').with( + :port => '8000', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + is_expected.to contain_firewall('100 allow heat-cloudwatch access').with( + :port => '8003', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian', + :processorcount => '8' } + end + + it_configures 'openstack orchestration api' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat', + :processorcount => '8' } + end + + it_configures 'openstack orchestration api' + end + +end diff --git a/cloud/spec/classes/cloud_orchestration_engine_spec.rb b/cloud/spec/classes/cloud_orchestration_engine_spec.rb new file mode 100644 index 000000000..a33084bf7 --- /dev/null +++ b/cloud/spec/classes/cloud_orchestration_engine_spec.rb @@ -0,0 +1,108 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::orchestration::engine class +# + +require 'spec_helper' + +describe 'cloud::orchestration::engine' do + + shared_examples_for 'openstack orchestration engine' do + + let :pre_condition do + "class { 'cloud::orchestration': + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + ks_keystone_internal_host => '10.0.0.1', + ks_keystone_internal_port => '5000', + ks_keystone_internal_proto => 'http', + ks_keystone_admin_host => '10.0.0.1', + ks_keystone_admin_port => '5000', + ks_keystone_admin_proto => 'http', + ks_heat_public_host => '10.0.0.1', + ks_heat_public_proto => 'http', + ks_heat_password => 'secrete', + heat_db_host => '10.0.0.1', + heat_db_user => 'heat', + heat_db_password => 'secrete', + verbose => true, + log_facility => 'LOG_LOCAL0', + use_syslog => true, + debug => true, + os_endpoint_type => 'internalURL' }" + end + + let :params do + { :enabled => true, + :auth_encryption_key => 'secrete', + :ks_heat_public_host => '10.0.0.1', + :ks_heat_public_proto => 'http', + :ks_heat_cfn_public_port => '8000', + :ks_heat_cloudwatch_public_port => '8003', + :ks_heat_password => 'secrete' } + end + + it 'configure heat common' do + is_expected.to contain_class('heat').with( + :verbose => true, + :debug => true, + :log_facility => 'LOG_LOCAL0', + :use_syslog => true, + :rabbit_userid => 'heat', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :keystone_host => '10.0.0.1', + :keystone_port => '5000', + :keystone_protocol => 'http', + :keystone_password => 'secrete', + :auth_uri => 'http://10.0.0.1:5000/v2.0', + :keystone_ec2_uri => 'http://10.0.0.1:5000/v2.0/ec2tokens', + :sql_connection => 'mysql://heat:secrete@10.0.0.1/heat?charset=utf8', + :log_dir => false + ) + is_expected.to contain_heat_config('clients/endpoint_type').with('value' => 'internalURL') + end + + it 'configure heat engine' do + is_expected.to contain_class('heat::engine').with( + :enabled => true, + :auth_encryption_key => 'secrete', + :heat_metadata_server_url => 'http://10.0.0.1:8000', + :heat_waitcondition_server_url => 'http://10.0.0.1:8000/v1/waitcondition', + :heat_watch_server_url => 'http://10.0.0.1:8003', + :deferred_auth_method => 'password', + ) + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack orchestration engine' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'openstack orchestration engine' + end + +end diff --git a/cloud/spec/classes/cloud_selinux_spec.rb b/cloud/spec/classes/cloud_selinux_spec.rb new file mode 100644 index 000000000..1da314f19 --- /dev/null +++ b/cloud/spec/classes/cloud_selinux_spec.rb @@ -0,0 +1,107 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::cache +# + +require 'spec_helper' + +describe 'cloud::selinux' do + + shared_examples_for 'manage selinux' do + + context 'with selinux disabled' do + before :each do + facts.merge!( :selinux_current_mode => 'enforcing' ) + end + + let :params do + { :mode => 'disabled', + :booleans => ['foo', 'bar'], + :modules => ['module1', 'module2'], + :directory => '/path/to/modules'} + end + + it 'runs setenforce 0' do + is_expected.to contain_exec('setenforce 0') + end + + it 'enables the SELinux boolean' do + is_expected.to contain_selboolean('foo').with( + :persistent => true, + :value => 'on', + ) + end + + it 'enables the SELinux modules' do + is_expected.to contain_selmodule('module1').with( + :ensure => 'present', + :selmoduledir => '/path/to/modules', + ) + end + + end + + context 'with selinux enforcing' do + before :each do + facts.merge!( :selinux => 'false' ) + end + + let :params do + { :mode => 'enforcing', + :booleans => ['foo', 'bar'], + :modules => ['module1', 'module2'], + :directory => '/path/to/modules'} + end + + it 'runs setenforce 1' do + is_expected.to contain_exec('setenforce 1') + end + + it 'enables the SELinux boolean' do + is_expected.to contain_selboolean('foo').with( + :persistent => true, + :value => 'on', + ) + end + + it 'enables the SELinux modules' do + is_expected.to contain_selmodule('module1').with( + :ensure => 'present', + :selmoduledir => '/path/to/modules', + ) + end + + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_raises 'a Puppet::Error', /OS family unsuppored yet \(Debian\), SELinux support is only limited to RedHat family OS/ + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'manage selinux' + end + +end diff --git a/cloud/spec/classes/cloud_spof_spec.rb b/cloud/spec/classes/cloud_spof_spec.rb new file mode 100644 index 000000000..306de31c3 --- /dev/null +++ b/cloud/spec/classes/cloud_spof_spec.rb @@ -0,0 +1,173 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::spof class +# + +require 'spec_helper' + +describe 'cloud::spof' do + + shared_examples_for 'cloud spof' do + + let :params do + { :cluster_ip => '10.0.0.1', + :multicast_address => '239.1.1.2', + :cluster_members => false, + :cluster_password => 'verysecrete' } + end + + context 'with Pacemaker on Debian' do + before :each do + facts.merge!( :osfamily => 'Debian' ) + end + + it 'configure pacemaker/corosync' do + is_expected.to contain_class('corosync').with( + :enable_secauth => false, + :authkey => '/var/lib/puppet/ssl/certs/ca.pem', + :bind_address => '10.0.0.1', + :multicast_address => '239.1.1.2', + ) + is_expected.to contain_file('/usr/lib/ocf/resource.d/heartbeat/ceilometer-agent-central').with( + :source => 'puppet:///modules/cloud/heartbeat/ceilometer-agent-central', + :mode => '0755', + :owner => 'root', + :group => 'root' + ) + is_expected.to contain_class('cloud::telemetry::centralagent').with(:enabled => false) + is_expected.to contain_exec('cleanup_ceilometer_agent_central').with( + :command => 'crm resource cleanup ceilometer-agent-central', + :path => ['/usr/sbin', '/bin'], + :user => 'root', + :unless => 'crm resource show ceilometer-agent-central | grep Started' + ) + end + end + + context 'with Pacemaker on Red-Hat' do + before :each do + facts.merge!( :osfamily => 'RedHat' ) + params.merge!( :cluster_members => 'srv1 srv2 srv3') + end + + it 'configure pacemaker/corosync' do + is_expected.to contain_class('pacemaker').with(:hacluster_pwd => 'verysecrete') + is_expected.to contain_class('pacemaker::stonith').with(:disable => true) + is_expected.to contain_class('pacemaker::corosync').with( + :cluster_name => 'openstack', + :settle_timeout => 10, + :settle_tries => 2, + :settle_try_sleep => 5, + :manage_fw => false, + :cluster_members => 'srv1 srv2 srv3') + is_expected.to contain_file('/usr/lib/ocf/resource.d/heartbeat/ceilometer-agent-central').with( + :source => 'puppet:///modules/cloud/heartbeat/ceilometer-agent-central', + :mode => '0755', + :owner => 'root', + :group => 'root' + ) + is_expected.to contain_exec('pcmk_ceilometer_agent_central').with( + :command => 'pcs resource create ceilometer-agent-central ocf:heartbeat:ceilometer-agent-central', + :path => ['/usr/bin','/usr/sbin','/sbin/','/bin'], + :user => 'root', + :unless => '/usr/sbin/pcs resource | /bin/grep ceilometer-agent-central | /bin/grep Started' + ) + is_expected.to contain_class('cloud::telemetry::centralagent').with(:enabled => false) + end + end + + context 'with Pacemaker on Red-Hat with missing parameters' do + before :each do + facts.merge!( :osfamily => 'RedHat' ) + params.merge!( :cluster_members => false) + end + it { is_expected.to compile.and_raise_error(/cluster_members is a required parameter./) } + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!( :cluster_members => 'srv1 srv2 srv3') + end + it 'configure pacemaker firewall rules' do + is_expected.to contain_firewall('100 allow vrrp access').with( + :port => nil, + :proto => 'vrrp', + :action => 'accept', + ) + is_expected.to contain_firewall('100 allow corosync tcp access').with( + :port => ['2224','3121','21064'], + :action => 'accept', + ) + is_expected.to contain_firewall('100 allow corosync udp access').with( + :port => ['5404','5405'], + :proto => 'udp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!( + :firewall_settings => { 'limit' => '50/sec' }, + :cluster_members => 'srv1 srv2 srv3' + ) + end + it 'configure pacemaker firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow vrrp access').with( + :port => nil, + :proto => 'vrrp', + :action => 'accept', + :limit => '50/sec', + ) + is_expected.to contain_firewall('100 allow corosync tcp access').with( + :port => ['2224','3121','21064'], + :action => 'accept', + :limit => '50/sec', + ) + is_expected.to contain_firewall('100 allow corosync udp access').with( + :port => ['5404','5405'], + :proto => 'udp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'cloud spof' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + it_configures 'cloud spof' + end + +end diff --git a/cloud/spec/classes/cloud_storage_rbd_mon_spec.rb b/cloud/spec/classes/cloud_storage_rbd_mon_spec.rb new file mode 100644 index 000000000..b9aa8a4c4 --- /dev/null +++ b/cloud/spec/classes/cloud_storage_rbd_mon_spec.rb @@ -0,0 +1,102 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::storage::rbd::monitor class +# + +require 'spec_helper' + +describe 'cloud::storage::rbd::monitor' do + + shared_examples_for 'ceph monitor' do + + let :pre_condition do + "class { 'cloud::storage::rbd': + fsid => '123', + cluster_network => '10.0.0.0/24', + public_network => '192.168.0.0/24' }" + end + + let :params do + { :mon_addr => '10.0.0.1', + :monitor_secret => 'secret' } + end + + it 'configure ceph common' do + is_expected.to contain_class('ceph::conf').with( + :fsid => '123', + :auth_type => 'cephx', + :cluster_network => '10.0.0.0/24', + :public_network => '192.168.0.0/24', + :enable_service => true + ) + end + + it 'configure ceph mon' do + is_expected.to contain_ceph__mon('123').with( + :monitor_secret => 'secret', + :mon_port => '6789', + :mon_addr => '10.0.0.1' + ) + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure ceph monitor firewall rules' do + is_expected.to contain_firewall('100 allow ceph-mon access').with( + :port => '6789', + :proto => 'tcp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure ceph monitor firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow ceph-mon access').with( + :port => '6789', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'ceph monitor' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + it_configures 'ceph monitor' + end + +end diff --git a/cloud/spec/classes/cloud_storage_rbd_osd_spec.rb b/cloud/spec/classes/cloud_storage_rbd_osd_spec.rb new file mode 100644 index 000000000..63229940c --- /dev/null +++ b/cloud/spec/classes/cloud_storage_rbd_osd_spec.rb @@ -0,0 +1,110 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::storage::rbd::osd class +# + +require 'spec_helper' + +describe 'cloud::storage::rbd::osd' do + + shared_examples_for 'ceph osd' do + + let :pre_condition do + "class { 'cloud::storage::rbd': + fsid => '123', + cluster_network => '10.0.0.0/24', + public_network => '192.168.0.0/24' }" + end + + let :params do + { :public_address => '10.0.0.1', + :cluster_address => '192.168.0.1' } + end + + it 'configure ceph common' do + is_expected.to contain_class('ceph::conf').with( + :fsid => '123', + :auth_type => 'cephx', + :cluster_network => '10.0.0.0/24', + :public_network => '192.168.0.0/24', + :enable_service => true + ) + end + + it 'configure ceph osd' do + is_expected.to contain_class('ceph::osd').with( + :public_address => '10.0.0.1', + :cluster_address => '192.168.0.1' + ) + end + + context 'without specified journal' do + before :each do + params.merge!( :devices => ['sdb','sdc','sdd'] ) + end + + it 'configure ceph osd with a mixed full-qualified and short device name' do + is_expected.to contain_ceph__osd__device('/dev/sdb','/dev/sdc','sdd') + end + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure ceph osd firewall rules' do + is_expected.to contain_firewall('100 allow ceph-osd access').with( + :port => '6800-6810', + :proto => 'tcp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure ceph osd firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow ceph-osd access').with( + :port => '6800-6810', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + it_configures 'ceph osd' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + it_configures 'ceph osd' + end + +end diff --git a/cloud/spec/classes/cloud_telemetry_alarmevaluator_spec.rb b/cloud/spec/classes/cloud_telemetry_alarmevaluator_spec.rb new file mode 100644 index 000000000..41b775d05 --- /dev/null +++ b/cloud/spec/classes/cloud_telemetry_alarmevaluator_spec.rb @@ -0,0 +1,84 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::telemetry::alarmevaluator class +# + +require 'spec_helper' + +describe 'cloud::telemetry::alarmevaluator' do + + shared_examples_for 'openstack telemetry alarmevaluator' do + + let :pre_condition do + "class { 'cloud::telemetry': + ceilometer_secret => 'secrete', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + ks_keystone_internal_host => '10.0.0.1', + ks_keystone_internal_port => '5000', + ks_keystone_internal_proto => 'http', + ks_ceilometer_password => 'secrete', + region => 'MyRegion', + log_facility => 'LOG_LOCAL0', + use_syslog => true, + verbose => true, + debug => true }" + end + + it 'configure ceilometer common' do + is_expected.to contain_class('ceilometer').with( + :verbose => true, + :debug => true, + :rabbit_userid => 'ceilometer', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :metering_secret => 'secrete', + :use_syslog => true, + :log_facility => 'LOG_LOCAL0', + :log_dir => false + ) + is_expected.to contain_class('ceilometer::agent::auth').with( + :auth_password => 'secrete', + :auth_url => 'http://10.0.0.1:5000/v2.0', + :auth_region => 'MyRegion' + ) + end + + it 'configure ceilometer alarm evaluator' do + is_expected.to contain_class('ceilometer::alarm::evaluator') + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian', + :hostname => 'node1' } + end + + it_configures 'openstack telemetry alarmevaluator' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat', + :hostname => 'node1' } + end + + it_configures 'openstack telemetry alarmevaluator' + end + +end diff --git a/cloud/spec/classes/cloud_telemetry_alarmnotifier_spec.rb b/cloud/spec/classes/cloud_telemetry_alarmnotifier_spec.rb new file mode 100644 index 000000000..caa021cf8 --- /dev/null +++ b/cloud/spec/classes/cloud_telemetry_alarmnotifier_spec.rb @@ -0,0 +1,83 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::telemetry::alarmnotifier class +# + +require 'spec_helper' + +describe 'cloud::telemetry::alarmnotifier' do + + shared_examples_for 'openstack telemetry alarmnotifier' do + + let :pre_condition do + "class { 'cloud::telemetry': + ceilometer_secret => 'secrete', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + ks_keystone_internal_host => '10.0.0.1', + ks_keystone_internal_port => '5000', + ks_keystone_internal_proto => 'http', + ks_ceilometer_password => 'secrete', + region => 'MyRegion', + log_facility => 'LOG_LOCAL0', + use_syslog => true, + verbose => true, + debug => true }" + end + + it 'configure ceilometer common' do + is_expected.to contain_class('ceilometer').with( + :verbose => true, + :debug => true, + :rabbit_userid => 'ceilometer', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :metering_secret => 'secrete', + :use_syslog => true, + :log_facility => 'LOG_LOCAL0', + :log_dir => false + ) + is_expected.to contain_class('ceilometer::agent::auth').with( + :auth_password => 'secrete', + :auth_url => 'http://10.0.0.1:5000/v2.0', + :auth_region => 'MyRegion' + ) + end + + it 'configure ceilometer alarm notifier' do + is_expected.to contain_class('ceilometer::alarm::notifier') + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian', + :hostname => 'node1' } + end + + it_configures 'openstack telemetry alarmnotifier' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat', + :hostname => 'node1' } + end + + it_configures 'openstack telemetry alarmnotifier' + end + +end diff --git a/cloud/spec/classes/cloud_telemetry_api_spec.rb b/cloud/spec/classes/cloud_telemetry_api_spec.rb new file mode 100644 index 000000000..b486ae6d3 --- /dev/null +++ b/cloud/spec/classes/cloud_telemetry_api_spec.rb @@ -0,0 +1,135 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::telemetry::api class +# + +require 'spec_helper' + +describe 'cloud::telemetry::api' do + + shared_examples_for 'openstack telemetry api' do + + let :pre_condition do + "class { 'cloud::telemetry': + ceilometer_secret => 'secrete', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + ks_keystone_internal_host => '10.0.0.1', + ks_keystone_internal_port => '5000', + ks_keystone_internal_proto => 'http', + ks_ceilometer_password => 'secrete', + region => 'MyRegion', + log_facility => 'LOG_LOCAL0', + use_syslog => true, + verbose => true, + debug => true }" + end + + let :params do + { :ks_keystone_internal_host => '127.0.0.1', + :ks_keystone_internal_proto => 'http', + :ks_ceilometer_internal_port => '8777', + :ks_ceilometer_password => 'rabbitpassword', + :api_eth => '127.0.0.1' } + end + + it 'configure ceilometer common' do + is_expected.to contain_class('ceilometer').with( + :verbose => true, + :debug => true, + :rabbit_userid => 'ceilometer', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :metering_secret => 'secrete', + :use_syslog => true, + :log_facility => 'LOG_LOCAL0', + :log_dir => false + ) + is_expected.to contain_class('ceilometer::agent::auth').with( + :auth_password => 'secrete', + :auth_url => 'http://10.0.0.1:5000/v2.0', + :auth_region => 'MyRegion' + ) + end + + it 'configure ceilometer-api' do + is_expected.to contain_class('ceilometer::api').with( + :keystone_password => 'rabbitpassword', + :keystone_host => '127.0.0.1', + :keystone_protocol => 'http', + :host => '127.0.0.1' + ) + end + + it 'configure ceilometer-expirer' do + is_expected.to contain_class('ceilometer::expirer').with( + :time_to_live => '2592000', + :minute => '0', + :hour => '0' + ) + end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure ceilometer firewall rules' do + is_expected.to contain_firewall('100 allow ceilometer-api access').with( + :port => '8777', + :proto => 'tcp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure ceilometer firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow ceilometer-api access').with( + :port => '8777', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian', + :hostname => 'node1' } + end + + it_configures 'openstack telemetry api' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat', + :hostname => 'node1' } + end + + it_configures 'openstack telemetry api' + end + +end diff --git a/cloud/spec/classes/cloud_telemetry_centralagent_spec.rb b/cloud/spec/classes/cloud_telemetry_centralagent_spec.rb new file mode 100644 index 000000000..32659aefd --- /dev/null +++ b/cloud/spec/classes/cloud_telemetry_centralagent_spec.rb @@ -0,0 +1,90 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::telemetry::centralagent class +# + +require 'spec_helper' + +describe 'cloud::telemetry::centralagent' do + + shared_examples_for 'openstack telemetry centralagent' do + + let :pre_condition do + "class { 'cloud::telemetry': + ceilometer_secret => 'secrete', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + ks_keystone_internal_host => '10.0.0.1', + ks_keystone_internal_port => '5000', + ks_keystone_internal_proto => 'http', + ks_ceilometer_password => 'secrete', + region => 'MyRegion', + log_facility => 'LOG_LOCAL0', + use_syslog => true, + verbose => true, + debug => true }" + end + + let :params do + { :enabled => 'true', } + end + + it 'configure ceilometer common' do + is_expected.to contain_class('ceilometer').with( + :verbose => true, + :debug => true, + :rabbit_userid => 'ceilometer', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :metering_secret => 'secrete', + :use_syslog => true, + :log_facility => 'LOG_LOCAL0', + :log_dir => false + ) + is_expected.to contain_class('ceilometer::agent::auth').with( + :auth_password => 'secrete', + :auth_url => 'http://10.0.0.1:5000/v2.0', + :auth_region => 'MyRegion' + ) + end + + it 'configure ceilometer central agent' do + is_expected.to contain_class('ceilometer::agent::central').with({ + 'enabled' => 'true', + }) + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian', + :hostname => 'node1' } + end + + it_configures 'openstack telemetry centralagent' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat', + :hostname => 'node1' } + end + + it_configures 'openstack telemetry centralagent' + end + +end diff --git a/cloud/spec/classes/cloud_telemetry_collector_spec.rb b/cloud/spec/classes/cloud_telemetry_collector_spec.rb new file mode 100644 index 000000000..bb101f9ec --- /dev/null +++ b/cloud/spec/classes/cloud_telemetry_collector_spec.rb @@ -0,0 +1,107 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::telemetry::collector class +# + +require 'spec_helper' + +describe 'cloud::telemetry::collector' do + + shared_examples_for 'openstack telemetry collector' do + + let :pre_condition do + "class { 'cloud::telemetry': + ceilometer_secret => 'secrete', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + ks_keystone_internal_host => '10.0.0.1', + ks_keystone_internal_port => '5000', + ks_keystone_internal_proto => 'http', + ks_ceilometer_password => 'secrete', + region => 'MyRegion', + log_facility => 'LOG_LOCAL0', + use_syslog => true, + verbose => true, + debug => true }" + end + + let :params do + { :mongo_nodes => ['node1', 'node2', 'node3'] } + end + + it 'configure ceilometer common' do + is_expected.to contain_class('ceilometer').with( + :verbose => true, + :debug => true, + :rabbit_userid => 'ceilometer', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :metering_secret => 'secrete', + :use_syslog => true, + :log_facility => 'LOG_LOCAL0', + :log_dir => false + ) + is_expected.to contain_class('ceilometer::agent::auth').with( + :auth_password => 'secrete', + :auth_url => 'http://10.0.0.1:5000/v2.0', + :auth_region => 'MyRegion' + ) + end + + it 'configure ceilometer collector' do + is_expected.to contain_class('ceilometer::collector') + end + + it 'synchronize ceilometer db indexes' do + is_expected.to contain_class('ceilometer::db').with( + :sync_db => true, + :database_connection => 'mongodb://node1,node2,node3/ceilometer?replicaSet=ceilometer' + ) + end + + context 'without replica set' do + before :each do + params.merge!( :replicaset_enabled => false, + :mongo_nodes => ['node1'] ) + end + it 'do not configure mongodb replicasets' do + is_expected.to contain_class('ceilometer::db').with( + :sync_db => true, + :database_connection => 'mongodb://node1/ceilometer' + ) + end + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian', + :hostname => 'node1' } + end + + it_configures 'openstack telemetry collector' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat', + :hostname => 'node1' } + end + + it_configures 'openstack telemetry collector' + end + +end diff --git a/cloud/spec/classes/cloud_telemetry_notification_spec.rb b/cloud/spec/classes/cloud_telemetry_notification_spec.rb new file mode 100644 index 000000000..9fa7fb6d1 --- /dev/null +++ b/cloud/spec/classes/cloud_telemetry_notification_spec.rb @@ -0,0 +1,84 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::telemetry::notification class +# + +require 'spec_helper' + +describe 'cloud::telemetry::notification' do + + shared_examples_for 'openstack telemetry notification' do + + let :pre_condition do + "class { 'cloud::telemetry': + ceilometer_secret => 'secrete', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + ks_keystone_internal_host => '10.0.0.1', + ks_keystone_internal_port => '5000', + ks_keystone_internal_proto => 'http', + ks_ceilometer_password => 'secrete', + region => 'MyRegion', + log_facility => 'LOG_LOCAL0', + use_syslog => true, + verbose => true, + debug => true }" + end + + it 'configure ceilometer common' do + is_expected.to contain_class('ceilometer').with( + :verbose => true, + :debug => true, + :rabbit_userid => 'ceilometer', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :metering_secret => 'secrete', + :use_syslog => true, + :log_facility => 'LOG_LOCAL0', + :log_dir => false + ) + is_expected.to contain_class('ceilometer::agent::auth').with( + :auth_password => 'secrete', + :auth_url => 'http://10.0.0.1:5000/v2.0', + :auth_region => 'MyRegion' + ) + end + + it 'configure ceilometer notification agent' do + is_expected.to contain_class('ceilometer::agent::notification') + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian', + :hostname => 'node1' } + end + + it_configures 'openstack telemetry notification' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat', + :hostname => 'node1' } + end + + it_configures 'openstack telemetry notification' + end + +end diff --git a/cloud/spec/classes/cloud_volume_api_spec.rb b/cloud/spec/classes/cloud_volume_api_spec.rb new file mode 100644 index 000000000..81a731acd --- /dev/null +++ b/cloud/spec/classes/cloud_volume_api_spec.rb @@ -0,0 +1,164 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::volume::api class +# + +require 'spec_helper' + +describe 'cloud::volume::api' do + + shared_examples_for 'openstack volume api' do + + let :pre_condition do + "class { 'cloud::volume': + cinder_db_host => '10.0.0.1', + cinder_db_user => 'cinder', + cinder_db_password => 'secrete', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + verbose => true, + debug => true, + log_facility => 'LOG_LOCAL0', + storage_availability_zone => 'nova', + use_syslog => true, + nova_endpoint_type => 'internalURL' }" + end + + let :params do + { :ks_cinder_password => 'secrete', + :ks_cinder_internal_port => '8776', + :ks_keystone_internal_host => '10.0.0.1', + :ks_keystone_internal_proto => 'https', + :ks_glance_internal_host => '10.0.0.2', + :ks_glance_api_internal_port => '9292', + :default_volume_type => 'ceph', + # TODO(EmilienM) Disabled for now: http://git.io/kfTmcA + #:backup_ceph_user => 'cinder', + #:backup_ceph_pool => 'ceph_backup_cinder', + :api_eth => '10.0.0.1' } + end + + it 'configure cinder common' do + is_expected.to contain_class('cinder').with( + :verbose => true, + :debug => true, + :rabbit_userid => 'cinder', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :log_facility => 'LOG_LOCAL0', + :use_syslog => true, + :log_dir => false, + :storage_availability_zone => 'nova' + ) + is_expected.to contain_class('cinder::ceilometer') + is_expected.to contain_cinder_config('DEFAULT/nova_catalog_info').with('value' => 'compute:nova:internalURL') + end + + it 'checks if Cinder DB is populated' do + is_expected.to contain_exec('cinder_db_sync').with( + :command => 'cinder-manage db sync', + :user => 'cinder', + :path => '/usr/bin', + :unless => '/usr/bin/mysql cinder -h 10.0.0.1 -u cinder -psecrete -e "show tables" | /bin/grep Tables' + ) + end + + it 'configure cinder glance backend' do + is_expected.to contain_class('cinder::glance').with( + :glance_api_servers => 'http://10.0.0.2:9292', + :glance_request_timeout => '10', + :glance_num_retries => '10' + ) + end + + it 'configure cinder api' do + is_expected.to contain_class('cinder::api').with( + :keystone_password => 'secrete', + :keystone_auth_host => '10.0.0.1', + :keystone_auth_protocol => 'https', + :bind_host => '10.0.0.1', + :default_volume_type => 'ceph', + ) + end + + context 'without default volume type' do + before :each do + params.delete(:default_volume_type) + end + it 'should raise an error and fail' do + is_expected.not_to compile + end + end + + # TODO(EmilienM) Disabled for now: http://git.io/kfTmcA + #it 'configure cinder backup using ceph backend' do + # should contain_class('cinder::backup') + # should contain_class('cinder::backup::ceph').with( + # :backup_ceph_user => 'cinder', + # :backup_ceph_pool => 'ceph_backup_cinder' + # ) + #end + + context 'with default firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + it 'configure cinder firewall rules' do + is_expected.to contain_firewall('100 allow cinder-api access').with( + :port => '8776', + :proto => 'tcp', + :action => 'accept', + ) + end + end + + context 'with custom firewall enabled' do + let :pre_condition do + "class { 'cloud': manage_firewall => true }" + end + before :each do + params.merge!(:firewall_settings => { 'limit' => '50/sec' } ) + end + it 'configure cinder firewall rules with custom parameter' do + is_expected.to contain_firewall('100 allow cinder-api access').with( + :port => '8776', + :proto => 'tcp', + :action => 'accept', + :limit => '50/sec', + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack volume api' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'openstack volume api' + end + +end diff --git a/cloud/spec/classes/cloud_volume_scheduler_spec.rb b/cloud/spec/classes/cloud_volume_scheduler_spec.rb new file mode 100644 index 000000000..fcf4b9974 --- /dev/null +++ b/cloud/spec/classes/cloud_volume_scheduler_spec.rb @@ -0,0 +1,94 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::volume::controller class +# + +require 'spec_helper' + +describe 'cloud::volume::scheduler' do + + shared_examples_for 'openstack volume scheduler' do + + let :pre_condition do + "class { 'cloud::volume': + cinder_db_host => '10.0.0.1', + cinder_db_user => 'cinder', + cinder_db_password => 'secrete', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secrete', + verbose => true, + debug => true, + log_facility => 'LOG_LOCAL0', + storage_availability_zone => 'nova', + use_syslog => true, + nova_endpoint_type => 'internalURL' }" + end + + let :params do + {} + end + + it 'configure cinder common' do + is_expected.to contain_class('cinder').with( + :verbose => true, + :debug => true, + :rabbit_userid => 'cinder', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secrete', + :rabbit_virtual_host => '/', + :log_facility => 'LOG_LOCAL0', + :use_syslog => true, + :log_dir => false, + :storage_availability_zone => 'nova' + ) + is_expected.to contain_class('cinder::ceilometer') + is_expected.to contain_cinder_config('DEFAULT/nova_catalog_info').with('value' => 'compute:nova:internalURL') + end + + it 'checks if Cinder DB is populated' do + is_expected.to contain_exec('cinder_db_sync').with( + :command => 'cinder-manage db sync', + :user => 'cinder', + :path => '/usr/bin', + :unless => '/usr/bin/mysql cinder -h 10.0.0.1 -u cinder -psecrete -e "show tables" | /bin/grep Tables' + ) + end + + it 'configure cinder scheduler' do + is_expected.to contain_class('cinder::scheduler').with( + :scheduler_driver => 'cinder.scheduler.filter_scheduler.FilterScheduler' + ) + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack volume scheduler' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'openstack volume scheduler' + end + +end diff --git a/cloud/spec/classes/cloud_volume_storage_spec.rb b/cloud/spec/classes/cloud_volume_storage_spec.rb new file mode 100644 index 000000000..f2c1d77e7 --- /dev/null +++ b/cloud/spec/classes/cloud_volume_storage_spec.rb @@ -0,0 +1,323 @@ +# +# Copyright (C) 2014 eNovance SAS +# +# 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. +# +# Unit tests for cloud::volume::storage class +# + +require 'spec_helper' + +describe 'cloud::volume::storage' do + + shared_examples_for 'openstack volume storage' do + + let :pre_condition do + "class { 'cloud::volume': + cinder_db_host => '10.0.0.1', + cinder_db_user => 'cinder', + cinder_db_password => 'secret', + rabbit_hosts => ['10.0.0.1'], + rabbit_password => 'secret', + verbose => true, + debug => true, + log_facility => 'LOG_LOCAL0', + storage_availability_zone => 'nova', + use_syslog => true }" + end + + let :params do + { :cinder_rbd_pool => 'ceph_cinder', + :cinder_rbd_user => 'cinder', + :cinder_rbd_secret_uuid => 'secret', + :cinder_rbd_max_clone_depth => '10', + :cinder_backends => { + 'rbd' => { + 'lowcost' => { + 'rbd_pool' => 'ceph_cinder', + 'rbd_user' => 'cinder', + 'rbd_secret_uuid' => 'secret' + } + }, + 'netapp' => { + 'premium' => { + 'netapp_server_hostname' => 'netapp-server.host', + 'netapp_login' => 'joe', + 'netapp_password' => 'secret' + } + }, + 'iscsi' => { + 'fast' => { + 'iscsi_ip_address' => '10.0.0.1', + 'volume_group' => 'fast-vol' + } + }, + 'emc_vnx' => { + 'very-fast' => { + 'iscsi_ip_address' => '10.0.0.1', + 'san_ip' => '10.0.0.2', + 'san_password' => 'secrete', + 'storage_vnx_pool_name' => 'emc-volumes', + } + }, + 'eqlx' => { + 'dell' => { + 'san_ip' => '10.0.0.1', + 'san_login' => 'admin', + 'san_password' => 'secrete', + 'eqlx_group_name' => 'dell-volumes', + } + }, + 'glusterfs' => { + 'gluster' => { + 'glusterfs_shares' => ['/mnt/share'], + 'glusterfs_shares_config' => '/etc/cinder/shares-gluster.conf', + } + }, + 'nfs' => { + 'freenas' => { + 'nfs_servers' => ['10.0.0.1:/myshare'], + 'nfs_mount_options' => 'defaults', + 'nfs_disk_util' => 'df', + 'nfs_mount_point_base' => '/mnt/shares', + 'nfs_shares_config' => '/etc/cinder/shares.conf', + 'nfs_used_ratio' => '0.6', + 'nfs_oversub_ratio' => '1.0' + } + } + }, + :ks_keystone_internal_proto => 'http', + :ks_keystone_internal_port => '5000', + :ks_keystone_internal_host => 'keystone.host', + :ks_cinder_password => 'secret' } + end + + it 'configure cinder common' do + is_expected.to contain_class('cinder').with( + :verbose => true, + :debug => true, + :rabbit_userid => 'cinder', + :rabbit_hosts => ['10.0.0.1'], + :rabbit_password => 'secret', + :rabbit_virtual_host => '/', + :log_facility => 'LOG_LOCAL0', + :use_syslog => true, + :log_dir => false, + :storage_availability_zone => 'nova' + ) + + is_expected.to contain_cinder_config('DEFAULT/notification_driver').with('value' => 'cinder.openstack.common.notifier.rpc_notifier') + + end + + it 'checks if Cinder DB is populated' do + is_expected.to contain_exec('cinder_db_sync').with( + :command => 'cinder-manage db sync', + :user => 'cinder', + :path => '/usr/bin', + :unless => '/usr/bin/mysql cinder -h 10.0.0.1 -u cinder -psecret -e "show tables" | /bin/grep Tables' + ) + end + + it 'configure cinder volume service' do + is_expected.to contain_class('cinder::volume') + end + + context 'with RBD backend' do + it 'configures rbd volume driver' do + is_expected.to contain_cinder_config('lowcost/volume_backend_name').with_value('lowcost') + is_expected.to contain_cinder_config('lowcost/rbd_pool').with_value('ceph_cinder') + is_expected.to contain_cinder_config('lowcost/rbd_user').with_value('cinder') + is_expected.to contain_cinder_config('lowcost/rbd_secret_uuid').with_value('secret') + is_expected.to contain_cinder_config('lowcost/volume_tmp_dir').with_value('/tmp') + is_expected.to contain_cinder__type('lowcost').with( + :set_key => 'volume_backend_name', + :set_value => 'lowcost', + :os_tenant_name => 'services', + :os_username => 'cinder', + :os_password => 'secret', + :os_auth_url => 'http://keystone.host:5000/v2.0' + ) + is_expected.to contain_group('cephkeyring').with(:ensure => 'present') + is_expected.to contain_exec('add-cinder-to-group').with( + :command => 'usermod -a -G cephkeyring cinder', + :path => ['/usr/sbin', '/usr/bin', '/bin', '/sbin'], + :unless => 'groups cinder | grep cephkeyring' + ) + end + end + + context 'with NetApp backend' do + it 'configures netapp volume driver' do + is_expected.to contain_cinder_config('premium/volume_backend_name').with_value('premium') + is_expected.to contain_cinder_config('premium/netapp_login').with_value('joe') + is_expected.to contain_cinder_config('premium/netapp_password').with_value('secret') + is_expected.to contain_cinder_config('premium/netapp_server_hostname').with_value('netapp-server.host') + is_expected.to contain_cinder__type('premium').with( + :set_key => 'volume_backend_name', + :set_value => 'premium', + :notify => 'Service[cinder-volume]' + ) + end + end + + context 'with iSCSI backend' do + it 'configures iSCSI volume driver' do + is_expected.to contain_cinder_config('fast/volume_backend_name').with_value('fast') + is_expected.to contain_cinder_config('fast/iscsi_ip_address').with_value('10.0.0.1') + is_expected.to contain_cinder_config('fast/volume_group').with_value('fast-vol') + is_expected.to contain_cinder__type('fast').with( + :set_key => 'volume_backend_name', + :set_value => 'fast', + :notify => 'Service[cinder-volume]' + ) + end + end + + context 'with EMC VNX backend' do + it 'configures EMC VNX volume driver' do + should contain_cinder_config('very-fast/volume_backend_name').with_value('very-fast') + should contain_cinder_config('very-fast/iscsi_ip_address').with_value('10.0.0.1') + should contain_cinder_config('very-fast/san_ip').with_value('10.0.0.2') + should contain_cinder_config('very-fast/san_password').with_value('secrete') + should contain_cinder_config('very-fast/storage_vnx_pool_name').with_value('emc-volumes') + should contain_cinder__type('very-fast').with( + :set_key => 'volume_backend_name', + :set_value => 'very-fast', + :notify => 'Service[cinder-volume]' + ) + end + end + + context 'with EQLX backend' do + it 'configures EQLX volume driver' do + should contain_cinder_config('dell/volume_backend_name').with_value('dell') + should contain_cinder_config('dell/san_ip').with_value('10.0.0.1') + should contain_cinder_config('dell/san_login').with_value('admin') + should contain_cinder_config('dell/san_password').with_value('secrete') + should contain_cinder_config('dell/eqlx_group_name').with_value('dell-volumes') + should contain_cinder__type('dell').with( + :set_key => 'volume_backend_name', + :set_value => 'dell', + :notify => 'Service[cinder-volume]' + ) + end + end + + context 'with GlusterFS backend' do + it 'configures GlusterFS volume driver' do + should contain_cinder_config('gluster/volume_backend_name').with_value('gluster') + should contain_cinder_config('gluster/glusterfs_shares_config').with_value('/etc/cinder/shares-gluster.conf') + should contain_cinder__type('gluster').with( + :set_key => 'volume_backend_name', + :set_value => 'gluster', + :notify => 'Service[cinder-volume]' + ) + end + end + + context 'with NFS backend' do + it 'configures NFS volume driver' do + is_expected.to contain_cinder_config('freenas/volume_backend_name').with_value('freenas') + is_expected.to contain_cinder_config('freenas/nfs_mount_options').with_value('defaults') + is_expected.to contain_cinder_config('freenas/nfs_mount_point_base').with_value('/mnt/shares') + is_expected.to contain_cinder_config('freenas/nfs_disk_util').with_value('df') + is_expected.to contain_cinder_config('freenas/nfs_shares_config').with_value('/etc/cinder/shares.conf') + is_expected.to contain_cinder_config('freenas/nfs_used_ratio').with_value('0.6') + is_expected.to contain_cinder_config('freenas/nfs_oversub_ratio').with_value('1.0') + is_expected.to contain_cinder__type('freenas').with( + :set_key => 'volume_backend_name', + :set_value => 'freenas', + :notify => 'Service[cinder-volume]' + ) + should contain_file('/etc/cinder/shares.conf').with_content(/^10.0.0.1:\/myshare$/) + end + end + + context 'with two RBD backends' do + before :each do + params.merge!( + :cinder_backends => { + 'rbd' => { + 'lowcost' => { + 'rbd_pool' => 'low', + 'rbd_user' => 'cinder', + 'rbd_secret_uuid' => 'secret', + }, + 'normal' => { + 'rbd_pool' => 'normal', + 'rbd_user' => 'cinder', + 'rbd_secret_uuid' => 'secret', + } + } + } + ) + end + + it 'configures two rbd volume backends' do + is_expected.to contain_cinder_config('lowcost/volume_backend_name').with_value('lowcost') + is_expected.to contain_cinder_config('lowcost/rbd_pool').with_value('low') + is_expected.to contain_cinder_config('lowcost/rbd_user').with_value('cinder') + is_expected.to contain_cinder_config('lowcost/rbd_secret_uuid').with_value('secret') + is_expected.to contain_cinder_config('lowcost/volume_tmp_dir').with_value('/tmp') + is_expected.to contain_cinder__type('lowcost').with( + :set_key => 'volume_backend_name', + :set_value => 'lowcost', + :os_tenant_name => 'services', + :os_username => 'cinder', + :os_password => 'secret', + :os_auth_url => 'http://keystone.host:5000/v2.0' + ) + is_expected.to contain_cinder_config('normal/volume_backend_name').with_value('normal') + is_expected.to contain_cinder_config('normal/rbd_pool').with_value('normal') + is_expected.to contain_cinder_config('normal/rbd_user').with_value('cinder') + is_expected.to contain_cinder_config('normal/rbd_secret_uuid').with_value('secret') + is_expected.to contain_cinder_config('normal/volume_tmp_dir').with_value('/tmp') + is_expected.to contain_cinder__type('normal').with( + :set_key => 'volume_backend_name', + :set_value => 'normal', + :os_tenant_name => 'services', + :os_username => 'cinder', + :os_password => 'secret', + :os_auth_url => 'http://keystone.host:5000/v2.0' + ) + end + end + + context 'with all backends enabled' do + it 'configure all cinder backends' do + is_expected.to contain_class('cinder::backends').with( + :enabled_backends => ['lowcost', 'premium', 'fast', 'very-fast', 'dell', 'freenas', 'gluster'] + ) + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'openstack volume storage' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'openstack volume storage' + end + +end diff --git a/cloud/spec/classes/coverage_spec.rb b/cloud/spec/classes/coverage_spec.rb new file mode 100644 index 000000000..fbe09652b --- /dev/null +++ b/cloud/spec/classes/coverage_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +if ENV['COV'] + at_exit { RSpec::Puppet::Coverage.report! } +end diff --git a/cloud/spec/shared_examples.rb b/cloud/spec/shared_examples.rb new file mode 100644 index 000000000..fec0eacc9 --- /dev/null +++ b/cloud/spec/shared_examples.rb @@ -0,0 +1,5 @@ +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 diff --git a/cloud/spec/spec_helper.rb b/cloud/spec/spec_helper.rb new file mode 100644 index 000000000..0171d5dd0 --- /dev/null +++ b/cloud/spec/spec_helper.rb @@ -0,0 +1,16 @@ +require 'puppetlabs_spec_helper/module_spec_helper' +require 'shared_examples' + +RSpec.configure do |c| + c.alias_it_should_behave_like_to :it_configures, 'configures' + c.alias_it_should_behave_like_to :it_raises, 'raises' + + c.default_facts = { + :kernel => 'Linux', + :concat_basedir => '/var/lib/puppet/concat', + :memorysize => '1000 MB', + :processorcount => '1', + :puppetversion => '3.7.3', + :uniqueid => '123' + } +end diff --git a/cloud/templates/database/clustercheck.erb b/cloud/templates/database/clustercheck.erb new file mode 100644 index 000000000..5336bac60 --- /dev/null +++ b/cloud/templates/database/clustercheck.erb @@ -0,0 +1,57 @@ +#!/bin/bash +# Managed by puppet +# Module cloud +# +# Script to make a proxy (ie HAProxy) capable of monitoring Percona XtraDB Cluster nodes properly +# +# Author: Olaf van Zandwijk +# Mehdi Abaakouk +# +# Documentation and download: https://github.com/olafz/percona-clustercheck +# +# Based on the original script from Unai Rodriguez +# +MYSQL_USERNAME='<%= @galera_clustercheck_dbuser %>' +MYSQL_PASSWORD='<%= @galera_clustercheck_dbpassword %>' + +TIMEOUT=10 +ERR_FILE="/dev/null" +AVAILABLE_WHEN_DONOR=0 + +MYSQL_CMDLINE="mysql -nNE --connect-timeout=$TIMEOUT --user=${MYSQL_USERNAME} --password=${MYSQL_PASSWORD} " + +mysql_get_status(){ + ( $MYSQL_CMDLINE -e "SHOW STATUS LIKE '$1';" | tail -1 ) 2>>${ERR_FILE} +} +mysql_get_var(){ + ( $MYSQL_CMDLINE -e "SHOW GLOBAL VARIABLES LIKE '$1';" | tail -1 ) 2>>${ERR_FILE} +} + +http_response(){ + status=$1 + shift + msg="$@" + if [ "$status" == 200 ]; then + /bin/echo -en "HTTP/1.1 200 OK\r\n" + else + /bin/echo -en "HTTP/1.1 503 Service Unavailable\r\n" + fi + /bin/echo -en "Content-Type: text/plain\r\n" + /bin/echo -en "\r\n" + /bin/echo -en "$msg\r\n" + /bin/echo -en "\r\n" +} + + +WSREP_LOCAL_STATE=$(mysql_get_status wsrep_local_state) +WSREP_READY=$(mysql_get_status wsrep_ready) +WSREP_CONNECTED=$(mysql_get_status wsrep_connected) +READY_ONLY=$(mysql_get_var read_only) + +case ${AVAILABLE_WHEN_DONOR}-${WSREP_LOCAL_STATE}-${WSREP_READY}-${WSREP_CONNECTED}-${READY_ONLY} in + 1-2-ON-ON-OFF|0-4-ON-ON-OFF) http_response 200 "Mariadb Cluster Node is synced, ready and connected." ;; + *-*-OFF-*-*) http_response 503 "Mariadb Cluster Node is not ready." ;; + *-*-*-OFF-*) http_response 503 "Mariadb Cluster Node is not connected" ;; + *-*-*-*-ON) http_response 503 "Mariadb Cluster Node is readonly" ;; + *) http_response 503 "Mariadb Cluster Node is not synced" ;; +esac diff --git a/cloud/templates/database/debian.cnf.erb b/cloud/templates/database/debian.cnf.erb new file mode 100644 index 000000000..11d258aa4 --- /dev/null +++ b/cloud/templates/database/debian.cnf.erb @@ -0,0 +1,14 @@ +# Managed by Puppet +# Module cloud::database::sql +# +[client] +host = localhost +user = debian-sys-maint +password = <%= @mysql_sys_maint_password %> +socket = /var/run/mysqld/mysqld.sock +[mysql_upgrade] +host = localhost +user = debian-sys-maint +password = <%= @mysql_sys_maint_password %> +socket = /var/run/mysqld/mysqld.sock +basedir = /usr diff --git a/cloud/templates/database/etc_initd_mysql_Debian b/cloud/templates/database/etc_initd_mysql_Debian new file mode 100755 index 000000000..3b4c3c6ab --- /dev/null +++ b/cloud/templates/database/etc_initd_mysql_Debian @@ -0,0 +1,200 @@ +#!/bin/bash +# +### BEGIN INIT INFO +# Provides: mysql-bootstrap +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Should-Start: $network $named $time +# Should-Stop: $network $named $time +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Start and stop the mysql database server daemon +# Description: Controls the main MariaDB database server daemon "mysqld" +# and its wrapper script "mysqld_safe". +### END INIT INFO +# +MYSQLD_STARTUP_TIMEOUT=${MYSQLD_STARTUP_TIMEOUT:-60} +[ -e /etc/mysql/my.cnf ] && \ + MYSQLD_DATA_DIR=$(awk -F= '/^datadir/{print $2}' /etc/mysql/my.cnf | sed -e 's/^ *//') +MYSQLD_DATA_DIR=${MYSQLD_DATA_DIR:-<%= scope.lookupvar('::mysql::datadir') %>} +set -e +set -u +${DEBIAN_SCRIPT_DEBUG:+ set -v -x} + +test -x /usr/sbin/mysqld || exit 0 + +. /lib/lsb/init-functions + +SELF=$(cd $(dirname $0); pwd -P)/$(basename $0) +CONF=/etc/mysql/my.cnf +MYADMIN="/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf" + +# priority can be overriden and "-s" adds output to stderr +ERR_LOGGER="logger -p daemon.err -t /etc/init.d/mysql -i" + +# Safeguard (relative paths, core dumps..) +cd / +umask 077 + +# mysqladmin likes to read /root/.my.cnf. This is usually not what I want +# as many admins e.g. only store a password without a username there and +# so break my scripts. +export HOME=/etc/mysql/ + +## Fetch a particular option from mysql's invocation. +# +# Usage: void mysqld_get_param option +mysqld_get_param() { + /usr/sbin/mysqld --print-defaults \ + | tr " " "\n" \ + | grep -- "--$1" \ + | tail -n 1 \ + | cut -d= -f2 +} + +## Do some sanity checks before even trying to start mysqld. +sanity_checks() { + # check for config file + if [ ! -r /etc/mysql/my.cnf ]; then + log_warning_msg "$0: WARNING: /etc/mysql/my.cnf cannot be read. See README.Debian.gz" + echo "WARNING: /etc/mysql/my.cnf cannot be read. See README.Debian.gz" | $ERR_LOGGER + fi + + # check for diskspace shortage + datadir=`mysqld_get_param datadir` + if LC_ALL=C BLOCKSIZE= df --portability $datadir/. | tail -n 1 | awk '{ exit ($4>4096) }'; then + log_failure_msg "$0: ERROR: The partition with $datadir is too full!" + echo "ERROR: The partition with $datadir is too full!" | $ERR_LOGGER + exit 1 + fi +} + +## Checks if there is a server running and if so if it is accessible. +# +# check_alive insists on a pingable server +# check_dead also fails if there is a lost mysqld in the process list +# +# Usage: boolean mysqld_status [check_alive|check_dead] [warn|nowarn] +mysqld_status () { + ping_output=`$MYADMIN ping 2>&1`; ping_alive=$(( ! $? )) + + ps_alive=0 + pidfile=`mysqld_get_param pid-file` + if [ -f "$pidfile" ] && ps `cat $pidfile` >/dev/null 2>&1; then ps_alive=1; fi + + if [ "$1" = "check_alive" -a $ping_alive = 1 ] || + [ "$1" = "check_dead" -a $ping_alive = 0 -a $ps_alive = 0 ]; then + return 0 # EXIT_SUCCESS + else + if [ "$2" = "warn" ]; then + echo -e "$ps_alive processes alive and '$MYADMIN ping' resulted in\n$ping_output\n" | $ERR_LOGGER -p daemon.debug + fi + return 1 # EXIT_FAILURE + fi +} + +# +# main() +# + +case "${1:-''}" in + 'start') + sanity_checks; + # Start daemon + log_daemon_msg "Starting MariaDB database server" "mysqld" + if mysqld_status check_alive nowarn; then + log_progress_msg "already running" + log_end_msg 0 + else + # Could be removed during boot + test -e /var/run/mysqld || install -m 755 -o mysql -g root -d /var/run/mysqld + + # Start MariaDB! in a Galera setup we want to use + # new-cluster only when the galera cluster hasn't been + # bootstraped + if [ -e ${MYSQLD_DATA_DIR}/grastate.dat ]; then + # normal boot + /usr/bin/mysqld_safe "${@:2}" > /dev/null 2>&1 & + else + # bootstrap boot + log_progress_msg " (Galera bootstrap) " + /usr/bin/mysqld_safe "${@:2}" --wsrep-new-cluster > /dev/null 2>&1 & + fi + + # 6s was reported in #352070 to be too few when using ndbcluster + for i in $(seq 1 "${MYSQLD_STARTUP_TIMEOUT:-30}"); do + sleep 1 + if mysqld_status check_alive nowarn ; then break; fi + log_progress_msg "." + done + if mysqld_status check_alive warn; then + log_end_msg 0 + # Now start mysqlcheck or whatever the admin wants. + output=$(/etc/mysql/debian-start) + [ -n "$output" ] && log_action_msg "$output" + else + log_end_msg 1 + log_failure_msg "Please take a look at the syslog" + fi + fi + ;; + + 'stop') + # * As a passwordless mysqladmin (e.g. via ~/.my.cnf) must be possible + # at least for cron, we can rely on it here, too. (although we have + # to specify it explicit as e.g. sudo environments points to the normal + # users home and not /root) + log_daemon_msg "Stopping MariaDB database server" "mysqld" + if ! mysqld_status check_dead nowarn; then + set +e + shutdown_out=`$MYADMIN shutdown 2>&1`; r=$? + set -e + if [ "$r" -ne 0 ]; then + log_end_msg 1 + [ "$VERBOSE" != "no" ] && log_failure_msg "Error: $shutdown_out" + log_daemon_msg "Killing MariaDB database server by signal" "mysqld" + killall -15 mysqld + server_down= + for i in `seq 1 600`; do + sleep 1 + if mysqld_status check_dead nowarn; then server_down=1; break; fi + done + if test -z "$server_down"; then killall -9 mysqld; fi + fi + fi + + if ! mysqld_status check_dead warn; then + log_end_msg 1 + log_failure_msg "Please stop MariaDB manually and read /usr/share/doc/mariadb-server-5.5/README.Debian.gz!" + exit -1 + else + log_end_msg 0 + fi + ;; + + 'restart') + set +e; $SELF stop; set -e + $SELF start + ;; + + 'reload'|'force-reload') + log_daemon_msg "Reloading MariaDB database server" "mysqld" + $MYADMIN reload + log_end_msg 0 + ;; + + 'status') + if mysqld_status check_alive nowarn; then + log_action_msg "$($MYADMIN version)" + else + log_action_msg "MariaDB is stopped." + exit 3 + fi + ;; + + *) + echo "Usage: $SELF start|stop|restart|reload|force-reload|status" + exit 1 + ;; +esac + diff --git a/cloud/templates/database/etc_initd_mysql_RedHat b/cloud/templates/database/etc_initd_mysql_RedHat new file mode 100755 index 000000000..c81996a93 --- /dev/null +++ b/cloud/templates/database/etc_initd_mysql_RedHat @@ -0,0 +1,45 @@ +# It's not recommended to modify this file in-place, because it will be +# overwritten during package upgrades. If you want to customize, the +# best way is to create a file "/etc/systemd/system/mariadb.service", +# containing +# .include /lib/systemd/system/mariadb.service +# ...make your changes here... +# or create a file "/etc/systemd/system/mariadb.service.d/foo.conf", +# which doesn't need to include ".include" call and which will be parsed +# after the file mariadb.service itself is parsed. +# +# For more info about custom unit files, see systemd.unit(5) or +# http://fedoraproject.org/wiki/Systemd#How_do_I_customize_a_unit_file.2F_add_a_custom_unit_file.3F +# For example, if you want to increase mysql's open-files-limit to 10000, +# you need to increase systemd's LimitNOFILE setting, so create a file named +# "/etc/systemd/system/mariadb.service.d/limits.conf" containing: +# [Service] +# LimitNOFILE=10000 +# Note: /usr/lib/... is recommended in the .include line though /lib/... +# still works. +# Don't forget to reload systemd daemon after you change unit configuration: +# root> systemctl --system daemon-reload + +[Unit] +Description=MariaDB database server +After=syslog.target +After=network.target + +[Service] +Type=simple +User=mysql +Group=mysql +ExecStartPre=/usr/libexec/mariadb-prepare-db-dir %n +# Note: we set --basedir to prevent probes that might trigger SELinux alarms, +# per bug #547485 +ExecStart=/usr/bin/mysqld_safe --wsrep-new-cluster --basedir=/usr +ExecStartPost=/usr/libexec/mariadb-wait-ready $MAINPID + +# Give a reasonable amount of time for the server to start up/shut down +TimeoutSec=300 + +# Place temp files in a secure directory, not /tmp +PrivateTmp=true + +[Install] +WantedBy=multi-user.target diff --git a/cloud/templates/database/mysql.conf.erb b/cloud/templates/database/mysql.conf.erb new file mode 100644 index 000000000..3dbee4545 --- /dev/null +++ b/cloud/templates/database/mysql.conf.erb @@ -0,0 +1,70 @@ +# MANAGED BY PUPPET +# Module:: cloud::database::sql +# +[mysqld] +bind-address = <%= @api_eth %> +default-storage-engine = innodb +collation-server = utf8_general_ci +init-connect = 'SET NAMES utf8' +character-set-server = utf8 +max_connections = 1000 +skip_name_resolve = 1 +connect_timeout = 5 +wait_timeout = 600 +max_allowed_packet = 64M +thread_cache_size = 128 +sort_buffer_size = 4M +bulk_insert_buffer_size = 16M +tmp_table_size = 512M +max_heap_table_size = 128M +query_cache_type = 0 +myisam_recover = BACKUP +key_buffer_size = 16M +open-files-limit = 65535 +table_open_cache = 1024 +table_definition_cache = 500 +myisam_sort_buffer_size = 512M +concurrent_insert = 2 +read_buffer_size = 2M +read_rnd_buffer_size = 1M +slow_query_log = 1 +slow_query_log_file = /var/log/mysql/slow.log +log_error = /var/log/mysql/error.log +long_query_time = 1 +log_slow_verbosity = query_plan +innodb_buffer_pool_size = 512M +innodb_flush_log_at_trx_commit = 1 +innodb_lock_wait_timeout = 50 +innodb_thread_concurrency = 48 +innodb_file_per_table = 1 +innodb_open_files = 65535 +innodb_io_capacity = 1000 +innodb_file_format = Barracuda +innodb_file_format_max = Barracuda +innodb_max_dirty_pages_pct = 50 +binlog_format = ROW +innodb_autoinc_lock_mode = 2 +innodb_locks_unsafe_for_binlog = 1 +wsrep_provider = "<%= @wsrep_provider %>" +wsrep_cluster_name = "galera_cluster" +wsrep_cluster_address = "gcomm://<%= @gcomm_definition %>" +wsrep_sst_auth = root:<%= @mysql_root_password %> +wsrep_drupal_282555_workaround = 0 +wsrep_sst_method = rsync +wsrep_node_address = "<%= @api_eth %>" +wsrep_node_incoming_address = "<%= @api_eth %>" +# This is the minimal value (proc*2) +wsrep_slave_threads = "<%= @processorcount.to_i * 2 %>" + +# Thoses TWEAK assume that the galera cluster is used in master/slave mode +wsrep_provider_options = "gcache.size=<%= @galera_gcache %>;gcs.fc_master_slave=1;gcs.fc_limit=256;gcs.fc_factor=0.9" + +# this value here are used by /usr/bin/innobackupex +# and wsrep_sst_xtrabackup take only one configuration file and use the last one +# (/etc/mysql/my.cnf is not used) +datadir = /var/lib/mysql +tmpdir = /tmp/ +innodb_flush_method = O_DIRECT +innodb_log_buffer_size = 32M +innodb_log_file_size = 256M +innodb_log_files_in_group = 2 diff --git a/cloud/templates/database/mysqlchk.erb b/cloud/templates/database/mysqlchk.erb new file mode 100644 index 000000000..ef0473754 --- /dev/null +++ b/cloud/templates/database/mysqlchk.erb @@ -0,0 +1,24 @@ +# Managed by puppet +# Module cloud +# +# default: on +# description: mysqlchk +service mysqlchk +{ +# this is a config for xinetd, place it in /etc/xinetd.d/ + disable = no + flags = REUSE + socket_type = stream + port = 9200 + wait = no + user = nobody + server = /usr/bin/clustercheck + log_on_failure += USERID + log_on_success = + #FIXME(sbadia) Security: Restrict this parameter to HAProxy pool. + only_from = 0.0.0.0/0 + bind = <%= @galera_clustercheck_ipaddress %> + # recommended to put the IPs that need + # to connect exclusively (security purposes) + per_source = UNLIMITED +} diff --git a/cloud/templates/installserver/autosign.conf.erb b/cloud/templates/installserver/autosign.conf.erb new file mode 100644 index 000000000..d7ae0e101 --- /dev/null +++ b/cloud/templates/installserver/autosign.conf.erb @@ -0,0 +1,5 @@ +<% if @autosign_domains -%> +<% @autosign_domains.each do | domain | -%> +<%= domain %> +<% end -%> +<% end -%> diff --git a/cloud/templates/loadbalancer/monitor.erb b/cloud/templates/loadbalancer/monitor.erb new file mode 100644 index 000000000..a8fbc604c --- /dev/null +++ b/cloud/templates/loadbalancer/monitor.erb @@ -0,0 +1,79 @@ +<%- if @swift_api -%> +acl swift_api_dead nbsrv(swift_api_cluster) lt 1 +monitor fail if swift_api_dead +<%- end -%> +<%- if @keystone_api -%> +acl keystone_api_dead nbsrv(keystone_api_cluster) lt 1 +monitor fail if keystone_api_dead +<% end -%> +<%- if @keystone_api_admin -%> +acl keystone_api_admin_dead nbsrv(keystone_api_admin_cluster) lt 1 +monitor fail if keystone_api_admin_dead +<% end -%> +<%- if @galera -%> +acl galera_dead nbsrv(galera_cluster) lt 1 +monitor fail if galera_dead +<%- end -%> +<%- if @neutron_api -%> +acl neutron_api_dead nbsrv(neutron_api_cluster) lt 1 +monitor fail if neutron_api_dead +<%- end -%> +<%- if @cinder_api -%> +acl cinder_api_dead nbsrv(cinder_api_cluster) lt 1 +monitor fail if cinder_api_dead +<%- end -%> +<%- if @nova_api -%> +acl nova_api_dead nbsrv(nova_api_cluster) lt 1 +monitor fail if nova_api_dead +<%- end -%> +<%- if @ec2_api -%> +acl ec2_api_dead nbsrv(ec2_api_cluster) lt 1 +monitor fail if ec2_api_dead +<%- end -%> +<%- if @metadata_api -%> +acl metadata_api_dead nbsrv(metadata_api_cluster) lt 1 +monitor fail if metadata_api_dead +<%- end -%> +<%- if @spice -%> +acl spice_dead nbsrv(spice_cluster) lt 1 +monitor fail if spice_dead +<%- end -%> +<%- if @rabbitmq -%> +acl rabbitmq_dead nbsrv(rabbitmq_cluster) lt 1 +monitor fail if rabbitmq_dead +<%- end -%> +<%- if @glance_api -%> +acl glance_api_dead nbsrv(glance_api_cluster) lt 1 +monitor fail if glance_api_dead +<%- end -%> +<%- if @glance_registry -%> +acl glance_registry_dead nbsrv(glance_registry_cluster) lt 1 +monitor fail if glance_registry_dead +<%- end -%> +<%- if @ceilometer_api -%> +acl ceilometer_api_dead nbsrv(ceilometer_api_cluster) lt 1 +monitor fail if ceilometer_api_dead +<%- end -%> +<%- if @heat_api -%> +acl heat_api_dead nbsrv(heat_api_cluster) lt 1 +monitor fail if heat_api_dead +<%- end -%> +<%- if @heat_cfn_api -%> +acl heat_cfn_api_dead nbsrv(heat_cfn_api_cluster) lt 1 +monitor fail if heat_cfn_api_dead +<%- end -%> +<%- if @heat_cloudwatch_api -%> +acl heat_cloudwatch_api_dead nbsrv(heat_cloudwatch_api_cluster) lt 1 +monitor fail if heat_cloudwatch_api_dead +<%- end -%> +<%- if @horizon -%> +acl horizon_dead nbsrv(horizon_cluster) lt 1 +monitor fail if horizon_dead +<%- end -%> +<%- if @trove_api -%> +acl trove_api_dead nbsrv(trove_api_cluster) lt 1 +monitor fail if trove_api_dead +<%- end -%> + +# Used when forwarding SSL in http headers +acl is-ssl dst_port 443 diff --git a/cloud/templates/network/dnsmasq-neutron.conf.erb b/cloud/templates/network/dnsmasq-neutron.conf.erb new file mode 100644 index 000000000..979bd1a15 --- /dev/null +++ b/cloud/templates/network/dnsmasq-neutron.conf.erb @@ -0,0 +1 @@ +dhcp-option-force=26,<%= @veth_mtu %> diff --git a/cloud/templates/selinux/sysconfig_selinux.erb b/cloud/templates/selinux/sysconfig_selinux.erb new file mode 100644 index 000000000..e3bc2f856 --- /dev/null +++ b/cloud/templates/selinux/sysconfig_selinux.erb @@ -0,0 +1,11 @@ +# This file controls the state of SELinux on the system. +# SELINUX= can take one of these three values: +# enforcing - SELinux security policy is enforced. +# permissive - SELinux prints warnings instead of enforcing. +# disabled - No SELinux policy is loaded. +SELINUX=<%= @mode %> +# SELINUXTYPE= can take one of these two values: +# targeted - Targeted processes are protected, +# minimum - Modification of targeted policy. Only selected processes are protected. +# mls - Multi Level Security protection. +SELINUXTYPE=targeted diff --git a/cloud/templates/storage/ceph/ceph-client.conf.erb b/cloud/templates/storage/ceph/ceph-client.conf.erb new file mode 100644 index 000000000..7272d1658 --- /dev/null +++ b/cloud/templates/storage/ceph/ceph-client.conf.erb @@ -0,0 +1,8 @@ +<% if @clients %> +<% @clients.each do |client| %> + +[client.<%= client %>] + keyring = /etc/ceph/ceph.client.<%= client %>.keyring + +<% end %> +<% end %> diff --git a/cloud/templates/storage/ceph/secret-compute.xml.erb b/cloud/templates/storage/ceph/secret-compute.xml.erb new file mode 100644 index 000000000..52459f1d9 --- /dev/null +++ b/cloud/templates/storage/ceph/secret-compute.xml.erb @@ -0,0 +1,6 @@ + + + client.<%= @cinder_rbd_user %> secret + + <%= @ceph_fsid %> + diff --git a/common/.fixtures.yml b/common/.fixtures.yml new file mode 100644 index 000000000..545e8bd36 --- /dev/null +++ b/common/.fixtures.yml @@ -0,0 +1,7 @@ +fixtures: + repositories: + 'stdlib': + repo: 'git://github.com/puppetlabs/puppetlabs-stdlib.git' + ref: '3.2.0' + symlinks: + common: "#{source_dir}" diff --git a/common/.gitignore b/common/.gitignore index 2085807d2..96cd031e4 100644 --- a/common/.gitignore +++ b/common/.gitignore @@ -1,3 +1,29 @@ -old/ -rpmbuild/ +# Default .gitignore for Ruby +*.gem +*.rbc +.bundle +.config +coverage +InstalledFiles +lib/bundler/man +pkg +rdoc +spec/reports +test/tmp +test/version_tmp +tmp +# YARD artifacts +.yardoc +_yardoc +doc/ + +# Vim +*.swp + +# OS X +.DS_Store + +# Puppet +coverage/ +spec/fixtures/ diff --git a/common/.travis.yml b/common/.travis.yml new file mode 100644 index 000000000..69db3b743 --- /dev/null +++ b/common/.travis.yml @@ -0,0 +1,14 @@ +--- +env: +- PUPPET_VERSION=3.3.2 +- PUPPET_VERSION=3.4.2 +notifications: +email: false +rvm: +- 1.8.7 +- 1.9.3 +- 2.0.0 +language: ruby +before_script: "gem install --no-ri --no-rdoc bundler" +script: 'bundle exec rake validate && bundle exec rake lint && SPEC_OPTS="--format documentation" bundle exec rake spec' +gemfile: Gemfile diff --git a/common/CHANGELOG b/common/CHANGELOG new file mode 100644 index 000000000..8b0bb39c6 --- /dev/null +++ b/common/CHANGELOG @@ -0,0 +1,22 @@ +1.0.0 - 2013-05-24 + +Add spec tests and become viable for first major release. + +Albin Gustavsson + 2ef8ddb Removed some commented out code from the spec tests + d8fd795 Added spec testing for functions and defines + 558a99f Added spec testing for common::mkuser + +Garrett Honeycutt + 21c7d9a Ensure all modules are listed in README + 49f726d Add facter to .gemfile + fd2a905 Test for both attributes of mkdir_p with a variable + 81067a2 Travis - fix allowed failure for 2.7.x + 556d1be Travis - test against Puppet 3.2.1 and display graphic in README + 676bdeb Travis - remove 2.6.9 testing and test 3.x + c64244c Add travis-ci.org support + 419e530 Split mkuser testing into its own file and clarify usernames. + 93beb4f Add spec tests + +0.0.1 - 2013-05-12 Garrett Honeycutt +* Rework of some old code such that no action is taken by default diff --git a/common/COPYRIGHT b/common/COPYRIGHT deleted file mode 100644 index 480149fb7..000000000 --- a/common/COPYRIGHT +++ /dev/null @@ -1,16 +0,0 @@ -Copyright (C) 2012-2013+ James Shubin -Written by James Shubin - -This puppet module is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This puppet module is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . - diff --git a/common/Gemfile b/common/Gemfile new file mode 100644 index 000000000..b40b4c08a --- /dev/null +++ b/common/Gemfile @@ -0,0 +1,7 @@ +source "https://rubygems.org" + +puppetversion = ENV.key?('PUPPET_VERSION') ? "= #{ENV['PUPPET_VERSION']}" : ['>= 2.7'] +gem 'puppet', puppetversion +gem 'puppetlabs_spec_helper', '>= 0.1.0' +gem 'puppet-lint', '>= 0.3.2' +gem 'facter', '>= 1.7.0', "< 1.8.0" diff --git a/common/INSTALL b/common/INSTALL deleted file mode 100644 index f497841f7..000000000 --- a/common/INSTALL +++ /dev/null @@ -1,18 +0,0 @@ -To install this puppet module, copy this folder to your puppet modulepath. - -You can usually find out where this is by running: - -$ puppet config print modulepath - -on your puppetmaster. In my case, this contains the directory: - -/etc/puppet/modules/ - -I keep all of my puppet modules in git managed directories named: - -puppet- - -You must remove the 'puppet-' prefix from the directory name for it to work! - -Happy hacking! - diff --git a/common/LICENSE b/common/LICENSE new file mode 100644 index 000000000..a9ce44ded --- /dev/null +++ b/common/LICENSE @@ -0,0 +1,13 @@ +Copyright (C) 2007-2013 Garrett Honeycutt + +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. diff --git a/common/Makefile b/common/Makefile deleted file mode 100644 index 5ff251842..000000000 --- a/common/Makefile +++ /dev/null @@ -1,146 +0,0 @@ -# Common puppet utilities module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -.PHONY: all docs rpm srpm spec tar upload upload-sources upload-srpms upload-rpms -.SILENT: - -# version of the program -VERSION := $(shell cat VERSION) -RELEASE = 1 -SPEC = rpmbuild/SPECS/puppet-common.spec -SOURCE = rpmbuild/SOURCES/puppet-common-$(VERSION).tar.bz2 -SRPM = rpmbuild/SRPMS/puppet-common-$(VERSION)-$(RELEASE).src.rpm -RPM = rpmbuild/RPMS/puppet-common-$(VERSION)-$(RELEASE).rpm -SERVER = 'download.gluster.org' -REMOTE_PATH = 'purpleidea/puppet-common' - -all: docs rpm - -docs: puppet-common-documentation.pdf - -puppet-common-documentation.pdf: DOCUMENTATION.md - pandoc DOCUMENTATION.md -o 'puppet-common-documentation.pdf' - -# -# aliases -# -# TODO: does making an rpm depend on making a .srpm first ? -rpm: $(SRPM) $(RPM) - # do nothing - -srpm: $(SRPM) - # do nothing - -spec: $(SPEC) - # do nothing - -tar: $(SOURCE) - # do nothing - -upload: upload-sources upload-srpms upload-rpms - # do nothing - -# -# rpmbuild -# -$(RPM): $(SPEC) $(SOURCE) - @echo Running rpmbuild -bb... - rpmbuild --define '_topdir $(shell pwd)/rpmbuild' -bb $(SPEC) && \ - mv rpmbuild/RPMS/noarch/puppet-common-$(VERSION)-$(RELEASE).*.rpm $(RPM) - -$(SRPM): $(SPEC) $(SOURCE) - @echo Running rpmbuild -bs... - rpmbuild --define '_topdir $(shell pwd)/rpmbuild' -bs $(SPEC) - # renaming is not needed because we aren't using the dist variable - #mv rpmbuild/SRPMS/puppet-common-$(VERSION)-$(RELEASE).*.src.rpm $(SRPM) - -# -# spec -# -$(SPEC): rpmbuild/ puppet-common.spec.in - @echo Running templater... - #cat puppet-common.spec.in > $(SPEC) - sed -e s/__VERSION__/$(VERSION)/ -e s/__RELEASE__/$(RELEASE)/ < puppet-common.spec.in > $(SPEC) - # append a changelog to the .spec file - git log --format="* %cd %aN <%aE>%n- (%h) %s%d%n" --date=local | sed -r 's/[0-9]+:[0-9]+:[0-9]+ //' >> $(SPEC) - -# -# archive -# -$(SOURCE): rpmbuild/ - @echo Running git archive... - # use HEAD if tag doesn't exist yet, so that development is easier... - git archive --prefix=puppet-common-$(VERSION)/ -o $(SOURCE) $(VERSION) 2> /dev/null || (echo 'Warning: $(VERSION) does not exist.' && git archive --prefix=puppet-common-$(VERSION)/ -o $(SOURCE) HEAD) - -# TODO: ensure that each sub directory exists -rpmbuild/: - mkdir -p rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} - -# -# sha256sum -# -rpmbuild/SOURCES/SHA256SUMS: rpmbuild/SOURCES/ $(SOURCE) - @echo Running SOURCES sha256sum... - cd rpmbuild/SOURCES/ && sha256sum *.tar.bz2 > SHA256SUMS; cd - - -rpmbuild/SRPMS/SHA256SUMS: rpmbuild/SRPMS/ $(SRPM) - @echo Running SRPMS sha256sum... - cd rpmbuild/SRPMS/ && sha256sum *src.rpm > SHA256SUMS; cd - - -rpmbuild/RPMS/SHA256SUMS: rpmbuild/RPMS/ $(RPM) - @echo Running RPMS sha256sum... - cd rpmbuild/RPMS/ && sha256sum *.rpm > SHA256SUMS; cd - - -# -# gpg -# -rpmbuild/SOURCES/SHA256SUMS.asc: rpmbuild/SOURCES/SHA256SUMS - @echo Running SOURCES gpg... - # the --yes forces an overwrite of the SHA256SUMS.asc if necessary - gpg2 --yes --clearsign rpmbuild/SOURCES/SHA256SUMS - -rpmbuild/SRPMS/SHA256SUMS.asc: rpmbuild/SRPMS/SHA256SUMS - @echo Running SRPMS gpg... - gpg2 --yes --clearsign rpmbuild/SRPMS/SHA256SUMS - -rpmbuild/RPMS/SHA256SUMS.asc: rpmbuild/RPMS/SHA256SUMS - @echo Running RPMS gpg... - gpg2 --yes --clearsign rpmbuild/RPMS/SHA256SUMS - -# -# upload -# -# upload to public server -upload-sources: rpmbuild/SOURCES/ rpmbuild/SOURCES/SHA256SUMS rpmbuild/SOURCES/SHA256SUMS.asc - if [ "`cat rpmbuild/SOURCES/SHA256SUMS`" != "`ssh $(SERVER) 'cd $(REMOTE_PATH)/SOURCES/ && cat SHA256SUMS'`" ]; then \ - echo Running SOURCES upload...; \ - rsync -avz rpmbuild/SOURCES/ $(SERVER):$(REMOTE_PATH)/SOURCES/; \ - fi - -upload-srpms: rpmbuild/SRPMS/ rpmbuild/SRPMS/SHA256SUMS rpmbuild/SRPMS/SHA256SUMS.asc - if [ "`cat rpmbuild/SRPMS/SHA256SUMS`" != "`ssh $(SERVER) 'cd $(REMOTE_PATH)/SRPMS/ && cat SHA256SUMS'`" ]; then \ - echo Running SRPMS upload...; \ - rsync -avz rpmbuild/SRPMS/ $(SERVER):$(REMOTE_PATH)/SRPMS/; \ - fi - -upload-rpms: rpmbuild/RPMS/ rpmbuild/RPMS/SHA256SUMS rpmbuild/RPMS/SHA256SUMS.asc - if [ "`cat rpmbuild/RPMS/SHA256SUMS`" != "`ssh $(SERVER) 'cd $(REMOTE_PATH)/RPMS/ && cat SHA256SUMS'`" ]; then \ - echo Running RPMS upload...; \ - rsync -avz --prune-empty-dirs rpmbuild/RPMS/ $(SERVER):$(REMOTE_PATH)/RPMS/; \ - fi - -# vim: ts=8 diff --git a/common/Modulefile b/common/Modulefile new file mode 100644 index 000000000..9507a6b7f --- /dev/null +++ b/common/Modulefile @@ -0,0 +1,10 @@ +name 'ghoneycutt-common' +version '1.2.0' +source 'git://github.com/ghoneycutt/puppet-module-common.git' +author 'ghoneycutt' +license 'Apache License, Version 2.0' +summary 'common module to be applied to all nodes' +description 'Provide ability to include modules that are common to all nodes.' +project_page 'https://github.com/ghoneycutt/puppet-module-common' + +dependency 'puppetlabs/stdlib', '>= 3.2.0' diff --git a/common/README b/common/README deleted file mode 100644 index 2e03bc9ea..000000000 --- a/common/README +++ /dev/null @@ -1,22 +0,0 @@ -This is puppet-common a puppet module for common things. - -Please read the INSTALL file for instructions on getting this installed. -Look in the examples/ folder for usage. If none exist, please contribute one! -This code may be a work in progress. The interfaces may change without notice. -Patches are welcome, but please be patient. They are best received by email. -Please ping me if you have big changes in mind, before you write a giant patch. - -Module specific notes: -* This module is a useful dependency for some of my other modules. -* You may also find some of the individual pieces useful. -* I have removed functionality that now exists in the puppet "stdlib". -* Some of the functionality is so useful, that this imports types directly. - -Dependencies: -* puppetlabs-stdlib (required) -* my puppet-puppet module (optional) - - -Happy hacking, -James Shubin , https://ttboj.wordpress.com/ - diff --git a/common/README.md b/common/README.md new file mode 100644 index 000000000..a0a43e2b7 --- /dev/null +++ b/common/README.md @@ -0,0 +1,369 @@ +# puppet-module-common # + +[![Build Status]( +https://api.travis-ci.org/ghoneycutt/puppet-module-common.png?branch=master)](https://travis-ci.org/ghoneycutt/puppet-module-common) + +common module to be applied to **ALL** nodes + +# Compatibility # + +Module is generic enough to work on any system, though the individual modules that it could potentially include could be very platform specific. + +=== + +# Common class # +Optionally include classes that are common to all systems, such as `dnsclient`, `ntp`, `puppet::agent`, and `vim`. By default we do not take any action, so you must enable the classes. This should be done in Hiera such as the following example. Ideally you would do this in your least specific level of hiera (often times labeled as 'common' or 'global') and potentially override at other levels. + +
+---
+common::manage_root_password: true
+common::enable_dnsclient: true
+common::enable_ntp: true
+common::enable_puppet_agent: true
+common::enable_vim: true
+
+ +## Parameters for class `common`## + +users +----- +Hash of users to ensure with common::mkusers + +- *Default*: undef + +groups +------ +Hash of groups to ensure + +- *Default*: undef + +manage_root_password +-------------------- + +- *Default*: false + +root_password +------------- + +- *Default*: MD5 crypt of `puppet` + +create_opt_lsb_provider_name_dir +-------------------------------- +Boolean to ensure `/opt/${lsb_provider_name}` + +- *Default*: false + +lsb_provider_name +----------------- +LSB Provider Name as assigned by LANANA - [http://www.lanana.org/lsbreg/providers/index.html](http://www.lanana.org/lsbreg/providers/index.html) + +- *Default*: `UNSET` + +enable_dnsclient +---------------- +Boolean to include ghoneycutt/dnsclient + +- *Default*: false + +enable_hosts +------------ +Boolean to include ghoneycutt/hosts + +- *Default*: false + +enable_inittab +-------------- +Boolean to include ghoneycutt/inittab + +- *Default*: false + +enable_mailaliases +------------------ +Boolean to include ghoneycutt/mailaliases + +- *Default*: false + +enable_motd +----------- +Boolean to include ghoneycutt/motd + +- *Default*: false + +enable_network +-------------- +Boolean to include ghoneycutt/network + +- *Default*: false + +enable_nsswitch +--------------- +Boolean to include ghoneycutt/nsswitch + +- *Default*: false + +enable_ntp +---------- +Boolean to include ghoneycutt/ntp + +- *Default*: false + +enable_pam +---------- +Boolean to include ghoneycutt/pam + +- *Default*: false + +enable_puppet_agent +------------------- +Boolean to include ghoneycutt/puppet::agent + +- *Default*: false + +enable_rsyslog +-------------- +Boolean to include ghoneycutt/rsyslog + +- *Default*: false + +enable_selinux +-------------- +Boolean to include ghoneycutt/selinux + +- *Default*: false + +enable_ssh +---------- +Boolean to include ghoneycutt/ssh + +- *Default*: false + +enable_utils +------------ +Boolean to include ghoneycutt/utils + +- *Default*: false + +enable_vim +---------- +Boolean to include ghoneycutt/vim + +- *Default*: false + +enable_wget +----------- +Boolean to include ghoneycutt/wget + +- *Default*: false + +### includes classes based on `osfamily` fact ### + +enable_debian +----------- +Boolean to include ghoneycutt/debian + +- *Default*: false + +enable_redhat +----------- +Boolean to include ghoneycutt/redhat + +- *Default*: false + +enable_solaris +----------- +Boolean to include ghoneycutt/solaris + +- *Default*: false + +enable_suse +----------- +Boolean to include ghoneycutt/suse + +- *Default*: false + +=== + +# common::mkdir_p define # +Provide `mkdir -p` functionality for a directory. + +Used in conjunction with a file resource. + +## Example usage: ## +
+common::mkdir_p { '/some/dir/structure': }
+
+file { '/some/dir/structure':
+  ensure  => directory,
+  require => Common::Mkdir_p['/some/dir/structure'],
+}
+
+ +## Parameters for `common::mkdir_p` define ## + +None. + +=== + +# common::remove_if_empty define # +Removes a file if it exists and is empty. + +## Example usage: ## +
+common::remove_if_empty { '/path/to/potentially_empty_file': }
+
+ +## Parameters for `common::remove_if_empty` define ## + +None. + +=== + +# common::mkuser define # +Ensures user/groups + +## Usage ## +You can specify hash each for users and groups and use Hiera to manage them. + +This example uses the YAML backend, though that is not mandatory. + +In Hiera's hierarchy add two levels, `users`, and `groups` such as the following example. + +`hiera.yaml` +
+---
+:backends:
+  - yaml
+:hierarchy:
+  - fqdn/%{fqdn}
+  - users
+  - groups
+  - %{environment}
+  - common
+:yaml:
+  :datadir:
+
+ +`users.yaml` +
+---
+common::users:
+  gh:
+    uid: "30000"
+    comment: "Garrett Honeycutt"
+    groups: admin
+    ssh_auth_key: ssh-public-key
+
+ +`groups.yaml` +
+---
+common::groups:
+  admin:
+    gid: "32000"
+
+ + +## Parameters for `common::mkuser` define ## + +uid +--- +String - UID of user + +- *Required* + +gid +--- +String - GID of user + +- *Default*: `$uid` + +name +---- +String - username + +group +----- +String - group name of user + +- *Default*: `$name` + +shell +----- +String - user's shell + +- *Default*: '/bin/bash' + +home +------ +String - home directory + +- *Default*: `/home/${username}` + +ensure +------ +Present or Absent + +- *Default*: present + +managehome +---------- +Boolean for manage home attribute of user resource + +- *Default*: true + +manage_dotssh +------------- +Boolean to optionally create `~/.ssh` directory + +- *Default*: true + +comment +------- +String - GECOS field for passed + +- *Default*: 'created via puppet' + +groups +------ +Array - additional groups the user should be associated with + +- *Default*: undef + +password +-------- +String - password crypt for user + +- *Default*: '!!' + +mode +---- +String - mode of home directory + +- *Default*: 0700 + +ssh_auth_key +----------------- +String - The ssh key for the user + +- *Default*: undef + +ssh_auth_key_type +----------------- +String - Anything that the ssh_authorized_key resource can take for the type attribute, such as `ssh-dss` or `ssh-rsa`. + +- *Default*: 'ssh-dss' + +=== + +# Functions # + +## interface2factname() ## +Takes one argument, the interface name, and returns it formatted for use with facter. + +Example: `interface2factname('bond0:0')` would return `ipaddress_bond0_0`. + +## strip_file_extension() ## +Takes two arguments, a file name which can include the path, and the extension to be removed. Returns the file name without the extension as a string. + +Example: `strip_file_extension('myapp.war','war')` would return `myapp`. + diff --git a/common/Rakefile b/common/Rakefile new file mode 100644 index 000000000..0a28d845e --- /dev/null +++ b/common/Rakefile @@ -0,0 +1,18 @@ +require 'rubygems' +require 'puppetlabs_spec_helper/rake_tasks' +require 'puppet-lint/tasks/puppet-lint' +PuppetLint.configuration.send('disable_80chars') +PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "pkg/**/*.pp"] + +desc "Run puppet in noop mode and check for syntax errors." +task :validate do + Dir['manifests/**/*.pp'].each do |manifest| + sh "puppet parser validate --noop #{manifest}" + end + Dir['spec/**/*.rb','lib/**/*.rb'].each do |ruby_file| + sh "ruby -c #{ruby_file}" unless ruby_file =~ /spec\/fixtures/ + end + Dir['templates/**/*.erb'].each do |template| + sh "erb -P -x -T '-' #{template} | ruby -c" + end +end diff --git a/common/VERSION b/common/VERSION deleted file mode 100644 index 4e379d2bf..000000000 --- a/common/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.0.2 diff --git a/common/examples/again-delta-example.pp b/common/examples/again-delta-example.pp deleted file mode 100644 index d1e73d76f..000000000 --- a/common/examples/again-delta-example.pp +++ /dev/null @@ -1,23 +0,0 @@ -# this is just a proof of concept example, to help you visualize how this works - -include common::again - -# when notified, this timer will run puppet again, delta seconds after it ends! -common::again::delta { 'delta-timer': - delta => 120, # 2 minutes - #start_timer_now => true, # use this to start the countdown asap! -} - -file { '/tmp/foo': - content => "Something happened!\n", - # NOTE: that as long as you don't remove or change the /tmp/foo file, - # this will only cause a notify when the file needs changes done. This - # is what prevents this from infinite recursion, and lets puppet sleep. - notify => Common::Again::Delta['delta-timer'], # notify puppet! -} - -# always exec so that i can easily see when puppet runs... proof that it works! -exec { 'proof': - command => '/bin/date >> /tmp/puppet.again', -} - diff --git a/common/examples/again-example.pp b/common/examples/again-example.pp deleted file mode 100644 index 25cc6255f..000000000 --- a/common/examples/again-example.pp +++ /dev/null @@ -1,17 +0,0 @@ -# this is just a proof of concept example, to help you visualize how this works - -include common::again - -file { '/tmp/foo': - content => "Something happened!\n", - # NOTE: that as long as you don't remove or change the /tmp/foo file, - # this will only cause a notify when the file needs changes done. This - # is what prevents this from infinite recursion, and lets puppet sleep. - notify => Exec['again'], # notify puppet! -} - -# always exec so that i can easily see when puppet runs... proof that it works! -exec { 'proof': - command => '/bin/date >> /tmp/puppet.again', -} - diff --git a/common/lib/puppet/parser/functions/interface2factname.rb b/common/lib/puppet/parser/functions/interface2factname.rb new file mode 100644 index 000000000..8ef558685 --- /dev/null +++ b/common/lib/puppet/parser/functions/interface2factname.rb @@ -0,0 +1,14 @@ +module Puppet::Parser::Functions + newfunction(:interface2factname, :type => :rvalue, :doc => <<-EOS + Takes one argument, the interface name, and returns it formatted for use + with facter. Example: interface2factname(bond0:0) would return 'ipaddress_bond0_0' + EOS + ) do |args| + + raise(Puppet::ParseError, "interface2factname(): Wrong number of arguments " + + "given (#{args.size} for 1)") if args.size != 1 + + interface = "ipaddress_#{args[0]}" + interface.gsub(/[^a-z0-9_]/i, '_') + end +end diff --git a/common/lib/puppet/parser/functions/strip_file_extension.rb b/common/lib/puppet/parser/functions/strip_file_extension.rb new file mode 100644 index 000000000..a31590e21 --- /dev/null +++ b/common/lib/puppet/parser/functions/strip_file_extension.rb @@ -0,0 +1,23 @@ +module Puppet::Parser::Functions + newfunction(:strip_file_extension, :type => :rvalue, :doc => <<-EOS + Takes two arguments, a file name which can include the path, and the + extension to be removed. Returns the file name without the extension + as a string. + EOS + ) do |args| + + raise(Puppet::ParseError, "strip_file_extension(): Wrong number of arguments " + + "given (#{args.size} for 2)") if args.size != 2 + + filename = args[0] + + # allow the extension to optionally start with a period. + if args[1] =~ /^\./ + extension = args[1] + else + extension = ".#{args[1]}" + end + + File.basename(filename,extension) + end +end diff --git a/common/manifests/again.pp b/common/manifests/again.pp deleted file mode 100644 index cd2bf7418..000000000 --- a/common/manifests/again.pp +++ /dev/null @@ -1,86 +0,0 @@ -# Run puppet again if notified to do so -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class common::again { - - include common::vardir - - #$vardir = $::common::vardir::module_vardir # with trailing slash - $vardir = regsubst($::common::vardir::module_vardir, '\/$', '') - - # store 'again' specific code in a separate directory - file { "${vardir}/again/": - ensure => directory, # make sure this is a directory - recurse => true, # don't recurse into directory - purge => true, # don't purge unmanaged files - force => true, # don't purge subdirs and links - require => File["${vardir}/"], - } - - file { "${vardir}/again/again.py": - # NOTE: this is actually templated, but no templating - # is actually being used. This gives us the option to - # pass in some variables if we decide we would like a - # way to get values in other than via command line... - # we could pass in some environ data or other data... - content => template('common/again/again.py.erb'), - owner => root, - group => root, - mode => 754, # if you're not root, you can't run it! - ensure => present, - require => File["${vardir}/again/"], - } - - # notify this command whenever you want to trigger another puppet run! - exec { 'again': - command => "${vardir}/again/again.py", - logoutput => on_failure, - refreshonly => true, # run whenever someone requests it! - require => File["${vardir}/again/again.py"], - } -} - -## NOTE: splitting this into a separate file didn't work properly in this module -#define common::again::delta( -# $delta = 0, -# # start timer counting now! (default is to start when puppet finishes!) -# $start_timer_now = false -#) { -# include common::vardir -# include common::again - -# #$vardir = $::common::vardir::module_vardir # with trailing slash -# $vardir = regsubst($::common::vardir::module_vardir, '\/$', '') - -# $valid_start_timer_now = $start_timer_now ? { -# true => '--start-timer-now', -# default => '', -# } - -# $arglist = ["--delta ${delta}", "${valid_start_timer_now}"] -# $args = join(delete($arglist, ''), ' ') - -# # notify this command whenever you want to trigger another puppet run! -# exec { "again-delta-${name}": -# command => "${vardir}/again/again.py ${args}", -# logoutput => on_failure, -# refreshonly => true, # run whenever someone requests it! -# require => File["${vardir}/again/again.py"], -# } -#} - -# vim: ts=8 diff --git a/common/manifests/again/delta.pp b/common/manifests/again/delta.pp deleted file mode 100644 index 2c2c27ed9..000000000 --- a/common/manifests/again/delta.pp +++ /dev/null @@ -1,52 +0,0 @@ -# Run puppet again if notified to do so -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: see ../again.pp for the delta code - -# NOTE: splitting this into a separate file didn't work properly in this module -define common::again::delta( - $delta = 0, - # start timer counting now! (default is to start when puppet finishes!) - $start_timer_now = false -) { - include common::vardir - include common::again - - #$vardir = $::common::vardir::module_vardir # with trailing slash - $vardir = regsubst($::common::vardir::module_vardir, '\/$', '') - - $valid_delta = inline_template('<%= [Fixnum, String].include?(@delta.class) ? @delta.to_i : 0 %>') - - $valid_start_timer_now = $start_timer_now ? { - true => '--start-timer-now', - default => '', - } - - $arglist = ["--delta ${valid_delta}", "${valid_start_timer_now}"] - $args = join(delete($arglist, ''), ' ') - - # notify this command whenever you want to trigger another puppet run! - exec { "again-delta-${name}": - command => "${vardir}/again/again.py ${args}", - logoutput => on_failure, - refreshonly => true, # run whenever someone requests it! - require => File["${vardir}/again/again.py"], - } -} - - -# vim: ts=8 diff --git a/common/manifests/frag/frag.pp b/common/manifests/frag/frag.pp deleted file mode 100644 index 3938006d9..000000000 --- a/common/manifests/frag/frag.pp +++ /dev/null @@ -1,124 +0,0 @@ -# Create a whole file (whole {}) from fragments (frag {}) -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# thanks to #puppet@freenode.net for some good implementation advice - -define whole( - $dir, # the directory to store the fragment - $owner = root, # the file {} defaults were used here - $group = root, - $mode = '644', - $backup = undef, # the default value is actually: puppet - $pattern = '', # /usr/bin/find -name is used - $frag = false, # set true to also act like a frag obj! - $ensure = present # TODO: does absent even work properly? -) { - $d = sprintf("%s/", regsubst($dir, '\/$', '')) # ensure trailing slash - $safe_d = shellquote($d) - $safe_p = shellquote($pattern) - $command = $pattern ? { - '' => "/usr/bin/find ${safe_d} -maxdepth 1 -type f -print0 | /bin/sort -z | /usr/bin/xargs -0 /bin/cat", - default => "/usr/bin/find ${safe_d} -maxdepth 1 -name ${safe_p} -type f -print0 | /bin/sort -z | /usr/bin/xargs -0 /bin/cat", - } - - # this is the parent (basename) dir of $name which is special if i frag - $frag_d = sprintf("%s/", regsubst($name, '((\/[\w.-]+)*)(\/)([\w.-]+)', '\1')) - - # the file (used to set perms and as a placeholder so it's not deleted) - file { "${name}": - ensure => $ensure, - owner => $owner, - group => $group, - mode => $mode, - backup => $backup, - checksum => 'md5', - before => $frag ? { # used when this is also a frag - true => Exec["${frag_d}"], - default => undef, - } - } - - # ensure directory exists and is managed - file { "${d}": - ensure => directory, # make sure this is a directory - recurse => true, # recursively manage directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - before => Exec["${d}"], - } - - # actually make the file from fragments with this command - # use >| to force target file clobbering (ignore a: "set -o noclobber") - exec { "${d}": - command => "${command} >| ${name}", - # NOTE: if we don't use 'bash -c' this spurriously sends notify - # actually check that the file matches what it really should be - unless => "/bin/bash -c '/usr/bin/diff -q <(/bin/cat ${name}) <(${command})'", - logoutput => on_failure, - # if we're a frag, make sure that we build ourself out before - # the parent `whole` object which uses us, builds itself out! - before => $frag ? { - true => Exec["${frag_d}"], - default => undef, - }, - # if we're a frag, then it's important to wait for the parent - # frag collection directory to get created before i make this - require => $frag ? { - true => File["${frag_d}"], - default => undef, - }, - } -} - -# frag supports both $source and $content. if $source is not empty, it is used, -# otherwise content is used. frag should behave like a first class file object. -define frag( # dir to store frag, is path in namevar - $owner = root, # the file {} defaults were chosen here - $group = root, - $mode = '644', - $backup = undef, # the default value is actually: puppet - $ensure = present, - $content = '', - $source = '' - # TODO: add more file object features if someone needs them or if bored -) { - # finds the file name in a complete path; eg: /tmp/dir/file => file - #$x = regsubst($name, '(\/[\w.]+)*(\/)([\w.]+)', '\3') - # finds the basepath in a complete path; eg: /tmp/dir/file => /tmp/dir/ - $d = sprintf("%s/", regsubst($name, '((\/[\w.-]+)*)(\/)([\w.-]+)', '\1')) - - # the file fragment - file { "${name}": - ensure => $ensure, - owner => $owner, - group => $group, - mode => $mode, - backup => $backup, - content => $source ? { - '' => $content, - default => undef, - }, - source => $source ? { - '' => undef, - default => $source, - }, - before => Exec["${d}"], - require => File["${d}"], - } -} - -# vim: ts=8 diff --git a/common/manifests/init.pp b/common/manifests/init.pp index fa9bec3eb..b75108c74 100644 --- a/common/manifests/init.pp +++ b/common/manifests/init.pp @@ -1,23 +1,336 @@ -# Common puppet utilities module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin +# == Class: common # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# This class is applied to *ALL* nodes # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. +# === Copyright # -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . +# Copyright 2013 GH Solutions, LLC +# +class common ( + $users = undef, + $groups = undef, + $manage_root_password = false, + $root_password = '$1$cI5K51$dexSpdv6346YReZcK2H1k.', # puppet + $create_opt_lsb_provider_name_dir = false, + $lsb_provider_name = 'UNSET', + $enable_dnsclient = false, + $enable_hosts = false, + $enable_inittab = false, + $enable_mailaliases = false, + $enable_motd = false, + $enable_network = false, + $enable_nsswitch = false, + $enable_ntp = false, + $enable_pam = false, + $enable_puppet_agent = false, + $enable_rsyslog = false, + $enable_selinux = false, + $enable_ssh = false, + $enable_utils = false, + $enable_vim = false, + $enable_wget = false, + # include classes based on osfamily fact + $enable_debian = false, + $enable_redhat = false, + $enable_solaris = false, + $enable_suse = false, +) { + + # validate type and convert string to boolean if necessary + $enable_dnsclient_type = type($enable_dnsclient) + if $enable_dnsclient_type == 'string' { + $dnsclient_enabled = str2bool($enable_dnsclient) + } else { + $dnsclient_enabled = $enable_dnsclient + } + if $dnsclient_enabled == true { + include dnsclient + } + + # validate type and convert string to boolean if necessary + $enable_hosts_type = type($enable_hosts) + if $enable_hosts_type == 'string' { + $hosts_enabled = str2bool($enable_hosts) + } else { + $hosts_enabled = $enable_hosts + } + if $hosts_enabled == true { + include hosts + } + + # validate type and convert string to boolean if necessary + $enable_inittab_type = type($enable_inittab) + if $enable_inittab_type == 'string' { + $inittab_enabled = str2bool($enable_inittab) + } else { + $inittab_enabled = $enable_inittab + } + if $inittab_enabled == true { + include inittab + } + + # validate type and convert string to boolean if necessary + $enable_mailaliases_type = type($enable_mailaliases) + if $enable_mailaliases_type == 'string' { + $mailaliases_enabled = str2bool($enable_mailaliases) + } else { + $mailaliases_enabled = $enable_mailaliases + } + if $mailaliases_enabled == true { + include mailaliases + } + + # validate type and convert string to boolean if necessary + $enable_motd_type = type($enable_motd) + if $enable_motd_type == 'string' { + $motd_enabled = str2bool($enable_motd) + } else { + $motd_enabled = $enable_motd + } + if $motd_enabled == true { + include motd + } + + # validate type and convert string to boolean if necessary + $enable_network_type = type($enable_network) + if $enable_network_type == 'string' { + $network_enabled = str2bool($enable_network) + } else { + $network_enabled = $enable_network + } + if $network_enabled == true { + include network + } + + # validate type and convert string to boolean if necessary + $enable_nsswitch_type = type($enable_nsswitch) + if $enable_nsswitch_type == 'string' { + $nsswitch_enabled = str2bool($enable_nsswitch) + } else { + $nsswitch_enabled = $enable_nsswitch + } + if $nsswitch_enabled == true { + include nsswitch + } + + # validate type and convert string to boolean if necessary + $enable_ntp_type = type($enable_ntp) + if $enable_ntp_type == 'string' { + $ntp_enabled = str2bool($enable_ntp) + } else { + $ntp_enabled = $enable_ntp + } + if $ntp_enabled == true { + include ntp + } + + # validate type and convert string to boolean if necessary + $enable_pam_type = type($enable_pam) + if $enable_pam_type == 'string' { + $pam_enabled = str2bool($enable_pam) + } else { + $pam_enabled = $enable_pam + } + if $pam_enabled == true { + include pam + } + + # validate type and convert string to boolean if necessary + $enable_puppet_agent_type = type($enable_puppet_agent) + if $enable_puppet_agent_type == 'string' { + $puppet_agent_enabled = str2bool($enable_puppet_agent) + } else { + $puppet_agent_enabled = $enable_puppet_agent + } + if $puppet_agent_enabled == true { + include puppet::agent + } + + # validate type and convert string to boolean if necessary + $enable_rsyslog_type = type($enable_rsyslog) + if $enable_rsyslog_type == 'string' { + $rsyslog_enabled = str2bool($enable_rsyslog) + } else { + $rsyslog_enabled = $enable_rsyslog + } + if $rsyslog_enabled == true { + include rsyslog + } + + # validate type and convert string to boolean if necessary + $enable_selinux_type = type($enable_selinux) + if $enable_selinux_type == 'string' { + $selinux_enabled = str2bool($enable_selinux) + } else { + $selinux_enabled = $enable_selinux + } + if $selinux_enabled == true { + include selinux + } + + # validate type and convert string to boolean if necessary + $enable_ssh_type = type($enable_ssh) + if $enable_ssh_type == 'string' { + $ssh_enabled = str2bool($enable_ssh) + } else { + $ssh_enabled = $enable_ssh + } + if $ssh_enabled == true { + include ssh + } + + # validate type and convert string to boolean if necessary + $enable_utils_type = type($enable_utils) + if $enable_utils_type == 'string' { + $utils_enabled = str2bool($enable_utils) + } else { + $utils_enabled = $enable_utils + } + if $utils_enabled == true { + include utils + } + + # validate type and convert string to boolean if necessary + $enable_vim_type = type($enable_vim) + if $enable_vim_type == 'string' { + $vim_enabled = str2bool($enable_vim) + } else { + $vim_enabled = $enable_vim + } + if $vim_enabled == true { + include vim + } + + # validate type and convert string to boolean if necessary + $enable_wget_type = type($enable_wget) + if $enable_wget_type == 'string' { + $wget_enabled = str2bool($enable_wget) + } else { + $wget_enabled = $enable_wget + } + if $wget_enabled == true { + include wget + } + + # only allow supported OS's + case $::osfamily { + 'debian': { + # validate type and convert string to boolean if necessary + $enable_debian_type = type($enable_debian) + if $enable_debian_type == 'string' { + $debian_enabled = str2bool($enable_debian) + } else { + $debian_enabled = $enable_debian + } + if $debian_enabled == true { + include debian + } + } + 'redhat': { + # validate type and convert string to boolean if necessary + $enable_redhat_type = type($enable_redhat) + if $enable_redhat_type == 'string' { + $redhat_enabled = str2bool($enable_redhat) + } else { + $redhat_enabled = $enable_redhat + } + if $redhat_enabled == true { + include redhat + } + } + 'solaris': { + # validate type and convert string to boolean if necessary + $enable_solaris_type = type($enable_solaris) + if $enable_solaris_type == 'string' { + $solaris_enabled = str2bool($enable_solaris) + } else { + $solaris_enabled = $enable_solaris + } + if $solaris_enabled == true { + include solaris + } + } + 'suse': { + # validate type and convert string to boolean if necessary + $enable_suse_type = type($enable_suse) + if $enable_suse_type == 'string' { + $suse_enabled = str2bool($enable_suse) + } else { + $suse_enabled = $enable_suse + } + if $suse_enabled == true { + include suse + } + } + default: { + fail("Supported OS families are Debian, RedHat, Solaris, and Suse. Detected osfamily is ${::osfamily}.") + } + } + + # validate type and convert string to boolean if necessary + $manage_root_password_type = type($manage_root_password) + if $manage_root_password_type == 'string' { + $manage_root_password_real = str2bool($manage_root_password) + } else { + $manage_root_password_real = $manage_root_password + } + + if $manage_root_password_real == true { + + # validate root_password - fail if not a string + $root_password_type = type($root_password) + if $root_password_type != 'string' { + fail('common::root_password is not a string.') + } + + user { 'root': + password => $root_password, + } + } + + # validate type and convert string to boolean if necessary + $create_opt_lsb_provider_name_dir_type = type($create_opt_lsb_provider_name_dir) + if $create_opt_lsb_provider_name_dir_type == 'string' { + $create_opt_lsb_provider_name_dir_real = str2bool($create_opt_lsb_provider_name_dir) + } else { + $create_opt_lsb_provider_name_dir_real = $create_opt_lsb_provider_name_dir + } + + if $create_opt_lsb_provider_name_dir_real == true { + + # validate lsb_provider_name - fail if not a string + $lsb_provider_name_type = type($lsb_provider_name) + if $lsb_provider_name_type != 'string' { + fail('common::lsb_provider_name is not a string.') + } + + if $lsb_provider_name != 'UNSET' { + + # basic filesystem requirements + file { "/opt/${lsb_provider_name}": + ensure => directory, + owner => 'root', + group => 'root', + mode => '0755', + } + } + } + + if $users != undef { + + # Create virtual user resources + create_resources('@common::mkuser',$common::users) -import 'frag/*.pp' + # Collect all virtual users + Common::Mkuser <||> + } -class common { + if $groups != undef { + # Create virtual group resources + create_resources('@group',$common::groups) + # Collect all virtual groups + Group <||> + } } diff --git a/common/manifests/mkdir_p.pp b/common/manifests/mkdir_p.pp new file mode 100644 index 000000000..c19b77255 --- /dev/null +++ b/common/manifests/mkdir_p.pp @@ -0,0 +1,25 @@ +# == Define: common::mkdir_p +# +# Provide `mkdir -p` functionality for a directory +# +# Idea is to use this mkdir_p in conjunction with a file resource +# +# Example usage: +# +# common::mkdir_p { '/some/dir/structure': } +# +# file { '/some/dir/structure': +# ensure => directory, +# require => Common::Mkdir_p['/some/dir/structure'], +# } +# +define common::mkdir_p () { + + validate_absolute_path($name) + + exec { "mkdir_p-${name}": + command => "mkdir -p ${name}", + unless => "test -d ${name}", + path => '/bin:/usr/bin', + } +} diff --git a/common/manifests/mkuser.pp b/common/manifests/mkuser.pp new file mode 100644 index 000000000..0d8e133b1 --- /dev/null +++ b/common/manifests/mkuser.pp @@ -0,0 +1,185 @@ +# == Define: common::mkuser +# +# mkuser creates a user/group that can be realized in the module that employs it +# +# Copyright 2007-2013 Garrett Honeycutt +# contact@garretthoneycutt.com - Licensed GPLv2 +# +# Parameters: +# $uid - UID of user +# $gid - GID of user, defaults to UID +# $group - group name of user, defaults to username +# $shell - user's shell, defaults to '/bin/bash' +# $home - home directory, defaults to /home/ +# $ensure - present by default +# $managehome - true by default +# $manage_dotssh - true by default. creates ~/.ssh +# $comment - comment field for passwd +# $groups - additional groups the user should be associated with +# $password - defaults to '!!' +# $mode - mode of home directory, defaults to 0700 +# $ssh_auth_key - ssh key of the user +# $ssh_auth_key_type - defaults to 'ssh-dss' +# +# Actions: creates a user/group +# +# Requires: +# $uid +# +# Sample Usage: +# # create apachehup user and realize it +# @mkuser { 'apachehup': +# uid => '32001', +# home => '/home/apachehup', +# comment => 'Apache Restart User', +# } # @mkuser +# +# realize Common::Mkuser[apachehup] +# +define common::mkuser ( + $uid, + $gid = undef, + $group = undef, + $shell = undef, + $home = undef, + $ensure = 'present', + $managehome = true, + $manage_dotssh = true, + $comment = 'created via puppet', + $groups = undef, + $password = undef, + $mode = undef, + $ssh_auth_key = undef, + $create_group = true, + $ssh_auth_key_type = undef, +) { + + if $shell { + $myshell = $shell + } else { + $myshell = '/bin/bash' + } + + # if gid is unspecified, match with uid + if $gid { + $mygid = $gid + } else { + $mygid = $uid + } # fi $gid + + # if groups is unspecified, match with name + if $groups { + $mygroups = $groups + } else { + $mygroups = $name + } + + # if group is unspecified, use the username + if $group { + $mygroup = $group + } else { + $mygroup = $name + } + + if $password { + $mypassword = $password + } else { + $mypassword = '!!' + } + + # if home is unspecified, use /home/ + if $home { + $myhome = $home + } else { + $myhome = "/home/${name}" + } + + # if mode is unspecified, use 0700, which is the default when you enable the + # managehome attribute. + if $mode { + $mymode = $mode + } else { + $mymode = '0700' + } + + # create user + user { $name: + ensure => $ensure, + uid => $uid, + gid => $mygid, + shell => $myshell, + groups => $mygroups, + password => $mypassword, + managehome => $managehome, + home => $myhome, + comment => $comment, + } # user + + if $create_group { + group { $name: + ensure => $ensure, + gid => $mygid, + name => $mygroup, + } # group + } + + # If managing home, then set the mode of the home directory. This allows for + # modes other than 0700 for $HOME. + if $managehome == true { + file { $myhome: + owner => $name, + mode => $mymode, + } + } + + # ensure manage_dotssh is boolean + $manage_dotssh_type = type($manage_dotssh) + case $manage_dotssh_type { + 'boolean': { + $my_manage_dotssh = $manage_dotssh + } + 'string': { + $my_manage_dotssh = str2bool($manage_dotssh) + } + default: { + fail("${name}::manage_dotssh is type <${manage_dotssh_type}> and must be boolean or string.") + } + } + + # create ~/.ssh + case $my_manage_dotssh { + true: { + file { "${myhome}/.ssh": + ensure => directory, + mode => '0700', + owner => $name, + group => $name, + require => User[$name], + } # file + } # 'ensure' or true + false: { + # noop + } + default: { + fail("${name}::manage_dotssh is <${manage_dotssh}> and must be true or false") + } + } # case + + # if ssh_auth_key_type is unspecified, use ssh-dss + if $ssh_auth_key_type { + $my_ssh_auth_key_type = $ssh_auth_key_type + } else { + $my_ssh_auth_key_type = 'ssh-dss' + } + + # if we specify a key, then it should be present + if $ssh_auth_key { + ssh_authorized_key { $name: + ensure => present, + user => $name, + key => $ssh_auth_key, + type => $my_ssh_auth_key_type, + require => File["${myhome}/.ssh"], + } + } +} diff --git a/common/manifests/remove_if_empty.pp b/common/manifests/remove_if_empty.pp new file mode 100644 index 000000000..354c53973 --- /dev/null +++ b/common/manifests/remove_if_empty.pp @@ -0,0 +1,18 @@ +# == Define: common::remove_if_empty +# +# Removes a file if it exists and is empty. +# +# Example usage: +# +# common::remove_if_empty { '/path/to/potentially_empty_file': } +# +define common::remove_if_empty () { + + validate_absolute_path($name) + + exec { "remove_if_empty-${name}": + command => "rm -f ${name}", + unless => "test -f ${name}; if [ $? == '0' ]; then test -s ${name}; fi", + path => '/bin:/usr/bin:/sbin:/usr/sbin', + } +} diff --git a/common/manifests/vardir.pp b/common/manifests/vardir.pp deleted file mode 100644 index f7626c94d..000000000 --- a/common/manifests/vardir.pp +++ /dev/null @@ -1,52 +0,0 @@ -# Common puppet utilities module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class common::vardir { # module vardir snippet - if "${::puppet_vardirtmp}" == '' { - if "${::puppet_vardir}" == '' { - # here, we require that the puppetlabs fact exist! - fail('Fact: $puppet_vardir is missing!') - } - $tmp = sprintf("%s/tmp/", regsubst($::puppet_vardir, '\/$', '')) - # base directory where puppet modules can work and namespace in - file { "${tmp}": - ensure => directory, # make sure this is a directory - recurse => false, # don't recurse into directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, - group => nobody, - mode => 600, - backup => false, # don't backup to filebucket - #before => File["${module_vardir}"], # redundant - #require => Package['puppet'], # no puppet module seen - } - } else { - $tmp = sprintf("%s/", regsubst($::puppet_vardirtmp, '\/$', '')) - } - $module_vardir = sprintf("%s/common/", regsubst($tmp, '\/$', '')) - file { "${module_vardir}": # /var/lib/puppet/tmp/common/ - ensure => directory, # make sure this is a directory - recurse => true, # recursively manage directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, group => nobody, mode => 600, backup => false, - require => File["${tmp}"], # File['/var/lib/puppet/tmp/'] - } -} - -# vim: ts=8 diff --git a/common/metadata.json b/common/metadata.json new file mode 100644 index 000000000..9e370c01e --- /dev/null +++ b/common/metadata.json @@ -0,0 +1,53 @@ +{ + "requirements": [ + { + "name": "pe", + "version_requirement": "3.2.x" + }, + { + "name": "puppet", + "version_requirement": "3.x" + } + ], + "operatingsystem_support": [ + { + "operatingsystem": "Debian" + }, + { + "operatingsystem": "RedHat" + }, + { + "operatingsystem": "CentOS" + }, + { + "operatingsystem": "OracleLinux" + }, + { + "operatingsystem": "Scientific" + }, + { + "operatingsystem": "Solaris" + }, + { + "operatingsystem": "SLES" + }, + { + "operatingsystem": "SLED" + }, + { + "operatingsystem": "Ubuntu" + } + ], + "name": "ghoneycutt-common", + "version": "1.2.0", + "author": "ghoneycutt", + "summary": "common module to be applied to all nodes", + "license": "Apache License, Version 2.0", + "source": "git://github.com/ghoneycutt/puppet-module-common.git", + "project_page": "https://github.com/ghoneycutt/puppet-module-common", + "issues_url": "https://github.com/ghoneycutt/puppet-module-common/issues", + "description": "Provide ability to include modules that are common to all nodes.", + "dependencies": [ + {"name":"puppetlabs/stdlib","version_requirement":">= 3.2.0"} + ] +} diff --git a/common/puppet-common.spec.in b/common/puppet-common.spec.in deleted file mode 100644 index 9cd21ab8a..000000000 --- a/common/puppet-common.spec.in +++ /dev/null @@ -1,40 +0,0 @@ -%global puppet_module_version __VERSION__ - -Name: puppet-common -Version: __VERSION__ -#Release: __RELEASE__%{?dist} # use this to make dist specific builds -Release: __RELEASE__ -Summary: A puppet module filled with common puppet utilities -License: AGPLv3+ -URL: https://github.com/purpleidea/puppet-common -Source0: https://download.gluster.org/pub/gluster/purpleidea/puppet-common/SOURCES/puppet-common-%{puppet_module_version}.tar.bz2 -BuildArch: noarch - -Requires: puppet >= 3.0.0 -Requires: puppetlabs-stdlib >= 4.1.0 - -%description -Common puppet utilities that are useful for other puppet modules. - -%prep -%setup -c -q -T -D -a 0 - -find %{_builddir} -type f -name ".*" -exec rm {} + -find %{_builddir} -size 0 -exec rm {} + -find %{_builddir} \( -name "*.pl" -o -name "*.sh" \) -exec chmod +x {} + -find %{_builddir} \( -name "*.pp" -o -name "*.py" \) -exec chmod -x {} + -find %{_builddir} \( -name "*.rb" -o -name "*.erb" \) -exec chmod -x {} + -exec sed -i "/^#!/{d;q}" {} + - -%build - -%install -rm -rf %{buildroot} -# _datadir is typically /usr/share/ -install -d -m 0755 %{buildroot}/%{_datadir}/puppet/modules/ -cp -r puppet-common-%{puppet_module_version} %{buildroot}/%{_datadir}/puppet/modules/common - -%files -%{_datadir}/puppet/modules/* - -# this changelog is auto-generated by git log -%changelog diff --git a/common/spec/classes/init_spec.rb b/common/spec/classes/init_spec.rb new file mode 100644 index 000000000..6ccfd0292 --- /dev/null +++ b/common/spec/classes/init_spec.rb @@ -0,0 +1,88 @@ +require 'spec_helper' +describe 'common' do + + describe 'class common' do + + context 'default options with supported OS' do + let(:facts) { { :osfamily => 'RedHat' } } + + it { should contain_class('common') } + end + + context 'default options with unsupported osfamily, Gentoo, should fail' do + let(:facts) { { :osfamily => 'Gentoo' } } + it do + expect { + should contain_class('common') + }.to raise_error(Puppet::Error,/Supported OS families are Debian, RedHat, Solaris, and Suse. Detected osfamily is Gentoo./) + end + end + + describe 'managing root password' do + context 'manage_root_password => true with default root_password' do + let(:facts) { { :osfamily => 'RedHat' } } + let(:params) { { :manage_root_password => true } } + + it { should contain_class('common') } + + it { + should contain_user('root').with({ + 'password' => '$1$cI5K51$dexSpdv6346YReZcK2H1k.', + }) + } + end + + context 'manage_root_password => true and root_password => foo' do + let(:facts) { { :osfamily => 'RedHat' } } + let(:params) do + { :manage_root_password => true, + :root_password => 'foo', + } + end + + it { should contain_class('common') } + + it { + should contain_user('root').with({ + 'password' => 'foo', + }) + } + end + end + + describe 'managing /opt/$lanana' do + context 'create_opt_lsb_provider_name_dir => true and lsb_provider_name => UNSET [default]' do + let(:facts) { { :osfamily => 'RedHat' } } + let(:params) do + { :create_opt_lsb_provider_name_dir => true, + :lsb_provider_name => 'UNSET', + } + end + + it { should contain_class('common') } + + it { should_not contain_file('/opt/UNSET') } + end + + context 'create_opt_lsb_provider_name_dir => true and lsb_provider_name => foo' do + let(:facts) { { :osfamily => 'RedHat' } } + let(:params) do + { :create_opt_lsb_provider_name_dir => true, + :lsb_provider_name => 'foo', + } + end + + it { should contain_class('common') } + + it { + should contain_file('/opt/foo').with({ + 'ensure' => 'directory', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0755', + }) + } + end + end + end +end diff --git a/common/spec/classes/mkuser_spec.rb b/common/spec/classes/mkuser_spec.rb new file mode 100644 index 000000000..a12eb8cf3 --- /dev/null +++ b/common/spec/classes/mkuser_spec.rb @@ -0,0 +1,286 @@ +require 'spec_helper' + +describe 'common' do + + context 'one user with default values' do + let(:facts) { { :osfamily => 'RedHat' } } + let(:params) do + { :users => { + 'alice' => { + 'uid' => 1000, + } + } + } + end + + it { + should contain_user('alice').with({ + 'uid' => '1000', + 'gid' => '1000', + 'shell' => '/bin/bash', + 'home' => '/home/alice', + 'ensure' => 'present', + 'groups' => 'alice', + 'password' => '!!', + 'managehome' => 'true', + 'comment' => 'created via puppet', + }) + } + + it { + should contain_file('/home/alice').with({ + 'owner' => 'alice', + 'mode' => '0700', + }) + } + + it { + should contain_file('/home/alice/.ssh').with({ + 'ensure' => 'directory', + 'mode' => '0700', + 'owner' => 'alice', + 'group' => 'alice', + }) + } + + it { + should contain_group('alice').with({ + 'ensure' => 'present', + 'gid' => 1000, + 'name' => 'alice', + }) + } + + it { should_not contain_ssh_authorized_key('alice') } + end + + context 'one user with custom values' do + let(:facts) { { :osfamily => 'RedHat' } } + let(:params) do + { :users => { + 'myuser' => { + 'uid' => 2000, + 'group' => 'superusers', + 'gid' => 2000, + 'shell' => '/bin/zsh', + 'home' => '/home/superu', + 'groups' => ['superusers', 'development', 'admins'], + 'password' => 'puppet', + 'mode' => '0701', + 'comment' => 'a puppet master', + } + } + } + end + + it { + should contain_user('myuser').with({ + 'uid' => '2000', + 'gid' => '2000', + 'shell' => '/bin/zsh', + 'home' => '/home/superu', + 'groups' => ['superusers', 'development', 'admins'], + 'password' => 'puppet', + 'comment' => 'a puppet master', + }) + } + + it { + should contain_file('/home/superu').with({ + 'owner' => 'myuser', + 'mode' => '0701', + }) + } + + it { + should contain_file('/home/superu/.ssh').with({ + 'ensure' => 'directory', + 'mode' => '0700', + 'owner' => 'myuser', + 'group' => 'myuser', + }) + } + + it { should_not contain_ssh_authorized_key('myuser') } + end + + context 'two users with default values' do + let(:facts) { { :osfamily => 'RedHat' } } + let(:params) do + { :users => { + 'alice' => { + 'uid' => 1000, + }, + 'bob' => { + 'uid' => 1001, + } + } + } + end + + it { + should contain_user('alice').with({ + 'uid' => 1000, + 'gid' => 1000, + 'shell' => '/bin/bash', + 'home' => '/home/alice', + 'ensure' => 'present', + 'managehome' => true, + 'groups' => 'alice', + 'password' => '!!', + 'comment' => 'created via puppet', + }) + } + + it { + should contain_user('bob').with({ + 'uid' => 1001, + 'gid' => 1001, + 'shell' => '/bin/bash', + 'home' => '/home/bob', + 'ensure' => 'present', + 'managehome' => true, + 'groups' => 'bob', + 'password' => '!!', + 'comment' => 'created via puppet', + }) + } + + it { + should contain_file('/home/alice').with({ + 'owner' => 'alice', + 'mode' => '0700', + }) + } + + it { + should contain_file('/home/bob').with({ + 'owner' => 'bob', + 'mode' => '0700', + }) + } + + it { + should contain_file('/home/alice/.ssh').with({ + 'ensure' => 'directory', + 'mode' => '0700', + 'owner' => 'alice', + 'group' => 'alice', + }) + } + + it { + should contain_file('/home/bob/.ssh').with({ + 'ensure' => 'directory', + 'mode' => '0700', + 'owner' => 'bob', + 'group' => 'bob', + }) + } + + it { + should contain_group('alice').with({ + 'ensure' => 'present', + 'gid' => 1000, + 'name' => 'alice', + }) + } + + it { + should contain_group('bob').with({ + 'ensure' => 'present', + 'gid' => 1001, + 'name' => 'bob', + }) + } + + ['alice','bob'].each do |name| + it { should_not contain_ssh_authorized_key(name) } + end + end + + context 'do not manage home' do + let(:facts) { { :osfamily => 'RedHat' } } + let(:params) do + { :users => { + 'alice' => { + 'uid' => 1000, + 'managehome' => false + } + } + } + end + + it { should_not contain_file('/home/alice') } + + it { should contain_user('alice').with_managehome(false) } + end + + context 'do not manage dotssh' do + let(:facts) { { :osfamily => 'RedHat' } } + let(:params) do + { :users => { + 'alice' => { + 'uid' => 1000, + 'manage_dotssh' => false + } + } + } + end + + it { should_not contain_file('/home/alice/.ssh') } + + it { should_not contain_ssh_authorized_key('alice') } + end + + describe 'with ssh_auth_key parameter specified' do + context 'with defaults for ssh_auth_key_type parameter' do + let(:facts) { { :osfamily => 'RedHat' } } + let(:params) do + { + :users => { + 'alice' => { + 'uid' => 1000, + 'ssh_auth_key' => 'AAAB3NzaC1yc2EAAAABIwAAAQEArGElx46pD6NNnlxVaTbp0ZJMgBKCmbTCT3RaeCk0ZUJtQ8wkcwTtqIXmmiuFsynUT0DFSd8UIodnBOPqitimmooAVAiAi30TtJVzADfPScMiUnBJKZajIBkEMkwUcqsfh630jyBvLPE/kyQcxbEeGtbu1DG3monkeymanOBW1AKc5o+cJLXcInLnbowMG7NXzujT3BRYn/9s5vtT1V9cuZJs4XLRXQ50NluxJI7sVfRPVvQI9EMbTS4AFBXUej3yfgaLSV+nPZC/lmJ2gR4t/tKvMFF9m16f8IcZKK7o0rK7v81G/tREbOT5YhcKLK+0wBfR6RsmHzwy4EddZloyLQ==', + } + } + } + end + + it { + should contain_ssh_authorized_key('alice').with({ + 'ensure' => 'present', + 'user' => 'alice', + 'key' => 'AAAB3NzaC1yc2EAAAABIwAAAQEArGElx46pD6NNnlxVaTbp0ZJMgBKCmbTCT3RaeCk0ZUJtQ8wkcwTtqIXmmiuFsynUT0DFSd8UIodnBOPqitimmooAVAiAi30TtJVzADfPScMiUnBJKZajIBkEMkwUcqsfh630jyBvLPE/kyQcxbEeGtbu1DG3monkeymanOBW1AKc5o+cJLXcInLnbowMG7NXzujT3BRYn/9s5vtT1V9cuZJs4XLRXQ50NluxJI7sVfRPVvQI9EMbTS4AFBXUej3yfgaLSV+nPZC/lmJ2gR4t/tKvMFF9m16f8IcZKK7o0rK7v81G/tREbOT5YhcKLK+0wBfR6RsmHzwy4EddZloyLQ==', + 'type' => 'ssh-dss', + 'require' => 'File[/home/alice/.ssh]', + }) + } + end + + context 'with ssh_auth_key_type parameter specified' do + let(:facts) { { :osfamily => 'RedHat' } } + let(:params) do + { + :users => { + 'alice' => { + 'uid' => 1000, + 'ssh_auth_key' => 'AAAB3NzaC1yc2EAAAABIwAAAQEArGElx46pD6NNnlxVaTbp0ZJMgBKCmbTCT3RaeCk0ZUJtQ8wkcwTtqIXmmiuFsynUT0DFSd8UIodnBOPqitimmooAVAiAi30TtJVzADfPScMiUnBJKZajIBkEMkwUcqsfh630jyBvLPE/kyQcxbEeGtbu1DG3monkeymanOBW1AKc5o+cJLXcInLnbowMG7NXzujT3BRYn/9s5vtT1V9cuZJs4XLRXQ50NluxJI7sVfRPVvQI9EMbTS4AFBXUej3yfgaLSV+nPZC/lmJ2gR4t/tKvMFF9m16f8IcZKK7o0rK7v81G/tREbOT5YhcKLK+0wBfR6RsmHzwy4EddZloyLQ==', + 'ssh_auth_key_type' => 'ssh-rsa', + } + } + } + end + + it { + should contain_ssh_authorized_key('alice').with({ + 'ensure' => 'present', + 'user' => 'alice', + 'key' => 'AAAB3NzaC1yc2EAAAABIwAAAQEArGElx46pD6NNnlxVaTbp0ZJMgBKCmbTCT3RaeCk0ZUJtQ8wkcwTtqIXmmiuFsynUT0DFSd8UIodnBOPqitimmooAVAiAi30TtJVzADfPScMiUnBJKZajIBkEMkwUcqsfh630jyBvLPE/kyQcxbEeGtbu1DG3monkeymanOBW1AKc5o+cJLXcInLnbowMG7NXzujT3BRYn/9s5vtT1V9cuZJs4XLRXQ50NluxJI7sVfRPVvQI9EMbTS4AFBXUej3yfgaLSV+nPZC/lmJ2gR4t/tKvMFF9m16f8IcZKK7o0rK7v81G/tREbOT5YhcKLK+0wBfR6RsmHzwy4EddZloyLQ==', + 'type' => 'ssh-rsa', + 'require' => 'File[/home/alice/.ssh]', + }) + } + end + end +end diff --git a/common/spec/defines/mkdir_p_spec.rb b/common/spec/defines/mkdir_p_spec.rb new file mode 100644 index 000000000..65d86c112 --- /dev/null +++ b/common/spec/defines/mkdir_p_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe 'common::mkdir_p' do + context 'should create new directory' do + let(:title) { '/some/dir/structure' } + + it { + should contain_exec('mkdir_p-/some/dir/structure').with({ + 'command' => 'mkdir -p /some/dir/structure', + 'unless' => 'test -d /some/dir/structure', + }) + } + end + + context 'should fail with a path that is not absolute' do + let(:title) { 'not/a/valid/absolute/path' } + + it do + expect { + should contain_exec('mkdir_p-not/a/valid/absolute/path').with({ + 'command' => 'mkdir -p not/a/valid/absolute/path', + 'unless' => 'test -d not/a/valid/absolute/path', + }) + }.to raise_error(Puppet::Error) + end + end +end diff --git a/common/spec/defines/remove_if_empty_spec.rb b/common/spec/defines/remove_if_empty_spec.rb new file mode 100644 index 000000000..cbe61b2b1 --- /dev/null +++ b/common/spec/defines/remove_if_empty_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe 'common::remove_if_empty' do + context 'should create new directory' do + let(:title) { '/some/dir/structure' } + + it { + should contain_exec('remove_if_empty-/some/dir/structure').with({ + 'command' => 'rm -f /some/dir/structure', + 'unless' => 'test -f /some/dir/structure; if [ $? == \'0\' ]; then test -s /some/dir/structure; fi', + 'path' => '/bin:/usr/bin:/sbin:/usr/sbin', + }) + } + end + + context 'should fail with a path that is not absolute' do + let(:title) { 'not/a/valid/absolute/path' } + + it do + expect { + should contain_exec('remove_if_empty-not/a/valid/absolute/path') + }.to raise_error(Puppet::Error) + end + end +end diff --git a/common/spec/functions/interface2factname_spec.rb b/common/spec/functions/interface2factname_spec.rb new file mode 100644 index 000000000..dbac24511 --- /dev/null +++ b/common/spec/functions/interface2factname_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe 'interface2factname' do + + describe 'should return correct results' do + + it 'should run with eth0' do + should run.with_params('eth0').and_return('ipaddress_eth0') + end + + it 'should run with bond0:0' do + should run.with_params('bond0:0').and_return('ipaddress_bond0_0') + end + + it 'should run with bond0:1' do + should run.with_params('bond0:1').and_return('ipaddress_bond0_1') + end + end +end diff --git a/common/spec/functions/strip_file_extension_spec.rb b/common/spec/functions/strip_file_extension_spec.rb new file mode 100644 index 000000000..77db6500f --- /dev/null +++ b/common/spec/functions/strip_file_extension_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe 'strip_file_extension' do + + describe 'strips file extensions' do + + it 'from single files' do + should run.with_params('puppet.conf', 'conf').and_return('puppet') + end + + it 'and removes full path' do + should run.with_params('/etc/puppet/puppet.conf', 'conf').and_return('puppet') + end + end +end diff --git a/staging/spec/spec_helper.rb b/common/spec/spec_helper.rb similarity index 100% rename from staging/spec/spec_helper.rb rename to common/spec/spec_helper.rb diff --git a/common/templates/again/again.py.erb b/common/templates/again/again.py.erb deleted file mode 100755 index 5eb1c3125..000000000 --- a/common/templates/again/again.py.erb +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -""" -Run puppet again if notified to do so - -This script is only executed by a puppet exec type. When run, it forks and then -waits for the parent process (puppet) to exit. It then runs puppet "again", -with the same arguments it was originally started with. - -The exec type for "again" is already provided. To activate it, you notify it: - - include again - sometype { 'foo': - notify => Exec['again'], # notify it like this - } - -This is particularly useful if you know that when one of your types runs, it -*will* need another puppet execution to finish building. Sadly, for certain -complex puppet modules, this is unavoidable. You can however make sure to avoid -infinite loops, which will just waste system resources. -""" -# Run puppet again if notified to do so -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -import os -import sys -import time -import math -import errno -import argparse - -DELAY = 0 # wait this many seconds after puppet exit before running again -LOCKFILE = '/var/lib/puppet/state/agent_catalog_run.lock' # lockfile path - -def merge(a, b): # merge two hashes and return the union - r = a.copy() - r.update(b) - return r - -def is_running(pid): - """ - os.waitpid() may not work if the process is not a child of the current - process. In that case, using os.kill(pid, 0) may indeed be the best - solution. Note that, in general, there are three likely outcomes of - calling os.kill() on a process: - - * If the process exists and belongs to you, the call succeeds. - * If the process exists but belong to another user, it throws an - OSError with the errno attribute set to errno.EPERM. - * If the process does not exist, it throws an OSError with the errno - attribute set to errno.ESRCH. - """ - try: - os.kill(pid, 0) - except OSError as e: - if e.errno == errno.ESRCH: - return False - return True - -def is_locked(): - """ - Lets us know if a puppet agent is currently running. - """ - # TODO: is there a more reliable way to do this ? This is sort of racy. - return os.path.isfile(LOCKFILE) - -parser = argparse.ArgumentParser(description="Utility used by Puppet Exec['again'].") -parser.add_argument('--delta', dest='delta', action='store', type=int, required=False, default=0) -# start delta timer immediately, instead of waiting till puppet finishes... -parser.add_argument('--start-timer-now', dest='start_timer_now', action='store_true', default=False, required=False) -args = parser.parse_args() - -start = time.time() # TODO: use time.monotonic() if exists -service = False # are we running as a service ? -pid = os.getpid() # my pid -ppid = os.getppid() # parent pid - -# parse parent cmdline -with open("/proc/%d/cmdline" % ppid, 'r') as f: - cmdline = f.read() -argv = cmdline.split("\0") # separated by nulls (the: ^@ character in vim) -if argv[-1] == '': argv.pop() # remove empty element at end of list if exists - -# parse parent environ -with open("/proc/%d/environ" % ppid, 'r') as f: - environ = f.read() - -env = environ.split("\0") -if env[-1] == '': env.pop() # as above, there is a trailing null to remove! -env = map(lambda x: {x.split('=')[0]: '='.join(x.split('=')[1:])}, env) # list! -env = reduce(lambda a,b: merge(a,b), env) # merge the hash list into hash - -# TODO: does the noop detection work when we run as a service ? (probably not!) -# if we're running as no op, then repeat execution won't help -if '--noop' in argv: - sys.exit(0) - -# TODO: do a sanity check to verify that we've got a valid puppet cmdline... - -# heuristic, based on previous experiments, that service agents have a weird $0 -# see: $0 = "puppet agent: applying configuration" in: [...]/puppet/agent.rb -if argv[0].startswith('puppet agent: '): - service = True # bonus - argv = ['/usr/bin/puppet', 'agent', '--test'] - # NOTE: create a shim, if you ever need help debugging :) - # argv.insert(0, '/tmp/shim.sh') - -# if we're running as a service, kick off a "one of" run -# TODO: is there a more reliable way to detect this ? -if not('--test' in argv) and not('-t' in argv): - service = True # this is the main setter of this variable - argv.append('--test') # TODO: this isn't ideal, but it's safe enough! - -# fork a child process. return 0 in the child and the child’s process id in the -# parent. if an error occurs OSError is raised. -try: - fork = os.fork() # TODO: forkpty() instead ? - -except OSError, e: - print >> sys.stderr, "Error forking: %s" % str(e) - sys.exit(1) - -# branch -if fork == 0: # child - # wait for ppid to exit... - # TODO: we can probably remove the service check altogether, because - # actually the ppid does spawn, and then exit i think... it doesn't - # mean we can skip the is_locked() check, but we don't have to verify - # that we're really a service, because the ppid does dissapear. - if not service: # the service pid shouldn't ever exit... - while is_running(ppid): - time.sleep(1) - - if not args.start_timer_now: - start = time.time() # TODO: use time.monotonic() if exists - - # wait for any agent runs to finish... - while is_locked(): - time.sleep(1) - - if service: # we need to wait for is_locked to end first... - if not args.start_timer_now: - start = time.time() # TODO: use time.monotonic() if exists - - # optionally delay before starting puppet again - if DELAY > 0: - time.sleep(int(DELAY)) - - # wait out the --delta timer - while True: - delta = time.time() - start # time elapsed - timeleft = args.delta - delta # time left - if int(timeleft) <= 0: - break - - time.sleep(int(math.ceil(timeleft))) # this much time left - - # wait for any agents to finish running, now that we waited for timers! - # NOTE: if puppet starts running immediately after this spinlock ended, - # but before our exec call runs, then when our exec runs puppet it will - # exit right away due to the puppet lock. this is a race condition, but - # it isn't a problem because the effect of having puppet run right away - # after this spinlock, will be successful, albeit by different means... - while is_locked(): - time.sleep(1) - - # now run puppet the same way it ran in cmdline - # NOTE: env is particularly important or puppet breaks :( - os.execvpe(argv[0], argv, env) # this command does not return! - -else: # parent - print "pid: %d will try to puppet again..." % fork - sys.exit(0) # let puppet exit successfully! - -# vim: ts=8 diff --git a/common/tests/init.pp b/common/tests/init.pp new file mode 100644 index 000000000..1d5279869 --- /dev/null +++ b/common/tests/init.pp @@ -0,0 +1,11 @@ +# The baseline for module testing used by Puppet Labs is that each manifest +# should have a corresponding test manifest that declares that class or defined +# type. +# +# Tests are then run by using puppet apply --noop (to check for compilation errors +# and view a log of events) or by fully applying the test in a virtual environment +# (to compare the resulting system state to the desired state). +# +# Learn more about module testing here: http://docs.puppetlabs.com/guides/tests_smoke.html +# +include common diff --git a/concat/CHANGELOG.md b/concat/CHANGELOG.md index 54367bef3..ec4652b2a 100644 --- a/concat/CHANGELOG.md +++ b/concat/CHANGELOG.md @@ -1,3 +1,16 @@ +##2014-10-28 - Supported Release 1.1.2 + +###Summary + +This release includes bugfixes and test improvements. The module was tested against SLES10 and SLES12 and found to work against those platforms with no module improvements. Metadata was updated to include those as supported platforms. + +####Bugfixes +- newline didn't work for Windows and Solaris. This has been fixed. +- Install certs on Windows for acceptance tests +- Update tests to work with strict variables (no module updates were required) +- Update tests to work on Windows +- Fix typo in CHANGELOG.md + ##2014-09-10 - Supported Release 1.1.1 ###Summary diff --git a/concat/metadata.json b/concat/metadata.json index 5a48a8fd4..62501ab2d 100644 --- a/concat/metadata.json +++ b/concat/metadata.json @@ -1,12 +1,12 @@ { "name": "puppetlabs-concat", - "version": "1.1.1", + "version": "1.1.2", "author": "Puppet Labs", "summary": "Concat module", "license": "Apache-2.0", "source": "https://github.com/puppetlabs/puppetlabs-concat", "project_page": "https://github.com/puppetlabs/puppetlabs-concat", - "issues_url": "https://github.com/puppetlabs/puppetlabs-concat/issues", + "issues_url": "https://tickets.puppetlabs.com/browse/MODULES", "operatingsystem_support": [ { "operatingsystem": "RedHat", diff --git a/concat/spec/spec_helper_acceptance.rb b/concat/spec/spec_helper_acceptance.rb index b34a188d6..1a44ad86b 100644 --- a/concat/spec/spec_helper_acceptance.rb +++ b/concat/spec/spec_helper_acceptance.rb @@ -4,9 +4,13 @@ unless ENV['RS_PROVISION'] == 'no' or ENV['BEAKER_provision'] == 'no' # This will install the latest available package on el and deb based # systems fail on windows and osx, and install via gem on other *nixes - foss_opts = { :default_action => 'gem_install' } + foss_opts = {:default_action => 'gem_install'} - if default.is_pe?; then install_pe; else install_puppet( foss_opts ); end + if default.is_pe?; then + install_pe; + else + install_puppet(foss_opts); + end hosts.each do |host| on hosts, "mkdir -p #{host['distmoduledir']}" @@ -24,6 +28,16 @@ c.before :suite do # Install module and dependencies hosts.each do |host| + if fact_on(host, 'osfamily') == 'windows' + pp = < ['C:\Windows\System32\WindowsPowershell\v1.0','C:\Windows\Sysnative\WindowsPowershell\v1.0'], + command => 'powershell.exe -command "(New-Object System.Net.Webclient).DownloadString(\"https://forge.puppetlabs.com\")"', + } +EOS + apply_manifest_on(host, pp) + end + on host, "mkdir -p #{host['distmoduledir']}/concat" result = on host, "echo #{host['distmoduledir']}/concat" target = result.raw_output.chomp @@ -32,7 +46,7 @@ scp_to host, "#{proj_root}/#{file}", target end #copy_module_to(host, :source => proj_root, :module_name => 'concat') - on host, puppet('module','install','puppetlabs-stdlib'), { :acceptable_exit_codes => [0,1] } + on host, puppet('module', 'install', 'puppetlabs-stdlib'), {:acceptable_exit_codes => [0, 1]} end end diff --git a/corosync/.fixtures.yml b/corosync/.fixtures.yml new file mode 100644 index 000000000..03ea6c47c --- /dev/null +++ b/corosync/.fixtures.yml @@ -0,0 +1,5 @@ +fixtures: + repositories: + stdlib: "git://github.com/puppetlabs/puppetlabs-stdlib.git" + symlinks: + corosync: "#{source_dir}" diff --git a/corosync/.gitignore b/corosync/.gitignore new file mode 100644 index 000000000..353d51f79 --- /dev/null +++ b/corosync/.gitignore @@ -0,0 +1,3 @@ +Gemfile.lock +spec/fixtures +.bundle diff --git a/corosync/.travis.yml b/corosync/.travis.yml new file mode 100644 index 000000000..71054bb2e --- /dev/null +++ b/corosync/.travis.yml @@ -0,0 +1,26 @@ +language: ruby +bundler_args: --without development +script: "bundle exec rake spec SPEC_OPTS='--format documentation'" +rvm: + - 1.8.7 + - 1.9.3 + - 2.0.0 +env: + - PUPPET_GEM_VERSION="~> 2.7.0" + - PUPPET_GEM_VERSION="~> 3.0.0" + - PUPPET_GEM_VERSION="~> 3.1.0" + - PUPPET_GEM_VERSION="~> 3.2.0" +matrix: + exclude: + - rvm: 1.9.3 + env: PUPPET_GEM_VERSION="~> 2.7.0" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 2.7.0" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 3.0.0" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 3.1.0" + - rvm: 1.8.7 + env: PUPPET_GEM_VERSION="~> 3.2.0" +notifications: + email: false diff --git a/corosync/CHANGELOG b/corosync/CHANGELOG new file mode 100644 index 000000000..876ca8fd2 --- /dev/null +++ b/corosync/CHANGELOG @@ -0,0 +1,5 @@ +2012-10-14 - Version 0.1.0 +- Added robustness for general corosync management (read the merges) +- Added `cs_group` type +- Added some testing +- Generally tried to get on top of this thing. diff --git a/corosync/Gemfile b/corosync/Gemfile new file mode 100644 index 000000000..0c9691d2b --- /dev/null +++ b/corosync/Gemfile @@ -0,0 +1,16 @@ +source 'https://rubygems.org' + +group :development, :test do + gem 'rake', :require => false + gem 'rspec-puppet', :require => false + gem 'puppetlabs_spec_helper', :require => false + gem 'puppet-lint', :require => false +end + +if puppetversion = ENV['PUPPET_GEM_VERSION'] + gem 'puppet', puppetversion, :require => false +else + gem 'puppet', :require => false +end + +# vim:ft=ruby diff --git a/corosync/LICENSE b/corosync/LICENSE new file mode 100644 index 000000000..297f85cfa --- /dev/null +++ b/corosync/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2013 Puppet Labs + + 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. diff --git a/corosync/Modulefile b/corosync/Modulefile new file mode 100644 index 000000000..62c57f9d5 --- /dev/null +++ b/corosync/Modulefile @@ -0,0 +1,9 @@ +name 'puppetlabs-corosync' +version '0.1.0' +source 'https://github.com/puppetlabs/puppetlabs-corosync.git' +author 'puppetlabs' +license 'APL 2.0' +summary 'Sets up and manages Corosync' +description 'This module is a set of manifests and types/providers for quickly setting up highly available clusters using Corosync' +project_page 'https://github.com/puppetlabs/puppetlabs-corosync' +dependency 'puppetlabs/stdlib', '>= 2.3.1' diff --git a/corosync/README.md b/corosync/README.md new file mode 100644 index 000000000..decbb2e7f --- /dev/null +++ b/corosync/README.md @@ -0,0 +1,155 @@ +Puppet Labs module for Corosync +============================ + +Corosync is a cluster stack written as a reimplementation of all the core +functionalities required by openais. Meant to provide 100% correct operation +during failures or partitionable networks. + +Most famous for being the cluster stack used by Pacemaker to support n-code +clusters that can respond to node and resource level events. + +Basic usage +----------- + +*To install and configure Corosync* + +```puppet +class { 'corosync': + enable_secauth => true, + authkey => '/var/lib/puppet/ssl/certs/ca.pem', + bind_address => $ipaddress, + multicast_address => '239.1.1.2', +} +``` + +*To enable Pacemaker* + +```puppet +corosync::service { 'pacemaker': + version => '0', +} +``` + +Configuring primitives +------------------------ + +The resources that Corosync will manage can be referred to as a primitive. +These are things like virtual IPs or services like drbd, nginx, and apache. + +*To assign a VIP to a network interface to be used by Nginx* + +```puppet +cs_primitive { 'nginx_vip': + primitive_class => 'ocf', + primitive_type => 'IPaddr2', + provided_by => 'heartbeat', + parameters => { 'ip' => '172.16.210.100', 'cidr_netmask' => '24' }, + operations => { 'monitor' => { 'interval' => '10s' } }, +} +``` + +*Make Corosync manage and monitor the state of Nginx using a custom OCF agent* + +```puppet +cs_primitive { 'nginx_service': + primitive_class => 'ocf', + primitive_type => 'nginx_fixed', + provided_by => 'pacemaker', + operations => { + 'monitor' => { 'interval' => '10s', 'timeout' => '30s' }, + 'start' => { 'interval' => '0', 'timeout' => '30s', 'on-fail' => 'restart' } + }, + require => Cs_primitive['nginx_vip'], +} +``` + +*Make Corosync manage and monitor the state of Apache using a LSB agent* + +```puppet +cs_primitive { 'nginx_service': + primitive_class => 'lsb', + primitive_type => 'apache2', + provided_by => 'heartbeat', + operations => { + 'monitor' => { 'interval' => '10s', 'timeout' => '30s' }, + 'start' => { 'interval' => '0', 'timeout' => '30s', 'on-fail' => 'restart' } + }, + require => Cs_primitive['apache2_vip'], +} +``` + +Configuring colocations +----------------------- + +Colocations keep primitives together. Meaning if a vip moves to web02 from web01 +because web01 just hit the dirt it will drag the nginx service with it. + +```puppet +cs_colocation { 'vip_with_service': + primitives => [ 'nginx_vip', 'nginx_service' ], +} +``` + +Configuring migration or state order +------------------------------------ + +Colocation defines that a set of primitives must live together on the same node +but order definitions will define the order of which each primitive is started. If +Nginx is configured to listen only on our vip we definitely want the vip to be +migrated to a new node before nginx comes up or the migration will fail. + +```puppet +cs_order { 'vip_before_service': + first => 'nginx_vip', + second => 'nginx_service', + require => Cs_colocation['vip_with_service'], +} +``` + +Dependencies +------------ + +Tested and built on Debian 6 using backports so version 1.4.2 of Corosync is validated +to function. + +Notes +----- + +This module doesn't abstract away everything about managing Corosync but makes setup +and automation easier. Things that are currently outstanding... + + * Needs a lot more tests. + * There is already a handful of bugs that need to be worked out. + * Doesn't have any way to configure STONITH + * Plus a other things since Corosync and Pacemaker do a lot. + +We suggest you at least go read the [Clusters from Scratch](http://www.clusterlabs.org/doc/en-US/Pacemaker/1.1/html-single/Clusters_from_Scratch) document +from Cluster Labs. It will help you out a lot when understanding how all the pieces +fall together a point you in the right direction when Corosync fails unexpectedly. + +A simple but complete manifest example can be found on [Cody Herriges' Github](https://github.com/ody/ha-demo), plus +there are more incomplete examples spread across the [Puppet Labs Github](https://github.com/puppetlabs). + +Contributors +------------ + + * [See Puppet Labs Github](https://github.com/puppetlabs/puppetlabs-corosync/graphs/contributors) + +Copyright and License +--------------------- + +Copyright (C) 2012 [Puppet Labs](https://www.puppetlabs.com/) Inc + +Puppet Labs can be contacted at: info@puppetlabs.com + +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. diff --git a/corosync/Rakefile b/corosync/Rakefile new file mode 100644 index 000000000..cd3d37995 --- /dev/null +++ b/corosync/Rakefile @@ -0,0 +1 @@ +require 'puppetlabs_spec_helper/rake_tasks' diff --git a/corosync/lib/puppet/provider/corosync.rb b/corosync/lib/puppet/provider/corosync.rb new file mode 100644 index 000000000..670b1a751 --- /dev/null +++ b/corosync/lib/puppet/provider/corosync.rb @@ -0,0 +1,49 @@ +class Puppet::Provider::Corosync < Puppet::Provider + + # Yep, that's right we are parsing XML...FUN! (It really wasn't that bad) + require 'rexml/document' + + initvars + commands :crm_attribute => 'crm_attribute' + + # Corosync takes a while to build the initial CIB configuration once the + # service is started for the first time. This provides us a way to wait + # until we're up so we can make changes that don't disappear in to a black + # hole. + def self.ready? + cmd = [ command(:crm_attribute), '--type', 'crm_config', '--query', '--name', 'dc-version' ] + raw, status = Puppet::Util::SUIDManager.run_and_capture(cmd) + if status == 0 + return true + else + return false + end + end + + def self.block_until_ready(timeout = 120) + Timeout::timeout(timeout) do + until ready? + debug('Corosync not ready, retrying') + sleep 2 + end + # Sleeping a spare two since it seems that dc-version is returning before + # It is really ready to take config changes, but it is close enough. + # Probably need to find a better way to check for reediness. + sleep 2 + end + end + + def self.prefetch(resources) + instances.each do |prov| + if res = resources[prov.name.to_s] + res.provider = prov + end + end + end + + def exists? + self.class.block_until_ready + debug(@property_hash.inspect) + !(@property_hash[:ensure] == :absent or @property_hash.empty?) + end +end diff --git a/corosync/lib/puppet/provider/cs_colocation/crm.rb b/corosync/lib/puppet/provider/cs_colocation/crm.rb new file mode 100644 index 000000000..893f665a6 --- /dev/null +++ b/corosync/lib/puppet/provider/cs_colocation/crm.rb @@ -0,0 +1,112 @@ +require 'pathname' +require Pathname.new(__FILE__).dirname.dirname.expand_path + 'corosync' + +Puppet::Type.type(:cs_colocation).provide(:crm, :parent => Puppet::Provider::Corosync) do + desc 'Specific provider for a rather specific type since I currently have no plan to + abstract corosync/pacemaker vs. keepalived. This provider will check the state + of current primitive colocations on the system; add, delete, or adjust various + aspects.' + + # Path to the crm binary for interacting with the cluster configuration. + # Decided to just go with relative. + commands :crm => 'crm' + + def self.instances + + block_until_ready + + instances = [] + + cmd = [ command(:crm), 'configure', 'show', 'xml' ] + raw, status = Puppet::Util::SUIDManager.run_and_capture(cmd) + doc = REXML::Document.new(raw) + + doc.root.elements['configuration'].elements['constraints'].each_element('rsc_colocation') do |e| + items = e.attributes + + if items['rsc-role'] + rsc = "#{items['rsc']}:#{items['rsc-role']}" + else + rsc = items['rsc'] + end + + if items ['with-rsc-role'] + with_rsc = "#{items['with-rsc']}:#{items['with-rsc-role']}" + else + with_rsc = items['with-rsc'] + end + + # Sorting the array of primitives because order doesn't matter so someone + # switching the order around shouldn't generate an event. + colocation_instance = { + :name => items['id'], + :ensure => :present, + :primitives => [rsc, with_rsc].sort, + :score => items['score'], + :provider => self.name + } + instances << new(colocation_instance) + end + instances + end + + # Create just adds our resource to the property_hash and flush will take care + # of actually doing the work. + def create + @property_hash = { + :name => @resource[:name], + :ensure => :present, + :primitives => @resource[:primitives], + :score => @resource[:score], + :cib => @resource[:cib], + } + end + + # Unlike create we actually immediately delete the item. + def destroy + debug('Revmoving colocation') + crm('configure', 'delete', @resource[:name]) + @property_hash.clear + end + + # Getter that obtains the primitives array for us that should have + # been populated by prefetch or instances (depends on if your using + # puppet resource or not). + def primitives + @property_hash[:primitives] + end + + # Getter that obtains the our score that should have been populated by + # prefetch or instances (depends on if your using puppet resource or not). + def score + @property_hash[:score] + end + + # Our setters for the primitives array and score. Setters are used when the + # resource already exists so we just update the current value in the property + # hash and doing this marks it to be flushed. + def primitives=(should) + @property_hash[:primitives] = should.sort + end + + def score=(should) + @property_hash[:score] = should + end + + # Flush is triggered on anything that has been detected as being + # modified in the property_hash. It generates a temporary file with + # the updates that need to be made. The temporary file is then used + # as stdin for the crm command. + def flush + unless @property_hash.empty? + updated = "colocation " + updated << "#{@property_hash[:name]} #{@property_hash[:score]}: #{@property_hash[:primitives].join(' ')}" + Tempfile.open('puppet_crm_update') do |tmpfile| + tmpfile.write(updated) + tmpfile.flush + ENV["CIB_shadow"] = @resource[:cib] + crm('configure', 'load', 'update', tmpfile.path.to_s) + end + end + end +end diff --git a/corosync/lib/puppet/provider/cs_commit/crm.rb b/corosync/lib/puppet/provider/cs_commit/crm.rb new file mode 100644 index 000000000..be8406d5d --- /dev/null +++ b/corosync/lib/puppet/provider/cs_commit/crm.rb @@ -0,0 +1,15 @@ +require 'pathname' +require Pathname.new(__FILE__).dirname.dirname.expand_path + 'corosync' + +Puppet::Type.type(:cs_commit).provide(:crm, :parent => Puppet::Provider::Corosync) do + commands :crm => 'crm' + + def self.instances + block_until_ready + [] + end + + def sync(cib) + crm('cib', 'commit', cib) + end +end diff --git a/corosync/lib/puppet/provider/cs_group/crm.rb b/corosync/lib/puppet/provider/cs_group/crm.rb new file mode 100644 index 000000000..92cf0b529 --- /dev/null +++ b/corosync/lib/puppet/provider/cs_group/crm.rb @@ -0,0 +1,95 @@ +require 'pathname' +require Pathname.new(__FILE__).dirname.dirname.expand_path + 'corosync' + +Puppet::Type.type(:cs_group).provide(:crm, :parent => Puppet::Provider::Corosync) do + desc 'Provider to add, delete, manipulate primitive groups.' + + # Path to the crm binary for interacting with the cluster configuration. + commands :crm => '/usr/sbin/crm' + + def self.instances + + block_until_ready + + instances = [] + + cmd = [ command(:crm), 'configure', 'show', 'xml' ] + raw, status = Puppet::Util::SUIDManager.run_and_capture(cmd) + doc = REXML::Document.new(raw) + + REXML::XPath.each(doc, '//group') do |e| + + items = e.attributes + group = { :name => items['id'].to_sym } + + primitives = [] + + if ! e.elements['primitive'].nil? + e.each_element do |p| + primitives << p.attributes['id'] + end + end + + group_instance = { + :name => group[:name], + :ensure => :present, + :primitives => primitives, + :provider => self.name + } + instances << new(group_instance) + end + instances + end + + # Create just adds our resource to the property_hash and flush will take care + # of actually doing the work. + def create + @property_hash = { + :name => @resource[:name], + :ensure => :present, + :primitives => @resource[:primitives] + } + @property_hash[:cib] = @resource[:cib] if ! @resource[:cib].nil? + end + + # Unlike create we actually immediately delete the item but first, like primitives, + # we need to stop the group. + def destroy + debug('Stopping group before removing it') + crm('resource', 'stop', @resource[:name]) + debug('Revmoving group') + crm('configure', 'delete', @resource[:name]) + @property_hash.clear + end + + # Getter that obtains the primitives array for us that should have + # been populated by prefetch or instances (depends on if your using + # puppet resource or not). + def primitives + @property_hash[:primitives] + end + + # Our setters for the primitives array and score. Setters are used when the + # resource already exists so we just update the current value in the property + # hash and doing this marks it to be flushed. + def primitives=(should) + @property_hash[:primitives] = should.sort + end + + # Flush is triggered on anything that has been detected as being + # modified in the property_hash. It generates a temporary file with + # the updates that need to be made. The temporary file is then used + # as stdin for the crm command. + def flush + unless @property_hash.empty? + updated = 'group ' + updated << "#{@property_hash[:name]} #{@property_hash[:primitives].join(' ')}" + Tempfile.open('puppet_crm_update') do |tmpfile| + tmpfile.write(updated) + tmpfile.flush + ENV['CIB_shadow'] = @resource[:cib] + crm('configure', 'load', 'update', tmpfile.path.to_s) + end + end + end +end diff --git a/corosync/lib/puppet/provider/cs_order/crm.rb b/corosync/lib/puppet/provider/cs_order/crm.rb new file mode 100644 index 000000000..c5cd9e07c --- /dev/null +++ b/corosync/lib/puppet/provider/cs_order/crm.rb @@ -0,0 +1,118 @@ +require 'pathname' +require Pathname.new(__FILE__).dirname.dirname.expand_path + 'corosync' + +Puppet::Type.type(:cs_order).provide(:crm, :parent => Puppet::Provider::Corosync) do + desc 'Specific provider for a rather specific type since I currently have no plan to + abstract corosync/pacemaker vs. keepalived. This provider will check the state + of current primitive start orders on the system; add, delete, or adjust various + aspects.' + + # Path to the crm binary for interacting with the cluster configuration. + commands :crm => 'crm' + + def self.instances + + block_until_ready + + instances = [] + + cmd = [ command(:crm), 'configure', 'show', 'xml' ] + raw, status = Puppet::Util::SUIDManager.run_and_capture(cmd) + doc = REXML::Document.new(raw) + + doc.root.elements['configuration'].elements['constraints'].each_element('rsc_order') do |e| + items = e.attributes + + if items['first-action'] + first = "#{items['first']}:#{items['first-action']}" + else + first = items['first'] + end + + if items['then-action'] + second = "#{items['then']}:#{items['then-action']}" + else + second = items['then'] + end + + order_instance = { + :name => items['id'], + :ensure => :present, + :first => first, + :second => second, + :score => items['score'], + :provider => self.name + } + instances << new(order_instance) + end + instances + end + + # Create just adds our resource to the property_hash and flush will take care + # of actually doing the work. + def create + @property_hash = { + :name => @resource[:name], + :ensure => :present, + :first => @resource[:first], + :second => @resource[:second], + :score => @resource[:score], + :cib => @resource[:cib], + } + end + + # Unlike create we actually immediately delete the item. + def destroy + debug('Revmoving order directive') + crm('configure', 'delete', @resource[:name]) + @property_hash.clear + end + + # Getters that obtains the first and second primitives and score in our + # ordering definintion that have been populated by prefetch or instances + # (depends on if your using puppet resource or not). + def first + @property_hash[:first] + end + + def second + @property_hash[:second] + end + + def score + @property_hash[:score] + end + + # Our setters for the first and second primitives and score. Setters are + # used when the resource already exists so we just update the current value + # in the property hash and doing this marks it to be flushed. + def first=(should) + @property_hash[:first] = should + end + + def second=(should) + @property_hash[:second] = should + end + + def score=(should) + @property_hash[:score] = should + end + + # Flush is triggered on anything that has been detected as being + # modified in the property_hash. It generates a temporary file with + # the updates that need to be made. The temporary file is then used + # as stdin for the crm command. + def flush + unless @property_hash.empty? + updated = 'order ' + updated << "#{@property_hash[:name]} #{@property_hash[:score]}: " + updated << "#{@property_hash[:first]} #{@property_hash[:second]}" + Tempfile.open('puppet_crm_update') do |tmpfile| + tmpfile.write(updated) + tmpfile.flush + ENV['CIB_shadow'] = @resource[:cib] + crm('configure', 'load', 'update', tmpfile.path.to_s) + end + end + end +end diff --git a/corosync/lib/puppet/provider/cs_primitive/crm.rb b/corosync/lib/puppet/provider/cs_primitive/crm.rb new file mode 100644 index 000000000..8e3bac69f --- /dev/null +++ b/corosync/lib/puppet/provider/cs_primitive/crm.rb @@ -0,0 +1,235 @@ +require 'pathname' +require Pathname.new(__FILE__).dirname.dirname.expand_path + 'corosync' + +Puppet::Type.type(:cs_primitive).provide(:crm, :parent => Puppet::Provider::Corosync) do + desc 'Specific provider for a rather specific type since I currently have no + plan to abstract corosync/pacemaker vs. keepalived. Primitives in + Corosync are the thing we desire to monitor; websites, ipaddresses, + databases, etc, etc. Here we manage the creation and deletion of + these primitives. We will accept a hash for what Corosync calls + operations and parameters. A hash is used instead of constucting a + better model since these values can be almost anything.' + + # Path to the crm binary for interacting with the cluster configuration. + commands :crm => 'crm' + + # given an XML element containing some s, return a hash. Return an + # empty hash if `e` is nil. + def self.nvpairs_to_hash(e) + return {} if e.nil? + + hash = {} + e.each_element do |i| + hash[(i.attributes['name'])] = i.attributes['value'] + end + + hash + end + + # given an XML element (a from cibadmin), produce a hash suitible + # for creating a new provider instance. + def self.element_to_hash(e) + hash = { + :primitive_class => e.attributes['class'], + :primitive_type => e.attributes['type'], + :provided_by => e.attributes['provider'], + :name => e.attributes['id'].to_sym, + :ensure => :present, + :provider => self.name, + :parameters => nvpairs_to_hash(e.elements['instance_attributes']), + :operations => {}, + :utilization => nvpairs_to_hash(e.elements['utilization']), + :metadata => nvpairs_to_hash(e.elements['meta_attributes']), + :ms_metadata => {}, + :promotable => :false + } + + if ! e.elements['operations'].nil? + e.elements['operations'].each_element do |o| + valids = o.attributes.reject do |k,v| k == 'id' end + hash[:operations][valids['name']] = {} + valids.each do |k,v| + hash[:operations][valids['name']][k] = v if k != 'name' + end + end + end + if e.parent.name == 'master' + hash[:promotable] = :true + if ! e.parent.elements['meta_attributes'].nil? + e.parent.elements['meta_attributes'].each_element do |m| + hash[:ms_metadata][(m.attributes['name'])] = m.attributes['value'] + end + end + end + + hash + end + + def self.instances + + block_until_ready + + instances = [] + + cmd = [ command(:crm), 'configure', 'show', 'xml' ] + raw, status = Puppet::Util::SUIDManager.run_and_capture(cmd) + doc = REXML::Document.new(raw) + + REXML::XPath.each(doc, '//primitive') do |e| + instances << new(element_to_hash(e)) + end + instances + end + + # Create just adds our resource to the property_hash and flush will take care + # of actually doing the work. + def create + @property_hash = { + :name => @resource[:name], + :ensure => :present, + :primitive_class => @resource[:primitive_class], + :provided_by => @resource[:provided_by], + :primitive_type => @resource[:primitive_type], + :promotable => @resource[:promotable] + } + @property_hash[:parameters] = @resource[:parameters] if ! @resource[:parameters].nil? + @property_hash[:operations] = @resource[:operations] if ! @resource[:operations].nil? + @property_hash[:utilization] = @resource[:utilization] if ! @resource[:utilization].nil? + @property_hash[:metadata] = @resource[:metadata] if ! @resource[:metadata].nil? + @property_hash[:ms_metadata] = @resource[:ms_metadata] if ! @resource[:ms_metadata].nil? + @property_hash[:cib] = @resource[:cib] if ! @resource[:cib].nil? + end + + # Unlike create we actually immediately delete the item. Corosync forces us + # to "stop" the primitive before we are able to remove it. + def destroy + debug('Stopping primitive before removing it') + crm('resource', 'stop', @resource[:name]) + debug('Revmoving primitive') + crm('configure', 'delete', @resource[:name]) + @property_hash.clear + end + + # Getters that obtains the parameters and operations defined in our primitive + # that have been populated by prefetch or instances (depends on if your using + # puppet resource or not). + def parameters + @property_hash[:parameters] + end + + def operations + @property_hash[:operations] + end + + def utilization + @property_hash[:utilization] + end + + def metadata + @property_hash[:metadata] + end + + def ms_metadata + @property_hash[:ms_metadata] + end + + def promotable + @property_hash[:promotable] + end + + # Our setters for parameters and operations. Setters are used when the + # resource already exists so we just update the current value in the + # property_hash and doing this marks it to be flushed. + def parameters=(should) + @property_hash[:parameters] = should + end + + def operations=(should) + @property_hash[:operations] = should + end + + def utilization=(should) + @property_hash[:utilization] = should + end + + def metadata=(should) + @property_hash[:metadata] = should + end + + def ms_metadata=(should) + @property_hash[:ms_metadata] = should + end + + def promotable=(should) + case should + when :true + @property_hash[:promotable] = should + when :false + @property_hash[:promotable] = should + crm('resource', 'stop', "ms_#{@resource[:name]}") + crm('configure', 'delete', "ms_#{@resource[:name]}") + end + end + + # Flush is triggered on anything that has been detected as being + # modified in the property_hash. It generates a temporary file with + # the updates that need to be made. The temporary file is then used + # as stdin for the crm command. We have to do a bit of munging of our + # operations and parameters hash to eventually flatten them into a string + # that can be used by the crm command. + def flush + unless @property_hash.empty? + unless @property_hash[:operations].empty? + operations = '' + @property_hash[:operations].each do |o| + operations << "op #{o[0]} " + o[1].each_pair do |k,v| + operations << "#{k}=#{v} " + end + end + end + unless @property_hash[:parameters].empty? + parameters = 'params ' + @property_hash[:parameters].each_pair do |k,v| + parameters << "#{k}=#{v} " + end + end + unless @property_hash[:utilization].empty? + utilization = 'utilization ' + @property_hash[:utilization].each_pair do |k,v| + utilization << "#{k}=#{v} " + end + end + unless @property_hash[:metadata].empty? + metadatas = 'meta ' + @property_hash[:metadata].each_pair do |k,v| + metadatas << "#{k}=#{v} " + end + end + updated = "primitive " + updated << "#{@property_hash[:name]} #{@property_hash[:primitive_class]}:" + updated << "#{@property_hash[:provided_by]}:" if @property_hash[:provided_by] + updated << "#{@property_hash[:primitive_type]} " + updated << "#{operations} " unless operations.nil? + updated << "#{parameters} " unless parameters.nil? + updated << "#{utilization} " unless utilization.nil? + updated << "#{metadatas} " unless metadatas.nil? + if @property_hash[:promotable] == :true + updated << "\n" + updated << "ms ms_#{@property_hash[:name]} #{@property_hash[:name]} " + unless @property_hash[:ms_metadata].empty? + updated << 'meta ' + @property_hash[:ms_metadata].each_pair do |k,v| + updated << "#{k}=#{v} " + end + end + end + Tempfile.open('puppet_crm_update') do |tmpfile| + tmpfile.write(updated) + tmpfile.flush + ENV['CIB_shadow'] = @resource[:cib] + crm('configure', 'load', 'update', tmpfile.path.to_s) + end + end + end +end diff --git a/corosync/lib/puppet/provider/cs_property/crm.rb b/corosync/lib/puppet/provider/cs_property/crm.rb new file mode 100644 index 000000000..5b7287937 --- /dev/null +++ b/corosync/lib/puppet/provider/cs_property/crm.rb @@ -0,0 +1,81 @@ +require 'pathname' # JJM WORK_AROUND #14073 +require Pathname.new(__FILE__).dirname.dirname.expand_path + 'corosync' + +Puppet::Type.type(:cs_property).provide(:crm, :parent => Puppet::Provider::Corosync) do + desc 'Specific provider for a rather specific type since I currently have no plan to + abstract corosync/pacemaker vs. keepalived. This provider will check the state + of Corosync cluster configuration properties.' + + # Path to the crm binary for interacting with the cluster configuration. + commands :crm => 'crm' + commands :cibadmin => 'cibadmin' + + def self.instances + + block_until_ready + + instances = [] + + cmd = [ command(:crm), 'configure', 'show', 'xml' ] + raw, status = Puppet::Util::SUIDManager.run_and_capture(cmd) + doc = REXML::Document.new(raw) + + doc.root.elements['configuration/crm_config/cluster_property_set'].each_element do |e| + items = e.attributes + property = { :name => items['name'], :value => items['value'] } + + property_instance = { + :name => property[:name], + :ensure => :present, + :value => property[:value], + :provider => self.name + } + instances << new(property_instance) + end + instances + end + + # Create just adds our resource to the property_hash and flush will take care + # of actually doing the work. + def create + @property_hash = { + :name => @resource[:name], + :ensure => :present, + :value => @resource[:value], + } + end + + # Unlike create we actually immediately delete the item. + def destroy + debug('Revmoving cluster property') + cibadmin('--scope', 'crm_config', '--delete', '--xpath', "//nvpair[@name='#{resource[:name]}']") + @property_hash.clear + end + + # Getters that obtains the first and second primitives and score in our + # ordering definintion that have been populated by prefetch or instances + # (depends on if your using puppet resource or not). + def value + @property_hash[:value] + end + + # Our setters for the first and second primitives and score. Setters are + # used when the resource already exists so we just update the current value + # in the property hash and doing this marks it to be flushed. + def value=(should) + @property_hash[:value] = should + end + + # Flush is triggered on anything that has been detected as being + # modified in the property_hash. It generates a temporary file with + # the updates that need to be made. The temporary file is then used + # as stdin for the crm command. + def flush + unless @property_hash.empty? + # clear this on properties, in case it's set from a previous + # run of a different corosync type + ENV['CIB_shadow'] = nil + crm('configure', 'property', '$id="cib-bootstrap-options"', "#{@property_hash[:name]}=#{@property_hash[:value]}") + end + end +end diff --git a/corosync/lib/puppet/provider/cs_shadow/crm.rb b/corosync/lib/puppet/provider/cs_shadow/crm.rb new file mode 100644 index 000000000..6ab5c8ef8 --- /dev/null +++ b/corosync/lib/puppet/provider/cs_shadow/crm.rb @@ -0,0 +1,20 @@ +require 'pathname' +require Pathname.new(__FILE__).dirname.dirname.expand_path + 'corosync' + +Puppet::Type.type(:cs_shadow).provide(:crm, :parent => Puppet::Provider::Corosync) do + commands :crm => 'crm' + + def self.instances + block_until_ready + [] + end + + def sync(cib) + begin + crm('cib', 'delete', cib) + rescue => e + # If the CIB doesn't exist, we don't care. + end + crm('cib', 'new', cib) + end +end diff --git a/corosync/lib/puppet/type/cs_colocation.rb b/corosync/lib/puppet/type/cs_colocation.rb new file mode 100644 index 000000000..07772576e --- /dev/null +++ b/corosync/lib/puppet/type/cs_colocation.rb @@ -0,0 +1,91 @@ +module Puppet + newtype(:cs_colocation) do + @doc = "Type for manipulating corosync/pacemaker colocation. Colocation + is the grouping together of a set of primitives so that they travel + together when one of them fails. For instance, if a web server vhost + is colocated with a specific ip address and the web server software + crashes, the ip address with migrate to the new host with the vhost. + + More information on Corosync/Pacemaker colocation can be found here: + + * http://www.clusterlabs.org/doc/en-US/Pacemaker/1.1/html/Clusters_from_Scratch/_ensuring_resources_run_on_the_same_host.html" + + ensurable + + newparam(:name) do + desc "Identifier of the colocation entry. This value needs to be unique + across the entire Corosync/Pacemaker configuration since it doesn't have + the concept of name spaces per type." + + isnamevar + end + + newproperty(:primitives, :array_matching => :all) do + desc "Two Corosync primitives to be grouped together. Colocation groups + come in twos and order is irrelavent. Property will raise an error if + you do not provide a two value array." + + # Have to redefine should= here so we can sort the array that is given to + # us by the manifest. While were checking on the class of our value we + # are going to go ahead and do some validation too. The way Corosync + # colocation works we need to only accept two value arrays. + def should=(value) + super + if value.is_a? Array + raise Puppet::Error, "Puppet::Type::Cs_Colocation: The primitives property must be a two value array." unless value.size == 2 + @should.sort! + else + raise Puppet::Error, "Puppet::Type::Cs_Colocation: The primitives property must be a two value array." + @should + end + end + end + + newparam(:cib) do + desc "Corosync applies its configuration immediately. Using a CIB allows + you to group multiple primitives and relationships to be applied at + once. This can be necessary to insert complex configurations into + Corosync correctly. + + This paramater sets the CIB this colocation should be created in. A + cs_shadow resource with a title of the same name as this value should + also be added to your manifest." + end + + newproperty(:score) do + desc "The priority of this colocation. Primitives can be a part of + multiple colocation groups and so there is a way to control which + primitives get priority when forcing the move of other primitives. + This value can be an integer but is often defined as the string + INFINITY." + + defaultto 'INFINITY' + end + + autorequire(:cs_shadow) do + [ @parameters[:cib] ] + end + + autorequire(:service) do + [ 'corosync' ] + end + + autorequire(:cs_primitive) do + autos = [] + @parameters[:primitives].should.each do |val| + autos << unmunge_cs_primitive(val) + end + + autos + end + + def unmunge_cs_primitive(name) + name = name.split(':')[0] + if name.start_with? 'ms_' + name = name[3..-1] + end + + name + end + end +end diff --git a/corosync/lib/puppet/type/cs_commit.rb b/corosync/lib/puppet/type/cs_commit.rb new file mode 100644 index 000000000..ef894680b --- /dev/null +++ b/corosync/lib/puppet/type/cs_commit.rb @@ -0,0 +1,54 @@ +module Puppet + newtype(:cs_commit) do + @doc = "This type is an implementation detail. DO NOT use it directly" + newproperty(:cib) do + def sync + provider.sync(self.should) + end + + def retrieve + :absent + end + + def insync?(is) + false + end + + defaultto { @resource[:name] } + end + + newparam(:name) do + isnamevar + end + + autorequire(:cs_shadow) do + [ @parameters[:cib].should ] + end + + autorequire(:service) do + [ 'corosync' ] + end + + autorequire(:cs_primitive) do + resources_with_cib :cs_primitive + end + + autorequire(:cs_colocation) do + resources_with_cib :cs_colocation + end + + autorequire(:cs_order) do + resources_with_cib :cs_order + end + + def resources_with_cib(cib) + autos = [] + + catalog.resources.find_all { |r| r.is_a?(Puppet::Type.type(cib)) and param = r.parameter(:cib) and param.value == @parameters[:cib].should }.each do |r| + autos << r + end + + autos + end + end +end diff --git a/corosync/lib/puppet/type/cs_group.rb b/corosync/lib/puppet/type/cs_group.rb new file mode 100644 index 000000000..fc5d42638 --- /dev/null +++ b/corosync/lib/puppet/type/cs_group.rb @@ -0,0 +1,72 @@ +module Puppet + newtype(:cs_group) do + @doc = "Type for manipulating Corosync/Pacemkaer group entries. + Groups are a set or resources (primitives) that need to be + grouped together. + + More information can be found at the following link: + + * http://www.clusterlabs.org/doc/en-US/Pacemaker/1.1/html/Pacemaker_Explained/ch-advanced-resources.html#group-resources" + + ensurable + + newparam(:name) do + desc "Name identifier of this group entry. This value needs to be unique + across the entire Corosync/Pacemaker configuration since it doesn't have + the concept of name spaces per type." + isnamevar + end + + newproperty(:primitives, :array_matching => :all) do + desc "An array of primitives to have in this group. Must be listed in the + order that you wish them to start." + + # Have to redefine should= here so we can sort the array that is given to + # us by the manifest. While were checking on the class of our value we + # are going to go ahead and do some validation too. The way Corosync + # colocation works we need to only accept two value arrays. + def should=(value) + super + raise Puppet::Error, "Puppet::Type::Cs_Group: primitives property must be at least a 2-element array." unless value.is_a? Array and value.length > 1 + @should + end + end + + newparam(:cib) do + desc "Corosync applies its configuration immediately. Using a CIB allows + you to group multiple primitives and relationships to be applied at + once. This can be necessary to insert complex configurations into + Corosync correctly. + + This paramater sets the CIB this order should be created in. A + cs_shadow resource with a title of the same name as this value should + also be added to your manifest." + end + + autorequire(:cs_shadow) do + [ @parameters[:cib] ] + end + + autorequire(:service) do + [ 'corosync' ] + end + + autorequire(:cs_primitive) do + autos = [] + @parameters[:primitives].should.each do |val| + autos << unmunge_cs_primitive(val) + end + + autos + end + + def unmunge_cs_primitive(name) + name = name.split(':')[0] + if name.start_with? 'ms_' + name = name[3..-1] + end + + name + end + end +end diff --git a/corosync/lib/puppet/type/cs_order.rb b/corosync/lib/puppet/type/cs_order.rb new file mode 100644 index 000000000..4c755c745 --- /dev/null +++ b/corosync/lib/puppet/type/cs_order.rb @@ -0,0 +1,80 @@ +module Puppet + newtype(:cs_order) do + @doc = "Type for manipulating Corosync/Pacemkaer ordering entries. Order + entries are another type of constraint that can be put on sets of + primitives but unlike colocation, order does matter. These designate + the order at which you need specific primitives to come into a desired + state before starting up a related primitive. + + More information can be found at the following link: + + * http://www.clusterlabs.org/doc/en-US/Pacemaker/1.1/html/Clusters_from_Scratch/_controlling_resource_start_stop_ordering.html" + + ensurable + + newparam(:name) do + desc "Name identifier of this ordering entry. This value needs to be unique + across the entire Corosync/Pacemaker configuration since it doesn't have + the concept of name spaces per type." + isnamevar + end + + newproperty(:first) do + desc "First Corosync primitive. Just like colocation, our primitives for + ording come in pairs but this time order matters so we need to define + which primitive starts the desired state change chain." + end + + newproperty(:second) do + desc "Second Corosync primitive. Our second primitive will move to the + desired state after the first primitive." + end + + newparam(:cib) do + desc "Corosync applies its configuration immediately. Using a CIB allows + you to group multiple primitives and relationships to be applied at + once. This can be necessary to insert complex configurations into + Corosync correctly. + + This paramater sets the CIB this order should be created in. A + cs_shadow resource with a title of the same name as this value should + also be added to your manifest." + end + + newproperty(:score) do + desc "The priority of the this ordered grouping. Primitives can be a part + of multiple order groups and so there is a way to control which + primitives get priority when forcing the order of state changes on + other primitives. This value can be an integer but is often defined + as the string INFINITY." + + defaultto 'INFINITY' + end + + autorequire(:cs_shadow) do + [ @parameters[:cib] ] + end + + autorequire(:service) do + [ 'corosync' ] + end + + autorequire(:cs_primitive) do + autos = [] + + autos << unmunge_cs_primitive(@parameters[:first].should) + autos << unmunge_cs_primitive(@parameters[:second].should) + + autos + end + + def unmunge_cs_primitive(name) + name = name.split(':')[0] + if name.start_with? 'ms_' + name = name[3..-1] + end + + name + end + end +end diff --git a/corosync/lib/puppet/type/cs_primitive.rb b/corosync/lib/puppet/type/cs_primitive.rb new file mode 100644 index 000000000..b36fed6a9 --- /dev/null +++ b/corosync/lib/puppet/type/cs_primitive.rb @@ -0,0 +1,173 @@ +module Puppet + newtype(:cs_primitive) do + @doc = "Type for manipulating Corosync/Pacemaker primitives. Primitives + are probably the most important building block when creating highly + available clusters using Corosync and Pacemaker. Each primitive defines + an application, ip address, or similar to monitor and maintain. These + managed primitives are maintained using what is called a resource agent. + These resource agents have a concept of class, type, and subsystem that + provides the functionality. Regretibly these pieces of vocabulary + clash with those used in Puppet so to overcome the name clashing the + property and parameter names have been qualified a bit for clarity. + + More information on primitive definitions can be found at the following + link: + + * http://www.clusterlabs.org/doc/en-US/Pacemaker/1.1/html/Clusters_from_Scratch/_adding_a_resource.html" + + ensurable + + newparam(:name) do + desc "Name identifier of primitive. This value needs to be unique + across the entire Corosync/Pacemaker configuration since it doesn't have + the concept of name spaces per type." + + isnamevar + end + + newparam(:primitive_class) do + desc "Corosync class of the primitive. Examples of classes are lsb or ocf. + Lsb funtions a lot like the init provider in Puppet for services, an init + script is ran periodically on each host to identify status, or to start + and stop a particular application. Ocf of the other hand is a script with + meta-data and stucture that is specific to Corosync and Pacemaker." + end + + newparam(:primitive_type) do + desc "Corosync primitive type. Type generally matches to the specific + 'thing' your managing, i.e. ip address or vhost. Though, they can be + completely arbitarily named and manage any number of underlying + applications or resources." + end + + newparam(:provided_by) do + desc "Corosync primitive provider. All resource agents used in a primitve + have something that provides them to the system, be it the Pacemaker or + redhat plugins...they're not always obvious though so currently you're + left to understand Corosync enough to figure it out. Usually, if it isn't + obvious it is because there is only one provider for the resource agent. + + To find the list of providers for a resource agent run the following + from the command line has Corosync installed: + + * `crm configure ra providers `" + end + + newparam(:cib) do + desc "Corosync applies its configuration immediately. Using a CIB allows + you to group multiple primitives and relationships to be applied at + once. This can be necessary to insert complex configurations into + Corosync correctly. + + This paramater sets the CIB this primitive should be created in. A + cs_shadow resource with a title of the same name as this value should + also be added to your manifest." + end + + # Our parameters and operations properties must be hashes. + newproperty(:parameters) do + desc "A hash of params for the primitive. Parameters in a primitive are + used by the underlying resource agent, each class using them slightly + differently. In ocf scripts they are exported and pulled into the + script as variables to be used. Since the list of these parameters + are completely arbitrary and validity not enforced we simply defer + defining a model and just accept a hash." + + validate do |value| + raise Puppet::Error, "Puppet::Type::Cs_Primitive: parameters property must be a hash." unless value.is_a? Hash + end + + defaultto Hash.new + end + + newproperty(:operations) do + desc "A hash of operations for the primitive. Operations defined in a + primitive are little more predictable as they are commonly things like + monitor or start and their values are in seconds. Since each resource + agent can define its own set of operations we are going to defer again + and just accept a hash. There maybe room to model this one but it + would require a review of all resource agents to see if each operation + is valid." + + validate do |value| + raise Puppet::Error, "Puppet::Type::Cs_Primitive: operations property must be a hash." unless value.is_a? Hash + end + + defaultto Hash.new + end + + newproperty(:utilization) do + desc "A hash of utilization attributes for the primitive. If nodes are + also configured with available resources, and Pacemaker's placement + stratgey is set appropriately, then Pacemaker can place primitives on + nodes only where resources are available. + + See the Pacemaker documentation: + + http://clusterlabs.org/doc/en-US/Pacemaker/1.1/html/Pacemaker_Explained/ch11.html" + + validate do |value| + raise Puppet::Error, "Puppet::Type::Cs_Primitive: utilization property must be a hash." unless value.is_a? Hash + end + + defaultto Hash.new + end + + newproperty(:metadata) do + desc "A hash of metadata for the primitive. A primitive can have a set of + metadata that doesn't affect the underlying Corosync type/provider but + affect that concept of a resource. This metadata is similar to Puppet's + resources resource and some meta-parameters, they change resource + behavior but have no affect of the data that is synced or manipulated." + + validate do |value| + raise Puppet::Error, "Puppet::Type::Cs_Primitive: metadata property must be a hash." unless value.is_a? Hash + end + + defaultto Hash.new + end + + newproperty(:ms_metadata) do + desc "A hash of metadata for the master/slave primitive state." + + munge do |value_hash| + value_hash.inject({}) do |memo,(key,value)| + memo[key] = String(value) + memo + end + end + + validate do |value| + raise Puppet::Error, "Puppet::Type::Cs_Primitive: ms_metadata property must be a hash" unless value.is_a? Hash + end + + defaultto Hash.new + end + + newproperty(:promotable) do + desc "Designates if the primitive is capable of being managed in a master/slave + state. This will create a new ms resource in your Corosync config and add + this primitive to it. Concequently Corosync will be helpful and update all + your colocation and order resources too but Puppet won't. Currenlty we unmunge + configuraiton entries that start with ms_ so that you don't have to account for + name change in all our manifests." + + newvalues(:true, :false) + + defaultto :false + end + + autorequire(:cs_shadow) do + autos = [] + if @parameters[:cib] + autos << @parameters[:cib].value + end + + autos + end + + autorequire(:service) do + [ 'corosync' ] + end + end +end diff --git a/corosync/lib/puppet/type/cs_property.rb b/corosync/lib/puppet/type/cs_property.rb new file mode 100644 index 000000000..f4f3d2ad2 --- /dev/null +++ b/corosync/lib/puppet/type/cs_property.rb @@ -0,0 +1,39 @@ +module Puppet + newtype(:cs_property) do + @doc = "Type for manipulating corosync/pacemaker configuration properties. + Besides the configuration file that is managed by the module the contains + all these related Corosync types and providers, there is a set of cluster + properties that can be set and saved inside the CIB (A CIB being a set of + configuration that is synced across the cluster, it can be exported as XML + for processing and backup). The type is pretty simple interface for + setting key/value pairs or removing them completely. Removing them will + result in them taking on their default value. + + More information on cluster properties can be found here: + + * http://www.clusterlabs.org/doc/en-US/Pacemaker/1.1/html/Pacemaker_Explained/_cluster_options.html + + P.S Looked at generating a type dynamically from the cluster's property + meta-data that would result in a single type with puppet type properties + of every cluster property...may still do so in a later iteration." + + ensurable + + newparam(:name) do + desc "Name identifier of this property. Simply the name of the cluster + property. Happily most of these are unique." + + isnamevar + end + + newproperty(:value) do + desc "Value of the property. It is expected that this will be a single + value but we aren't validating string vs. integer vs. boolean because + cluster properties can range the gambit." + end + + autorequire(:service) do + [ 'corosync' ] + end + end +end diff --git a/corosync/lib/puppet/type/cs_shadow.rb b/corosync/lib/puppet/type/cs_shadow.rb new file mode 100644 index 000000000..29968af3e --- /dev/null +++ b/corosync/lib/puppet/type/cs_shadow.rb @@ -0,0 +1,38 @@ +module Puppet + newtype(:cs_shadow) do + @doc = "cs_shadow resources represent a Corosync shadow CIB. Any corosync + resources defined with 'cib' set to the title of a cs_shadow resource + will not become active until all other resources with the same cib + value have also been applied." + + newproperty(:cib) do + def sync + provider.sync(self.should) + end + + def retrieve + :absent + end + + def insync?(is) + false + end + + defaultto { @resource[:name] } + end + + newparam(:name) do + desc "Name of the shadow CIB to create and manage" + isnamevar + end + + def generate + options = { :name => @title } + [ Puppet::Type.type(:cs_commit).new(options) ] + end + + autorequire(:service) do + [ 'corosync' ] + end + end +end diff --git a/corosync/manifests/init.pp b/corosync/manifests/init.pp new file mode 100644 index 000000000..b9a375cb9 --- /dev/null +++ b/corosync/manifests/init.pp @@ -0,0 +1,227 @@ +# == Class: corosync +# +# This class will set up corosync for use by the Puppet Enterprise console to +# facilitate an active/standby configuration for high availability. It is +# assumed that this module has been initially ran on a Puppet master with the +# capabilities of signing certificates to do the initial key generation. +# +# === Parameters +# +# [*enable_secauth*] +# Controls corosync's ability to authenticate and encrypt multicast messages. +# +# [*authkey*] +# Specifies the path to the CA which is used to sign Corosync's certificate. +# +# [*threads*] +# How many threads you are going to let corosync use to encode and decode +# multicast messages. If you turn off secauth then corosync wil ignore +# threads. +# +# [*bind_address*] +# The ip address we are going to bind the corosync daemon too. +# +# [*port*] +# The udp port that corosync will use to do its multcast communication. Be +# aware that corosync used this defined port plus minus one. +# +# [*multicast_address*] +# An IP address that has been reserved for multicast traffic. This is the +# default way that Corosync accomplishes communication across the cluster. +# +# [*unicast_addresses*] +# An array of IP addresses that make up the cluster's members. These are +# use if you are able to use multicast on your network and instead opt for +# the udpu transport. You need a relatively recent version of Corosync to +# make this possible. +# +# [*force_online*] +# True/false parameter specifying whether to force nodes that have been put +# in standby back online. +# +# [*check_standby*] +# True/false parameter specifying whether puppet should return an error log +# message if a node is in standby. Useful for monitoring node state. +# +# [*debug*] +# True/false parameter specifying whether Corosync should produce debug +# output in its logs. +# +# === Examples +# +# class { 'corosync': +# enable_secauth => false, +# bind_address => '192.168.2.10', +# multicast_address => '239.1.1.2', +# } +# +# === Authors +# +# Cody Herriges +# +# === Copyright +# +# Copyright 2012, Puppet Labs, LLC. +# +class corosync( + $enable_secauth = 'UNSET', + $authkey = '/etc/puppet/ssl/certs/ca.pem', + $threads = 'UNSET', + $port = 'UNSET', + $bind_address = 'UNSET', + $multicast_address = 'UNSET', + $unicast_addresses = 'UNSET', + $force_online = false, + $check_standby = false, + $debug = false, +) { + + # Making it possible to provide data with parameterized class declarations or + # Console. + $threads_real = $threads ? { + 'UNSET' => $::threads ? { + undef => $::processorcount, + default => $::threads, + }, + default => $threads, + } + + $port_real = $port ? { + 'UNSET' => $::port ? { + undef => '5405', + default => $::port, + }, + default => $port, + } + + $bind_address_real = $bind_address ? { + 'UNSET' => $::bind_address ? { + undef => $::ipaddress, + default => $::bind_address, + }, + default => $bind_address, + } + + $unicast_addresses_real = $unicast_addresses ? { + 'UNSET' => $::unicast_addresses ? { + undef => 'UNSET', + default => $::unicast_addresses + }, + default => $unicast_addresses + } + if $unicast_addresses_real == 'UNSET' { + $corosync_conf = "${module_name}/corosync.conf.erb" + } else { + $corosync_conf = "${module_name}/corosync.conf.udpu.erb" + } + + # We use an if here instead of a selector since we need to fail the catalog if + # this value is provided. This is emulating a required variable as defined in + # parameterized class. + + # $multicast_address is NOT required if $unicast_address is provided + if $multicast_address == 'UNSET' and $unicast_addresses_real == 'UNSET' { + if ! $::multicast_address { + fail('You must provide a value for multicast_address') + } else { + $multicast_address_real = $::multicast_address + } + } else { + $multicast_address_real = $multicast_address + } + + if $enable_secauth == 'UNSET' { + case $::enable_secauth { + true: { $enable_secauth_real = 'on' } + false: { $enable_secauth_real = 'off' } + undef: { $enable_secauth_real = 'on' } + '': { $enable_secauth_real = 'on' } + default: { validate_re($::enable_secauth, '^true$|^false$') } + } + } else { + case $enable_secauth { + true: { $enable_secauth_real = 'on' } + false: { $enable_secauth_real = 'off' } + default: { fail('The enable_secauth class parameter requires a true or false boolean') } + } + } + + # Using the Puppet infrastructure's ca as the authkey, this means any node in + # Puppet can join the cluster. Totally not ideal, going to come up with + # something better. + if $enable_secauth_real == 'on' { + file { '/etc/corosync/authkey': + ensure => file, + source => $authkey, + mode => '0400', + owner => 'root', + group => 'root', + notify => Service['corosync'], + require => Package['corosync'], + } + } + + package { [ 'corosync', 'pacemaker' ]: ensure => present } + + # Template uses: + # - $unicast_addresses + # - $multicast_address + # - $debug + # - $bind_address_real + # - $port_real + # - $enable_secauth_real + # - $threads_real + file { '/etc/corosync/corosync.conf': + ensure => file, + mode => '0644', + owner => 'root', + group => 'root', + content => template($corosync_conf), + require => Package['corosync'], + } + + file { '/etc/corosync/service.d': + ensure => directory, + mode => '0755', + owner => 'root', + group => 'root', + recurse => true, + purge => true, + require => Package['corosync'] + } + + if $::osfamily == 'Debian' { + exec { 'enable corosync': + command => 'sed -i s/START=no/START=yes/ /etc/default/corosync', + path => [ '/bin', '/usr/bin' ], + unless => 'grep START=yes /etc/default/corosync', + require => Package['corosync'], + before => Service['corosync'], + } + } + + if $check_standby == true { + # Throws a puppet error if node is on standby + exec { 'check_standby node': + command => 'echo "Node appears to be on standby" && false', + path => [ '/bin', '/usr/bin', '/sbin', '/usr/sbin' ], + onlyif => "crm node status|grep ${::hostname}-standby|grep 'value=\"on\"'", + require => Service['corosync'], + } + } + + if $force_online == true { + exec { 'force_online node': + command => 'crm node online', + path => [ '/bin', '/usr/bin', '/sbin', '/usr/sbin' ], + onlyif => "crm node status|grep ${::hostname}-standby|grep 'value=\"on\"'", + require => Service['corosync'], + } + } + + service { 'corosync': + ensure => running, + enable => true, + subscribe => File[ [ '/etc/corosync/corosync.conf', '/etc/corosync/service.d' ] ], + } +} diff --git a/corosync/manifests/reprobe.pp b/corosync/manifests/reprobe.pp new file mode 100644 index 000000000..6a4617ff8 --- /dev/null +++ b/corosync/manifests/reprobe.pp @@ -0,0 +1,37 @@ +# == Class: corosync::reprobe +# +# Include this class to reprobe the corosync cluster when there are changes in +# any of the native cs_* types. Useful for multi-node provisioning when the +# nodes are not always in a stable state after provisioning. +# +# === Examples +# +# Reprobe corosync after making cluster configuration changes: +# +# include corosync::reprobe +# +# === Copyright +# +# Copyright 2012 Puppet Labs, LLC. +# +class corosync::reprobe { + exec { 'crm resource reprobe': + path => ['/bin','/usr/bin','/sbin','/usr/sbin'], + refreshonly => true, + } + Cs_primitive <| |> { + notify => Exec['crm resource reprobe'], + } + Cs_colocation <| |> { + notify => Exec['crm resource reprobe'], + } + Cs_order <| |> { + notify => Exec['crm resource reprobe'], + } + Cs_group <| |> { + notify => Exec['crm resource reprobe'], + } + Cs_commit <| |> { + notify => Exec['crm resource reprobe'], + } +} diff --git a/corosync/manifests/service.pp b/corosync/manifests/service.pp new file mode 100644 index 000000000..a40a5e72b --- /dev/null +++ b/corosync/manifests/service.pp @@ -0,0 +1,44 @@ +# == Define: corosync::service +# +# Models a Corosync service. Corosync services are plugins that provide +# functionality for monitoring cluster resources. One of the most common +# of these plugins being Pacemaker. +# +# === Parameters +# +# [*namevar*] +# The namevar in this type is the title you give it when you define a resource +# instance. It is used for a handful of purposes; defining the name of the +# config file and the name defined inside the file itself. +# +# [*version*] +# Version of the protocol used by this service. +# +# === Examples +# +# Provide some examples on how to use this type: +# +# corosync::service { 'pacemaker': +# version => '0', +# } +# +# === Authors +# +# Cody Herriges +# +# === Copyright +# +# Copyright 2012 Puppet Labs, LLC. +# +define corosync::service($version) { + + file { "/etc/corosync/service.d/${name}": + ensure => file, + content => template("${module_name}/service.erb"), + mode => '0644', + owner => 'root', + group => 'root', + require => Package['corosync'], + notify => Service['corosync'], + } +} diff --git a/corosync/spec/spec.opts b/corosync/spec/spec.opts new file mode 100644 index 000000000..91cd6427e --- /dev/null +++ b/corosync/spec/spec.opts @@ -0,0 +1,6 @@ +--format +s +--colour +--loadby +mtime +--backtrace diff --git a/tempest/spec/spec_helper.rb b/corosync/spec/spec_helper.rb similarity index 100% rename from tempest/spec/spec_helper.rb rename to corosync/spec/spec_helper.rb diff --git a/corosync/spec/unit/puppet/provider/corosync_spec.rb b/corosync/spec/unit/puppet/provider/corosync_spec.rb new file mode 100644 index 000000000..106725b11 --- /dev/null +++ b/corosync/spec/unit/puppet/provider/corosync_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' +require 'puppet/provider/corosync' + +describe Puppet::Provider::Corosync do + let :provider do + described_class.new + end + + it 'declares a crm_attribute command' do + expect{ + described_class.command :crm_attribute + }.to_not raise_error(Puppet::DevError) + end + + describe '#ready' do + before do + # this would probably return nil on the test platform, unless + # crm_attribute happens to be installed. + described_class.stubs(:command).with(:crm_attribute).returns 'crm_attribute' + end + + it 'returns true when crm_attribute exits successfully' do + Puppet::Util::SUIDManager.expects(:run_and_capture).with(['crm_attribute', '--type', 'crm_config', '--query', '--name', 'dc-version']).returns(['', 0]) + + expect(described_class.ready?).to be_true + end + + it 'returns false when crm_attribute exits unsuccessfully' do + Puppet::Util::SUIDManager.expects(:run_and_capture).with(['crm_attribute', '--type', 'crm_config', '--query', '--name', 'dc-version']).returns(['', 1]) + + expect(described_class.ready?).to be_false + end + end +end diff --git a/corosync/spec/unit/puppet/provider/cs_primitive_crm_spec.rb b/corosync/spec/unit/puppet/provider/cs_primitive_crm_spec.rb new file mode 100644 index 000000000..43d646f5c --- /dev/null +++ b/corosync/spec/unit/puppet/provider/cs_primitive_crm_spec.rb @@ -0,0 +1,172 @@ +require 'spec_helper' + +describe Puppet::Type.type(:cs_primitive).provider(:crm) do + before do + described_class.stubs(:command).with(:crm).returns 'crm' + end + + context 'when getting instances' do + let :instances do + + test_cib = <<-EOS + + + + + + + + + + + + + + + + + + + + + + EOS + + described_class.expects(:block_until_ready).returns(nil) + Puppet::Util::SUIDManager.expects(:run_and_capture).with(['crm', 'configure', 'show', 'xml']).at_least_once.returns([test_cib, 0]) + instances = described_class.instances + end + + it 'should have an instance for each ' do + expect(instances.count).to eq(1) + end + + describe 'each instance' do + let :instance do + instances.first + end + + it "is a kind of #{described_class.name}" do + expect(instance).to be_a_kind_of(described_class) + end + + it "is named by the 's id attribute" do + expect(instance.name).to eq(:example_vm) + end + + it "has an primitive_class parameter corresponding to the 's class attribute" do + pending 'knowing the proper way to assert this' + expect(instance.primitive_class).to eq("ocf") + end + + it "has an primitive_type parameter corresponding to the 's type attribute" do + pending 'knowing the proper way to assert this' + expect(instance.primitive_type).to eq("Xen") + end + + it "has an provided_by parameter corresponding to the 's provider attribute" do + pending 'knowing the proper way to assert this' + expect(instance.provided_by).to eq("heartbeat") + end + + it 'has a parameters property corresponding to ' do + expect(instance.parameters).to eq({ + "xmfile" => "/etc/xen/example_vm.cfg", + "name" => "example_vm_name", + }) + end + + it 'has an operations property corresponding to ' do + expect(instance.operations).to eq({ + "start" => {"interval" => "0", "timeout" => "60"}, + "stop" => {"interval" => "0", "timeout" => "40"}, + }) + end + + it 'has a utilization property corresponding to ' do + expect(instance.utilization).to eq({ + "ram" => "256", + }) + end + + it 'has a metadata property corresponding to ' do + expect(instance.metadata).to eq({ + "target-role" => "Started", + "priority" => "7", + }) + end + + it 'has an ms_metadata property' do + pending 'investigation into what should be asserted here' + expect(instance).to respond_to(:ms_metadata) + end + + it "has a promotable property that is :false" do + pending "more investigation into what is appropriate to assert here" + expect(instance.promotable).to eq(:false) + end + end + end + + context 'when flushing' do + let :resource do + Puppet::Type.type(:cs_primitive).new( + :name => 'testResource', + :provider => :crm, + :primitive_class => 'ocf', + :provided_by => 'heartbeat', + :primitive_type => 'IPaddr2') + end + + let :instance do + instance = described_class.new(resource) + instance.create + instance + end + + def expect_update(pattern) + instance.expects(:crm).with { |*args| + if args.slice(0..2) == ['configure', 'load', 'update'] + expect(File.read(args[3])).to match(pattern) + true + else + false + end + } + end + + it 'can flush without changes' do + expect_update(//) + instance.flush + end + + it 'sets operations' do + instance.operations = {'monitor' => {'interval' => '10s'}} + expect_update(/op monitor interval=10s/) + instance.flush + end + + it 'sets utilization' do + instance.utilization = {'waffles' => '5'} + expect_update(/utilization waffles=5/) + instance.flush + end + + it 'sets parameters' do + instance.parameters = {'fluffyness' => '12'} + expect_update(/params fluffyness=12/) + instance.flush + end + + it 'sets metadata' do + instance.metadata = {'target-role' => 'Started'} + expect_update(/meta target-role=Started/) + instance.flush + end + + it 'sets the primitive name and type' do + expect_update(/primitive testResource ocf:heartbeat:IPaddr2/) + instance.flush + end + end +end diff --git a/corosync/spec/unit/puppet/provider/cs_property_crm_spec.rb b/corosync/spec/unit/puppet/provider/cs_property_crm_spec.rb new file mode 100644 index 000000000..feeb8ba89 --- /dev/null +++ b/corosync/spec/unit/puppet/provider/cs_property_crm_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +describe Puppet::Type.type(:cs_property).provider(:crm) do + before do + described_class.stubs(:command).with(:crm).returns 'crm' + end + + context 'when getting instances' do + let :instances do + + test_cib = <<-EOS + + + + + + + + + + + EOS + + described_class.expects(:block_until_ready).returns(nil) + Puppet::Util::SUIDManager.expects(:run_and_capture).with(['crm', 'configure', 'show', 'xml']).at_least_once.returns([test_cib, 0]) + instances = described_class.instances + end + + it 'should have an instance for each in ' do + expect(instances.count).to eq(2) + end + + describe 'each instance' do + let :instance do + instances.first + end + + it "is a kind of #{described_class.name}" do + expect(instance).to be_a_kind_of(described_class) + end + + it "is named by the 's name attribute" do + expect(instance.name).to eq("apples") + end + + it "has a value corresponding to the 's value attribute" do + expect(instance.value).to eq("red") + end + end + end +end diff --git a/corosync/spec/unit/puppet/type/cs_primitive_spec.rb b/corosync/spec/unit/puppet/type/cs_primitive_spec.rb new file mode 100644 index 000000000..1c51c6b5d --- /dev/null +++ b/corosync/spec/unit/puppet/type/cs_primitive_spec.rb @@ -0,0 +1,73 @@ +require 'spec_helper' + +describe Puppet::Type.type(:cs_primitive) do + subject do + Puppet::Type.type(:cs_primitive) + end + + it "should have a 'name' parameter" do + subject.new(:name => "mock_primitive")[:name].should == "mock_primitive" + end + + describe "basic structure" do + it "should be able to create an instance" do + provider_class = Puppet::Type::Cs_primitive.provider(Puppet::Type::Cs_primitive.providers[0]) + Puppet::Type::Cs_primitive.expects(:defaultprovider).returns(provider_class) + + subject.new(:name => "mock_primitive").should_not be_nil + end + + [:name, :primitive_class, :primitive_type, :provided_by, :cib].each do |param| + it "should have a #{param} parameter" do + subject.validparameter?(param).should be_true + end + + it "should have documentation for its #{param} parameter" do + subject.paramclass(param).doc.should be_instance_of(String) + end + end + + [:parameters, :operations, :metadata, :ms_metadata, :promotable].each do |property| + it "should have a #{property} property" do + subject.validproperty?(property).should be_true + end + + it "should have documentation for its #{property} property" do + subject.propertybyname(property).doc.should be_instance_of(String) + end + end + end + + describe "when validating attributes" do + [:parameters, :operations, :metadata, :ms_metadata].each do |attribute| + it "should validate that the #{attribute} attribute defaults to a hash" do + subject.new(:name => "mock_primitive")[:parameters].should == {} + end + + it "should validate that the #{attribute} attribute must be a hash" do + expect { subject.new( + :name => "mock_primitive", + :parameters => "fail" + ) }.to raise_error Puppet::Error, /hash/ + end + end + + it "should validate that the promotable attribute can be true/false" do + [true, false].each do |value| + subject.new( + :name => "mock_primitive", + :promotable => value + )[:promotable].should == value.to_s.to_sym + end + end + + it "should validate that the promotable attribute cannot be other values" do + ["fail", 42].each do |value| + expect { subject.new( + :name => "mock_primitive", + :promotable => value + ) }.to raise_error Puppet::Error, /(true|false)/ + end + end + end +end diff --git a/corosync/templates/corosync.conf.erb b/corosync/templates/corosync.conf.erb new file mode 100644 index 000000000..f79170d38 --- /dev/null +++ b/corosync/templates/corosync.conf.erb @@ -0,0 +1,45 @@ +compatibility: whitetank + +totem { + version: 2 + token: 3000 + token_retransmits_before_loss_const: 10 + join: 60 + consensus: 3600 + vsftype: none + max_messages: 20 + clear_node_high_bit: yes + rrp_mode: none + secauth: <%= @enable_secauth_real %> + threads: <%= @threads_real %> + interface { + ringnumber: 0 + bindnetaddr: <%= @bind_address_real %> + mcastaddr: <%= @multicast_address_real %> + mcastport: <%= @port_real %> + } +} + +logging { + fileline: off + to_stderr: yes + to_logfile: no + to_syslog: yes + syslog_facility: daemon + debug: <%= scope.lookupvar('debug') ? 'on' : 'off' %> + timestamp: on + logger_subsys { + subsys: AMF + debug: off + tags: enter|leave|trace1|trace2|trace3|trace4|trace6 + } +} + +amf { + mode: disabled +} + +aisexec { + user: root + group: root +} diff --git a/corosync/templates/corosync.conf.udpu.erb b/corosync/templates/corosync.conf.udpu.erb new file mode 100644 index 000000000..b67b00e8c --- /dev/null +++ b/corosync/templates/corosync.conf.udpu.erb @@ -0,0 +1,50 @@ +compatibility: whitetank + +totem { + version: 2 + token: 3000 + token_retransmits_before_loss_const: 10 + join: 60 + consensus: 3600 + vsftype: none + max_messages: 20 + clear_node_high_bit: yes + rrp_mode: none + secauth: <%= @enable_secauth_real %> + threads: <%= @threads_real %> + transport: udpu + interface { +<% @unicast_addresses.each do |addr| -%> + member { + memberaddr: <%= addr %> + } +<% end -%> + ringnumber: 0 + bindnetaddr: <%= @bind_address_real %> + mcastport: <%= @port_real %> + } +} + +logging { + fileline: off + to_stderr: yes + to_logfile: no + to_syslog: yes + syslog_facility: daemon + debug: <%= scope.lookupvar('debug') ? 'on' : 'off' %> + timestamp: on + logger_subsys { + subsys: AMF + debug: off + tags: enter|leave|trace1|trace2|trace3|trace4|trace6 + } +} + +amf { + mode: disabled +} + +aisexec { + user: root + group: root +} diff --git a/corosync/templates/service.erb b/corosync/templates/service.erb new file mode 100644 index 000000000..3c10c4ead --- /dev/null +++ b/corosync/templates/service.erb @@ -0,0 +1,4 @@ +service { + name: <%= @name %> + ver: <%= @version %> +} diff --git a/corosync/tests/init.pp b/corosync/tests/init.pp new file mode 100644 index 000000000..81a9c4396 --- /dev/null +++ b/corosync/tests/init.pp @@ -0,0 +1,9 @@ +class { 'corosync': + enable_secauth => false, + bind_address => '0.0.0.0', + multicast_address => '239.1.1.2', +} +corosync::service { 'pacemaker': + version => '0', + notify => Service['corosync'], +} diff --git a/corosync/tests/resources.pp b/corosync/tests/resources.pp new file mode 100644 index 000000000..14a607991 --- /dev/null +++ b/corosync/tests/resources.pp @@ -0,0 +1,65 @@ +cs_property { 'expected-quorum-votes': + ensure => present, + value => '2', +} -> +cs_property { 'no-quorum-policy': + ensure => present, + value => 'ignore', +} -> +cs_property { 'stonith-enabled': + ensure => present, + value => false, +} -> +cs_property { 'placement-strategy': + ensure => absent, + value => 'default', +} -> +cs_primitive { 'bar': + ensure => present, + primitive_class => 'ocf', + provided_by => 'pacemaker', + primitive_type => 'Dummy', + operations => { + 'monitor' => { + 'interval' => '20' + } + }, +} -> +cs_primitive { 'blort': + ensure => present, + primitive_class => 'ocf', + provided_by => 'pacemaker', + primitive_type => 'Dummy', + promotable => true, + operations => { + 'monitor' => { + 'interval' => '20' + }, + 'start' => { + 'interval' => '0', + 'timeout' => '20' + } + }, +} -> +cs_primitive { 'foo': + ensure => present, + primitive_class => 'ocf', + provided_by => 'pacemaker', + primitive_type => 'Dummy', +} -> +cs_colocation { 'foo-with-bar': + ensure => present, + primitives => [ 'foo', 'bar' ], + score => 'INFINITY', +} -> +cs_colocation { 'bar-with-blort': + ensure => present, + primitives => [ 'bar', 'ms_blort' ], + score => 'INFINITY', +} -> +cs_order { 'foo-before-bar': + ensure => present, + first => 'foo', + second => 'bar', + score => 'INFINITY', +} diff --git a/datacat/.fixtures.yml b/datacat/.fixtures.yml new file mode 100644 index 000000000..3b590ff6f --- /dev/null +++ b/datacat/.fixtures.yml @@ -0,0 +1,3 @@ +fixtures: + symlinks: + datacat: "#{source_dir}" diff --git a/datacat/.gitignore b/datacat/.gitignore new file mode 100644 index 000000000..2f19f7df6 --- /dev/null +++ b/datacat/.gitignore @@ -0,0 +1,7 @@ +Gemfile.lock +/spec/fixtures/manifests/site.pp +/spec/fixtures/modules/datacat +/pkg +/.rspec_system +/.ruby-version +/.bundle diff --git a/datacat/.nodeset.yml b/datacat/.nodeset.yml new file mode 100644 index 000000000..767f9cd2f --- /dev/null +++ b/datacat/.nodeset.yml @@ -0,0 +1,31 @@ +--- +default_set: 'centos-64-x64' +sets: + 'centos-59-x64': + nodes: + "main.foo.vm": + prefab: 'centos-59-x64' + 'centos-64-x64': + nodes: + "main.foo.vm": + prefab: 'centos-64-x64' + 'fedora-18-x64': + nodes: + "main.foo.vm": + prefab: 'fedora-18-x64' + 'debian-607-x64': + nodes: + "main.foo.vm": + prefab: 'debian-607-x64' + 'debian-70rc1-x64': + nodes: + "main.foo.vm": + prefab: 'debian-70rc1-x64' + 'ubuntu-server-10044-x64': + nodes: + "main.foo.vm": + prefab: 'ubuntu-server-10044-x64' + 'ubuntu-server-12042-x64': + nodes: + "main.foo.vm": + prefab: 'ubuntu-server-12042-x64' diff --git a/datacat/.rspec b/datacat/.rspec new file mode 100644 index 000000000..09af0ba3b --- /dev/null +++ b/datacat/.rspec @@ -0,0 +1,2 @@ +--format documentation +--backtrace diff --git a/datacat/.travis.yml b/datacat/.travis.yml new file mode 100644 index 000000000..a488b42f3 --- /dev/null +++ b/datacat/.travis.yml @@ -0,0 +1,17 @@ +language: ruby +bundler_args: --without development +script: "bundle exec rake spec SPEC_OPTS='--format documentation'" +rvm: + - 1.8.7 + - 1.9.3 +env: + - PUPPET_GEM_VERSION="~> 3.0.0" + - PUPPET_GEM_VERSION="~> 3.1.0" + - PUPPET_GEM_VERSION="~> 3.2.0" + - PUPPET_GEM_VERSION="~> 3.3.0" + - PUPPET_GEM_VERSION="~> 3.4.0" + - PUPPET_GEM_VERSION="~> 3.5.0" + - PUPPET_GEM_VERSION="~> 3.6.0" + - PUPPET_GEM_VERSION="~> 3.7.0" +notifications: + email: false diff --git a/datacat/Changes b/datacat/Changes new file mode 100644 index 000000000..277500528 --- /dev/null +++ b/datacat/Changes @@ -0,0 +1,56 @@ +0.6.1 Tuesday 23rd September, 2014 + Fix usage with the future parser (Github #16 - Oscar Ferrer) + +0.6.0 Monday 16th June, 2014 + Add ensure to the datacat define (Github #13 - Igor Galić) + +0.5.0 Saturday 10th May, 2014 + Add show_diff to the datacat define for parity with the file + type in puppet 3.2 and onwards (Github #10 - Sebastian Schmidt) + +0.4.3 Saturday 5th April, 2014 + Change the README.md back to github-flavoured markdown as the forge + supports this now (Github #6 - Igor Galić) + Fix for compatibilty with puppet 3.5.0 (Github #7 - Daniel Beyer) + +0.4.2 Thursday 26th September, 2013 + Fix for catalog changes in 3.3.0 when using master/agent (Github #4) + +0.4.1 Thursday 8th August, 2013 + Changes `datacat` define to name the wrapped `file` resource with the + $title of the resource itself. + +0.4.0 Thursday 25th July, 2013 + Added `source_key` parameter to datacat_collector, to allow bypassing the + templating step. + +0.3.0 Tuesday 9th July, 2013 + Add collects parameter to the datacat_collector type, for acts-as + relationships. + +0.2.1 Monday 8th July, 2013 + Bugfix - when modifying the datacat_collector map on path the File should + still match to $path + +0.2.0 Monday 8th July, 2013 + Allow the target parameter of a datacat_fragment to be an array to target + multiple collectors. + The datacat_collector wrapped by the datacat type will now be named for the + resource rather than the file path. + +0.1.0 Friday 5th July, 2013 + Add a order parameter to the datacat_fragment type for relative ordering + of fragment merging. + Exposed the datacat_collector type and allow it to be used for updating + more than just the file type's content parameter. + +0.0.3 Wednesday 26th June, 2013 + Reworked the behaviour of datacat_collector to only refresh the resource + when the output from the template evaluation changes. (Github #3) + +0.0.2 Tuesday 16th April, 2013 + Changed README.md from Github flavored markup to standard markdown to make + the forge happier. + +0.0.1 Tuesday 16th April, 2013 + Initial forge release diff --git a/datacat/Gemfile b/datacat/Gemfile new file mode 100644 index 000000000..76e5abe87 --- /dev/null +++ b/datacat/Gemfile @@ -0,0 +1,22 @@ +#!ruby +source 'https://rubygems.org' + +group :development, :test do + gem 'rake' + gem 'puppetlabs_spec_helper', :require => false + gem 'rspec-system-puppet', '~>2.0' + gem 'puppet-lint' +end + +group :development do + gem 'pry' + gem 'pry-debugger' + gem 'rb-readline' + gem 'awesome_print' +end + +if puppetversion = ENV['PUPPET_GEM_VERSION'] + gem 'puppet', puppetversion, :require => false +else + gem 'puppet', :require => false +end diff --git a/datacat/Modulefile b/datacat/Modulefile new file mode 100644 index 000000000..f3861d938 --- /dev/null +++ b/datacat/Modulefile @@ -0,0 +1,8 @@ +name 'richardc-datacat' +version '0.6.1' +source 'git://github.com/richardc/puppet-datacat.git' +author 'richardc' +license 'Apache 2.0' +summary 'Puppet type for handling data fragments' +description 'A pair of types for handling data fragments and templating' +project_page 'https://github.com/richardc/puppet-datacat' diff --git a/datacat/NOTES.md b/datacat/NOTES.md new file mode 100644 index 000000000..c52744454 --- /dev/null +++ b/datacat/NOTES.md @@ -0,0 +1,4 @@ +As we're using the rspec fixtures structure, we can run them quite simply + + rake spec_prep + puppet apply --modulepath spec/fixtures/modules -e 'include demo1' diff --git a/datacat/README.md b/datacat/README.md new file mode 100644 index 000000000..7260703e4 --- /dev/null +++ b/datacat/README.md @@ -0,0 +1,125 @@ +Puppet types for concatenating data via a template +================================================== + +The `datacat` and `datacat_fragment` types allow you to build up a data +structure which is rendered using a template. This is similar to some of the +common concatenation patterns though the intent should be clearer as it pushes +the boilerplate down into the type. + +[![Build Status](https://travis-ci.org/richardc/puppet-datacat.png)](https://travis-ci.org/richardc/puppet-datacat) + +Sample Usage +------------ + +```puppet +datacat { '/etc/nagios/objects/hostgroups.cfg': + template => "${module_name}/hostgroups.cfg.erb", +} + +datacat_fragment { "${::fqdn} in device hostgroup": + target => '/etc/nagios/objects/hostgroups.cfg', + data => { + device => [ $::fqdn ], + }, +} + +# fred.dc1.notreal has an ilo fred-ilo.dc1.notreal +$ilo_fqdn = regsubst($::fqdn, '\.', '-ilo.') +datacat_fragment { "${ilo_fqdn} in device hostgroup": + target => '/etc/nagios/objects/hostgroups.cfg', + data => { + device => [ $ilo_fqdn ], + }, +} +``` + +And then in your `hostgroups.cfg.erb` + +```erb +# hostgroups.cfg.erb +<% @data.keys.sort.each do |hostgroup| %> +define hostgroup { + name <%= hostgroup %> + members <%= @data[hostgroup].sort.join(',') %> +} +<% end %> +``` + +Will produce something like: + +``` +# /etc/nagios/objects/hostgroups.cfg +define hostgroup { + name device + members fred.dc1.notreal,fred-ilo.dc1.notreal +} +``` + +There are additional samples in a blog post I wrote to describe the approach, +http://richardc.unixbeard.net/2013/02/puppet-concat-patterns/ + +Types and Definitions +--------------------- + +## Defined Type: `datacat` + +Wraps the `datacat_collector` and `file` types to cover the most common +use-case, collecting data for and templating an entire file. + +The `ensure` parameter defaults to `file` (an alias for `present`). `ensure` +can be set to `absent`. In that case `datacat` will make sure the file *does +not exist* and will not collect anything with `datacat_collector`. + +## Type: `datacat_collector` + +The `datacat_collector` type deeply merges a data hash from +the `datacat_fragment` resources that target it. + +These fragments are then rendered via an erb template specified by the +`template_body` parameter and used to update the `target_field` property +of the related `target_resource`. + +Sample usage: + +```puppet +datacat_collector { 'open_ports': + template_body => '<%= @data["ports"].sort.join(",") %>', + target_resource => File_line['open_ports'], + target_field => 'line', +} + +datacat_fragment { 'open webserver': + target => 'open_ports', + data => { ports => [ 80, 443 ] }, +} + +datacat_fragment { 'open ssh': + target => 'open_ports', + data => { ports => [ 22 ] }, +} +``` + +Caveats +------- + +The template is evaluated by the agent at the point of catalog evaluation, +this means you cannot call out to puppet parser functions as you would when +using the usual `template()` function. + + +Copyright and License +--------------------- + +Copyright (C) 2013 Richard Clamp + +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. diff --git a/datacat/Rakefile b/datacat/Rakefile new file mode 100644 index 000000000..4847ddd20 --- /dev/null +++ b/datacat/Rakefile @@ -0,0 +1,12 @@ +require 'rubygems' +require 'bundler/setup' + +Bundler.require :default + +require 'rspec/core/rake_task' +require 'puppetlabs_spec_helper/rake_tasks' +require 'rspec-system/rake_task' + +task :default do + sh %{rake -T} +end diff --git a/datacat/TODO.md b/datacat/TODO.md new file mode 100644 index 000000000..0843d94ed --- /dev/null +++ b/datacat/TODO.md @@ -0,0 +1,11 @@ +# Docs +README.md + +# Add the ability for datacat_fragment to load data from files on agent + + # No reason why the datacat_fragment can't load it's data_from a + # file on the agents disk + datacat_fragment { "hostgroups from yaml file": + target => '/etc/nagios/objects/hostgroups.cfg', + data_from => '/etc/nagios/build/hostgroups.yaml', + } diff --git a/datacat/lib/puppet/parser/functions/template_body.rb b/datacat/lib/puppet/parser/functions/template_body.rb new file mode 100644 index 000000000..1cfcdf82d --- /dev/null +++ b/datacat/lib/puppet/parser/functions/template_body.rb @@ -0,0 +1,8 @@ +Puppet::Parser::Functions::newfunction(:template_body, :type => :rvalue) do |args| + args.collect do |file| + unless filename = Puppet::Parser::Files.find_template(file, self.compiler.environment) + raise Puppet::ParseError, "Could not find template '#{filename}'" + end + File.read(filename) + end.join('') +end diff --git a/datacat/lib/puppet/provider/datacat_collector/datacat_collector.rb b/datacat/lib/puppet/provider/datacat_collector/datacat_collector.rb new file mode 100644 index 000000000..c5cbf20b3 --- /dev/null +++ b/datacat/lib/puppet/provider/datacat_collector/datacat_collector.rb @@ -0,0 +1,74 @@ +require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'puppet_x', 'richardc', 'datacat.rb')) + +Puppet::Type.type(:datacat_collector).provide(:datacat_collector) do + def exists? + # Find the datacat_fragments that point at this collector + our_names = [ resource[:path], resource[:collects] ].flatten.compact + + fragments = resource.catalog.resources.find_all do |r| + r.is_a?(Puppet::Type.type(:datacat_fragment)) && ((our_names & [ r[:target] ].flatten).size > 0) + end + + # order fragments on their :order property + fragments = fragments.sort { |a,b| a[:order] <=> b[:order] } + + # deep merge their data chunks + deep_merge = Puppet_X::Richardc::Datacat.deep_merge + data = {} + fragments.each do |fragment| + data.merge!(fragment[:data], &deep_merge) + end + + debug "Collected #{data.inspect}" + + if @resource[:source_key] + debug "Selecting source_key #{@resource[:source_key]}" + content = data[@resource[:source_key]] + else + vars = Puppet_X::Richardc::Datacat_Binding.new(data, resource[:template]) + + debug "Applying template #{@resource[:template]}" + template = ERB.new(@resource[:template_body] || '', 0, '-') + template.filename = @resource[:template] + content = template.result(vars.get_binding) + end + + # Find the resource to modify + target_resource = resolve_resource(@resource[:target_resource]) + target_field = @resource[:target_field].to_sym + + unless target_resource.is_a?(Puppet::Type) + raise "Failed to map #{@resource[:target_resource]} into a resource, got to #{target_resource.inspect} of class #{target_resource.class}" + end + + debug "Now setting field #{target_field.inspect}" + target_resource[target_field] = content + + # and claim there's nothing to change about *this* resource + true + end + + private + + def resolve_resource(reference) + if reference.is_a?(Puppet::Type) + # Probably from a unit test, use the resource as-is + return reference + end + + if reference.is_a?(Puppet::Resource) + # Already part resolved - puppet apply? + # join it to the catalog where we live and ask it to resolve + reference.catalog = resource.catalog + return reference.resolve + end + + if reference.is_a?(String) + # 3.3.0 catalogs you need to resolve like so + return resource.catalog.resource(reference) + end + + # If we got here, panic + raise "Don't know how to convert '#{reference.inspect}' of class #{reference.class} into a resource" + end +end diff --git a/datacat/lib/puppet/provider/datacat_fragment/datacat_fragment.rb b/datacat/lib/puppet/provider/datacat_fragment/datacat_fragment.rb new file mode 100644 index 000000000..d69ce093b --- /dev/null +++ b/datacat/lib/puppet/provider/datacat_fragment/datacat_fragment.rb @@ -0,0 +1,3 @@ +Puppet::Type.type(:datacat_fragment).provide(:datacat_fragment) do + mk_resource_methods +end diff --git a/datacat/lib/puppet/type/datacat_collector.rb b/datacat/lib/puppet/type/datacat_collector.rb new file mode 100644 index 000000000..e4ba3de28 --- /dev/null +++ b/datacat/lib/puppet/type/datacat_collector.rb @@ -0,0 +1,62 @@ +Puppet::Type.newtype(:datacat_collector) do + desc %q{Manages the merging of data and updating a related resource parameter. + + The `datacat_collector` type deeply merges a data hash from + the `datacat_fragment` resources that target it. + + These fragments are then rendered via an erb template specified by the + `template_body` parameter and used to update the `target_field` property + of the related `target_resource`. + + Sample usage: + + datacat_collector { 'open_ports': + template_body => '<%= @data["ports"].sort.join(",") %>', + target_resource => File_line['open_ports'], + target_field => 'line', + } + + datacat_fragment { 'open webserver': + target => 'open_ports', + data => { ports => [ 80, 443 ] }, + } + + datacat_fragment { 'open ssh': + target => 'open_ports', + data => { ports => [ 22 ] }, + } + + + For convenience the common use case of targeting a file is wrapped in the + datacat defined type.} + + ensurable + + newparam(:path, :namevar => true) do + desc "An identifier (typically a file path) that can be used by datacat_fragments so they know where to target the data." + end + + newparam(:collects) do + desc "Other resources we want to collect data from. Allows for many-many datacats." + end + + newparam(:target_resource) do + desc "The resource that we're going to set the field (eg File['/tmp/demo']) set theto set data tor" + end + + newparam(:target_field) do + desc 'The field of the resource to put the results in' + end + + newparam(:source_key) do + desc 'If specified, the key from @data to copy across to the target_field (bypasses template evaluation)' + end + + newparam(:template) do + desc 'Path to the template to render. Used in error reporting.' + end + + newparam(:template_body) do + desc 'The slurped body of the template to render.' + end +end diff --git a/datacat/lib/puppet/type/datacat_fragment.rb b/datacat/lib/puppet/type/datacat_fragment.rb new file mode 100644 index 000000000..2f3031c0a --- /dev/null +++ b/datacat/lib/puppet/type/datacat_fragment.rb @@ -0,0 +1,20 @@ +Puppet::Type.newtype(:datacat_fragment) do + desc 'A fragment of data for a datacat resource.' + + newparam(:name, :namevar => true) do + desc 'The name of this fragment.' + end + + newparam(:target) do + desc 'The title of the datacat resource that the data should be considered part of. May be an array to indicate multiple targetted collectors.' + end + + newparam(:order) do + desc 'The order in which to merge this fragment into the datacat resource. Defaults to the string "50"' + defaultto "50" + end + + newparam(:data) do + desc 'A hash of data to be merged for this resource.' + end +end diff --git a/datacat/lib/puppet_x/richardc/datacat.rb b/datacat/lib/puppet_x/richardc/datacat.rb new file mode 100644 index 000000000..f28fad67f --- /dev/null +++ b/datacat/lib/puppet_x/richardc/datacat.rb @@ -0,0 +1,45 @@ +module Puppet_X + module Richardc + class Datacat + def self.deep_merge + deep_merge = Proc.new do |key,oldval,newval| + newval.is_a?(Hash) && oldval.is_a?(Hash) ? + oldval.merge(newval, &deep_merge) : + newval.is_a?(Array) && oldval.is_a?(Array) ? + oldval + newval : + newval + end + end + end + + # Our much simpler version of Puppet::Parser::TemplateWrapper + class Datacat_Binding + def initialize(d, template) + @data = d + @__file__ = template + end + + def file + @__file__ + end + + # Find which line in the template (if any) we were called from. + # @return [String] the line number + # @api private + def script_line + identifier = Regexp.escape(@__file__ || "(erb)") + (caller.find { |l| l =~ /#{identifier}:/ }||"")[/:(\d+):/,1] + end + private :script_line + + def method_missing(name, *args) + line_number = script_line + raise "Could not find value for '#{name}' #{@__file__}:#{line_number}" + end + + def get_binding + binding() + end + end + end +end diff --git a/datacat/manifests/init.pp b/datacat/manifests/init.pp new file mode 100644 index 000000000..a6e2f9444 --- /dev/null +++ b/datacat/manifests/init.pp @@ -0,0 +1,95 @@ +# Definition: datacat +# +# This definition allows you to declare datacat managed files. +# +# Parameters: +# All parameters are as for the file type, with the addition of a $template +# parameter which is a path to a template to be used as the content of the +# file. +# +# Sample Usage: +# datacat { '/etc/motd': +# owner => 'root', +# group => 'root, +# template => 'motd/motd.erb', +# } +# +define datacat( + $ensure = 'file', + $template = undef, + $template_body = undef, + $collects = [], + $backup = undef, + $checksum = undef, + $force = undef, + $group = undef, + $owner = undef, + $mode = undef, + $path = $title, + $replace = undef, + $selinux_ignore_defaults = undef, + $selrange = undef, + $selrole = undef, + $seltype = undef, + $seluser = undef, + $show_diff = 'UNSET' +) { + if $show_diff != 'UNSET' { + if versioncmp($settings::puppetversion, '3.2.0') >= 0 { + File { show_diff => $show_diff } + } else { + warning('show_diff not supported in puppet prior to 3.2, ignoring') + } + } + + # we could validate ensure by simply passing it to file, but unfortunately + # someone could try to be smart and pass 'directory', so we only allow a limited range + if $ensure != 'absent' and $ensure != 'present' and $ensure != 'file' { + fail("Datacat[${title}] invalid value for ensure") + } + + if $ensure == 'absent' { + file { $title: + ensure => $ensure, + path => $path, + backup => $backup, + force => $force, + } + } else { + file { $title: + path => $path, + backup => $backup, + checksum => $checksum, + content => "To be replaced by datacat_collector[${title}]\n", + force => $force, + group => $group, + mode => $mode, + owner => $owner, + replace => $replace, + selinux_ignore_defaults => $selinux_ignore_defaults, + selrange => $selrange, + selrole => $selrole, + seltype => $seltype, + seluser => $seluser, + } + + $template_real = $template ? { + undef => 'inline', + default => $template, + } + + $template_body_real = $template_body ? { + undef => template_body($template_real), + default => $template_body, + } + + datacat_collector { $title: + template => $template_real, + template_body => $template_body_real, + target_resource => File[$title], # when we evaluate we modify the private data of this resource + target_field => 'content', + collects => $collects, + before => File[$title], # we want to evaluate before that resource so it can do the work + } + } +} diff --git a/datacat/spec/classes/demo1_spec.rb b/datacat/spec/classes/demo1_spec.rb new file mode 100644 index 000000000..988d5778e --- /dev/null +++ b/datacat/spec/classes/demo1_spec.rb @@ -0,0 +1,8 @@ +require 'spec_helper' + +describe 'demo1' do + it { should create_notify('demo1') } + it { should create_datacat('/tmp/demo1').with_template('demo1/sheeps.erb') } + it { should create_datacat_fragment('data foo => 1').with_data({'foo'=>'1'}) } + it { should create_datacat_fragment('data bar => 2').with_data({'bar'=>'2'}) } +end diff --git a/datacat/spec/defines/datacat_spec.rb b/datacat/spec/defines/datacat_spec.rb new file mode 100644 index 000000000..77d5c4bd0 --- /dev/null +++ b/datacat/spec/defines/datacat_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe "datacat" do + context "specifying template by path" do + let(:title) { 'test' } + let(:params) { { :template => "demo1/sheeps.erb" } } + it { should contain_datacat_collector('test') } + it { should contain_datacat_collector('test').with_template('demo1/sheeps.erb') } + it { should contain_datacat_collector('test').with_template_body(/baah/) } + end + + context "specifying template by body" do + let(:title) { 'test' } + let(:params) { { :template_body => "# Baah!" } } + it { should contain_datacat_collector('test') } + it { should contain_datacat_collector('test').with_template('inline') } + it { should contain_datacat_collector('test').with_template_body(/Baah/) } + end + + context "specifying ensure absent" do + let(:title) { 'no-file' } + let(:params) { { :ensure => "absent" } } + it { should_not contain_datacat_collector('no-file') } + it { should contain_file('no-file') } + it { should contain_file('no-file').with_ensure('absent') } + end +end diff --git a/datacat/spec/fixtures/modules/demo1/manifests/init.pp b/datacat/spec/fixtures/modules/demo1/manifests/init.pp new file mode 100644 index 000000000..4f0f1e99f --- /dev/null +++ b/datacat/spec/fixtures/modules/demo1/manifests/init.pp @@ -0,0 +1,18 @@ +class demo1 { + notify { 'demo1': } + + datacat { '/tmp/demo1': + template => 'demo1/sheeps.erb', + } + + datacat_fragment { 'data foo => 1': + target => '/tmp/demo1', + data => { foo => 1 }, + } + + datacat_fragment { 'data bar => 2': + target => '/tmp/demo1', + data => { bar => 2 }, + } +} + diff --git a/datacat/spec/fixtures/modules/demo1/templates/sheeps.erb b/datacat/spec/fixtures/modules/demo1/templates/sheeps.erb new file mode 100644 index 000000000..b3781a825 --- /dev/null +++ b/datacat/spec/fixtures/modules/demo1/templates/sheeps.erb @@ -0,0 +1,3 @@ +# This is a super simple demonstration - baah! + +<%= @data.to_yaml %> diff --git a/datacat/spec/fixtures/modules/demo2/manifests/init.pp b/datacat/spec/fixtures/modules/demo2/manifests/init.pp new file mode 100644 index 000000000..90310e0b5 --- /dev/null +++ b/datacat/spec/fixtures/modules/demo2/manifests/init.pp @@ -0,0 +1,18 @@ +class demo2 { + notify { 'demo2': } + + datacat { '/tmp/demo2': + template => 'demo2/merging.erb', + } + + datacat_fragment { 'data foo => 1, 2': + target => '/tmp/demo2', + data => { foo => [ 1, 2 ] }, + } + + datacat_fragment { 'data foo => 2, 3': + target => '/tmp/demo2', + data => { foo => [ 2, 3 ] }, + } +} + diff --git a/datacat/spec/fixtures/modules/demo2/templates/merging.erb b/datacat/spec/fixtures/modules/demo2/templates/merging.erb new file mode 100644 index 000000000..b05594fb5 --- /dev/null +++ b/datacat/spec/fixtures/modules/demo2/templates/merging.erb @@ -0,0 +1,2 @@ +data.foo should be a merged array <%= @data['foo'].join(', ') %> +and we can uniq it <%= @data['foo'].uniq.join(', ') %> diff --git a/datacat/spec/fixtures/modules/demo3/manifests/init.pp b/datacat/spec/fixtures/modules/demo3/manifests/init.pp new file mode 100644 index 000000000..1e194384d --- /dev/null +++ b/datacat/spec/fixtures/modules/demo3/manifests/init.pp @@ -0,0 +1,22 @@ +# +class demo3 { + datacat { '/tmp/demo3': + template => 'demo3/hostgroups.cfg.erb', + } + + $host1 = 'foo.example.com' + datacat_fragment { 'foo host': + target => '/tmp/demo3', + data => { + device => [ $host1 ], + }, + } + + $ilo_fqdn = 'foo-ilo.example.com' + datacat_fragment { 'foo ilo': + target => '/tmp/demo3', + data => { + device => [ $ilo_fqdn ], + }, + } +} diff --git a/datacat/spec/fixtures/modules/demo3/templates/hostgroups.cfg.erb b/datacat/spec/fixtures/modules/demo3/templates/hostgroups.cfg.erb new file mode 100644 index 000000000..53bbbcbd2 --- /dev/null +++ b/datacat/spec/fixtures/modules/demo3/templates/hostgroups.cfg.erb @@ -0,0 +1,7 @@ +# hostgroups.cfg.erb +<% @data.keys.sort.each do |hostgroup| %> +define hostgroup { + name <%= hostgroup %> + members <%= @data[hostgroup].sort.join(',') %> +} +<% end %> diff --git a/datacat/spec/fixtures/modules/issue1/manifests/init.pp b/datacat/spec/fixtures/modules/issue1/manifests/init.pp new file mode 100644 index 000000000..810be2be7 --- /dev/null +++ b/datacat/spec/fixtures/modules/issue1/manifests/init.pp @@ -0,0 +1,6 @@ +# This should repo the scope error from github issue #1 +class issue1 { + datacat { "/tmp/issue1.1": + template => "issue1/refers_to_scope.erb", + } +} diff --git a/datacat/spec/fixtures/modules/issue1/templates/refers_to_scope.erb b/datacat/spec/fixtures/modules/issue1/templates/refers_to_scope.erb new file mode 100644 index 000000000..e4756c77d --- /dev/null +++ b/datacat/spec/fixtures/modules/issue1/templates/refers_to_scope.erb @@ -0,0 +1,7 @@ +We don't expect this to actually work as scope is only avilable to +templates evaluated by the template() function. + +It should however say that it's line 7 of refers_to_scope.erb in the error +that is raised. + +<%= scope.lookupvar("pies") %> diff --git a/datacat/spec/fixtures/modules/template_body/templates/test1.erb b/datacat/spec/fixtures/modules/template_body/templates/test1.erb new file mode 100644 index 000000000..7b6637f67 --- /dev/null +++ b/datacat/spec/fixtures/modules/template_body/templates/test1.erb @@ -0,0 +1 @@ +Goodbye cruel world diff --git a/datacat/spec/functions/template_body_spec.rb b/datacat/spec/functions/template_body_spec.rb new file mode 100644 index 000000000..985db76fd --- /dev/null +++ b/datacat/spec/functions/template_body_spec.rb @@ -0,0 +1,6 @@ +require 'spec_helper' + +describe 'template_body' do + it { should run.with_params('template_body/really_should_never_exist.erb').and_raise_error(Puppet::ParseError) } + it { should run.with_params('template_body/test1.erb').and_return("Goodbye cruel world\n") } +end diff --git a/datacat/spec/spec_helper.rb b/datacat/spec/spec_helper.rb new file mode 100644 index 000000000..2c6f56649 --- /dev/null +++ b/datacat/spec/spec_helper.rb @@ -0,0 +1 @@ +require 'puppetlabs_spec_helper/module_spec_helper' diff --git a/datacat/spec/spec_helper_system.rb b/datacat/spec/spec_helper_system.rb new file mode 100644 index 000000000..cba2ad741 --- /dev/null +++ b/datacat/spec/spec_helper_system.rb @@ -0,0 +1,23 @@ +require 'rspec-system/spec_helper' +require 'rspec-system-puppet/helpers' + +include RSpecSystemPuppet::Helpers + +RSpec.configure do |c| + proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) + + # Enable colour in Jenkins + c.tty = true + + c.include RSpecSystemPuppet::Helpers + + c.before :suite do + puppet_install + puppet_master_install + + puppet_module_install(:source => proj_root, :module_name => 'datacat') + puppet_module_install(:source => proj_root + '/spec/fixtures/modules/demo1', :module_name => 'demo1') + puppet_module_install(:source => proj_root + '/spec/fixtures/modules/demo2', :module_name => 'demo2') + puppet_module_install(:source => proj_root + '/spec/fixtures/modules/demo3', :module_name => 'demo3') + end +end diff --git a/datacat/spec/system/basic_spec.rb b/datacat/spec/system/basic_spec.rb new file mode 100644 index 000000000..627547a72 --- /dev/null +++ b/datacat/spec/system/basic_spec.rb @@ -0,0 +1,58 @@ +require 'spec_helper_system' + +describe 'basic tests:' do + # Using puppet_apply as a subject + context puppet_apply 'notice("foo")' do + its(:stdout) { should =~ /foo/ } + its(:stderr) { should be_empty } + its(:exit_code) { should be_zero } + end + + # Using puppet_apply as a helper + it 'my class should work with no errors' do + pp = <<-EOS + datacat { "/tmp/demo1": + template_body => "<% @data.keys.sort.each do |k| %><%= k %>: <%= @data[k] %>, <% end %>", + } + + datacat_fragment { "foo": + target => '/tmp/demo1', + data => { foo => "one" }, + } + + datacat_fragment { "bar": + target => '/tmp/demo1', + data => { bar => "two" }, + } + + exec { '/bin/echo I have changed': + refreshonly => true, + subscribe => Datacat["/tmp/demo1"], + } + EOS + + # Run it twice and test for idempotency + puppet_apply(pp) do |r| + r.exit_code.should_not == 1 + r.refresh + r.exit_code.should be_zero + end + + shell('cat /tmp/demo1') do |r| + r.stdout.should =~ /^bar: two, foo: one/ + end + end + + it 'should run the example from the documentation via a master' do + shell 'sudo sh -c "echo include demo3 > /etc/puppet/manifests/site.pp"' + puppet_agent(:debug => true) do |r| + r.exit_code.should_not == 1 + r.refresh + r.exit_code.should be_zero + end + + shell('cat /tmp/demo3') do |r| + r.stdout.should =~ /\s+name device\n\s+members foo-ilo.example.com,foo.example.com/ + end + end +end diff --git a/datacat/spec/unit/type/datacat_collector_spec.rb b/datacat/spec/unit/type/datacat_collector_spec.rb new file mode 100644 index 000000000..46d7abdc4 --- /dev/null +++ b/datacat/spec/unit/type/datacat_collector_spec.rb @@ -0,0 +1,163 @@ +require 'spec_helper' + +describe Puppet::Type.type(:datacat_collector) do + context "simple merging" do + before :each do + @catalog = Puppet::Resource::Catalog.new + @file = Puppet::Type.type(:file).new(:path => '/test') + @catalog.add_resource @file + + @collector = Puppet::Type.type(:datacat_collector).new({ + :title => "/test", + :template_body => '<%= @data.keys.sort.map { |x| "#{x}=#{@data[x]}" }.join(",") %>', + :target_resource => @file, + :target_field => :content, + :collects => [ '/secret-name' ], + }) + @catalog.add_resource @collector + end + + it "should do work when exists?" do + @file.expects(:[]=).with(:content, "") + @collector.provider.exists? + end + + it "should combine one hash" do + @catalog.add_resource Puppet::Type.type(:datacat_fragment).new({ + :title => "hash one", + :target => '/test', + :data => { "foo" => "one" }, + }) + + @file.expects(:[]=).with(:content, "foo=one") + @collector.provider.exists? + end + + it "should combine two hashes, disjunct keys" do + @catalog.add_resource Puppet::Type.type(:datacat_fragment).new({ + :title => "hash one", + :target => '/test', + :data => { "alpha" => "one" }, + }) + @catalog.add_resource Puppet::Type.type(:datacat_fragment).new({ + :title => "hash two", + :target => '/test', + :data => { "bravo" => "two" }, + }) + + @file.expects(:[]=).with(:content, "alpha=one,bravo=two") + @collector.provider.exists? + end + + describe "ordering" do + it "should support explicit orderings 10 20" do + @catalog.add_resource Puppet::Type.type(:datacat_fragment).new({ + :title => "hash one", + :order => "10", + :target => '/test', + :data => { "alpha" => "one" }, + }) + @catalog.add_resource Puppet::Type.type(:datacat_fragment).new({ + :title => "hash two", + :order => "20", + :target => '/test', + :data => { "alpha" => "two" }, + }) + + @file.expects(:[]=).with(:content, "alpha=two") + @collector.provider.exists? + end + + it "should support explicit orderings 30 10" do + @catalog.add_resource Puppet::Type.type(:datacat_fragment).new({ + :title => "hash one", + :order => "30", + :target => '/test', + :data => { "alpha" => "one" }, + }) + @catalog.add_resource Puppet::Type.type(:datacat_fragment).new({ + :title => "hash two", + :order => "10", + :target => '/test', + :data => { "alpha" => "two" }, + }) + + @file.expects(:[]=).with(:content, "alpha=one") + @collector.provider.exists? + end + + it "should support implicit ordering '50' 10" do + @catalog.add_resource Puppet::Type.type(:datacat_fragment).new({ + :title => "hash one", + :target => '/test', + :data => { "alpha" => "one" }, + }) + @catalog.add_resource Puppet::Type.type(:datacat_fragment).new({ + :title => "hash two", + :order => "10", + :target => '/test', + :data => { "alpha" => "two" }, + }) + + @file.expects(:[]=).with(:content, "alpha=one") + @collector.provider.exists? + end + end + + describe "targeting multiple collectors" do + it "should support an array in the target attribute" do + @catalog.add_resource Puppet::Type.type(:datacat_fragment).new({ + :title => "target two", + :target => [ "/test", "/othertest" ], + :data => { "alpha" => "one" }, + }) + + @file.expects(:[]=).with(:content, "alpha=one") + @collector.provider.exists? + end + end + + describe "collects parameter" do + it "should be able to collect for things targeted via the collects parameter" do + @catalog.add_resource Puppet::Type.type(:datacat_fragment).new({ + :title => "target two", + :target => "/secret-name", + :data => { "alpha" => "one" }, + }) + + @file.expects(:[]=).with(:content, "alpha=one") + @collector.provider.exists? + end + end + + describe "source_key parameter" do + before :each do + @source_key_collector = Puppet::Type.type(:datacat_collector).new({ + :title => "/source_key", + :target_resource => @file, + :target_field => :source, + :source_key => 'source_path', + }) + @catalog.add_resource @source_key_collector + end + + it "should set an array when asked" do + @catalog.add_resource Puppet::Type.type(:datacat_fragment).new({ + :title => "target one", + :order => "10", + :target => "/source_key", + :data => { "source_path" => [ "one" ] }, + }) + @catalog.add_resource Puppet::Type.type(:datacat_fragment).new({ + :title => "target two", + :order => "20", + :target => "/source_key", + :data => { "source_path" => [ "two" ] }, + }) + + @file.expects(:[]=).with(:source, [ 'one', 'two' ]) + @source_key_collector.provider.exists? + end + end + end +end diff --git a/dnsclient/.fixtures.yml b/dnsclient/.fixtures.yml new file mode 100644 index 000000000..164176f00 --- /dev/null +++ b/dnsclient/.fixtures.yml @@ -0,0 +1,7 @@ +fixtures: + repositories: + 'stdlib': + repo: 'git://github.com/puppetlabs/puppetlabs-stdlib.git' + ref: '3.2.0' + symlinks: + 'dnsclient': "#{source_dir}" diff --git a/dnsclient/.gemfile b/dnsclient/.gemfile new file mode 100644 index 000000000..6e86a9f15 --- /dev/null +++ b/dnsclient/.gemfile @@ -0,0 +1,6 @@ +source :rubygems + +puppetversion = ENV.key?('PUPPET_VERSION') ? "= #{ENV['PUPPET_VERSION']}" : ['>= 2.7'] +gem 'puppet', puppetversion +gem 'puppetlabs_spec_helper', '>= 0.1.0' +gem 'puppet-lint', '>= 0.3.2' diff --git a/dnsclient/.gitignore b/dnsclient/.gitignore new file mode 100644 index 000000000..d9aede90b --- /dev/null +++ b/dnsclient/.gitignore @@ -0,0 +1,6 @@ +pkg/ +*.swp +.DS_Store +metadata.json +coverage/ +spec/fixtures/modules/* diff --git a/dnsclient/.travis.yml b/dnsclient/.travis.yml new file mode 100644 index 000000000..0e44a4aac --- /dev/null +++ b/dnsclient/.travis.yml @@ -0,0 +1,16 @@ +--- +env: +- PUPPET_VERSION=2.7.23 +- PUPPET_VERSION=3.3.2 +notifications: +email: false +rvm: +- 1.9.3 +- 1.8.7 +matrix: + allow_failures: + - env: PUPPET_VERSION=2.7.23 +language: ruby +before_script: "gem install --no-ri --no-rdoc bundler" +script: 'bundle exec rake validate && bundle exec rake lint && SPEC_OPTS="--format documentation" bundle exec rake spec' +gemfile: Gemfile diff --git a/dnsclient/CHANGELOG b/dnsclient/CHANGELOG new file mode 100644 index 000000000..6e14f52fc --- /dev/null +++ b/dnsclient/CHANGELOG @@ -0,0 +1,25 @@ +3.0.4 - 2013-06-08 Garrett Honeycutt +* Fix warnings by using @ in front of variables in template +* .fixtures.yml is tracking correct version of puppetlabs/stdlib +* Drop Puppet v2.6 from travis-ci +* anders-larsson updates README to document support for SLES 10 +* anders-larsson updates README to document support for all EL, not just CentOS + +3.0.1 - 2013-03-16 Garrett Honeycutt +* fixed spec bug relating to lack of .fixtures.yml +* cleaned up testing framework + +3.0.0 - 2013-03-16 Garrett Honeycutt +* Switched to semantic versioning - http://semver.org +* Implemented new design pattern that manages data with Hiera +* Truly portable module! Code is completely data driven, so you can make changes + through Hiera and never have to edit the code itself. + +2.0.0 - 2011-09-09 Garrett Honeycutt +* Implemented design pattern based on http://www.puppetlabs.com/blog/design-pattern-for-dealing-with-data/ + +1.0.1 - 2010-09-24 Garrett Honeycutt +* Added documentation + +1.0.0 - 2010-06-24 Garrett Honeycutt +* Initial release diff --git a/dnsclient/Gemfile b/dnsclient/Gemfile new file mode 100644 index 000000000..b40b4c08a --- /dev/null +++ b/dnsclient/Gemfile @@ -0,0 +1,7 @@ +source "https://rubygems.org" + +puppetversion = ENV.key?('PUPPET_VERSION') ? "= #{ENV['PUPPET_VERSION']}" : ['>= 2.7'] +gem 'puppet', puppetversion +gem 'puppetlabs_spec_helper', '>= 0.1.0' +gem 'puppet-lint', '>= 0.3.2' +gem 'facter', '>= 1.7.0', "< 1.8.0" diff --git a/dnsclient/LICENSE b/dnsclient/LICENSE new file mode 100644 index 000000000..b1b251c34 --- /dev/null +++ b/dnsclient/LICENSE @@ -0,0 +1,13 @@ +Copyright (C) 2010-2013 Garrett Honeycutt + +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. diff --git a/dnsclient/Modulefile b/dnsclient/Modulefile new file mode 100644 index 000000000..4ea63036d --- /dev/null +++ b/dnsclient/Modulefile @@ -0,0 +1,10 @@ +name 'ghoneycutt-dnsclient' +version '3.1.0' +source 'git://github.com/ghoneycutt/puppet-module-dnsclient.git' +author 'ghoneycutt' +license 'Apache License, Version 2.0' +summary 'Manage resolver' +description "Manage a DNS client's resolver" +project_page 'https://github.com/ghoneycutt/puppet-module-dnsclient/' + +dependency 'puppetlabs/stdlib', '>= 3.2.0' diff --git a/dnsclient/README.markdown b/dnsclient/README.markdown new file mode 100644 index 000000000..2ae0cf629 --- /dev/null +++ b/dnsclient/README.markdown @@ -0,0 +1,92 @@ +# dnsclient module # + +[![Build Status]( +https://api.travis-ci.org/ghoneycutt/puppet-module-dnsclient.png?branch=master)](https://travis-ci.org/ghoneycutt/puppet-module-dnsclient) + +This module manages /etc/resolv.conf and its various options. + +It makes use of Hiera (http://github.com/puppetlabs/hiera) and demonstrates a +new design pattern in module development that allows for totally data driven +code with no modifications to the module itself as a guiding principle. + +# Compatibility # + +This module has been tested to work on the following systems. + + * EL 5 + * EL 6 + * Debian 6 + * SLES 10 + * SLES 11 + * Solaris 10 + * Ubuntu 10.04 LTS (Lucid Lynx) + * Ubuntu 12.04 LTS (Precise Pangolin) + +# Parameters # + +See RESOLV.CONF(5) for more information regarding /etc/resolv.conf settings + + +nameservers +----------- +Array of name servers. + +- *Default*: Google's public name servers + +options +------- +Array of options. + +- *Default*: 'rotate' and 'timeout:1' + +search +------ +Array of domains for search list. This is mutually exclusive with **domain**. If both are set, search will be used and domain will be ignored. + +- *Default*: none + +domain +------ +Domain setting. See **search**. + +- *Default*: none + +sortlist +-------- +Array of sortlist addresses. + +- *Default*: none + +resolver_config_file +-------------------- +Path to resolv.conf. + +- *Default*: '/etc/resolv.conf' + +resolver_config_file_ensure +--------------------------- +ensure attribute for file resource. Valid values are 'file', 'present' and 'absent'. + +- *Default*: file + +resolver_config_file_owner +-------------------------- +resolv.conf's owner. + +- *Default*: 'root' + + +resolver_config_file_group +-------------------------- +resolv.conf's group. + +- *Default*: 'root' + + +resolver_config_file_mode +------------------------- +resolv.conf's mode. + +- *Default*: '0644' + + diff --git a/dnsclient/Rakefile b/dnsclient/Rakefile new file mode 100644 index 000000000..0a28d845e --- /dev/null +++ b/dnsclient/Rakefile @@ -0,0 +1,18 @@ +require 'rubygems' +require 'puppetlabs_spec_helper/rake_tasks' +require 'puppet-lint/tasks/puppet-lint' +PuppetLint.configuration.send('disable_80chars') +PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "pkg/**/*.pp"] + +desc "Run puppet in noop mode and check for syntax errors." +task :validate do + Dir['manifests/**/*.pp'].each do |manifest| + sh "puppet parser validate --noop #{manifest}" + end + Dir['spec/**/*.rb','lib/**/*.rb'].each do |ruby_file| + sh "ruby -c #{ruby_file}" unless ruby_file =~ /spec\/fixtures/ + end + Dir['templates/**/*.erb'].each do |template| + sh "erb -P -x -T '-' #{template} | ruby -c" + end +end diff --git a/dnsclient/manifests/init.pp b/dnsclient/manifests/init.pp new file mode 100644 index 000000000..b159dd25f --- /dev/null +++ b/dnsclient/manifests/init.pp @@ -0,0 +1,44 @@ +# ## Class: dnsclient ## +# +# This module manages /etc/resolv.conf and is meant to be included in the +# common class that applies to all systems. +# +class dnsclient ( + $nameservers = [ '8.8.8.8', + '8.8.4.4' ], + $options = [ 'rotate', + 'timeout:1'], + $search = ['UNSET'], + $domain = 'UNSET', + $sortlist = ['UNSET'], + $resolver_config_file = '/etc/resolv.conf', + $resolver_config_file_ensure = 'file', + $resolver_config_file_owner = 'root', + $resolver_config_file_group = 'root', + $resolver_config_file_mode = '0644', +) { + + # Validates domain + if is_domain_name($domain) != true { + fail("Domain name, ${domain}, is invalid.") + } + + # Validates $resolver_config_file_ensure + case $resolver_config_file_ensure { + 'file', 'present', 'absent': { + # noop, these values are valid + } + default: { + fail("Valid values for \$resolver_config_file_ensure are \'absent\', \'file\', or \'present\'. Specified value is ${resolver_config_file_ensure}") + } + } + + file { 'dnsclient_resolver_config_file': + ensure => $resolver_config_file_ensure, + content => template('dnsclient/resolv.conf.erb'), + path => $resolver_config_file, + owner => $resolver_config_file_owner, + group => $resolver_config_file_group, + mode => $resolver_config_file_mode, + } +} diff --git a/dnsclient/spec/classes/init_spec.rb b/dnsclient/spec/classes/init_spec.rb new file mode 100644 index 000000000..09b9ff223 --- /dev/null +++ b/dnsclient/spec/classes/init_spec.rb @@ -0,0 +1,616 @@ +require 'spec_helper' +describe 'dnsclient' do + + it { should compile.with_all_deps } + + context 'when using default values for class' do + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'file', + 'path' => '/etc/resolv.conf', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + should contain_file('dnsclient_resolver_config_file').with_content( +%{# This file is being maintained by Puppet. +# DO NOT EDIT +options rotate timeout:1 +nameserver 8.8.8.8 +nameserver 8.8.4.4 +}) + } + end + + context 'with parameter nameservers set' do + let :params do + { :nameservers => ['4.2.2.2', '4.2.2.1'] } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'file', + 'path' => '/etc/resolv.conf', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + should contain_file('dnsclient_resolver_config_file').with_content( +%{# This file is being maintained by Puppet. +# DO NOT EDIT +options rotate timeout:1 +nameserver 4.2.2.2 +nameserver 4.2.2.1 +}) + } + end + + context 'with parameter nameservers set to a single nameserver as a string' do + let :params do + { :nameservers => '4.2.2.2' } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'file', + 'path' => '/etc/resolv.conf', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + should contain_file('dnsclient_resolver_config_file').with_content( +%{# This file is being maintained by Puppet. +# DO NOT EDIT +options rotate timeout:1 +nameserver 4.2.2.2 +}) + } + end + + context 'with no options' do + let :params do + { :options => 'UNSET' } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'file', + 'path' => '/etc/resolv.conf', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + should contain_file('dnsclient_resolver_config_file').with_content( +%{# This file is being maintained by Puppet. +# DO NOT EDIT +nameserver 8.8.8.8 +nameserver 8.8.4.4 +}) + } + end + + context 'with options set to a single value' do + let :params do + { :options => 'ndots:2' } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'file', + 'path' => '/etc/resolv.conf', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + should contain_file('dnsclient_resolver_config_file').with_content( +%{# This file is being maintained by Puppet. +# DO NOT EDIT +options ndots:2 +nameserver 8.8.8.8 +nameserver 8.8.4.4 +}) + } + end + + context 'with options set to multiple values' do + let :params do + { :options => ['ndots:2', 'rotate'] } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'file', + 'path' => '/etc/resolv.conf', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + should contain_file('dnsclient_resolver_config_file').with_content( +%{# This file is being maintained by Puppet. +# DO NOT EDIT +options ndots:2 rotate +nameserver 8.8.8.8 +nameserver 8.8.4.4 +}) + } + end + + context 'with search set to multiple values' do + let :params do + { :search => ['foo.example.tld', 'example.tld'] } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'file', + 'path' => '/etc/resolv.conf', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + should contain_file('dnsclient_resolver_config_file').with_content( +%{# This file is being maintained by Puppet. +# DO NOT EDIT +search foo.example.tld example.tld +options rotate timeout:1 +nameserver 8.8.8.8 +nameserver 8.8.4.4 +}) + } + end + + context 'with search set to a single value' do + let :params do + { :search => 'example.tld' } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'file', + 'path' => '/etc/resolv.conf', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + should contain_file('dnsclient_resolver_config_file').with_content( +%{# This file is being maintained by Puppet. +# DO NOT EDIT +search example.tld +options rotate timeout:1 +nameserver 8.8.8.8 +nameserver 8.8.4.4 +}) + } + end + + context 'with search and domain set' do + let :params do + { + :search => ['foo.example.tld', 'example.tld'], + :domain => 'valid.tld', + } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'file', + 'path' => '/etc/resolv.conf', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + should contain_file('dnsclient_resolver_config_file').with_content( +%{# This file is being maintained by Puppet. +# DO NOT EDIT +search foo.example.tld example.tld +options rotate timeout:1 +nameserver 8.8.8.8 +nameserver 8.8.4.4 +}) + } + end + + context 'with domain set' do + let :params do + { :domain => 'valid.tld' } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'file', + 'path' => '/etc/resolv.conf', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + should contain_file('dnsclient_resolver_config_file').with_content( +%{# This file is being maintained by Puppet. +# DO NOT EDIT +domain valid.tld +options rotate timeout:1 +nameserver 8.8.8.8 +nameserver 8.8.4.4 +}) + } + end + + context 'with domain and no options set' do + let :params do + { + :domain => 'valid.tld', + :options => 'UNSET', + } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'file', + 'path' => '/etc/resolv.conf', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + should contain_file('dnsclient_resolver_config_file').with_content( +%{# This file is being maintained by Puppet. +# DO NOT EDIT +domain valid.tld +nameserver 8.8.8.8 +nameserver 8.8.4.4 +}) + } + end + + context 'with search set to an invalid single value' do + let :params do + { :search => '-notvalid.tld' } + end + + it 'should fail' do + expect { + should raise_error(Puppet::Error, /search parameter does not match regex./) + } + end + end + + context 'with search set to an invalid value in an array' do + let :params do + { :search => ['valid.tld', '-notvalid.tld'] } + end + + it 'should fail' do + expect { + should raise_error(Puppet::Error, /search parameter does not match regex./) + } + end + end + + context 'with only search' do + let :params do + { + :search => 'valid.tld', + :options => 'UNSET', + } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'file', + 'path' => '/etc/resolv.conf', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + should contain_file('dnsclient_resolver_config_file').with_content( +%{# This file is being maintained by Puppet. +# DO NOT EDIT +search valid.tld +nameserver 8.8.8.8 +nameserver 8.8.4.4 +}) + } + end + + context 'with search and sortlist' do + let :params do + { + :search => 'valid.tld', + :sortlist => ['10.10.10.0/24', '10.10.11.0/24'], + :options => 'UNSET', + } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'file', + 'path' => '/etc/resolv.conf', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + should contain_file('dnsclient_resolver_config_file').with_content( +%{# This file is being maintained by Puppet. +# DO NOT EDIT +search valid.tld +sortlist 10.10.10.0/24 10.10.11.0/24 +nameserver 8.8.8.8 +nameserver 8.8.4.4 +}) + } + end + + context 'with search, sortlist, and options' do + let :params do + { + :search => 'valid.tld', + :sortlist => ['10.10.10.0/24', '10.10.11.0/24'], + } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'file', + 'path' => '/etc/resolv.conf', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + should contain_file('dnsclient_resolver_config_file').with_content( +%{# This file is being maintained by Puppet. +# DO NOT EDIT +search valid.tld +sortlist 10.10.10.0/24 10.10.11.0/24 +options rotate timeout:1 +nameserver 8.8.8.8 +nameserver 8.8.4.4 +}) + } + end + + context 'with sortlist set to an array of values' do + let :params do + { :sortlist => ['10.10.10.0/24', '10.10.11.0/24'] } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'file', + 'path' => '/etc/resolv.conf', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + should contain_file('dnsclient_resolver_config_file').with_content( +%{# This file is being maintained by Puppet. +# DO NOT EDIT +sortlist 10.10.10.0/24 10.10.11.0/24 +options rotate timeout:1 +nameserver 8.8.8.8 +nameserver 8.8.4.4 +}) + } + end + + context 'with sortlist, options, and domain' do + let :params do + { + :sortlist => ['10.10.10.0/24', '10.10.11.0/24'], + :domain => 'valid.tld', + } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'file', + 'path' => '/etc/resolv.conf', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + should contain_file('dnsclient_resolver_config_file').with_content( +%{# This file is being maintained by Puppet. +# DO NOT EDIT +sortlist 10.10.10.0/24 10.10.11.0/24 +domain valid.tld +options rotate timeout:1 +nameserver 8.8.8.8 +nameserver 8.8.4.4 +}) + } + end + + context 'with sortlist, no options, and domain' do + let :params do + { + :sortlist => ['10.10.10.0/24', '10.10.11.0/24'], + :domain => 'valid.tld', + :options => 'UNSET', + } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'file', + 'path' => '/etc/resolv.conf', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + should contain_file('dnsclient_resolver_config_file').with_content( +%{# This file is being maintained by Puppet. +# DO NOT EDIT +sortlist 10.10.10.0/24 10.10.11.0/24 +domain valid.tld +nameserver 8.8.8.8 +nameserver 8.8.4.4 +}) + } + end + + context 'with sortlist set to a single value' do + let :params do + { :sortlist => '10.10.10.0/24' } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'file', + 'path' => '/etc/resolv.conf', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + should contain_file('dnsclient_resolver_config_file').with_content( +%{# This file is being maintained by Puppet. +# DO NOT EDIT +sortlist 10.10.10.0/24 +options rotate timeout:1 +nameserver 8.8.8.8 +nameserver 8.8.4.4 +}) + } + end + + context 'with parameter resolver_config_file_ensure not set to \'file\' \'present\' or \'absent\'' do + let :params do + { :resolver_config_file_ensure => 'invalid' } + end + + it 'should fail' do + expect { + should raise_error(Puppet::Error, /Valid values for \$resolver_config_file_ensure are \'absent\', \'file\', or \'present\'. Specified value is invalid/) + } + end + end + + context 'with parameter resolver_config_file_ensure set to present' do + let :params do + { :resolver_config_file_ensure => 'present' } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'present', + 'path' => '/etc/resolv.conf', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + }) + should contain_file('dnsclient_resolver_config_file').with_content( +%{# This file is being maintained by Puppet. +# DO NOT EDIT +options rotate timeout:1 +nameserver 8.8.8.8 +nameserver 8.8.4.4 +}) + } + end + + context 'with parameter resolver_config_file_ensure set to absent' do + let :params do + { :resolver_config_file_ensure => 'absent' } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'ensure' => 'absent', + }) + } + end + + context 'with parameter resolver_config_file set' do + let :params do + { :resolver_config_file => '/tmp/resolv.conf' } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'path' => '/tmp/resolv.conf', + }) + } + end + + context 'with parameter resolver_config_file_owner set' do + let :params do + { :resolver_config_file_owner => 'foo' } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'owner' => 'foo', + }) + } + end + + context 'with parameter resolver_config_file_group set' do + let :params do + { :resolver_config_file_group => 'bar' } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'group' => 'bar', + }) + } + end + + context 'with parameter resolver_config_file_mode set' do + let :params do + { :resolver_config_file_mode => '0777' } + end + + it { should contain_class('dnsclient') } + + it { + should contain_file('dnsclient_resolver_config_file').with({ + 'mode' => '0777', + }) + } + end +end diff --git a/dnsclient/spec/spec_helper.rb b/dnsclient/spec/spec_helper.rb new file mode 100644 index 000000000..dc7e9f4a0 --- /dev/null +++ b/dnsclient/spec/spec_helper.rb @@ -0,0 +1,2 @@ +require 'rubygems' +require 'puppetlabs_spec_helper/module_spec_helper' diff --git a/dnsclient/templates/resolv.conf.erb b/dnsclient/templates/resolv.conf.erb new file mode 100644 index 000000000..3f2c5f0ac --- /dev/null +++ b/dnsclient/templates/resolv.conf.erb @@ -0,0 +1,33 @@ +# This file is being maintained by Puppet. +# DO NOT EDIT +<% if @search[0] != 'UNSET' -%> +<% if @search.class == String -%> +search <%= @search %> +<% else -%> +search <%= @search.join(' ') %> +<% end -%> +<% end -%> +<% if @sortlist[0] != 'UNSET' -%> +<% if @sortlist.class == String -%> +sortlist <%= @sortlist %> +<% else -%> +sortlist <%= @sortlist.join(' ') %> +<% end -%> +<% end -%> +<% if @domain != 'UNSET' and @search[0] == 'UNSET' -%> +domain <%= @domain %> +<% end -%> +<% if @options != 'UNSET' -%> +<% if @options.class == String -%> +options <%= @options %> +<% else -%> +options<% @options.each do |option| %> <%= option %><% end %> +<% end -%> +<% end -%> +<% if @nameservers.class == String -%> +nameserver <%= @nameservers %> +<% else -%> +<% @nameservers.each do |nameserver| -%> +nameserver <%= nameserver %> +<% end -%> +<% end -%> diff --git a/dnsclient/tests/domain.pp b/dnsclient/tests/domain.pp new file mode 100644 index 000000000..2ab4d7e13 --- /dev/null +++ b/dnsclient/tests/domain.pp @@ -0,0 +1,3 @@ +class { 'dnsclient': + domain => 'example.tld', +} diff --git a/dnsclient/tests/init.pp b/dnsclient/tests/init.pp new file mode 100644 index 000000000..9c89beaca --- /dev/null +++ b/dnsclient/tests/init.pp @@ -0,0 +1 @@ +include dnsclient diff --git a/dnsclient/tests/path.pp b/dnsclient/tests/path.pp new file mode 100644 index 000000000..12c9047bb --- /dev/null +++ b/dnsclient/tests/path.pp @@ -0,0 +1,4 @@ +class { 'dnsclient': + resolver_config_file => '/tmp/resolv.conf', + resolver_config_file_group => 'wheel', +} diff --git a/dnsclient/tests/search.pp b/dnsclient/tests/search.pp new file mode 100644 index 000000000..1e19b374f --- /dev/null +++ b/dnsclient/tests/search.pp @@ -0,0 +1,4 @@ +class { 'dnsclient': + search => [ 'foo.example.tld', + 'example.tld' ], +} diff --git a/dnsclient/tests/stuff.pp b/dnsclient/tests/stuff.pp new file mode 100644 index 000000000..1c9499c9c --- /dev/null +++ b/dnsclient/tests/stuff.pp @@ -0,0 +1,8 @@ +class { 'dnsclient': + nameservers => [ '4.2.2.2', + '4.2.2.1' ], +# domain => '-invalid.tld', + domain => 'valid-domain.tld', + resolver_config_file_ensure => 'present', + resolver_config_file_group => 'wheel', +} diff --git a/elasticsearch/.fixtures.yml b/elasticsearch/.fixtures.yml new file mode 100644 index 000000000..cf02be47a --- /dev/null +++ b/elasticsearch/.fixtures.yml @@ -0,0 +1,9 @@ +fixtures: + repositories: + stdlib: + repo: "git://github.com/puppetlabs/puppetlabs-stdlib" + ref: "3.2.0" + apt: git://github.com/puppetlabs/puppetlabs-apt.git + zypprepo: https://github.com/deadpoint/puppet-zypprepo.git + symlinks: + elasticsearch: "#{source_dir}" diff --git a/elasticsearch/.gemfiles/Gemfile.beaker b/elasticsearch/.gemfiles/Gemfile.beaker new file mode 100644 index 000000000..e98b805f9 --- /dev/null +++ b/elasticsearch/.gemfiles/Gemfile.beaker @@ -0,0 +1,16 @@ +source 'https://rubygems.org' + +gem 'beaker', :git => 'https://github.com/electrical/beaker.git', :branch => 'docker_test' +gem 'beaker-rspec' +gem 'pry' +gem 'docker-api', '~> 1.12.0' +gem 'rubysl-securerandom' +gem 'rspec_junit_formatter' +gem 'rspec', '~> 2.14.0' + +case RUBY_VERSION +when '1.8.7' + gem 'rake', '~> 10.1.0' +else + gem 'rake' +end diff --git a/elasticsearch/.gemfiles/Gemfile.doclint b/elasticsearch/.gemfiles/Gemfile.doclint new file mode 100644 index 000000000..402697abc --- /dev/null +++ b/elasticsearch/.gemfiles/Gemfile.doclint @@ -0,0 +1,13 @@ +source 'https://rubygems.org' + +gem 'puppet' +gem 'puppetlabs_spec_helper', '>= 0.1.0' +gem 'rspec_junit_formatter' +gem 'puppet-doc-lint', :git => 'https://github.com/petems/puppet-doc-lint.git' + +case RUBY_VERSION +when '1.8.7' + gem 'rake', '~> 10.1.0' +else + gem 'rake' +end diff --git a/elasticsearch/.gemfiles/Gemfile.rspec b/elasticsearch/.gemfiles/Gemfile.rspec new file mode 100644 index 000000000..8c450a6a7 --- /dev/null +++ b/elasticsearch/.gemfiles/Gemfile.rspec @@ -0,0 +1,17 @@ +source 'https://rubygems.org' + +puppetversion = ENV['PUPPET_VERSION'] +gem 'puppet', puppetversion, :require => false +gem 'puppet-lint' +gem 'rspec-puppet', :git => 'https://github.com/electrical/rspec-puppet.git', :branch => 'future_parser_fix' +gem 'puppetlabs_spec_helper', '>= 0.1.0' +gem 'rspec_junit_formatter' +gem 'puppet-syntax' +gem 'rspec', '~> 2.14.0' + +case RUBY_VERSION +when '1.8.7' + gem 'rake', '~> 10.1.0' +else + gem 'rake' +end diff --git a/elasticsearch/.gitignore b/elasticsearch/.gitignore new file mode 100644 index 000000000..6c6e9478c --- /dev/null +++ b/elasticsearch/.gitignore @@ -0,0 +1,8 @@ +*.swp +spec/fixtures +.bundle +.vendor +.gemfiles/Gemfile.*.lock +.vagrant +.ruby-version +pkg/ diff --git a/elasticsearch/.travis.yml b/elasticsearch/.travis.yml new file mode 100644 index 000000000..847ebfdb0 --- /dev/null +++ b/elasticsearch/.travis.yml @@ -0,0 +1,22 @@ +language: ruby +rvm: + - 1.8.7 + - 1.9.3 + - 2.0.0 +before_install: rm .gemfiles/Gemfile.rspec.lock || true +gemfile: .gemfiles/Gemfile.rspec +script: + - "bundle exec rake lint" + - "bundle exec rake syntax" + - "bundle exec rake spec SPEC_OPTS='--format documentation --order random'" +env: + - PUPPET_VERSION="~> 2.7.0" + - PUPPET_VERSION="~> 3.1.0" + - PUPPET_VERSION="~> 3.2.0" + - PUPPET_VERSION="~> 3.3.0" + - PUPPET_VERSION="~> 3.4.0" + - PUPPET_VERSION="~> 3.5.0" + - PUPPET_VERSION="~> 3.6.0" +matrix: + allow_failures: + - rvm: 2.0.0 diff --git a/elasticsearch/CHANGELOG.md b/elasticsearch/CHANGELOG.md new file mode 100644 index 000000000..6f7fae5a9 --- /dev/null +++ b/elasticsearch/CHANGELOG.md @@ -0,0 +1,159 @@ +##0.4.0 ( Jun 18, 2014 ) - Backwards compatible breaking release + +###Summary +This release introduces instances to facilitate the option to have more then a single instance running on the host system. + +####Features +* Rewrite module to incorperate multi instance support +* New readme layout + +####Bugfixes +* None + +####Changes +* Adding ec2-linux osfamily for repo management +* Retry behaviour for plugin installation + +####Testing changes +* Adding Puppet 3.6.x testing +* Ubuntu 14.04 testing +* Using new docker images +* Pin rspec to 2.14.x + +####Known Bugs +* No known bugs + +##0.3.2 ( May 15, 2014 ) +* Add support for SLC/Scientific Linux CERN ( PR #121 ) +* Add support for custom package names ( PR #122 ) +* Fix python and ruby client defines to avoid name clashes. +* Add ability to use stage instead of anchor for repo class +* Minor fixes to system tests + +##0.3.1 ( April 22, 2014 ) +* Ensure we create the plugin directory before installing plugins +* Added Puppet 3.5.x to rspec and system tests + +##0.3.0 ( April 2, 2014 ) +* Fix minor issue with yumrepo in repo class ( PR #92 ) +* Implement OpenSuse support +* Implement Junit reporting for tests +* Adding more system tests and convert to Docker images +* Use Augeas for managing the defaults file +* Add retry to package download exec +* Add management to manage the logging.yml file +* Improve inline documentation +* Improve support for Debian 6 +* Improve augeas for values with spaces +* Run plugin install as ES user ( PR #108 ) +* Fix rights for the plugin directory +* Pin Rake for Ruby 1.8.7 +* Adding new metadata for Forge. +* Increase time for retry to insert the template + +##0.2.4 ( Feb 21, 2014 ) +* Set puppetlabs-stdlib dependency version from 3.0.0 to 3.2.0 to be inline with other modules +* Let puppet run fail when template insert fails +* Documentation improvements ( PR #77, #78, #83 ) +* Added beaker system tests +* Fixed template define after failing system tests +* Some fixes so variables are more inline with intended structure + +##0.2.3 ( Feb 06, 2014 ) +* Add repository management feature +* Improve testing coverage and implement basic resource coverage reporting +* Add puppet 3.4.x testing +* Fix dependency in template define ( PR #72 ) +* For apt repo change from key server to key file + +##0.2.2 ( Jan 23, 2014 ) +* Ensure exec names are unique. This caused issues when using our logstash module +* Add spec tests for plugin define + +##0.2.1 ( Jan 22, 2014 ) +* Simplify the management of the defaults file ( PR #64 ) +* Doc improvements for the plugin define ( PR #66 ) +* Allow creation of data directory ( PR #68 ) +* Fail early when package version and package_url are defined + +##0.2.0 ( Nov 19, 2013 ) +* Large rewrite of the entire module described below +* Make the core more dynamic for different service providers and multi instance capable +* Add better testing and devided into different files +* Fix template function. Replace of template is now only done when the file is changed +* Add different ways to install the package except from the repository ( puppet/http/https/ftp/file ) +* Update java class to install openjdk 1.7 +* Add tests for python function +* Update config file template to fix scoping issue ( from PR #57 ) +* Add validation of templates +* Small changes for preperation for system tests +* Update readme for new functionality +* Added more test scenario's +* Added puppet parser validate task for added checking +* Ensure we don't add stuff when removing the module +* Update python client define +* Add ruby client define +* Add tests for ruby clients and update python client tests + +##0.1.3 ( Sep 06, 2013 ) +* Exec path settings has been updated to fix warnings ( PR #37, #47 ) +* Adding define to install python bindings ( PR #43 ) +* Scope deprecation fixes ( PR #41 ) +* feature to install plugins ( PR #40 ) + +##0.1.2 ( Jun 21, 2013 ) +* Update rake file to ignore the param inherit +* Added missing documentation to the template define +* Fix for template define to allow multiple templates ( PR #36 by Bruce Morrison ) + +##0.1.1 ( Jun 14, 2013 ) +* Add Oracle Linux to the OS list ( PR #25 by Stas Alekseev ) +* Respect the restart_on_change on the defaults ( PR #29 by Simon Effenberg ) +* Make sure the config can be empty as advertised in the readme +* Remove dependency cycle when the defaults file is updated ( PR #31 by Bruce Morrison ) +* Enable retry on the template insert in case ES isn't started yet ( PR #32 by Bruce Morrison ) +* Update templates to avoid deprecation notice with Puppet 3.2.x +* Update template define to avoid auto insert issue with ES +* Update spec tests to reflect changes to template define + +##0.1.0 ( May 09, 2013 ) +* Populate .gitignore ( PR #19 by Igor Galić ) +* Add ability to install initfile ( PR #20 by Justin Lambert ) +* Add ability to manage default file service parameters ( PR #21 by Mathieu Bornoz ) +* Providing complete containment of the module ( PR #24 by Brian Lalor ) +* Add ability to specify package version ( PR #25 by Justin Lambert ) +* Adding license file + +##0.0.7 ( Mar 23, 2013 ) +* Ensure config directory is created and managed ( PR #13 by Martin Seener ) +* Dont backup package if it changes +* Create explicit dependency on template directory ( PR #16 by Igor Galić ) +* Make the config directory variable ( PR #17 by Igor Galić and PR #18 by Vincent Janelle ) +* Fixing template define + +##0.0.6 ( Mar 05, 2013 ) +* Fixing issue with configuration not printing out arrays +* New feature to write the config hash shorter +* Updated readme to reflect the new feature +* Adding spec tests for config file generation + +##0.0.5 ( Mar 03, 2013 ) +* Option to disable restart on config file change ( PR #10 by Chris Boulton ) + +##0.0.4 ( Mar 02, 2013 ) +* Fixed a major issue with the config template ( Issue #9 ) + +##0.0.3 ( Mar 02, 2013 ) +* Adding spec tests +* Fixed init issue on Ubuntu ( Issue #6 by Marcus Furlong ) +* Fixed config template problem ( Issue #8 by surfchris ) +* New feature to manage templates + +##0.0.2 ( Feb 16, 2013 ) +* Feature to supply a package instead of being dependent on the repository +* Feature to install java in case one doesn't manage it externally +* Adding RedHat and Amazon as Operating systems +* fixed a typo - its a shard not a shared :) ( PR #5 by Martin Seener ) + +##0.0.1 ( Jan 13, 2013 ) +* Initial release of the module diff --git a/elasticsearch/CONTRIBUTING.md b/elasticsearch/CONTRIBUTING.md new file mode 100644 index 000000000..46af96893 --- /dev/null +++ b/elasticsearch/CONTRIBUTING.md @@ -0,0 +1,47 @@ +If you have a bugfix or new feature that you would like to contribute to this puppet module, please find or open an issue about it first. Talk about what you would like to do. It may be that somebody is already working on it, or that there are particular issues that you should know about before implementing the change. + +We enjoy working with contributors to get their code accepted. There are many approaches to fixing a problem and it is important to find the best approach before writing too much code. + +The process for contributing to any of the Elasticsearch repositories is similar. + +1. Sign the contributor license agreement +Please make sure you have signed the [Contributor License Agreement](http://www.elasticsearch.org/contributor-agreement/). We are not asking you to assign copyright to us, but to give us the right to distribute your code without restriction. We ask this of all contributors in order to assure our users of the origin and continuing existence of the code. You only need to sign the CLA once. + +2. Run the rspec tests and ensure it completes without errors with your changes. + +3. Run the acceptance tests + +These instructions are for Ubuntu 14.04 + +* install docker 0.11.1 + * wget https://get.docker.io/ubuntu/pool/main/l/lxc-docker/lxc-docker_0.11.1_amd64.deb + * wget https://get.docker.io/ubuntu/pool/main/l/lxc-docker-0.11.1/lxc-docker-0.11.1_0.11.1_amd64.deb + * sudo dpkg -i lxc-docker_0.11.1_amd64.deb lxc-docker-0.11.1_0.11.1_amd64.deb + * sudo usermod -a -G docker $USER +* export BUNDLE_GEMFILE=.gemfiles/Gemfile.beaker +* export RS_SET='ubuntu-server-1404-x64' # see spec/acceptance/nodesets for more +* export VM_PUPPET_VERSION='3.6.0' +* wget https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.1.0.deb +* wget https://forgeapi.puppetlabs.com/v3/files/puppetlabs-stdlib-3.2.0.tar.gz +* wget https://forgeapi.puppetlabs.com/v3/files/puppetlabs-apt-1.4.2.tar.gz +* export files_dir=$(pwd) +* bundle install +* bundle exec rspec --format RspecJunitFormatter --out rspec.xml spec/acceptance/*_spec.rb + +``` + Hypervisor for ubuntu-14-04 is docker + Beaker::Hypervisor, found some docker boxes to create + Provisioning docker + provisioning ubuntu-14-04 + ... + Finished in 18 minutes 6 seconds + 224 examples, 0 failures, 3 pending +``` + +4. Rebase your changes +Update your local repository with the most recent code from the main this puppet module repository, and rebase your branch on top of the latest master branch. We prefer your changes to be squashed into a single commit. + +5. Submit a pull request +Push your local changes to your forked copy of the repository and submit a pull request. In the pull request, describe what your changes do and mention the number of the issue where discussion has taken place, eg “Closes #123″. + +Then sit back and wait. There will probably be discussion about the pull request and, if any changes are needed, we would love to work with you to get your pull request merged into this puppet module. diff --git a/elasticsearch/CONTRIBUTORS b/elasticsearch/CONTRIBUTORS new file mode 100644 index 000000000..ad0eb76c4 --- /dev/null +++ b/elasticsearch/CONTRIBUTORS @@ -0,0 +1,20 @@ +The following is a list of people who have contributed ideas, code, bug +reports, or in general have helped this puppet module along its way. + +Project Owner +* Richard Pijnenburg (electrical) + +Contributors: +Martin Seener (martinseener) +Marcus Furlong (furlongm) +Chris Boulton (chrisboulton) +Igor Galić (igalic) +Vincent Janelle (vjanelle) +Mathieu Bornoz (mbornoz) +Justin Lambert (jlambert121) +Brian Lalor (blalor) +Stas Alekseev (salekseev) +Simon Effenberg (Savar) +Bruce Morrison (brucem) +deanmalmgren +Matteo Sessa (msessa-cotd) diff --git a/elasticsearch/LICENSE b/elasticsearch/LICENSE new file mode 100644 index 000000000..f8b711d55 --- /dev/null +++ b/elasticsearch/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2012-2014 Elasticsearch + +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. diff --git a/elasticsearch/Modulefile b/elasticsearch/Modulefile new file mode 100644 index 000000000..29c4ff25f --- /dev/null +++ b/elasticsearch/Modulefile @@ -0,0 +1,9 @@ +name 'elasticsearch-elasticsearch' +version '0.4.0' +source 'https://github.com/elasticsearch/puppet-elasticsearch' +author 'elasticsearch' +license 'Apache License, Version 2.0' +summary 'Module for managing and configuring Elasticsearch nodes' +description 'Module for managing and configuring Elasticsearch nodes' +project_page 'https://github.com/elasticsearch/puppet-elasticsearch' +dependency 'puppetlabs/stdlib', '>= 3.2.0' diff --git a/elasticsearch/README.md b/elasticsearch/README.md new file mode 100644 index 000000000..db90323bd --- /dev/null +++ b/elasticsearch/README.md @@ -0,0 +1,430 @@ +#Elasticsearch Puppet module + +[![Build Status](https://travis-ci.org/elasticsearch/puppet-elasticsearch.png?branch=master)](https://travis-ci.org/elasticsearch/puppet-elasticsearch) + +####Table of Contents + +1. [Overview](#overview) +2. [Module description - What the module does and why it is useful](#module-description) +3. [Setup - The basics of getting started with Elasticsearch](#setup) + * [The module manages the following](#the-module-manages-the-following) + * [Requirements](#requirements) +4. [Usage - Configuration options and additional functionality](#usage) +5. [Advanced features - Extra information on advanced usage](#advanced-features) +6. [Limitations - OS compatibility, etc.](#limitations) +7. [Development - Guide for contributing to the module](#development) +8. [Support - When you need help with this module](#support) + + + +##Overview + +This module manages Elasticsearch (http://www.elasticsearch.org/overview/elasticsearch/) + +##Module description + +The elasticsearch module sets up Elasticsearch instances and can manage plugins and templates. + +This module has been tested against ES 1.0 and up. + +##Setup + +###The module manages the following + +* Elasticsearch repository files. +* Elasticsearch package. +* Elasticsearch configuration file. +* Elasticsearch service. +* Elasticsearch plugins. +* Elasticsearch templates. + +###Requirements + +* The [stdlib](https://forge.puppetlabs.com/puppetlabs/stdlib) Puppet library. + +#### Repository management +When using the repository management you will need the following dependency modules: + +* Debian/Ubuntu: [Puppetlabs/apt](http://forge.puppetlabs.com/puppetlabs/apt) +* OpenSuSE: [Darin/zypprepo](https://forge.puppetlabs.com/darin/zypprepo) + +##Usage + +###Main class + +####Install a specific version + +```puppet +class { 'elasticsearch': + version => '1.2.1' +} +``` + +Note: This will only work when using the repository. + +####Automatic upgrade of the software ( default set to false ) +``` +class { 'elasticsearch': + autoupgrade => true +} +``` + +####Removal/decommissioning +```puppet +class { 'elasticsearch': + ensure => 'absent' +} +``` + +####Install everything but disable service(s) afterwards +```puppet +class { 'elasticsearch': + status => 'disabled' +} +``` + +###Instances + +This module works with the concept of instances. + +####Quick setup +```puppet +elasticsearch::instance { 'es-01': } +``` + +This will set up its own data directory and set the node name to `$hostname-$instance_name` + +####Advanced options + +Instance specific options can be given: + +```puppet +elasticsearch::instance { 'es-01': + config => { }, # Configuration hash + init_defaults => { }, # Init defaults hash + datadir => [ ], # Data directory +} +``` + +See [Advanced features](#advanced-features) for more information + +###Plug-ins + +Install [a variety of plugins](http://www.elasticsearch.org/guide/plugins/): + +####From official repository +```puppet +elasticsearch::plugin{'lmenezes/elasticsearch-kopf': + module_dir => 'kopf', + instances => 'instance_name' +} +``` +####From custom url +```puppet +elasticsearch::plugin{ 'elasticsearch-jetty': + module_dir => 'jetty', + url => 'https://oss-es-plugins.s3.amazonaws.com/elasticsearch-jetty/elasticsearch-jetty-1.2.1.zip', + instances => 'instance_name' +} +``` +###Templates + +#### Add a new template + +This will install and/or replace the template in Elasticsearch: + +```puppet +elasticsearch::template { 'templatename': + file => 'puppet:///path/to/template.json' +} +``` + +#### Delete a template + +```puppet +elasticsearch::template { 'templatename': + ensure => 'absent' +} +``` + +#### Host + +By default it uses localhost:9200 as host. you can change this with the `host` and `port` variables + +```puppet +elasticsearch::template { 'templatename': + host => $::ipaddress, + port => 9200 +} +``` + +###Bindings / Clients + +Install a variety of [clients/bindings](http://www.elasticsearch.org/guide/clients/): + +####Python + +```puppet +elasticsearch::python { 'rawes': } +``` + +####Ruby +```puppet +elasticsearch::ruby { 'elasticsearch': } +``` + +###Package installation + +There are 2 different ways of installing the software + +####Repository + +This option allows you to use an existing repository for package installation. +The `repo_version` corresponds with the major version of Elasticsearch. + +```puppet +class { 'elasticsearch': + manage_repo => true, + repo_version => '1.2', +} +``` + +####Remote package source + +When a repository is not available or preferred you can install the packages from a remote source: + +#####http/https/ftp +```puppet +class { 'elasticsearch': + package_url => 'https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.2.1.deb' +} +``` + +#####puppet:// +```puppet +class { 'elasticsearch': + package_url => 'puppet:///path/to/elasticsearch-1.2.1.deb' +} +``` + +#####Local file +```puppet +class { 'elasticsearch': + package_url => 'file:/path/to/elasticsearch-1.2.1.deb' +} +``` + +###Java installation + +Most sites will manage Java separately; however, this module can attempt to install Java as well. + +```puppet +class { 'elasticsearch': + java_install => true +} +``` + +Specify a particular Java package/version to be installed: + +```puppet +class { 'elasticsearch': + java_install => true, + java_package => 'packagename' +} +``` + +###Service management + +Currently only the basic SysV-style [init](https://en.wikipedia.org/wiki/Init) and [Systemd](http://en.wikipedia.org/wiki/Systemd) service providers are supported, but other systems could be implemented as necessary (pull requests welcome). + + +####Defaults File + +The *defaults* file (`/etc/defaults/elasticsearch` or `/etc/sysconfig/elasticsearch`) for the Elasticsearch service can be populated as necessary. This can either be a static file resource or a simple key value-style [hash](http://docs.puppetlabs.com/puppet/latest/reference/lang_datatypes.html#hashes) object, the latter being particularly well-suited to pulling out of a data source such as Hiera. + +#####file source +```puppet +class { 'elasticsearch': + init_defaults_file => 'puppet:///path/to/defaults' +} +``` +#####hash representation +```puppet +$config_hash = { + 'ES_USER' => 'elasticsearch', + 'ES_GROUP' => 'elasticsearch', +} + +class { 'elasticsearch': + init_defaults => $config_hash +} +``` + +Note: `init_defaults` hash can be passed to the main class and to the instance. + +##Advanced features + + +###Data directories + +There are 4 different ways of setting data directories for Elasticsearch. +In every case the required configuration options are placed in the `elasticsearch.yml` file. + +####Default +By default we use: + +`/usr/share/elasticsearch/data/$instance_name` + +Which provides a data directory per instance. + + +####Single global data directory + +```puppet +class { 'elasticsearch': + datadir => '/var/lib/elasticsearch-data' +} +``` +Creates the following for each instance: + +`/var/lib/elasticsearch-data/$instance_name` + +####Multiple Global data directories + +```puppet +class { 'elasticsearch: + datadir => [ '/var/lib/es-data1', '/var/lib/es-data2'] +} +``` +Creates the following for each instance: +`/var/lib/es-data1/$instance_name` +and +`/var/lib/es-data2/$instance_name` + + +####Single instance data directory + +```puppet +class { 'elasticsearch': } + +elasticsearch::instance { 'es-01': + datadir => '/var/lib/es-data-es01' +} +``` +Creates the following for this instance: +`/var/lib/es-data-es01` + +####Multiple instance data directories + +```puppet +class { 'elasticsearch': } + +elasticsearch::instance { 'es-01': + datadir => ['/var/lib/es-data1-es01', '/var/lib/es-data2-es01'] +} +``` +Creates the following for this instance: +`/var/lib/es-data1-es01` +and +`/var/lib/es-data2-es01` + + +###Main and instance configurations + +The `config` option in both the main class and the instances can be configured to work together. + +The options in the `instance` config hash will merged with the ones from the main class and override any duplicates. + +#### Simple merging + +```puppet +class { 'elasticsearch': + config => { 'cluster.name' => 'clustername' } +} + +elasticsearch::instance { 'es-01': + config => { 'node.name' => 'nodename' } +} +elasticsearch::instance { 'es-02': + config => { 'node.name' => 'nodename2' } +} + +``` + +This example merges the `cluster.name` together with the `node.name` option. + +#### Overriding + +When duplicate options are provided, the option in the instance config overrides the ones from the main class. + +```puppet +class { 'elasticsearch': + config => { 'cluster.name' => 'clustername' } +} + +elasticsearch::instance { 'es-01': + config => { 'node.name' => 'nodename', 'cluster.name' => 'otherclustername' } +} + +elasticsearch::instance { 'es-02': + config => { 'node.name' => 'nodename2' } +} +``` + +This will set the cluster name to `otherclustername` for the instance `es-01` but will keep it to `clustername` for instance `es-02` + +####Configuration writeup + +The `config` hash can be written in 2 different ways: + +##### Full hash writeup + +Instead of writing the full hash representation: +```puppet +class { 'elasticsearch': + config => { + 'cluster' => { + 'name' => 'ClusterName', + 'routing' => { + 'allocation' => { + 'awareness' => { + 'attributes' => 'rack' + } + } + } + } + } +} +``` +##### Short hash writeu +```puppet +class { 'elasticsearch': + config => { + 'cluster' => { + 'name' => 'ClusterName', + 'routing.allocation.awareness.attributes' => 'rack' + } + } +} +``` + + +##Limitations + +This module has been built on and tested against Puppet 2.7 and higher. + +The module has been tested on: + +* Debian 6/7 +* CentOS 6 +* Ubuntu 12.04, 13.x, 14.x +* OpenSuSE 12.x + +Testing on other platforms has been light and cannot be guaranteed. + +##Development + + +##Support + +Need help? Join us in [#elasticsearch](https://webchat.freenode.net?channels=%23elasticsearch) on Freenode IRC or subscribe to the [elasticsearch@googlegroups.com](https://groups.google.com/forum/#!forum/elasticsearch) mailing list. diff --git a/elasticsearch/Rakefile b/elasticsearch/Rakefile new file mode 100644 index 000000000..77cc210d0 --- /dev/null +++ b/elasticsearch/Rakefile @@ -0,0 +1,29 @@ +require 'rubygems' +require 'puppetlabs_spec_helper/rake_tasks' + +exclude_paths = [ + "pkg/**/*", + "vendor/**/*", + "spec/**/*", +] + +begin + require 'puppet-doc-lint/rake_task' + PuppetDocLint.configuration.ignore_paths = exclude_paths +rescue LoadError +end + +begin + require 'puppet-lint/tasks/puppet-lint' + require 'puppet-syntax/tasks/puppet-syntax' + + PuppetSyntax.exclude_paths = exclude_paths + PuppetSyntax.future_parser = true if ENV['FUTURE_PARSER'] == 'true' + + PuppetLint.configuration.send("disable_80chars") + PuppetLint.configuration.send("disable_class_inherits_from_params_class") + PuppetLint.configuration.send('disable_class_parameter_defaults') + PuppetLint.configuration.ignore_paths = exclude_paths + PuppetLint.configuration.log_format = "%{path}:%{linenumber}:%{check}:%{KIND}:%{message}" +rescue LoadError +end diff --git a/elasticsearch/lib/puppet/parser/functions/array_suffix.rb b/elasticsearch/lib/puppet/parser/functions/array_suffix.rb new file mode 100644 index 000000000..0f2da68e7 --- /dev/null +++ b/elasticsearch/lib/puppet/parser/functions/array_suffix.rb @@ -0,0 +1,45 @@ +# +# suffix.rb +# + +module Puppet::Parser::Functions + newfunction(:array_suffix, :type => :rvalue, :doc => <<-EOS +This function applies a suffix to all elements in an array. + +*Examples:* + + array_suffix(['a','b','c'], 'p') + +Will return: ['ap','bp','cp'] + EOS + ) do |arguments| + + # Technically we support two arguments but only first is mandatory ... + raise(Puppet::ParseError, "array_suffix(): Wrong number of arguments " + + "given (#{arguments.size} for 1)") if arguments.size < 1 + + array = arguments[0] + + unless array.is_a?(Array) + raise Puppet::ParseError, "array_suffix(): expected first argument to be an Array, got #{array.inspect}" + end + + suffix = arguments[1] if arguments[1] + + if suffix + unless suffix.is_a? String + raise Puppet::ParseError, "array_suffix(): expected second argument to be a String, got #{suffix.inspect}" + end + end + + # Turn everything into string same as join would do ... + result = array.collect do |i| + i = i.to_s + suffix ? i + suffix : i + end + + return result + end +end + +# vim: set ts=2 sw=2 et : diff --git a/elasticsearch/manifests/config.pp b/elasticsearch/manifests/config.pp new file mode 100644 index 000000000..ddb470236 --- /dev/null +++ b/elasticsearch/manifests/config.pp @@ -0,0 +1,99 @@ +# == Class: elasticsearch::config +# +# This class exists to coordinate all configuration related actions, +# functionality and logical units in a central place. +# +# +# === Parameters +# +# This class does not provide any parameters. +# +# +# === Examples +# +# This class may be imported by other classes to use its functionality: +# class { 'elasticsearch::config': } +# +# It is not intended to be used directly by external resources like node +# definitions or other modules. +# +# +# === Authors +# +# * Richard Pijnenburg +# +class elasticsearch::config { + + #### Configuration + + File { + owner => $elasticsearch::elasticsearch_user, + group => $elasticsearch::elasticsearch_group + } + + Exec { + path => [ '/bin', '/usr/bin', '/usr/local/bin' ], + cwd => '/', + } + + if ( $elasticsearch::ensure == 'present' ) { + + $notify_service = $elasticsearch::restart_on_change ? { + true => Class['elasticsearch::service'], + false => undef, + } + + file { $elasticsearch::configdir: + ensure => directory, + mode => '0644' + } + + file { $elasticsearch::plugindir: + ensure => 'directory', + mode => '0644' + } + + exec { 'mkdir_templates_elasticsearch': + command => "mkdir -p ${elasticsearch::configdir}/templates_import", + creates => "${elasticsearch::configdir}/templates_import", + } + + file { "${elasticsearch::configdir}/templates_import": + ensure => 'directory', + mode => '0644', + require => [ Exec['mkdir_templates_elasticsearch'] ] + } + + # Removal of files that are provided with the package which we don't use + case $elasticsearch::real_service_provider { + init: { + file { '/etc/init.d/elasticsearch': + ensure => 'absent' + } + } + systemd: { + file { '/usr/lib/systemd/system/elasticsearch.service': + ensure => 'absent' + } + } + default: { + fail("Unknown service provider ${elasticsearch::real_service_provider}") + } + + } + file { "${elasticsearch::params::defaults_location}/elasticsearch": + ensure => 'absent' + } + + file { '/etc/elasticsearch/elasticsearch.yml': + ensure => 'absent' + } + file { '/etc/elasticsearch/logging.yml': + ensure => 'absent' + } + + } elsif ( $elasticsearch::ensure == 'absent' ) { + # don't remove anything for now + } + +} diff --git a/elasticsearch/manifests/init.pp b/elasticsearch/manifests/init.pp new file mode 100644 index 000000000..752edec24 --- /dev/null +++ b/elasticsearch/manifests/init.pp @@ -0,0 +1,323 @@ +# == Class: elasticsearch +# +# This class is able to install or remove elasticsearch on a node. +# It manages the status of the related service. +# +# === Parameters +# +# [*ensure*] +# String. Controls if the managed resources shall be present or +# absent. If set to absent: +# * The managed software packages are being uninstalled. +# * Any traces of the packages will be purged as good as possible. This may +# include existing configuration files. The exact behavior is provider +# dependent. Q.v.: +# * Puppet type reference: {package, "purgeable"}[http://j.mp/xbxmNP] +# * {Puppet's package provider source code}[http://j.mp/wtVCaL] +# * System modifications (if any) will be reverted as good as possible +# (e.g. removal of created users, services, changed log settings, ...). +# * This is thus destructive and should be used with care. +# Defaults to present. +# +# [*autoupgrade*] +# Boolean. If set to true, any managed package gets upgraded +# on each Puppet run when the package provider is able to find a newer +# version than the present one. The exact behavior is provider dependent. +# Q.v.: +# * Puppet type reference: {package, "upgradeable"}[http://j.mp/xbxmNP] +# * {Puppet's package provider source code}[http://j.mp/wtVCaL] +# Defaults to false. +# +# [*status*] +# String to define the status of the service. Possible values: +# * enabled: Service is running and will be started at boot time. +# * disabled: Service is stopped and will not be started at boot +# time. +# * running: Service is running but will not be started at boot time. +# You can use this to start a service on the first Puppet run instead of +# the system startup. +# * unmanaged: Service will not be started at boot time and Puppet +# does not care whether the service is running or not. For example, this may +# be useful if a cluster management software is used to decide when to start +# the service plus assuring it is running on the desired node. +# Defaults to enabled. The singular form ("service") is used for the +# sake of convenience. Of course, the defined status affects all services if +# more than one is managed (see service.pp to check if this is the +# case). +# +# [*version*] +# String to set the specific version you want to install. +# Defaults to false. +# +# [*restart_on_change*] +# Boolean that determines if the application should be automatically restarted +# whenever the configuration changes. Disabling automatic restarts on config +# changes may be desired in an environment where you need to ensure restarts +# occur in a controlled/rolling manner rather than during a Puppet run. +# +# Defaults to true, which will restart the application on any config +# change. Setting to false disables the automatic restart. +# +# [*configdir*] +# Path to directory containing the elasticsearch configuration. +# Use this setting if your packages deviate from the norm (/etc/elasticsearch) +# +# [*plugindir*] +# Path to directory containing the elasticsearch plugins +# Use this setting if your packages deviate from the norm (/usr/share/elasticsearch/plugins) +# +# [*plugintool*] +# Path to directory containing the elasticsearch plugin installation script +# Use this setting if your packages deviate from the norm (/usr/share/elasticsearch/bin/plugin) +# +# [*package_url*] +# Url to the package to download. +# This can be a http,https or ftp resource for remote packages +# puppet:// resource or file:/ for local packages +# +# [*package_provider*] +# Way to install the packages, currently only packages are supported. +# +# [*package_dir*] +# Directory where the packages are downloaded to +# +# [*package_name*] +# Name of the package to install +# +# [*purge_package_dir*] +# Purge package directory on removal +# +# [*package_dl_timeout*] +# For http,https and ftp downloads you can set howlong the exec resource may take. +# Defaults to: 600 seconds +# +# [*elasticsearch_user*] +# The user Elasticsearch should run as. This also sets the file rights. +# +# [*elasticsearch_group*] +# The group Elasticsearch should run as. This also sets the file rights +# +# [*purge_configdir*] +# Purge the config directory for any unmanaged files +# +# [*service_provider*] +# Service provider to use. By Default when a single service provider is possibe that one is selected. +# +# [*init_defaults*] +# Defaults file content in hash representation +# +# [*init_defaults_file*] +# Defaults file as puppet resource +# +# [*init_template*] +# Service file as a template +# +# [*config*] +# Elasticsearch configuration hash +# +# [*datadir*] +# Allows you to set the data directory of Elasticsearch +# +# [*java_install*] +# Install java which is required for Elasticsearch. +# Defaults to: false +# +# [*java_package*] +# If you like to install a custom java package, put the name here. +# +# [*manage_repo*] +# Enable repo management by enabling our official repositories +# +# [*repo_version*] +# Our repositories are versioned per major version (0.90, 1.0) select here which version you want +# +# [*logging_config*] +# Hash representation of information you want in the logging.yml file +# +# [*logging_file*] +# Instead of a hash you can supply a puppet:// file source for the logging.yml file +# +# [*default_logging_level*] +# Default logging level for Elasticsearch. +# Defaults to: INFO +# +# [*repo_stage*] +# Use stdlib stage setup for managing the repo, instead of anchoring +# +# The default values for the parameters are set in elasticsearch::params. Have +# a look at the corresponding params.pp manifest file if you need more +# technical information about them. +# +# === Examples +# +# * Installation, make sure service is running and will be started at boot time: +# class { 'elasticsearch': } +# +# * Removal/decommissioning: +# class { 'elasticsearch': +# ensure => 'absent', +# } +# +# * Install everything but disable service(s) afterwards +# class { 'elasticsearch': +# status => 'disabled', +# } +# +# +# === Authors +# +# * Richard Pijnenburg +# +class elasticsearch( + $ensure = $elasticsearch::params::ensure, + $status = $elasticsearch::params::status, + $restart_on_change = $elasticsearch::params::restart_on_change, + $autoupgrade = $elasticsearch::params::autoupgrade, + $version = false, + $package_provider = 'package', + $package_url = undef, + $package_dir = $elasticsearch::params::package_dir, + $package_name = $elasticsearch::params::package, + $purge_package_dir = $elasticsearch::params::purge_package_dir, + $package_dl_timeout = $elasticsearch::params::package_dl_timeout, + $elasticsearch_user = $elasticsearch::params::elasticsearch_user, + $elasticsearch_group = $elasticsearch::params::elasticsearch_group, + $configdir = $elasticsearch::params::configdir, + $purge_configdir = $elasticsearch::params::purge_configdir, + $service_provider = 'init', + $init_defaults = undef, + $init_defaults_file = undef, + $init_template = undef, + $config = undef, + $datadir = $elasticsearch::params::datadir, + $plugindir = $elasticsearch::params::plugindir, + $plugintool = $elasticsearch::params::plugintool, + $java_install = false, + $java_package = undef, + $manage_repo = false, + $repo_version = false, + $logging_file = undef, + $logging_config = undef, + $default_logging_level = $elasticsearch::params::default_logging_level, + $repo_stage = false +) inherits elasticsearch::params { + + anchor {'elasticsearch::begin': } + anchor {'elasticsearch::end': } + + + #### Validate parameters + + # ensure + if ! ($ensure in [ 'present', 'absent' ]) { + fail("\"${ensure}\" is not a valid ensure parameter value") + } + + # autoupgrade + validate_bool($autoupgrade) + + # service status + if ! ($status in [ 'enabled', 'disabled', 'running', 'unmanaged' ]) { + fail("\"${status}\" is not a valid status parameter value") + } + + # restart on change + validate_bool($restart_on_change) + + # purge conf dir + validate_bool($purge_configdir) + + if is_array($elasticsearch::params::service_providers) { + # Verify the service provider given is in the array + if ! ($service_provider in $elasticsearch::params::service_providers) { + fail("\"${service_provider}\" is not a valid provider for \"${::operatingsystem}\"") + } + $real_service_provider = $service_provider + } else { + # There is only one option so simply set it + $real_service_provider = $elasticsearch::params::service_providers + } + + if ($package_url != undef and $version != false) { + fail('Unable to set the version number when using package_url option.') + } + + if $ensure == 'present' { + # validate config hash + if ($config != undef) { + validate_hash($config) + } + } + + # java install validation + validate_bool($java_install) + + validate_bool($manage_repo) + + if ($manage_repo == true) { + validate_string($repo_version) + } + + #### Manage actions + + # package(s) + class { 'elasticsearch::package': } + + # configuration + class { 'elasticsearch::config': } + + if $java_install == true { + # Install java + class { 'elasticsearch::java': } + + # ensure we first java java and then manage the service + Anchor['elasticsearch::begin'] + -> Class['elasticsearch::java'] + -> Class['elasticsearch::package'] + } + + if ($manage_repo == true) { + + if ($repo_stage == false) { + # use anchor for ordering + + # Set up repositories + class { 'elasticsearch::repo': } + + # Ensure that we set up the repositories before trying to install + # the packages + Anchor['elasticsearch::begin'] + -> Class['elasticsearch::repo'] + -> Class['elasticsearch::package'] + + } else { + # use staging for ordering + + if !(defined(Stage[$repo_stage])) { + stage { $repo_stage: before => Stage['main'] } + } + + class { 'elasticsearch::repo': + stage => $repo_stage + } + } + } + + #### Manage relationships + + if $ensure == 'present' { + + # we need the software before configuring it + Anchor['elasticsearch::begin'] + -> Class['elasticsearch::package'] + -> Class['elasticsearch::config'] + + } else { + + # make sure all services are getting stopped before software removal + Class['elasticsearch::config'] + -> Class['elasticsearch::package'] + + } + +} diff --git a/elasticsearch/manifests/instance.pp b/elasticsearch/manifests/instance.pp new file mode 100644 index 000000000..18226b968 --- /dev/null +++ b/elasticsearch/manifests/instance.pp @@ -0,0 +1,275 @@ +# == Define: elasticsearch::instance +# +# This define allows you to create or remove an elasticsearch instance +# +# === Parameters +# +# [*ensure*] +# String. Controls if the managed resources shall be present or +# absent. If set to absent: +# * The managed software packages are being uninstalled. +# * Any traces of the packages will be purged as good as possible. This may +# include existing configuration files. The exact behavior is provider +# dependent. Q.v.: +# * Puppet type reference: {package, "purgeable"}[http://j.mp/xbxmNP] +# * {Puppet's package provider source code}[http://j.mp/wtVCaL] +# * System modifications (if any) will be reverted as good as possible +# (e.g. removal of created users, services, changed log settings, ...). +# * This is thus destructive and should be used with care. +# Defaults to present. +# +# [*status*] +# String to define the status of the service. Possible values: +# * enabled: Service is running and will be started at boot time. +# * disabled: Service is stopped and will not be started at boot +# time. +# * running: Service is running but will not be started at boot time. +# You can use this to start a service on the first Puppet run instead of +# the system startup. +# * unmanaged: Service will not be started at boot time and Puppet +# does not care whether the service is running or not. For example, this may +# be useful if a cluster management software is used to decide when to start +# the service plus assuring it is running on the desired node. +# Defaults to enabled. The singular form ("service") is used for the +# sake of convenience. Of course, the defined status affects all services if +# more than one is managed (see service.pp to check if this is the +# case). +# +# [*config*] +# Elasticsearch configuration hash +# +# [*configdir*] +# Path to directory containing the elasticsearch configuration. +# Use this setting if your packages deviate from the norm (/etc/elasticsearch) +# +# [*datadir*] +# Allows you to set the data directory of Elasticsearch +# +# [*logging_file*] +# Instead of a hash you can supply a puppet:// file source for the logging.yml file +# +# [*logging_cnofig*] +# Hash representation of information you want in the logging.yml file +# +# [*logging_level*] +# Default logging level for Elasticsearch. +# Defaults to: INFO +# +# [*init_defaults*] +# Defaults file content in hash representation +# +# === Authors +# +# * Richard Pijnenburg +# +define elasticsearch::instance( + $ensure = $elasticsearch::ensure, + $status = $elasticsearch::status, + $config = undef, + $configdir = undef, + $datadir = undef, + $logging_file = undef, + $logging_config = undef, + $logging_level = $elasticsearch::default_logging_level, + $init_defaults = undef +) { + + require elasticsearch + require elasticsearch::params + + File { + owner => $elasticsearch::elasticsearch_user, + group => $elasticsearch::elasticsearch_group + } + + Exec { + path => [ '/bin', '/usr/bin', '/usr/local/bin' ], + cwd => '/', + } + + # ensure + if ! ($ensure in [ 'present', 'absent' ]) { + fail("\"${ensure}\" is not a valid ensure parameter value") + } + + $notify_service = $elasticsearch::restart_on_change ? { + true => Elasticsearch::Service[$name], + false => undef, + } + + # Instance config directory + if ($configdir == undef) { + $instance_configdir = "${elasticsearch::configdir}/${name}" + } else { + $instance_configdir = $configdir + } + + if ($ensure == 'present') { + + # Configuration hash + if ($config == undef) { + $instance_config = {} + } else { + validate_hash($config) + $instance_config = $config + } + + if(has_key($instance_config, 'node.name')) { + $instance_node_name = {} + } elsif(has_key($instance_config,'node')) { + if(has_key($instance_config['node'], 'name')) { + $instance_node_name = {} + } else { + $instance_node_name = { 'node.name' => "${::hostname}-${name}" } + } + } else { + $instance_node_name = { 'node.name' => "${::hostname}-${name}" } + } + + # String or array for data dir(s) + if ($datadir == undef) { + if (is_array($elasticsearch::datadir)) { + $instance_datadir = array_suffix($elasticsearch::datadir, "/${name}") + } else { + $instance_datadir = "${elasticsearch::datadir}/${name}" + } + } else { + $instance_datadir = $datadir + } + + # Logging file or hash + if ($logging_file != undef) { + $logging_source = $logging_file + $logging_content = undef + } elsif ($elasticsearch::logging_file != undef) { + $logging_source = $elasticsearch::logging_file + $logging_content = undef + } else { + + if(is_hash($elasticsearch::logging_config)) { + $main_logging_config = $elasticsearch::logging_config + } else { + $main_logging_config = { } + } + + if(is_hash($logging_config)) { + $instance_logging_config = $logging_config + } else { + $instance_logging_config = { } + } + $logging_hash = merge($elasticsearch::params::logging_defaults, $main_logging_config, $instance_logging_config) + $logging_content = template("${module_name}/etc/elasticsearch/logging.yml.erb") + $logging_source = undef + } + + if ($elasticsearch::config != undef) { + $main_config = $elasticsearch::config + } else { + $main_config = { } + } + + $instance_datadir_config = { 'path.data' => $instance_datadir } + + if(is_array($instance_datadir)) { + $dirs = join($instance_datadir, ' ') + } else { + $dirs = $instance_datadir + } + + exec { "mkdir_datadir_elasticsearch_${name}": + command => "mkdir -p ${dirs}", + creates => $instance_datadir, + require => Class['elasticsearch::package'], + before => Elasticsearch::Service[$name] + } + + file { $instance_datadir: + ensure => 'directory', + owner => $elasticsearch::elasticsearch_user, + group => $elasticsearch::elasticsearch_group, + mode => '0770', + require => [ Exec["mkdir_datadir_elasticsearch_${name}"], Class['elasticsearch::package'] ], + before => Elasticsearch::Service[$name] + } + + exec { "mkdir_configdir_elasticsearch_${name}": + command => "mkdir -p ${instance_configdir}", + creates => $elasticsearch::configdir, + require => Class['elasticsearch::package'], + before => Elasticsearch::Service[$name] + } + + file { $instance_configdir: + ensure => 'directory', + mode => '0644', + purge => $elasticsearch::purge_configdir, + force => $elasticsearch::purge_configdir, + require => [ Exec["mkdir_configdir_elasticsearch_${name}"], Class['elasticsearch::package'] ], + before => Elasticsearch::Service[$name] + } + + file { "${instance_configdir}/logging.yml": + ensure => file, + content => $logging_content, + source => $logging_source, + mode => '0644', + notify => $notify_service, + require => Class['elasticsearch::package'], + before => Elasticsearch::Service[$name] + } + + $require = Class['elasticsearch::package'] + $before = undef + + # build up new config + $instance_conf = merge($main_config, $instance_node_name, $instance_config, $instance_datadir_config) + + # defaults file content + if (is_hash($elasticsearch::init_defaults)) { + $global_init_defaults = $elasticsearch::init_defaults + } else { + $global_init_defaults = { } + } + + $instance_init_defaults_main = { 'CONF_DIR' => $instance_configdir, 'CONF_FILE' => "${instance_configdir}/elasticsearch.yml", 'LOG_DIR' => "/var/log/elasticsearch/${name}", 'ES_HOME' => '/usr/share/elasticsearch' } + + if (is_hash($init_defaults)) { + $instance_init_defaults = $init_defaults + } else { + $instance_init_defaults = { } + } + $init_defaults_new = merge($global_init_defaults, $instance_init_defaults_main, $instance_init_defaults ) + + $user = $elasticsearch::elasticsearch_user + $group = $elasticsearch::elasticsearch_group + + file { "${instance_configdir}/elasticsearch.yml": + ensure => file, + content => template("${module_name}/etc/elasticsearch/elasticsearch.yml.erb"), + mode => '0644', + notify => $notify_service, + require => Class['elasticsearch::package'] + } + + } else { + + file { $instance_configdir: + ensure => 'absent', + recurse => true, + force => true, + before => Elasticsearch::Service[$name] + } + + $require = undef + $before = Class['elasticsearch::config'] + + } + + elasticsearch::service { $name: + init_defaults => $init_defaults_new, + init_template => "${module_name}/etc/init.d/${elasticsearch::params::init_template}", + require => $require, + before => $before + } + +} diff --git a/elasticsearch/manifests/java.pp b/elasticsearch/manifests/java.pp new file mode 100644 index 000000000..6db4f140c --- /dev/null +++ b/elasticsearch/manifests/java.pp @@ -0,0 +1,60 @@ +# == Class: elasticsearch::java +# +# This class exists to install java if its not managed from an other module +# +# +# === Parameters +# +# This class does not provide any parameters. +# +# +# === Examples +# +# This class may be imported by other classes to use its functionality: +# class { 'elasticsearch::java': } +# +# It is not intended to be used directly by external resources like node +# definitions or other modules. +# +# +# === Authors +# +# * Richard Pijnenburg +# +class elasticsearch::java { + + if $elasticsearch::java_package == undef { + # Default Java package + case $::operatingsystem { + 'CentOS', 'Fedora', 'Scientific', 'RedHat', 'Amazon', 'OracleLinux': { + $package = 'java-1.7.0-openjdk' + } + 'Debian', 'Ubuntu': { + case $::lsbdistcodename { + 'squeeze': { + $package = 'openjdk-6-jre-headless' + } + default: { + $package = 'openjdk-7-jre-headless' + } + } + } + 'OpenSuSE': { + $package = 'java-1_6_0-openjdk' + } + default: { + fail("\"${module_name}\" provides no java package + for \"${::operatingsystem}\"") + } + } + } else { + $package = $elasticsearch::java_package + } + + ## Install the java package unless already specified somewhere else + if !defined(Package[$package]) { + package { $package: + ensure => present + } + } +} diff --git a/elasticsearch/manifests/package.pp b/elasticsearch/manifests/package.pp new file mode 100644 index 000000000..6ff610222 --- /dev/null +++ b/elasticsearch/manifests/package.pp @@ -0,0 +1,181 @@ +# == Class: elasticsearch::package +# +# This class exists to coordinate all software package management related +# actions, functionality and logical units in a central place. +# +# +# === Parameters +# +# This class does not provide any parameters. +# +# +# === Examples +# +# This class may be imported by other classes to use its functionality: +# class { 'elasticsearch::package': } +# +# It is not intended to be used directly by external resources like node +# definitions or other modules. +# +# +# === Authors +# +# * Richard Pijnenburg +# +class elasticsearch::package { + + Exec { + path => [ '/bin', '/usr/bin', '/usr/local/bin' ], + cwd => '/', + tries => 3, + try_sleep => 10 + } + + #### Package management + + # set params: in operation + if $elasticsearch::ensure == 'present' { + + # Check if we want to install a specific version or not + if $elasticsearch::version == false { + + $package_ensure = $elasticsearch::autoupgrade ? { + true => 'latest', + false => 'present', + } + + } else { + + # install specific version + $package_ensure = $elasticsearch::version + + } + + # action + if ($elasticsearch::package_url != undef) { + + case $elasticsearch::package_provider { + 'package': { $before = Package[$elasticsearch::package_name] } + default: { fail("software provider \"${elasticsearch::software_provider}\".") } + } + + $package_dir = $elasticsearch::package_dir + + # Create directory to place the package file + exec { 'create_package_dir_elasticsearch': + cwd => '/', + path => ['/usr/bin', '/bin'], + command => "mkdir -p ${elasticsearch::package_dir}", + creates => $elasticsearch::package_dir; + } + + file { $package_dir: + ensure => 'directory', + purge => $elasticsearch::purge_package_dir, + force => $elasticsearch::purge_package_dir, + backup => false, + require => Exec['create_package_dir_elasticsearch'], + } + + $filenameArray = split($elasticsearch::package_url, '/') + $basefilename = $filenameArray[-1] + + $sourceArray = split($elasticsearch::package_url, ':') + $protocol_type = $sourceArray[0] + + $extArray = split($basefilename, '\.') + $ext = $extArray[-1] + + $pkg_source = "${package_dir}/${basefilename}" + + case $protocol_type { + + puppet: { + + file { $pkg_source: + ensure => present, + source => $elasticsearch::package_url, + require => File[$package_dir], + backup => false, + before => $before + } + + } + ftp, https, http: { + + exec { 'download_package_elasticsearch': + command => "${elasticsearch::params::download_tool} ${pkg_source} ${elasticsearch::package_url} 2> /dev/null", + creates => $pkg_source, + timeout => $elasticsearch::package_dl_timeout, + require => File[$package_dir], + before => $before + } + + } + file: { + + $source_path = $sourceArray[1] + file { $pkg_source: + ensure => present, + source => $source_path, + require => File[$package_dir], + backup => false, + before => $before + } + + } + default: { + fail("Protocol must be puppet, file, http, https, or ftp. You have given \"${protocol_type}\"") + } + } + + if ($elasticsearch::package_provider == 'package') { + + case $ext { + 'deb': { $pkg_provider = 'dpkg' } + 'rpm': { $pkg_provider = 'rpm' } + default: { fail("Unknown file extention \"${ext}\".") } + } + + } + + } else { + $pkg_source = undef + $pkg_provider = undef + } + + # Package removal + } else { + + $pkg_source = undef + if ($::operatingsystem == 'OpenSuSE') { + $pkg_provider = 'rpm' + } else { + $pkg_provider = undef + } + $package_ensure = 'absent' + + $package_dir = $elasticsearch::package_dir + + file { $package_dir: + ensure => 'absent', + purge => true, + force => true, + backup => false + } + + } + + if ($elasticsearch::package_provider == 'package') { + + package { $elasticsearch::package_name: + ensure => $package_ensure, + source => $pkg_source, + provider => $pkg_provider, + } + + } else { + fail("\"${elasticsearch::package_provider}\" is not supported") + } + +} diff --git a/elasticsearch/manifests/params.pp b/elasticsearch/manifests/params.pp new file mode 100644 index 000000000..5eee1da8f --- /dev/null +++ b/elasticsearch/manifests/params.pp @@ -0,0 +1,174 @@ +# == Class: elasticsearch::params +# +# This class exists to +# 1. Declutter the default value assignment for class parameters. +# 2. Manage internally used module variables in a central place. +# +# Therefore, many operating system dependent differences (names, paths, ...) +# are addressed in here. +# +# +# === Parameters +# +# This class does not provide any parameters. +# +# +# === Examples +# +# This class is not intended to be used directly. +# +# +# === Links +# +# * {Puppet Docs: Using Parameterized Classes}[http://j.mp/nVpyWY] +# +# +# === Authors +# +# * Richard Pijnenburg +# +class elasticsearch::params { + + #### Default values for the parameters of the main module class, init.pp + + # ensure + $ensure = 'present' + + # autoupgrade + $autoupgrade = false + + # service status + $status = 'enabled' + + # restart on configuration change? + $restart_on_change = true + + # Purge configuration directory + $purge_configdir = false + + $purge_package_dir = false + + # package download timeout + $package_dl_timeout = 600 # 300 seconds is default of puppet + + $default_logging_level = 'INFO' + + $logging_defaults = { + 'action' => 'DEBUG', + 'com.amazonaws' => 'WARN', + 'index.search.slowlog' => 'TRACE, index_search_slow_log_file', + 'index.indexing.slowlog' => 'TRACE, index_indexing_slow_log_file' + } + + #### Internal module values + + # User and Group for the files and user to run the service as. + case $::kernel { + 'Linux': { + $elasticsearch_user = 'elasticsearch' + $elasticsearch_group = 'elasticsearch' + } + 'Darwin': { + $elasticsearch_user = 'elasticsearch' + $elasticsearch_group = 'elasticsearch' + } + default: { + fail("\"${module_name}\" provides no user/group default value + for \"${::kernel}\"") + } + } + + # Download tool + + case $::kernel { + 'Linux': { + $download_tool = 'wget --no-check-certificate -O' + } + 'Darwin': { + $download_tool = 'curl --insecure -o' + } + default: { + fail("\"${module_name}\" provides no download tool default value + for \"${::kernel}\"") + } + } + + # Different path definitions + case $::kernel { + 'Linux': { + $configdir = '/etc/elasticsearch' + $package_dir = '/opt/elasticsearch/swdl' + $installpath = '/opt/elasticsearch' + $plugindir = '/usr/share/elasticsearch/plugins' + $plugintool = '/usr/share/elasticsearch/bin/plugin' + $datadir = '/usr/share/elasticsearch/data' + } + default: { + fail("\"${module_name}\" provides no config directory default value + for \"${::kernel}\"") + } + } + + # packages + case $::operatingsystem { + 'RedHat', 'CentOS', 'Fedora', 'Scientific', 'Amazon', 'OracleLinux', 'SLC': { + # main application + $package = [ 'elasticsearch' ] + } + 'Debian', 'Ubuntu': { + # main application + $package = [ 'elasticsearch' ] + } + 'OpenSuSE': { + $package = [ 'elasticsearch' ] + } + default: { + fail("\"${module_name}\" provides no package default value + for \"${::operatingsystem}\"") + } + } + + # service parameters + case $::operatingsystem { + 'RedHat', 'CentOS', 'Fedora', 'Scientific', 'Amazon', 'OracleLinux', 'SLC': { + $service_name = 'elasticsearch' + $service_hasrestart = true + $service_hasstatus = true + $service_pattern = $service_name + $service_providers = [ 'init' ] + $defaults_location = '/etc/sysconfig' + $init_template = 'elasticsearch.RedHat.erb' + } + 'Debian', 'Ubuntu': { + $service_name = 'elasticsearch' + $service_hasrestart = true + $service_hasstatus = true + $service_pattern = $service_name + $service_providers = [ 'init' ] + $defaults_location = '/etc/default' + $init_template = 'elasticsearch.Debian.erb' + } + 'Darwin': { + $service_name = 'FIXME/TODO' + $service_hasrestart = true + $service_hasstatus = true + $service_pattern = $service_name + $service_providers = [ 'launchd' ] + $defaults_location = false + } + 'OpenSuSE': { + $service_name = 'elasticsearch' + $service_hasrestart = true + $service_hasstatus = true + $service_pattern = $service_name + $service_providers = 'systemd' + $defaults_location = '/etc/sysconfig' + $init_template = 'elasticsearch.OpenSuSE.erb' + } + default: { + fail("\"${module_name}\" provides no service parameters + for \"${::operatingsystem}\"") + } + } + +} diff --git a/elasticsearch/manifests/plugin.pp b/elasticsearch/manifests/plugin.pp new file mode 100644 index 000000000..7959f0328 --- /dev/null +++ b/elasticsearch/manifests/plugin.pp @@ -0,0 +1,106 @@ +# == Define: elasticsearch::plugin +# +# This define allows you to install arbitrary Elasticsearch plugins +# either by using the default repositories or by specifying an URL +# +# All default values are defined in the elasticsearch::params class. +# +# +# === Parameters +# +# [*module_dir*] +# Directory name where the module will be installed +# Value type is string +# Default value: None +# This variable is required +# +# [*ensure*] +# Whether the plugin will be installed or removed. +# Set to 'absent' to ensure a plugin is not installed +# Value type is string +# Default value: present +# This variable is optional +# +# [*url*] +# Specify an URL where to download the plugin from. +# Value type is string +# Default value: None +# This variable is optional +# +# [*instances*] +# Specify all the instances related +# value type is string or array +# +# === Examples +# +# # From official repository +# elasticsearch::plugin{'mobz/elasticsearch-head': module_dir => 'head'} +# +# # From custom url +# elasticsearch::plugin{ 'elasticsearch-jetty': +# module_dir => 'elasticsearch-jetty', +# url => 'https://oss-es-plugins.s3.amazonaws.com/elasticsearch-jetty/elasticsearch-jetty-0.90.0.zip', +# } +# +# === Authors +# +# * Matteo Sessa +# * Dennis Konert +# * Richard Pijnenburg +# +define elasticsearch::plugin( + $module_dir, + $instances, + $ensure = 'present', + $url = '' +) { + + include elasticsearch + + Exec { + path => [ '/bin', '/usr/bin', '/usr/local/bin' ], + cwd => '/', + user => $elasticsearch::elasticsearch_user, + tries => 6, + try_sleep => 10 + } + + $notify_service = $elasticsearch::restart_on_change ? { + false => undef, + default => Elasticsearch::Service[$instances], + } + + if ($module_dir != '') { + validate_string($module_dir) + } else { + fail("module_dir undefined for plugin ${name}") + } + + if ($url != '') { + validate_string($url) + $install_cmd = "${elasticsearch::plugintool} -install ${name} -url ${url}" + $exec_rets = [0,1] + } else { + $install_cmd = "${elasticsearch::plugintool} -install ${name}" + $exec_rets = [0,] + } + + case $ensure { + 'installed', 'present': { + exec {"install_plugin_${name}": + command => $install_cmd, + creates => "${elasticsearch::plugindir}/${module_dir}", + returns => $exec_rets, + notify => $notify_service, + require => File[$elasticsearch::plugindir] + } + } + default: { + exec {"remove_plugin_${name}": + command => "${elasticsearch::plugintool} --remove ${module_dir}", + onlyif => "test -d ${elasticsearch::plugindir}/${module_dir}", + notify => $notify_service, + } + } + } +} diff --git a/elasticsearch/manifests/python.pp b/elasticsearch/manifests/python.pp new file mode 100644 index 000000000..fee783a76 --- /dev/null +++ b/elasticsearch/manifests/python.pp @@ -0,0 +1,70 @@ +# == Define: elasticsearch::python +# +# there are many python bindings for elasticsearch. This provides all +# the ones we know about http://www.elasticsearch.org/guide/clients/ +# +# === Parameters +# +# [*ensure*] +# String. Controls if the managed resources shall be present or +# absent. If set to absent: +# * The managed software packages are being uninstalled. +# * Any traces of the packages will be purged as good as possible. This may +# include existing configuration files. The exact behavior is provider +# dependent. Q.v.: +# * Puppet type reference: {package, "purgeable"}[http://j.mp/xbxmNP] +# * {Puppet's package provider source code}[http://j.mp/wtVCaL] +# * System modifications (if any) will be reverted as good as possible +# (e.g. removal of created users, services, changed log settings, ...). +# * This is thus destructive and should be used with care. +# Defaults to present. +# +# === Examples +# +# elasticsearch::python { 'pyes':; } +# +# === Authors +# +# * Richard Pijnenburg +# +define elasticsearch::python ( + $ensure = 'present' +) { + + if ! ($ensure in [ 'present', 'absent' ]) { + fail("\"${ensure}\" is not a valid ensure parameter value") + } + + # make sure the package name is valid and setup the provider as + # necessary + case $name { + 'pyes': { + $provider = 'pip' + } + 'rawes': { + $provider = 'pip' + } + 'pyelasticsearch': { + $provider = 'pip' + } + 'ESClient': { + $provider = 'pip' + } + 'elasticutils': { + $provider = 'pip' + } + 'elasticsearch': { + $provider = 'pip' + } + default: { + fail("unknown python binding package '${name}'") + } + } + + package { "python_${name}": + ensure => $ensure, + name => $name, + provider => $provider, + } + +} diff --git a/elasticsearch/manifests/repo.pp b/elasticsearch/manifests/repo.pp new file mode 100644 index 000000000..326652d99 --- /dev/null +++ b/elasticsearch/manifests/repo.pp @@ -0,0 +1,78 @@ +# == Class: elasticsearch::repo +# +# This class exists to install and manage yum and apt repositories +# that contain elasticsearch official elasticsearch packages +# +# +# === Parameters +# +# This class does not provide any parameters. +# +# +# === Examples +# +# This class may be imported by other classes to use its functionality: +# class { 'elasticsearch::repo': } +# +# It is not intended to be used directly by external resources like node +# definitions or other modules. +# +# +# === Authors +# +# * Phil Fenstermacher +# * Richard Pijnenburg +# +class elasticsearch::repo { + + Exec { + path => [ '/bin', '/usr/bin', '/usr/local/bin' ], + cwd => '/', + } + + case $::osfamily { + 'Debian': { + if !defined(Class['apt']) { + class { 'apt': } + } + + apt::source { 'elasticsearch': + location => "http://packages.elasticsearch.org/elasticsearch/${elasticsearch::repo_version}/debian", + release => 'stable', + repos => 'main', + key => 'D88E42B4', + key_source => 'http://packages.elasticsearch.org/GPG-KEY-elasticsearch', + include_src => false, + } + } + 'RedHat', 'Linux': { + yumrepo { 'elasticsearch': + descr => 'elasticsearch repo', + baseurl => "http://packages.elasticsearch.org/elasticsearch/${elasticsearch::repo_version}/centos", + gpgcheck => 1, + gpgkey => 'http://packages.elasticsearch.org/GPG-KEY-elasticsearch', + enabled => 1, + } + } + 'Suse': { + exec { 'elasticsearch_suse_import_gpg': + command => 'rpmkeys --import http://packages.elasticsearch.org/GPG-KEY-elasticsearch', + unless => 'test $(rpm -qa gpg-pubkey | grep -i "D88E42B4" | wc -l) -eq 1 ', + notify => [ Zypprepo['elasticsearch'] ] + } + + zypprepo { 'elasticsearch': + baseurl => "http://packages.elasticsearch.org/elasticsearch/${elasticsearch::repo_version}/centos", + enabled => 1, + autorefresh => 1, + name => 'elasticsearch', + gpgcheck => 1, + gpgkey => 'http://packages.elasticsearch.org/GPG-KEY-elasticsearch', + type => 'yum' + } + } + default: { + fail("\"${module_name}\" provides no repository information for OSfamily \"${::osfamily}\"") + } + } +} diff --git a/elasticsearch/manifests/ruby.pp b/elasticsearch/manifests/ruby.pp new file mode 100644 index 000000000..aba0bd7e5 --- /dev/null +++ b/elasticsearch/manifests/ruby.pp @@ -0,0 +1,67 @@ +# == Define: elasticsearch::ruby +# +# there are many ruby bindings for elasticsearch. This provides all +# the ones we know about http://www.elasticsearch.org/guide/clients/ +# +# === Parameters +# +# [*ensure*] +# String. Controls if the managed resources shall be present or +# absent. If set to absent: +# * The managed software packages are being uninstalled. +# * Any traces of the packages will be purged as good as possible. This may +# include existing configuration files. The exact behavior is provider +# dependent. Q.v.: +# * Puppet type reference: {package, "purgeable"}[http://j.mp/xbxmNP] +# * {Puppet's package provider source code}[http://j.mp/wtVCaL] +# * System modifications (if any) will be reverted as good as possible +# (e.g. removal of created users, services, changed log settings, ...). +# * This is thus destructive and should be used with care. +# Defaults to present. +# +# === Examples +# +# elasticsearch::ruby { 'elasticsearch':; } +# +# === Authors +# +# * Richard Pijnenburg +# +define elasticsearch::ruby ( + $ensure = 'present' +) { + + if ! ($ensure in [ 'present', 'absent' ]) { + fail("\"${ensure}\" is not a valid ensure parameter value") + } + + # make sure the package name is valid and setup the provider as + # necessary + case $name { + 'tire': { + $provider = 'gem' + } + 'stretcher': { + $provider = 'gem' + } + 'elastic_searchable': { + $provider = 'gem' + } + 'elasticsearch': { + $provider = 'gem' + } + 'flex': { + $provider = 'gem' + } + default: { + fail("unknown ruby client package '${name}'") + } + } + + package { "ruby_${name}": + ensure => $ensure, + name => $name, + provider => $provider, + } + +} diff --git a/elasticsearch/manifests/service.pp b/elasticsearch/manifests/service.pp new file mode 100644 index 000000000..f80022ebc --- /dev/null +++ b/elasticsearch/manifests/service.pp @@ -0,0 +1,91 @@ +# == Class: elasticsearch::service +# +# This class exists to coordinate all service management related actions, +# functionality and logical units in a central place. +# +# Note: "service" is the Puppet term and type for background processes +# in general and is used in a platform-independent way. E.g. "service" means +# "daemon" in relation to Unix-like systems. +# +# +# === Parameters +# +# [*ensure*] +# String. Controls if the managed resources shall be present or +# absent. If set to absent: +# * The managed software packages are being uninstalled. +# * Any traces of the packages will be purged as good as possible. This may +# include existing configuration files. The exact behavior is provider +# dependent. Q.v.: +# * Puppet type reference: {package, "purgeable"}[http://j.mp/xbxmNP] +# * {Puppet's package provider source code}[http://j.mp/wtVCaL] +# * System modifications (if any) will be reverted as good as possible +# (e.g. removal of created users, services, changed log settings, ...). +# * This is thus destructive and should be used with care. +# Defaults to present. +# +# [*status*] +# String to define the status of the service. Possible values: +# * enabled: Service is running and will be started at boot time. +# * disabled: Service is stopped and will not be started at boot +# time. +# * running: Service is running but will not be started at boot time. +# You can use this to start a service on the first Puppet run instead of +# the system startup. +# * unmanaged: Service will not be started at boot time and Puppet +# does not care whether the service is running or not. For example, this may +# be useful if a cluster management software is used to decide when to start +# the service plus assuring it is running on the desired node. +# Defaults to enabled. The singular form ("service") is used for the +# sake of convenience. Of course, the defined status affects all services if +# more than one is managed (see service.pp to check if this is the +# case). +# +# [*init_defaults*] +# Defaults file content in hash representation +# +# [*init_defaults_file*] +# Defaults file as puppet resource +# +# [*init_template*] +# Service file as a template +# +# === Authors +# +# * Richard Pijnenburg +# +define elasticsearch::service( + $ensure = $elasticsearch::ensure, + $status = $elasticsearch::status, + $init_defaults_file = undef, + $init_defaults = undef, + $init_template = undef, +) { + + case $elasticsearch::real_service_provider { + + init: { + elasticsearch::service::init { $name: + ensure => $ensure, + status => $status, + init_defaults_file => $init_defaults_file, + init_defaults => $init_defaults, + init_template => $init_template, + } + } + systemd: { + elasticsearch::service::systemd { $name: + ensure => $ensure, + status => $status, + init_defaults_file => $init_defaults_file, + init_defaults => $init_defaults, + init_template => $init_template, + } + } + default: { + fail("Unknown service provider ${elasticsearch::real_service_provider}") + } + + } + +} diff --git a/elasticsearch/manifests/service/init.pp b/elasticsearch/manifests/service/init.pp new file mode 100644 index 000000000..bcf380745 --- /dev/null +++ b/elasticsearch/manifests/service/init.pp @@ -0,0 +1,189 @@ +# == Define: elasticsearch::service::init +# +# This class exists to coordinate all service management related actions, +# functionality and logical units in a central place. +# +# Note: "service" is the Puppet term and type for background processes +# in general and is used in a platform-independent way. E.g. "service" means +# "daemon" in relation to Unix-like systems. +# +# +# === Parameters +# +# [*ensure*] +# String. Controls if the managed resources shall be present or +# absent. If set to absent: +# * The managed software packages are being uninstalled. +# * Any traces of the packages will be purged as good as possible. This may +# include existing configuration files. The exact behavior is provider +# dependent. Q.v.: +# * Puppet type reference: {package, "purgeable"}[http://j.mp/xbxmNP] +# * {Puppet's package provider source code}[http://j.mp/wtVCaL] +# * System modifications (if any) will be reverted as good as possible +# (e.g. removal of created users, services, changed log settings, ...). +# * This is thus destructive and should be used with care. +# Defaults to present. +# +# [*status*] +# String to define the status of the service. Possible values: +# * enabled: Service is running and will be started at boot time. +# * disabled: Service is stopped and will not be started at boot +# time. +# * running: Service is running but will not be started at boot time. +# You can use this to start a service on the first Puppet run instead of +# the system startup. +# * unmanaged: Service will not be started at boot time and Puppet +# does not care whether the service is running or not. For example, this may +# be useful if a cluster management software is used to decide when to start +# the service plus assuring it is running on the desired node. +# Defaults to enabled. The singular form ("service") is used for the +# sake of convenience. Of course, the defined status affects all services if +# more than one is managed (see service.pp to check if this is the +# case). +# +# [*init_defaults*] +# Defaults file content in hash representation +# +# [*init_defaults_file*] +# Defaults file as puppet resource +# +# [*init_template*] +# Service file as a template +# +# === Authors +# +# * Richard Pijnenburg +# +define elasticsearch::service::init( + $ensure = $elasticsearch::ensure, + $status = $elasticsearch::status, + $init_defaults_file = undef, + $init_defaults = undef, + $init_template = undef, +) { + + #### Service management + + # set params: in operation + if $ensure == 'present' { + + case $status { + # make sure service is currently running, start it on boot + 'enabled': { + $service_ensure = 'running' + $service_enable = true + } + # make sure service is currently stopped, do not start it on boot + 'disabled': { + $service_ensure = 'stopped' + $service_enable = false + } + # make sure service is currently running, do not start it on boot + 'running': { + $service_ensure = 'running' + $service_enable = false + } + # do not start service on boot, do not care whether currently running + # or not + 'unmanaged': { + $service_ensure = undef + $service_enable = false + } + # unknown status + # note: don't forget to update the parameter check in init.pp if you + # add a new or change an existing status. + default: { + fail("\"${status}\" is an unknown service status value") + } + } + + # set params: removal + } else { + + # make sure the service is stopped and disabled (the removal itself will be + # done by package.pp) + $service_ensure = 'stopped' + $service_enable = false + + } + + $notify_service = $elasticsearch::restart_on_change ? { + true => Service[$name], + false => undef, + } + + + if ( $status != 'unmanaged' and $ensure == 'present' ) { + + # defaults file content. Either from a hash or file + if ($init_defaults_file != undef) { + file { "${elasticsearch::params::defaults_location}/elasticsearch-${name}": + ensure => $ensure, + source => $init_defaults_file, + owner => 'root', + group => 'root', + mode => '0644', + before => Service[$name], + notify => $notify_service + } + + } elsif ($init_defaults != undef and is_hash($init_defaults) ) { + + $init_defaults_pre_hash = { 'ES_USER' => $elasticsearch::elasticsearch_user, 'ES_GROUP' => $elasticsearch::elasticsearch_group } + $new_init_defaults = merge($init_defaults_pre_hash, $init_defaults) + + augeas { "defaults_${name}": + incl => "${elasticsearch::params::defaults_location}/elasticsearch-${name}", + lens => 'Shellvars.lns', + changes => template("${module_name}/etc/sysconfig/defaults.erb"), + before => Service[$name], + notify => $notify_service + } + + } + + # init file from template + if ($init_template != undef) { + + file { "/etc/init.d/elasticsearch-${name}": + ensure => $ensure, + content => template($init_template), + owner => 'root', + group => 'root', + mode => '0755', + before => Service[$name], + notify => $notify_service + } + + } + + } elsif ($status != 'unmanaged') { + + file { "/etc/init.d/elasticsearch-${name}": + ensure => 'absent', + subscribe => Service[$name] + } + + file { "${elasticsearch::params::defaults_location}/elasticsearch-${name}": + ensure => 'absent', + subscribe => Service[$name] + } + + } + + + if ( $status != 'unmanaged') { + + # action + service { $name: + ensure => $service_ensure, + enable => $service_enable, + name => "elasticsearch-${name}", + hasstatus => $elasticsearch::params::service_hasstatus, + hasrestart => $elasticsearch::params::service_hasrestart, + pattern => $elasticsearch::params::service_pattern, + } + + } + +} diff --git a/elasticsearch/manifests/service/systemd.pp b/elasticsearch/manifests/service/systemd.pp new file mode 100644 index 000000000..7d5e68175 --- /dev/null +++ b/elasticsearch/manifests/service/systemd.pp @@ -0,0 +1,189 @@ +# == Define: elasticsearch::service::systemd +# +# This define exists to coordinate all service management related actions, +# functionality and logical units in a central place. +# +# Note: "service" is the Puppet term and type for background processes +# in general and is used in a platform-independent way. E.g. "service" means +# "daemon" in relation to Unix-like systems. +# +# +# === Parameters +# +# [*ensure*] +# String. Controls if the managed resources shall be present or +# absent. If set to absent: +# * The managed software packages are being uninstalled. +# * Any traces of the packages will be purged as good as possible. This may +# include existing configuration files. The exact behavior is provider +# dependent. Q.v.: +# * Puppet type reference: {package, "purgeable"}[http://j.mp/xbxmNP] +# * {Puppet's package provider source code}[http://j.mp/wtVCaL] +# * System modifications (if any) will be reverted as good as possible +# (e.g. removal of created users, services, changed log settings, ...). +# * This is thus destructive and should be used with care. +# Defaults to present. +# +# [*status*] +# String to define the status of the service. Possible values: +# * enabled: Service is running and will be started at boot time. +# * disabled: Service is stopped and will not be started at boot +# time. +# * running: Service is running but will not be started at boot time. +# You can use this to start a service on the first Puppet run instead of +# the system startup. +# * unmanaged: Service will not be started at boot time and Puppet +# does not care whether the service is running or not. For example, this may +# be useful if a cluster management software is used to decide when to start +# the service plus assuring it is running on the desired node. +# Defaults to enabled. The singular form ("service") is used for the +# sake of convenience. Of course, the defined status affects all services if +# more than one is managed (see service.pp to check if this is the +# case). +# +# [*init_defaults*] +# Defaults file content in hash representation +# +# [*init_defaults_file*] +# Defaults file as puppet resource +# +# [*init_template*] +# Service file as a template +# +# === Authors +# +# * Richard Pijnenburg +# +define elasticsearch::service::systemd( + $ensure = $elasticsearch::ensure, + $status = $elasticsearch::status, + $init_defaults_file = undef, + $init_defaults = undef, + $init_template = undef, +) { + + #### Service management + + # set params: in operation + if $ensure == 'present' { + + case $status { + # make sure service is currently running, start it on boot + 'enabled': { + $service_ensure = 'running' + $service_enable = true + } + # make sure service is currently stopped, do not start it on boot + 'disabled': { + $service_ensure = 'stopped' + $service_enable = false + } + # make sure service is currently running, do not start it on boot + 'running': { + $service_ensure = 'running' + $service_enable = false + } + # do not start service on boot, do not care whether currently running + # or not + 'unmanaged': { + $service_ensure = undef + $service_enable = false + } + # unknown status + # note: don't forget to update the parameter check in init.pp if you + # add a new or change an existing status. + default: { + fail("\"${status}\" is an unknown service status value") + } + } + } else { + # make sure the service is stopped and disabled (the removal itself will be + # done by package.pp) + $service_ensure = 'stopped' + $service_enable = false + } + + $notify_service = $elasticsearch::restart_on_change ? { + true => [ Exec['systemd_reload'], Service[$name] ], + false => Exec['systemd_reload'], + } + + if ( $status != 'unmanaged' and $ensure == 'present' ) { + + # defaults file content. Either from a hash or file + if ($init_defaults_file != undef) { + file { "${elasticsearch::params::defaults_location}/elasticsearch-${name}": + ensure => $ensure, + source => $init_defaults_file, + owner => 'root', + group => 'root', + mode => '0644', + before => Service[$name], + notify => $notify_service + } + + } elsif ($init_defaults != undef and is_hash($init_defaults) ) { + + $init_defaults_pre_hash = { 'ES_USER' => $elasticsearch::elasticsearch_user, 'ES_GROUP' => $elasticsearch::elasticsearch_group } + $new_init_defaults = merge($init_defaults_pre_hash, $init_defaults) + + augeas { "defaults_${name}": + incl => "${elasticsearch::params::defaults_location}/elasticsearch-${name}", + lens => 'Shellvars.lns', + changes => template("${module_name}/etc/sysconfig/defaults.erb"), + before => Service[$name], + notify => $notify_service + } + + } + + # init file from template + if ($init_template != undef) { + + file { "/usr/lib/systemd/system/elasticsearch-${name}.service": + ensure => $ensure, + content => template($init_template), + before => Service[$name], + notify => $notify_service + } + + } + + } elsif($status != 'unmanaged') { + + file { "/usr/lib/systemd/system/elasticsearch-${name}.service": + ensure => 'absent', + subscribe => Service[$name], + notify => Exec['systemd_reload'] + } + + file { "${elasticsearch::params::defaults_location}/elasticsearch-${name}": + ensure => 'absent', + subscribe => Service[$name] + } + + } + + if(!defined(Exec['systemd_reload'])) { + exec { 'systemd_reload': + command => '/bin/systemctl daemon-reload', + refreshonly => true, + } + } + + if ($status != 'unmanaged') { + + # action + service { $name: + ensure => $service_ensure, + enable => $service_enable, + name => "elasticsearch-${name}.service", + hasstatus => $elasticsearch::params::service_hasstatus, + hasrestart => $elasticsearch::params::service_hasrestart, + pattern => $elasticsearch::params::service_pattern, + provider => 'systemd' + } + + } + +} diff --git a/elasticsearch/manifests/template.pp b/elasticsearch/manifests/template.pp new file mode 100644 index 000000000..f5272d7db --- /dev/null +++ b/elasticsearch/manifests/template.pp @@ -0,0 +1,117 @@ +# == Define: elasticsearch::template +# +# This define allows you to insert, update or delete templates that are used within Elasticsearch for the indexes +# +# === Parameters +# +# [*ensure*] +# String. Controls if the managed resources shall be present or +# absent. If set to absent: +# * The managed software packages are being uninstalled. +# * Any traces of the packages will be purged as good as possible. This may +# include existing configuration files. The exact behavior is provider +# dependent. Q.v.: +# * Puppet type reference: {package, "purgeable"}[http://j.mp/xbxmNP] +# * {Puppet's package provider source code}[http://j.mp/wtVCaL] +# * System modifications (if any) will be reverted as good as possible +# (e.g. removal of created users, services, changed log settings, ...). +# * This is thus destructive and should be used with care. +# Defaults to present. +# +# [*file*] +# File path of the template ( json file ) +# Value type is string +# Default value: undef +# This variable is optional +# +# [*host*] +# Host name or IP address of the ES instance to connect to +# Value type is string +# Default value: localhost +# This variable is optional +# +# [*port*] +# Port number of the ES instance to connect to +# Value type is number +# Default value: 9200 +# This variable is optional +# +# === Authors +# +# * Richard Pijnenburg +# +define elasticsearch::template( + $ensure = 'present', + $file = undef, + $host = 'localhost', + $port = 9200 +) { + + require elasticsearch + + # ensure + if ! ($ensure in [ 'present', 'absent' ]) { + fail("\"${ensure}\" is not a valid ensure parameter value") + } + + if ! is_integer($port) { + fail("\"${port}\" is not an integer") + } + + Exec { + path => [ '/bin', '/usr/bin', '/usr/local/bin' ], + cwd => '/', + tries => 6, + try_sleep => 10 + } + + # Build up the url + $es_url = "http://${host}:${port}/_template/${name}" + + # Can't do a replace and delete at the same time + + if ($ensure == 'present') { + + # Fail when no file is supplied + if $file == undef { + fail('The variable "file" cannot be empty when inserting or updating a template') + + } else { # we are good to go. notify to insert in case we deleted + $insert_notify = Exec[ "insert_template_${name}" ] + } + + } else { + + $insert_notify = undef + + } + + # Delete the existing template + # First check if it exists of course + exec { "delete_template_${name}": + command => "curl -s -XDELETE ${es_url}", + onlyif => "test $(curl -s '${es_url}?pretty=true' | wc -l) -gt 1", + notify => $insert_notify, + refreshonly => true + } + + if ($ensure == 'present') { + + # place the template file + file { "${elasticsearch::configdir}/templates_import/elasticsearch-template-${name}.json": + ensure => 'present', + source => $file, + notify => Exec[ "delete_template_${name}" ], + require => Exec[ 'mkdir_templates_elasticsearch' ], + } + + exec { "insert_template_${name}": + command => "curl -sL -w \"%{http_code}\\n\" -XPUT ${es_url} -d @${elasticsearch::configdir}/templates_import/elasticsearch-template-${name}.json -o /dev/null | egrep \"(200|201)\" > /dev/null", + unless => "test $(curl -s '${es_url}?pretty=true' | wc -l) -gt 1", + refreshonly => true, + loglevel => 'debug' + } + + } + +} diff --git a/elasticsearch/metadata.json b/elasticsearch/metadata.json new file mode 100644 index 000000000..58d76bc9f --- /dev/null +++ b/elasticsearch/metadata.json @@ -0,0 +1,82 @@ +{ + "name": "elasticsearch-elasticsearch", + "version": "0.4.0", + "source": "https://github.com/elasticsearch/puppet-elasticsearch", + "author": "elasticsearch", + "license": "Apache License, Version 2.0", + "summary": "Module for managing and configuring Elasticsearch nodes", + "description": "Module for managing and configuring Elasticsearch nodes", + "project_page": "https://github.com/elasticsearch/puppet-elasticsearch", + "dependencies": [ + { + "name": "puppetlabs/stdlib", + "version_requirement": ">= 3.2.0" + } + ], + "operatingsystem_support": [ + { + "operatingsystem": "RedHat", + "operatingsystemrelease": [ + "5", + "6" + ] + }, + { + "operatingsystem": "CentOS", + "operatingsystemrelease": [ + "5", + "6" + ] + }, + { + "operatingsystem": "OracleLinux", + "operatingsystemrelease": [ + "5", + "6" + ] + }, + { + "operatingsystem": "Scientific", + "operatingsystemrelease": [ + "5", + "6" + ] + }, + { + "operatingsystem": "Debian", + "operatingsystemrelease": [ + "6", + "7" + ] + }, + { + "operatingsystem": "Ubuntu", + "operatingsystemrelease": [ + "10.04", + "10.10", + "12.04", + "12.10", + "13.04", + "13.10", + "14.04" + ] + }, + { + "operatingsystem": "OpenSuSE", + "operatingsystemrelease": [ + "12.x", + "13.x" + ] + } + ], + "requirements": [ + { + "name": "pe", + "version_requirement": ">= 3.1.3" + }, + { + "name": "puppet", + "version_requirement": ">=2.7.20 <4.0.0" + } + ] +} diff --git a/elasticsearch/spec/acceptance/001_basic_spec.rb b/elasticsearch/spec/acceptance/001_basic_spec.rb new file mode 100644 index 000000000..9c5432241 --- /dev/null +++ b/elasticsearch/spec/acceptance/001_basic_spec.rb @@ -0,0 +1,8 @@ +require 'spec_helper_acceptance' + +# Here we put the more basic fundamental tests, ultra obvious stuff. +describe "basic tests:" do + it 'make sure we have copied the module across' do + shell("ls #{default['distmoduledir']}/elasticsearch/Modulefile", {:acceptable_exit_codes => 0}) + end +end diff --git a/elasticsearch/spec/acceptance/002_class_spec.rb b/elasticsearch/spec/acceptance/002_class_spec.rb new file mode 100644 index 000000000..3515bd1ca --- /dev/null +++ b/elasticsearch/spec/acceptance/002_class_spec.rb @@ -0,0 +1,231 @@ +require 'spec_helper_acceptance' + +describe "elasticsearch class:" do + + cluster_name = SecureRandom.hex(10) + + case fact('osfamily') + when 'RedHat' + package_name = 'elasticsearch' + service_name_a = 'elasticsearch-es-01' + service_name_b = 'elasticsearch-es-02' + service_name_c = 'elasticsearch-es-03' + pid_file_a = '/var/run/elasticsearch/elasticsearch-es-01.pid' + pid_file_b = '/var/run/elasticsearch/elasticsearch-es-02.pid' + pid_file_c = '/var/run/elasticsearch/elasticsearch-es-03.pid' + port_a = '9200' + port_b = '9201' + port_c = '9202' + when 'Debian' + package_name = 'elasticsearch' + service_name_a = 'elasticsearch-es-01' + service_name_b = 'elasticsearch-es-02' + service_name_c = 'elasticsearch-es-03' + pid_file_a = '/var/run/elasticsearch-es-01.pid' + pid_file_b = '/var/run/elasticsearch-es-02.pid' + pid_file_c = '/var/run/elasticsearch-es-03.pid' + port_a = '9200' + port_b = '9201' + port_c = '9202' + when 'Suse' + package_name = 'elasticsearch' + service_name_a = 'elasticsearch-es-01' + service_name_b = 'elasticsearch-es-02' + service_name_c = 'elasticsearch-es-03' + pid_file_a = '/var/run/elasticsearch/elasticsearch-es-01.pid' + pid_file_b = '/var/run/elasticsearch/elasticsearch-es-02.pid' + pid_file_c = '/var/run/elasticsearch/elasticsearch-es-03.pid' + port_a = '9200' + port_b = '9201' + port_c = '9202' + + end + + describe "single instance" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': config => { 'cluster.name' => '#{cluster_name}'}, manage_repo => true, repo_version => '1.0', java_install => true } + elasticsearch::instance { 'es-01': config => { 'node.name' => 'elasticsearch001', 'http.port' => '#{port_a}' } } + " + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + + describe service(service_name_a) do + it { should be_enabled } + it { should be_running } + end + + describe package(package_name) do + it { should be_installed } + end + + describe file(pid_file_a) do + it { should be_file } + its(:content) { should match /[0-9]+/ } + end + + describe port(port_a) do + it { + sleep 15 + should be_listening + } + end + + describe "Elasticsearch serves requests on" do + it { + curl_with_retries("check ES on #{port_a}", default, "http://localhost:#{port_a}/?pretty=true", 0) + } + end + + describe file('/etc/elasticsearch/es-01/elasticsearch.yml') do + it { should be_file } + it { should contain 'name: elasticsearch001' } + end + + describe file('/etc/elasticsearch/templates_import') do + it { should be_directory } + end + + + end + + + describe "multiple instances" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': config => { 'cluster.name' => '#{cluster_name}'}, manage_repo => true, repo_version => '1.0', java_install => true } + elasticsearch::instance { 'es-01': config => { 'node.name' => 'elasticsearch001', 'http.port' => '#{port_a}' } } + elasticsearch::instance { 'es-02': config => { 'node.name' => 'elasticsearch002', 'http.port' => '#{port_b}' } } + " + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + + describe service(service_name_a) do + it { should be_enabled } + it { should be_running } + end + + describe service(service_name_b) do + it { should be_enabled } + it { should be_running } + end + + describe package(package_name) do + it { should be_installed } + end + + describe file(pid_file_a) do + it { should be_file } + its(:content) { should match /[0-9]+/ } + end + + describe file(pid_file_b) do + it { should be_file } + its(:content) { should match /[0-9]+/ } + end + + describe port(port_a) do + it { + should be_listening + } + end + + describe port(port_b) do + it { + sleep 10 + should be_listening + } + end + + describe "make sure elasticsearch can serve requests #{port_a}" do + it { + curl_with_retries("check ES on #{port_a}", default, "http://localhost:#{port_a}/?pretty=true", 0) + } + end + + describe "make sure elasticsearch can serve requests #{port_b}" do + it { + curl_with_retries("check ES on #{port_b}", default, "http://localhost:#{port_b}/?pretty=true", 0) + } + end + + describe file('/etc/elasticsearch/es-01/elasticsearch.yml') do + it { should be_file } + it { should contain 'name: elasticsearch001' } + end + + describe file('/etc/elasticsearch/es-02/elasticsearch.yml') do + it { should be_file } + it { should contain 'name: elasticsearch002' } + end + + end + + + describe "module removal" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': ensure => 'absent' } + elasticsearch::instance{ 'es-01': ensure => 'absent' } + elasticsearch::instance{ 'es-02': ensure => 'absent' } + " + + apply_manifest(pp, :catch_failures => true) + end + + describe file('/etc/elasticsearch/es-01') do + it { should_not be_directory } + end + + describe file('/etc/elasticsearch/es-02') do + it { should_not be_directory } + end + + describe file('/etc/elasticsearch/es-03') do + it { should_not be_directory } + end + + describe port(port_a) do + it { + should_not be_listening + } + end + + describe port(port_b) do + it { + should_not be_listening + } + end + + describe port(port_c) do + it { + should_not be_listening + } + end + + describe service(service_name_a) do + it { should_not be_enabled } + it { should_not be_running } + end + + describe service(service_name_b) do + it { should_not be_enabled } + it { should_not be_running } + end + + describe service(service_name_c) do + it { should_not be_enabled } + it { should_not be_running } + end + + end + +end diff --git a/elasticsearch/spec/acceptance/003_template_spec.rb b/elasticsearch/spec/acceptance/003_template_spec.rb new file mode 100644 index 000000000..0ada8902b --- /dev/null +++ b/elasticsearch/spec/acceptance/003_template_spec.rb @@ -0,0 +1,205 @@ +require 'spec_helper_acceptance' + + case fact('osfamily') + when 'RedHat' + package_name = 'elasticsearch' + service_name_a = 'elasticsearch-es-01' + service_name_b = 'elasticsearch-es-02' + service_name_c = 'elasticsearch-es-03' + pid_file_a = '/var/run/elasticsearch/elasticsearch-es-01.pid' + pid_file_b = '/var/run/elasticsearch/elasticsearch-es-02.pid' + pid_file_c = '/var/run/elasticsearch/elasticsearch-es-03.pid' + port_a = '9200' + port_b = '9201' + port_c = '9202' + when 'Debian' + package_name = 'elasticsearch' + service_name_a = 'elasticsearch-es-01' + service_name_b = 'elasticsearch-es-02' + service_name_c = 'elasticsearch-es-03' + pid_file_a = '/var/run/elasticsearch-es-01.pid' + pid_file_b = '/var/run/elasticsearch-es-02.pid' + pid_file_c = '/var/run/elasticsearch-es-03.pid' + port_a = '9200' + port_b = '9201' + port_c = '9202' + when 'Suse' + package_name = 'elasticsearch' + service_name_a = 'elasticsearch-es-01' + service_name_b = 'elasticsearch-es-02' + service_name_c = 'elasticsearch-es-03' + pid_file_a = '/var/run/elasticsearch/elasticsearch-es-01.pid' + pid_file_b = '/var/run/elasticsearch/elasticsearch-es-02.pid' + pid_file_c = '/var/run/elasticsearch/elasticsearch-es-03.pid' + port_a = '9200' + port_b = '9201' + port_c = '9202' + end + + + good_json='{ + "template" : "logstash-*", + "settings" : { + "index.refresh_interval" : "5s", + "analysis" : { + "analyzer" : { + "default" : { + "type" : "standard", + "stopwords" : "_none_" + } + } + } + }, + "mappings" : { + "_default_" : { + "_all" : {"enabled" : true}, + "dynamic_templates" : [ { + "string_fields" : { + "match" : "*", + "match_mapping_type" : "string", + "mapping" : { + "type" : "multi_field", + "fields" : { + "{name}" : {"type": "string", "index" : "analyzed", "omit_norms" : true }, + "raw" : {"type": "string", "index" : "not_analyzed", "ignore_above" : 256} + } + } + } + } ], + "properties" : { + "@version": { "type": "string", "index": "not_analyzed" }, + "geoip" : { + "type" : "object", + "dynamic": true, + "path": "full", + "properties" : { + "location" : { "type" : "geo_point" } + } + } + } + } + } +} +' + + bad_json='{ + "settings" : { + "index.refresh_interval" : "5s", + "analysis" : { + "analyzer" : { + "default" : { + "type" : "standard", + "stopwords" : "_none_" + } + } + } + }, + "mappings" : { + "_default_" : { + "_all" : {"enabled" : true}, + "dynamic_templates" : [ { + "string_fields" : { + "match" : "*", + "match_mapping_type" : "string", + "mapping" : { + "type" : "multi_field", + "fields" : { + "{name}" : {"type": "string", "index" : "analyzed", "omit_norms" : true }, + "raw" : {"type": "string", "index" : "not_analyzed", "ignore_above" : 256} + } + } + } + } ], + "properties" : { + "@version": { "type": "string", "index": "not_analyzed" }, + "geoip" : { + "type" : "object", + "dynamic": true, + "path": "full", + "properties" : { + "location" : { "type" : "geo_point" } + } + } + } + } + } +} +' + + +cluster_name = SecureRandom.hex(10) + +describe "elasticsearch template define:" do + + shell("mkdir -p #{default['distmoduledir']}/another/files") + shell("echo '#{good_json}' >> #{default['distmoduledir']}/another/files/good.json") + shell("echo '#{bad_json}' >> #{default['distmoduledir']}/another/files/bad.json") + + describe "Insert a template with valid json content" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': config => { 'node.name' => 'elasticsearch001', 'cluster.name' => '#{cluster_name}' }, manage_repo => true, repo_version => '1.0', java_install => true } + elasticsearch::instance { 'es-01': config => { 'node.name' => 'elasticsearch001', 'http.port' => '#{port_a}' } } + elasticsearch::template { 'foo': ensure => 'present', file => 'puppet:///modules/another/good.json' }" + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + it 'should report as existing in Elasticsearch' do + curl_with_retries('validate template as installed', default, 'http://localhost:9200/_template/foo | grep logstash', 0) + end + end + + if fact('puppetversion') =~ /3\.[2-9]\./ + describe "Insert a template with bad json content" do + + it 'run should fail' do + pp = "class { 'elasticsearch': config => { 'node.name' => 'elasticsearch001', 'cluster.name' => '#{cluster_name}' }, manage_repo => true, repo_version => '1.0', java_install => true } + elasticsearch::instance { 'es-01': config => { 'node.name' => 'elasticsearch001', 'http.port' => '#{port_a}' } } + elasticsearch::template { 'foo': ensure => 'present', file => 'puppet:///modules/another/bad.json' }" + + apply_manifest(pp, :expect_failures => true) + end + + end + + else + # The exit codes have changes since Puppet 3.2x + # Since beaker expectations are based on the most recent puppet code All runs on previous versions fails. + end + + describe "module removal" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': ensure => 'absent' } + elasticsearch::instance{ 'es-01': ensure => 'absent' } + " + + apply_manifest(pp, :catch_failures => true) + end + + describe file('/etc/elasticsearch/es-01') do + it { should_not be_directory } + end + + describe package(package_name) do + it { should_not be_installed } + end + + describe port(port_a) do + it { + should_not be_listening + } + end + + describe service(service_name_a) do + it { should_not be_enabled } + it { should_not be_running } + end + + end + + +end diff --git a/elasticsearch/spec/acceptance/004_plugin_spec.rb b/elasticsearch/spec/acceptance/004_plugin_spec.rb new file mode 100644 index 000000000..ae056fc15 --- /dev/null +++ b/elasticsearch/spec/acceptance/004_plugin_spec.rb @@ -0,0 +1,231 @@ +require 'spec_helper_acceptance' + +describe "elasticsearch plugin define:" do + + cluster_name = SecureRandom.hex(10) + + case fact('osfamily') + when 'RedHat' + package_name = 'elasticsearch' + service_name_a = 'elasticsearch-es-01' + service_name_b = 'elasticsearch-es-02' + service_name_c = 'elasticsearch-es-03' + pid_file_a = '/var/run/elasticsearch/elasticsearch-es-01.pid' + pid_file_b = '/var/run/elasticsearch/elasticsearch-es-02.pid' + pid_file_c = '/var/run/elasticsearch/elasticsearch-es-03.pid' + port_a = '9200' + port_b = '9201' + port_c = '9202' + when 'Debian' + package_name = 'elasticsearch' + service_name_a = 'elasticsearch-es-01' + service_name_b = 'elasticsearch-es-02' + service_name_c = 'elasticsearch-es-03' + pid_file_a = '/var/run/elasticsearch-es-01.pid' + pid_file_b = '/var/run/elasticsearch-es-02.pid' + pid_file_c = '/var/run/elasticsearch-es-03.pid' + port_a = '9200' + port_b = '9201' + port_c = '9202' + when 'Suse' + package_name = 'elasticsearch' + service_name_a = 'elasticsearch-es-01' + service_name_b = 'elasticsearch-es-02' + service_name_c = 'elasticsearch-es-03' + pid_file_a = '/var/run/elasticsearch/elasticsearch-es-01.pid' + pid_file_b = '/var/run/elasticsearch/elasticsearch-es-02.pid' + pid_file_c = '/var/run/elasticsearch/elasticsearch-es-03.pid' + port_a = '9200' + port_b = '9201' + port_c = '9202' + + end + + + describe "Install a plugin from official repository" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': config => { 'node.name' => 'elasticsearch001', 'cluster.name' => '#{cluster_name}' }, manage_repo => true, repo_version => '1.0', java_install => true } + elasticsearch::instance { 'es-01': config => { 'node.name' => 'elasticsearch001', 'http.port' => '#{port_a}' } } + elasticsearch::plugin{'mobz/elasticsearch-head': module_dir => 'head', instances => 'es-01' } + " + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + describe service(service_name_a) do + it { should be_enabled } + it { should be_running } + end + + describe package(package_name) do + it { should be_installed } + end + + describe file(pid_file_a) do + it { should be_file } + its(:content) { should match /[0-9]+/ } + end + + describe port(9200) do + it { + sleep 15 + should be_listening + } + end + + it 'make sure the directory exists' do + shell('ls /usr/share/elasticsearch/plugins/head/', {:acceptable_exit_codes => 0}) + end + + it 'make sure elasticsearch reports it as existing' do + curl_with_retries('validated plugin as installed', default, 'http://localhost:9200/_nodes/?plugin | grep head', 0) + end + + end + describe "Install a plugin from custom git repo" do + it 'should run successfully' do + pending("Not implemented yet") + end + + it 'make sure the directory exists' do + pending("Not implemented yet") + end + + it 'make sure elasticsearch reports it as existing' do + pending("Not implemented yet") + end + + end + + if fact('puppetversion') =~ /3\.[2-9]\./ + + describe "Install a non existing plugin" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': config => { 'node.name' => 'elasticearch001', 'cluster.name' => '#{cluster_name}' }, manage_repo => true, repo_version => '1.0', java_install => true } + elasticsearch::instance { 'es-01': config => { 'node.name' => 'elasticsearch001', 'http.port' => '#{port_a}' } } + elasticsearch::plugin{'elasticsearch/non-existing': module_dir => 'non-existing', instances => 'es-01' } + " + # Run it twice and test for idempotency + apply_manifest(pp, :expect_failures => true) + end + + end + + else + # The exit codes have changes since Puppet 3.2x + # Since beaker expectations are based on the most recent puppet code All runs on previous versions fails. + end + + describe "module removal" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': ensure => 'absent' } + elasticsearch::instance{ 'es-01': ensure => 'absent' } + " + + apply_manifest(pp, :catch_failures => true) + end + + describe file('/etc/elasticsearch/es-01') do + it { should_not be_directory } + end + + describe package(package_name) do + it { should_not be_installed } + end + + describe port(port_a) do + it { + should_not be_listening + } + end + + describe service(service_name_a) do + it { should_not be_enabled } + it { should_not be_running } + end + + end + + + describe "install plugin while running ES under user 'root'" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': config => { 'node.name' => 'elasticsearch001', 'cluster.name' => '#{cluster_name}' }, manage_repo => true, repo_version => '1.0', java_install => true, elasticsearch_user => 'root', elasticsearch_group => 'root' } + elasticsearch::instance { 'es-01': config => { 'node.name' => 'elasticsearch001', 'http.port' => '#{port_a}' } } + elasticsearch::plugin{'lmenezes/elasticsearch-kopf': module_dir => 'kopf', instances => 'es-01' } + " + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + describe service(service_name_a) do + it { should be_enabled } + it { should be_running } + end + + describe package(package_name) do + it { should be_installed } + end + + describe file(pid_file_a) do + it { should be_file } + its(:content) { should match /[0-9]+/ } + end + + describe port(9200) do + it { + sleep 15 + should be_listening + } + end + + it 'make sure the directory exists' do + shell('ls /usr/share/elasticsearch/plugins/kopf/', {:acceptable_exit_codes => 0}) + end + + it 'make sure elasticsearch reports it as existing' do + curl_with_retries('validated plugin as installed', default, 'http://localhost:9200/_nodes/?plugin | grep kopf', 0) + end + + end + + + describe "module removal" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': ensure => 'absent' } + elasticsearch::instance{ 'es-01': ensure => 'absent' } + " + + apply_manifest(pp, :catch_failures => true) + end + + describe file('/etc/elasticsearch/es-01') do + it { should_not be_directory } + end + + describe package(package_name) do + it { should_not be_installed } + end + + describe port(port_a) do + it { + should_not be_listening + } + end + + describe service(service_name_a) do + it { should_not be_enabled } + it { should_not be_running } + end + + end + +end diff --git a/elasticsearch/spec/acceptance/005_datapath_spec.rb b/elasticsearch/spec/acceptance/005_datapath_spec.rb new file mode 100644 index 000000000..780b69230 --- /dev/null +++ b/elasticsearch/spec/acceptance/005_datapath_spec.rb @@ -0,0 +1,463 @@ +require 'spec_helper_acceptance' + +describe "Data dir settings" do + + cluster_name = SecureRandom.hex(10) + + case fact('osfamily') + when 'RedHat' + package_name = 'elasticsearch' + service_name_a = 'elasticsearch-es-01' + service_name_b = 'elasticsearch-es-02' + service_name_c = 'elasticsearch-es-03' + pid_file_a = '/var/run/elasticsearch/elasticsearch-es-01.pid' + pid_file_b = '/var/run/elasticsearch/elasticsearch-es-02.pid' + pid_file_c = '/var/run/elasticsearch/elasticsearch-es-03.pid' + port_a = '9200' + port_b = '9201' + port_c = '9202' + when 'Debian' + package_name = 'elasticsearch' + service_name_a = 'elasticsearch-es-01' + service_name_b = 'elasticsearch-es-02' + service_name_c = 'elasticsearch-es-03' + pid_file_a = '/var/run/elasticsearch-es-01.pid' + pid_file_b = '/var/run/elasticsearch-es-02.pid' + pid_file_c = '/var/run/elasticsearch-es-03.pid' + port_a = '9200' + port_b = '9201' + port_c = '9202' + when 'Suse' + package_name = 'elasticsearch' + service_name_a = 'elasticsearch-es-01' + service_name_b = 'elasticsearch-es-02' + service_name_c = 'elasticsearch-es-03' + pid_file_a = '/var/run/elasticsearch/elasticsearch-es-01.pid' + pid_file_b = '/var/run/elasticsearch/elasticsearch-es-02.pid' + pid_file_c = '/var/run/elasticsearch/elasticsearch-es-03.pid' + port_a = '9200' + port_b = '9201' + port_c = '9202' + end + + datadir_1 = '/var/lib/elasticsearch-data/1/' + datadir_2 = '/var/lib/elasticsearch-data/2/' + datadir_3 = '/var/lib/elasticsearch-data/3/' + + + describe "Default data dir" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': config => { 'cluster.name' => '#{cluster_name}'}, manage_repo => true, repo_version => '1.0', java_install => true } + elasticsearch::instance { 'es-01': config => { 'node.name' => 'elasticsearch001', 'http.port' => '#{port_a}' } } + " + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + + describe service(service_name_a) do + it { should be_enabled } + it { should be_running } + end + + describe package(package_name) do + it { should be_installed } + end + + describe file(pid_file_a) do + it { should be_file } + its(:content) { should match /[0-9]+/ } + end + + describe port(port_a) do + it { + sleep 15 + should be_listening + } + end + + describe "Elasticsearch serves requests on" do + it { + curl_with_retries("check ES on #{port_a}", default, "http://localhost:#{port_a}/?pretty=true", 0) + } + end + + describe file('/etc/elasticsearch/es-01/elasticsearch.yml') do + it { should be_file } + it { should contain "/usr/share/elasticsearch/data/es-01" } + end + + describe "Elasticsearch config has the data path" do + it { + curl_with_retries("check data path on #{port_a}", default, "http://localhost:#{port_a}/_nodes?pretty=true | grep /usr/share/elasticsearch/data/es-01", 0) + } + + end + + describe file('/usr/share/elasticsearch/data/es-01') do + it { should be_directory } + end + + end + + + describe "Single data dir from main class" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': config => { 'cluster.name' => '#{cluster_name}'}, manage_repo => true, repo_version => '1.0', java_install => true, datadir => '/var/lib/elasticsearch-data' } + elasticsearch::instance { 'es-01': config => { 'node.name' => 'elasticsearch001', 'http.port' => '#{port_a}' } } + " + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + + describe service(service_name_a) do + it { should be_enabled } + it { should be_running } + end + + describe package(package_name) do + it { should be_installed } + end + + describe file(pid_file_a) do + it { should be_file } + its(:content) { should match /[0-9]+/ } + end + + describe port(port_a) do + it { + sleep 15 + should be_listening + } + end + + describe "Elasticsearch serves requests on" do + it { + curl_with_retries("check ES on #{port_a}", default, "http://localhost:#{port_a}/?pretty=true", 0) + } + end + + describe file('/etc/elasticsearch/es-01/elasticsearch.yml') do + it { should be_file } + it { should contain '/var/lib/elasticsearch-data/es-01' } + end + + describe "Elasticsearch config has the data path" do + it { + curl_with_retries("check data path on #{port_a}", default, "http://localhost:#{port_a}/_nodes?pretty=true | grep /var/lib/elasticsearch-data/es-01", 0) + } + + end + + describe file('/var/lib/elasticsearch-data/es-01') do + it { should be_directory } + end + + end + + describe "module removal" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': ensure => 'absent' } + elasticsearch::instance{ 'es-01': ensure => 'absent' } + " + + apply_manifest(pp, :catch_failures => true) + end + + describe file('/etc/elasticsearch/es-01') do + it { should_not be_directory } + end + + describe port(port_a) do + it { + should_not be_listening + } + end + + describe service(service_name_a) do + it { should_not be_enabled } + it { should_not be_running } + end + + end + + describe "Single data dir from instance config" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': config => { 'cluster.name' => '#{cluster_name}'}, manage_repo => true, repo_version => '1.0', java_install => true } + elasticsearch::instance { 'es-01': config => { 'node.name' => 'elasticsearch001', 'http.port' => '#{port_a}'}, datadir => '#{datadir_1}' } + " + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + + describe service(service_name_a) do + it { should be_enabled } + it { should be_running } + end + + describe package(package_name) do + it { should be_installed } + end + + describe file(pid_file_a) do + it { should be_file } + its(:content) { should match /[0-9]+/ } + end + + describe port(port_a) do + it { + sleep 15 + should be_listening + } + end + + describe "Elasticsearch serves requests on" do + it { + curl_with_retries("check ES on #{port_a}", default, "http://localhost:#{port_a}/?pretty=true", 0) + } + end + + describe file('/etc/elasticsearch/es-01/elasticsearch.yml') do + it { should be_file } + it { should contain "#{datadir_1}" } + end + + describe "Elasticsearch config has the data path" do + it { + curl_with_retries("check data path on #{port_a}", default, "http://localhost:#{port_a}/_nodes?pretty=true | grep #{datadir_1}", 0) + } + end + + describe file(datadir_1) do + it { should be_directory } + end + + end + + describe "module removal" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': ensure => 'absent' } + elasticsearch::instance{ 'es-01': ensure => 'absent' } + " + + apply_manifest(pp, :catch_failures => true) + end + + describe file('/etc/elasticsearch/es-01') do + it { should_not be_directory } + end + + describe port(port_a) do + it { + should_not be_listening + } + end + + describe service(service_name_a) do + it { should_not be_enabled } + it { should_not be_running } + end + + end + + describe "multiple data dir's from main class" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': config => { 'cluster.name' => '#{cluster_name}'}, manage_repo => true, repo_version => '1.0', java_install => true, datadir => [ '/var/lib/elasticsearch/01', '/var/lib/elasticsearch/02'] } + elasticsearch::instance { 'es-01': config => { 'node.name' => 'elasticsearch001', 'http.port' => '#{port_a}' } } + " + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + + describe service(service_name_a) do + it { should be_enabled } + it { should be_running } + end + + describe package(package_name) do + it { should be_installed } + end + + describe file(pid_file_a) do + it { should be_file } + its(:content) { should match /[0-9]+/ } + end + + describe port(port_a) do + it { + sleep 15 + should be_listening + } + end + + describe "Elasticsearch serves requests on" do + it { + curl_with_retries("check ES on #{port_a}", default, "http://localhost:#{port_a}/?pretty=true", 0) + } + end + + describe file('/etc/elasticsearch/es-01/elasticsearch.yml') do + it { should be_file } + it { should contain '/var/lib/elasticsearch/01/es-01' } + it { should contain '/var/lib/elasticsearch/02/es-01' } + end + + describe "Elasticsearch config has the data path" do + it { + curl_with_retries("check data path on #{port_a}", default, "http://localhost:#{port_a}/_nodes?pretty=true | grep /var/lib/elasticsearch/01/es-01", 0) + } + it { + curl_with_retries("check data path on #{port_a}", default, "http://localhost:#{port_a}/_nodes?pretty=true | grep /var/lib/elasticsearch/02/es-01", 0) + } + + end + + describe file('/var/lib/elasticsearch/01/es-01') do + it { should be_directory } + end + + describe file('/var/lib/elasticsearch/02/es-01') do + it { should be_directory } + end + + end + + describe "module removal" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': ensure => 'absent' } + elasticsearch::instance{ 'es-01': ensure => 'absent' } + " + + apply_manifest(pp, :catch_failures => true) + end + + describe file('/etc/elasticsearch/es-01') do + it { should_not be_directory } + end + + describe port(port_a) do + it { + should_not be_listening + } + end + + describe service(service_name_a) do + it { should_not be_enabled } + it { should_not be_running } + end + + end + + + describe "multiple data dir's from instance config" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': config => { 'cluster.name' => '#{cluster_name}'}, manage_repo => true, repo_version => '1.0', java_install => true } + elasticsearch::instance { 'es-01': config => { 'node.name' => 'elasticsearch001', 'http.port' => '#{port_a}' }, datadir => [ '#{datadir_2}', '#{datadir_3}'] } + " + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + + describe service(service_name_a) do + it { should be_enabled } + it { should be_running } + end + + describe package(package_name) do + it { should be_installed } + end + + describe file(pid_file_a) do + it { should be_file } + its(:content) { should match /[0-9]+/ } + end + + describe port(port_a) do + it { + sleep 15 + should be_listening + } + end + + describe "Elasticsearch serves requests on" do + it { + curl_with_retries("check ES on #{port_a}", default, "http://localhost:#{port_a}/?pretty=true", 0) + } + end + + describe file('/etc/elasticsearch/es-01/elasticsearch.yml') do + it { should be_file } + it { should contain "#{datadir_2}" } + it { should contain "#{datadir_3}" } + end + + describe "Elasticsearch config has the data path" do + it { + curl_with_retries("check data path on #{port_a}", default, "http://localhost:#{port_a}/_nodes?pretty=true | grep #{datadir_2}", 0) + } + it { + curl_with_retries("check data path on #{port_a}", default, "http://localhost:#{port_a}/_nodes?pretty=true | grep #{datadir_3}", 0) + } + + end + + describe file(datadir_1) do + it { should be_directory } + end + + describe file(datadir_2) do + it { should be_directory } + end + + end + + describe "module removal" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': ensure => 'absent' } + elasticsearch::instance{ 'es-01': ensure => 'absent' } + " + + apply_manifest(pp, :catch_failures => true) + end + + describe file('/etc/elasticsearch/es-01') do + it { should_not be_directory } + end + + describe port(port_a) do + it { + should_not be_listening + } + end + + describe service(service_name_a) do + it { should_not be_enabled } + it { should_not be_running } + end + + end + +end diff --git a/elasticsearch/spec/acceptance/010_pkg_url_spec.rb b/elasticsearch/spec/acceptance/010_pkg_url_spec.rb new file mode 100644 index 000000000..2b5a5836c --- /dev/null +++ b/elasticsearch/spec/acceptance/010_pkg_url_spec.rb @@ -0,0 +1,257 @@ +require 'spec_helper_acceptance' + +describe "Elasticsearch class:" do + + cluster_name = SecureRandom.hex(10) + + case fact('osfamily') + when 'RedHat' + package_name = 'elasticsearch' + service_name_a = 'elasticsearch-es-01' + service_name_b = 'elasticsearch-es-02' + service_name_c = 'elasticsearch-es-03' + pid_file_a = '/var/run/elasticsearch/elasticsearch-es-01.pid' + pid_file_b = '/var/run/elasticsearch/elasticsearch-es-02.pid' + pid_file_c = '/var/run/elasticsearch/elasticsearch-es-03.pid' + port_a = '9200' + port_b = '9201' + port_c = '9202' + url = 'http://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.1.0.noarch.rpm' + local = '/tmp/elasticsearch-1.1.0.noarch.rpm' + puppet = 'elasticsearch-1.1.0.noarch.rpm' + when 'Debian' + package_name = 'elasticsearch' + service_name_a = 'elasticsearch-es-01' + service_name_b = 'elasticsearch-es-02' + service_name_c = 'elasticsearch-es-03' + pid_file_a = '/var/run/elasticsearch-es-01.pid' + pid_file_b = '/var/run/elasticsearch-es-02.pid' + pid_file_c = '/var/run/elasticsearch-es-03.pid' + port_a = '9200' + port_b = '9201' + port_c = '9202' + url = 'http://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.1.0.deb' + local = '/tmp/elasticsearch-1.1.0.deb' + puppet = 'elasticsearch-1.1.0.deb' + when 'Suse' + package_name = 'elasticsearch' + service_name_a = 'elasticsearch-es-01' + service_name_b = 'elasticsearch-es-02' + service_name_c = 'elasticsearch-es-03' + pid_file_a = '/var/run/elasticsearch/elasticsearch-es-01.pid' + pid_file_b = '/var/run/elasticsearch/elasticsearch-es-02.pid' + pid_file_c = '/var/run/elasticsearch/elasticsearch-es-03.pid' + port_a = '9200' + port_b = '9201' + port_c = '9202' + url = 'http://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.1.0.noarch.rpm' + local = '/tmp/elasticsearch-1.1.0.noarch.rpm' + puppet = 'elasticsearch-1.1.0.noarch.rpm' + + end + + shell("mkdir -p #{default['distmoduledir']}/another/files") + shell("cp #{local} #{default['distmoduledir']}/another/files/#{puppet}") + + context "install via http resource" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': package_url => '#{url}', java_install => true, config => { 'node.name' => 'elasticsearch001', 'cluster.name' => '#{cluster_name}' } } + elasticsearch::instance{ 'es-01': } + " + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + + end + + describe package(package_name) do + it { should be_installed } + end + + describe file(pid_file_a) do + it { should be_file } + its(:content) { should match /[0-9]+/ } + end + + describe port(9200) do + it { + sleep 15 + should be_listening + } + end + + it 'make sure elasticsearch can serve requests' do + curl_with_retries('check ES', default, 'http://localhost:9200/?pretty=true', 0) + end + + describe service(service_name_a) do + it { should be_enabled } + it { should be_running } + end + + end + + context "Clean" do + it 'should run successfully' do + pp = "class { 'elasticsearch': ensure => 'absent' } + elasticsearch::instance{ 'es-01': ensure => 'absent' } + " + + apply_manifest(pp, :catch_failures => true) + end + + describe package(package_name) do + it { should_not be_installed } + end + + describe port(9200) do + it { + sleep 15 + should_not be_listening + } + end + + describe service(service_name_a) do + it { should_not be_enabled } + it { should_not be_running } + end + + end + + context "Install via local file resource" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': package_url => 'file:#{local}', java_install => true, config => { 'node.name' => 'elasticsearch001', 'cluster.name' => '#{cluster_name}' } } + elasticsearch::instance{ 'es-01': } + " + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + + end + + describe package(package_name) do + it { should be_installed } + end + + describe file(pid_file_a) do + it { should be_file } + its(:content) { should match /[0-9]+/ } + end + + describe port(9200) do + it { + sleep 15 + should be_listening + } + end + + it 'make sure elasticsearch can serve requests' do + curl_with_retries('check ES', default, 'http://localhost:9200/?pretty=true', 0) + end + + describe service(service_name_a) do + it { should be_enabled } + it { should be_running } + end + + end + + context "Clean" do + it 'should run successfully' do + pp = "class { 'elasticsearch': ensure => 'absent' } + elasticsearch::instance{ 'es-01': ensure => 'absent' } + " + + apply_manifest(pp, :catch_failures => true) + end + + describe package(package_name) do + it { should_not be_installed } + end + + describe port(9200) do + it { + sleep 15 + should_not be_listening + } + end + + describe service(service_name_a) do + it { should_not be_enabled } + it { should_not be_running } + end + + end + + context "Install via Puppet resource" do + + it 'should run successfully' do + pp = "class { 'elasticsearch': package_url => 'puppet:///modules/another/#{puppet}', java_install => true, config => { 'node.name' => 'elasticsearch001', 'cluster.name' => '#{cluster_name}' } } + elasticsearch::instance { 'es-01': } + " + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + + end + + describe package(package_name) do + it { should be_installed } + end + + describe file(pid_file_a) do + it { should be_file } + its(:content) { should match /[0-9]+/ } + end + + it 'make sure elasticsearch can serve requests' do + curl_with_retries('check ES', default, 'http://localhost:9200/?pretty=true', 0) + end + + describe port(9200) do + it { + sleep 15 + should be_listening + } + end + + describe service(service_name_a) do + it { should be_enabled } + it { should be_running } + end + + end + + context "Clean" do + it 'should run successfully' do + pp = "class { 'elasticsearch': ensure => 'absent' } + elasticsearch::instance{ 'es-01': ensure => 'absent' } + " + + apply_manifest(pp, :catch_failures => true) + end + + describe package(package_name) do + it { should_not be_installed } + end + + describe port(9200) do + it { + sleep 15 + should_not be_listening + } + end + + describe service(service_name_a) do + it { should_not be_enabled } + it { should_not be_running } + end + + end + +end diff --git a/elasticsearch/spec/acceptance/011_service_spec.rb b/elasticsearch/spec/acceptance/011_service_spec.rb new file mode 100644 index 000000000..88b16f3de --- /dev/null +++ b/elasticsearch/spec/acceptance/011_service_spec.rb @@ -0,0 +1,111 @@ +require 'spec_helper_acceptance' + +describe "Service tests:" do + + cluster_name = SecureRandom.hex(10) + + case fact('osfamily') + when 'RedHat' + package_name = 'elasticsearch' + service_name_a = 'elasticsearch-es-01' + service_name_b = 'elasticsearch-es-02' + service_name_c = 'elasticsearch-es-03' + pid_file_a = '/var/run/elasticsearch/elasticsearch-es-01.pid' + pid_file_b = '/var/run/elasticsearch/elasticsearch-es-02.pid' + pid_file_c = '/var/run/elasticsearch/elasticsearch-es-03.pid' + port_a = '9200' + port_b = '9201' + port_c = '9202' + defaults_file_a = '/etc/sysconfig/elasticsearch-es-01' + defaults_file_b = '/etc/sysconfig/elasticsearch-es-02' + defaults_file_c = '/etc/sysconfig/elasticsearch-es-03' + when 'Debian' + package_name = 'elasticsearch' + service_name_a = 'elasticsearch-es-01' + service_name_b = 'elasticsearch-es-02' + service_name_c = 'elasticsearch-es-03' + pid_file_a = '/var/run/elasticsearch-es-01.pid' + pid_file_b = '/var/run/elasticsearch-es-02.pid' + pid_file_c = '/var/run/elasticsearch-es-03.pid' + port_a = '9200' + port_b = '9201' + port_c = '9202' + defaults_file_a = '/etc/default/elasticsearch-es-01' + defaults_file_b = '/etc/default/elasticsearch-es-02' + defaults_file_c = '/etc/default/elasticsearch-es-03' + when 'Suse' + package_name = 'elasticsearch' + service_name_a = 'elasticsearch-es-01' + service_name_b = 'elasticsearch-es-02' + service_name_c = 'elasticsearch-es-03' + pid_file_a = '/var/run/elasticsearch/elasticsearch-es-01.pid' + pid_file_b = '/var/run/elasticsearch/elasticsearch-es-02.pid' + pid_file_c = '/var/run/elasticsearch/elasticsearch-es-03.pid' + port_a = '9200' + port_b = '9201' + port_c = '9202' + defaults_file_a = '/etc/sysconfig/elasticsearch-es-01' + defaults_file_b = '/etc/sysconfig/elasticsearch-es-02' + defaults_file_c = '/etc/sysconfig/elasticsearch-es-03' + end + + describe "Make sure we can manage the defaults file" do + + context "Change the defaults file" do + it 'should run successfully' do + pp = "class { 'elasticsearch': manage_repo => true, repo_version => '1.0', java_install => true, config => { 'cluster.name' => '#{cluster_name}' }, init_defaults => { 'ES_USER' => 'root', 'ES_JAVA_OPTS' => '\"-server -XX:+UseTLAB -XX:+CMSClassUnloadingEnabled\"' } } + elasticsearch::instance { 'es-01': config => { 'node.name' => 'elasticsearch001' } } + " + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + describe service(service_name_a) do + it { should be_enabled } + it { should be_running } + end + + describe package(package_name) do + it { should be_installed } + end + + describe file(pid_file_a) do + it { should be_file } + its(:content) { should match /[0-9]+/ } + end + + describe port(9200) do + it { + sleep 15 + should be_listening + } + end + + describe file('/etc/elasticsearch/es-01/elasticsearch.yml') do + it { should be_file } + it { should contain 'name: elasticsearch001' } + end + + describe 'make sure elasticsearch can serve requests' do + it { + curl_with_retries('check ES', default, 'http://localhost:9200/?pretty=true', 0) + } + end + + context "Make sure we have ES_USER=root" do + + describe file(defaults_file_a) do + its(:content) { should match /^ES_USER=root/ } + its(:content) { should match /^ES_JAVA_OPTS="-server -XX:\+UseTLAB -XX:\+CMSClassUnloadingEnabled"/ } + its(:content) { should_not match /^ES_USER=elasticsearch/ } + end + + end + + end + + end + +end diff --git a/elasticsearch/spec/acceptance/nodesets/centos-6-x64.yml b/elasticsearch/spec/acceptance/nodesets/centos-6-x64.yml new file mode 100644 index 000000000..d78d31410 --- /dev/null +++ b/elasticsearch/spec/acceptance/nodesets/centos-6-x64.yml @@ -0,0 +1,15 @@ +HOSTS: + centos-6-x64: + roles: + - master + - database + - dashboard + platform: el-6-x86_64 + image: electrical/centos:6.4 + hypervisor: docker + docker_cmd: '["/sbin/init"]' + docker_image_commands: + - 'yum install -y wget ntpdate rubygems ruby-augeas ruby-devel augeas-devel' + - 'touch /etc/sysconfig/network' +CONFIG: + type: foss diff --git a/elasticsearch/spec/acceptance/nodesets/debian-6-x64.yml b/elasticsearch/spec/acceptance/nodesets/debian-6-x64.yml new file mode 100644 index 000000000..646872137 --- /dev/null +++ b/elasticsearch/spec/acceptance/nodesets/debian-6-x64.yml @@ -0,0 +1,15 @@ +HOSTS: + debian-6: + roles: + - master + - database + - dashboard + platform: debian-6-amd64 + image: jordansissel/system:debian-6.0.8 + hypervisor: docker + docker_cmd: '["/sbin/init"]' + docker_image_commands: + - 'apt-get install -yq lsb-release wget net-tools ruby rubygems ruby1.8-dev libaugeas-dev libaugeas-ruby ntpdate locales-all' + - 'REALLY_GEM_UPDATE_SYSTEM=1 gem update --system --no-ri --no-rdoc' +CONFIG: + type: foss diff --git a/elasticsearch/spec/acceptance/nodesets/debian-7-x64.yml b/elasticsearch/spec/acceptance/nodesets/debian-7-x64.yml new file mode 100644 index 000000000..c90dafaef --- /dev/null +++ b/elasticsearch/spec/acceptance/nodesets/debian-7-x64.yml @@ -0,0 +1,15 @@ +HOSTS: + debian-7: + roles: + - master + - database + - dashboard + platform: debian-7-amd64 + image: jordansissel/system:debian-7.3 + hypervisor: docker + docker_cmd: '["/sbin/init"]' + docker_image_commands: + - 'apt-get install -yq lsb-release wget net-tools ruby rubygems ruby1.8-dev libaugeas-dev libaugeas-ruby ntpdate locales-all' + - 'REALLY_GEM_UPDATE_SYSTEM=1 gem update --system --no-ri --no-rdoc' +CONFIG: + type: foss diff --git a/elasticsearch/spec/acceptance/nodesets/opensuse-121-x64.yml b/elasticsearch/spec/acceptance/nodesets/opensuse-121-x64.yml new file mode 100644 index 000000000..9c5b49ce6 --- /dev/null +++ b/elasticsearch/spec/acceptance/nodesets/opensuse-121-x64.yml @@ -0,0 +1,12 @@ +HOSTS: + opensuse-121-x64: + roles: + - master + - database + - dashboard + platform: sles-12-x64 + box: opensuse-121-x64 + box_url: https://s3.amazonaws.com/circlejtp/OpenSuseVagrant/OpenSuse12_1x64_July14.box + hypervisor: vagrant +CONFIG: + type: foss diff --git a/elasticsearch/spec/acceptance/nodesets/ubuntu-server-1204-x64.yml b/elasticsearch/spec/acceptance/nodesets/ubuntu-server-1204-x64.yml new file mode 100644 index 000000000..e53d08047 --- /dev/null +++ b/elasticsearch/spec/acceptance/nodesets/ubuntu-server-1204-x64.yml @@ -0,0 +1,14 @@ +HOSTS: + ubuntu-12-04: + roles: + - master + - database + - dashboard + platform: ubuntu-12.04-amd64 + image: jordansissel/system:ubuntu-12.04 + hypervisor: docker + docker_cmd: '["/sbin/init"]' + docker_image_commands: + - 'apt-get install -yq ruby1.8-dev libaugeas-dev libaugeas-ruby ruby rubygems lsb-release wget net-tools curl' +CONFIG: + type: foss diff --git a/elasticsearch/spec/acceptance/nodesets/ubuntu-server-1210-x64.yml b/elasticsearch/spec/acceptance/nodesets/ubuntu-server-1210-x64.yml new file mode 100644 index 000000000..b1db238c0 --- /dev/null +++ b/elasticsearch/spec/acceptance/nodesets/ubuntu-server-1210-x64.yml @@ -0,0 +1,14 @@ +HOSTS: + ubuntu-12-10: + roles: + - master + - database + - dashboard + platform: ubuntu-12.10-amd64 + image: jordansissel/system:ubuntu-12.10 + hypervisor: docker + docker_cmd: '["/sbin/init"]' + docker_image_commands: + - 'apt-get install -yq ruby1.8-dev libaugeas-dev libaugeas-ruby ruby rubygems lsb-release wget net-tools curl' +CONFIG: + type: foss diff --git a/elasticsearch/spec/acceptance/nodesets/ubuntu-server-1304-x64.yml b/elasticsearch/spec/acceptance/nodesets/ubuntu-server-1304-x64.yml new file mode 100644 index 000000000..5735afc46 --- /dev/null +++ b/elasticsearch/spec/acceptance/nodesets/ubuntu-server-1304-x64.yml @@ -0,0 +1,14 @@ +HOSTS: + ubuntu-13-04: + roles: + - master + - database + - dashboard + platform: ubuntu-13.04-amd64 + image: jordansissel/system:ubuntu-13.04 + hypervisor: docker + docker_cmd: '["/sbin/init"]' + docker_image_commands: + - 'apt-get install -yq ruby1.8-dev libaugeas-dev libaugeas-ruby ruby rubygems lsb-release wget net-tools curl' +CONFIG: + type: foss diff --git a/elasticsearch/spec/acceptance/nodesets/ubuntu-server-1310-x64.yml b/elasticsearch/spec/acceptance/nodesets/ubuntu-server-1310-x64.yml new file mode 100644 index 000000000..c60388730 --- /dev/null +++ b/elasticsearch/spec/acceptance/nodesets/ubuntu-server-1310-x64.yml @@ -0,0 +1,14 @@ +HOSTS: + ubuntu-13-10: + roles: + - master + - database + - dashboard + platform: ubuntu-13.10-amd64 + image: jordansissel/system:ubuntu-13.10 + hypervisor: docker + docker_cmd: '["/sbin/init"]' + docker_image_commands: + - 'apt-get install -yq ruby1.8-dev libaugeas-dev libaugeas-ruby ruby rubygems lsb-release wget net-tools curl' +CONFIG: + type: foss diff --git a/elasticsearch/spec/acceptance/nodesets/ubuntu-server-1404-x64.yml b/elasticsearch/spec/acceptance/nodesets/ubuntu-server-1404-x64.yml new file mode 100644 index 000000000..c80ed90f7 --- /dev/null +++ b/elasticsearch/spec/acceptance/nodesets/ubuntu-server-1404-x64.yml @@ -0,0 +1,14 @@ +HOSTS: + ubuntu-14-04: + roles: + - master + - database + - dashboard + platform: ubuntu-14.04-amd64 + image: electrical/ubuntu:14.04 + hypervisor: docker + docker_cmd: '["/sbin/init"]' + docker_image_commands: + - 'apt-get install -yq ruby ruby1.9.1-dev libaugeas-dev libaugeas-ruby lsb-release wget net-tools curl' +CONFIG: + type: foss diff --git a/elasticsearch/spec/classes/001_elasticsearch_init_debian_spec.rb b/elasticsearch/spec/classes/001_elasticsearch_init_debian_spec.rb new file mode 100644 index 000000000..73de0b0d7 --- /dev/null +++ b/elasticsearch/spec/classes/001_elasticsearch_init_debian_spec.rb @@ -0,0 +1,211 @@ +require 'spec_helper' + +describe 'elasticsearch', :type => 'class' do + + default_params = { + :config => { 'node.name' => 'foo' } + } + + [ 'Debian', 'Ubuntu'].each do |distro| + + context "on #{distro} OS" do + + let :facts do { + :operatingsystem => distro, + :kernel => 'Linux', + :osfamily => 'Debian', + :lsbdistid => distro.downcase + } end + + let (:params) { + default_params + } + + context 'main class tests' do + + it { should compile.with_all_deps } + # init.pp + it { should contain_anchor('elasticsearch::begin') } + it { should contain_class('elasticsearch::params') } + it { should contain_class('elasticsearch::package').that_requires('Anchor[elasticsearch::begin]') } + it { should contain_class('elasticsearch::config').that_requires('Class[elasticsearch::package]') } + + # Base directories + it { should contain_file('/etc/elasticsearch') } + it { should contain_exec('mkdir_templates_elasticsearch').with(:command => 'mkdir -p /etc/elasticsearch/templates_import', :creates => '/etc/elasticsearch/templates_import') } + it { should contain_file('/etc/elasticsearch/templates_import').with(:require => 'Exec[mkdir_templates_elasticsearch]') } + it { should contain_file('/usr/share/elasticsearch/plugins') } + + # file removal from package + it { should contain_file('/etc/init.d/elasticsearch').with(:ensure => 'absent') } + it { should contain_file('/etc/default/elasticsearch').with(:ensure => 'absent') } + it { should contain_file('/etc/elasticsearch/elasticsearch.yml').with(:ensure => 'absent') } + it { should contain_file('/etc/elasticsearch/logging.yml').with(:ensure => 'absent') } + end + + context 'package installation' do + + context 'via repository' do + + context 'with default settings' do + + it { should contain_package('elasticsearch').with(:ensure => 'present') } + it { should_not contain_package('my-elasticsearch').with(:ensure => 'present') } + + end + + context 'with specified version' do + + let (:params) { + default_params.merge({ + :version => '1.0' + }) + } + + it { should contain_package('elasticsearch').with(:ensure => '1.0') } + end + + context 'with specified package name' do + + let (:params) { + default_params.merge({ + :package_name => 'my-elasticsearch' + }) + } + + it { should contain_package('my-elasticsearch').with(:ensure => 'present') } + it { should_not contain_package('elasticsearch').with(:ensure => 'present') } + end + + context 'with auto upgrade enabled' do + + let (:params) { + default_params.merge({ + :autoupgrade => true + }) + } + + it { should contain_package('elasticsearch').with(:ensure => 'latest') } + end + + end + + context 'when setting package version and package_url' do + + let (:params) { + default_params.merge({ + :version => '0.90.10', + :package_url => 'puppet:///path/to/some/elasticsearch-0.90.10.deb' + }) + } + + it { expect { should raise_error(Puppet::Error) } } + + end + + context 'via package_url setting' do + + context 'using puppet:/// schema' do + + let (:params) { + default_params.merge({ + :package_url => 'puppet:///path/to/package.deb' + }) + } + + it { should contain_file('/opt/elasticsearch/swdl/package.deb').with(:source => 'puppet:///path/to/package.deb', :backup => false) } + it { should contain_package('elasticsearch').with(:ensure => 'present', :source => '/opt/elasticsearch/swdl/package.deb', :provider => 'dpkg') } + end + + context 'using http:// schema' do + + let (:params) { + default_params.merge({ + :package_url => 'http://www.domain.com/path/to/package.deb' + }) + } + + it { should contain_exec('create_package_dir_elasticsearch').with(:command => 'mkdir -p /opt/elasticsearch/swdl') } + it { should contain_file('/opt/elasticsearch/swdl').with(:purge => false, :force => false, :require => "Exec[create_package_dir_elasticsearch]") } + it { should contain_exec('download_package_elasticsearch').with(:command => 'wget --no-check-certificate -O /opt/elasticsearch/swdl/package.deb http://www.domain.com/path/to/package.deb 2> /dev/null', :require => 'File[/opt/elasticsearch/swdl]') } + it { should contain_package('elasticsearch').with(:ensure => 'present', :source => '/opt/elasticsearch/swdl/package.deb', :provider => 'dpkg') } + end + + context 'using https:// schema' do + + let (:params) { + default_params.merge({ + :package_url => 'https://www.domain.com/path/to/package.deb' + }) + } + + it { should contain_exec('create_package_dir_elasticsearch').with(:command => 'mkdir -p /opt/elasticsearch/swdl') } + it { should contain_file('/opt/elasticsearch/swdl').with(:purge => false, :force => false, :require => 'Exec[create_package_dir_elasticsearch]') } + it { should contain_exec('download_package_elasticsearch').with(:command => 'wget --no-check-certificate -O /opt/elasticsearch/swdl/package.deb https://www.domain.com/path/to/package.deb 2> /dev/null', :require => 'File[/opt/elasticsearch/swdl]') } + it { should contain_package('elasticsearch').with(:ensure => 'present', :source => '/opt/elasticsearch/swdl/package.deb', :provider => 'dpkg') } + end + + context 'using ftp:// schema' do + + let (:params) { + default_params.merge({ + :package_url => 'ftp://www.domain.com/path/to/package.deb' + }) + } + + it { should contain_exec('create_package_dir_elasticsearch').with(:command => 'mkdir -p /opt/elasticsearch/swdl') } + it { should contain_file('/opt/elasticsearch/swdl').with(:purge => false, :force => false, :require => 'Exec[create_package_dir_elasticsearch]') } + it { should contain_exec('download_package_elasticsearch').with(:command => 'wget --no-check-certificate -O /opt/elasticsearch/swdl/package.deb ftp://www.domain.com/path/to/package.deb 2> /dev/null', :require => 'File[/opt/elasticsearch/swdl]') } + it { should contain_package('elasticsearch').with(:ensure => 'present', :source => '/opt/elasticsearch/swdl/package.deb', :provider => 'dpkg') } + end + + context 'using file:// schema' do + + let (:params) { + default_params.merge({ + :package_url => 'file:/path/to/package.deb' + }) + } + + it { should contain_exec('create_package_dir_elasticsearch').with(:command => 'mkdir -p /opt/elasticsearch/swdl') } + it { should contain_file('/opt/elasticsearch/swdl').with(:purge => false, :force => false, :require => 'Exec[create_package_dir_elasticsearch]') } + it { should contain_file('/opt/elasticsearch/swdl/package.deb').with(:source => '/path/to/package.deb', :backup => false) } + it { should contain_package('elasticsearch').with(:ensure => 'present', :source => '/opt/elasticsearch/swdl/package.deb', :provider => 'dpkg') } + end + + end + + end # package + + context 'when setting the module to absent' do + + let (:params) { + default_params.merge({ + :ensure => 'absent' + }) + } + + it { should contain_package('elasticsearch').with(:ensure => 'absent') } + + end + + context 'When managing the repository' do + + let (:params) { + default_params.merge({ + :manage_repo => true, + :repo_version => '1.0' + }) + } + + it { should contain_class('elasticsearch::repo').that_requires('Anchor[elasticsearch::begin]') } + it { should contain_class('apt') } + it { should contain_apt__source('elasticsearch').with(:release => 'stable', :repos => 'main', :location => 'http://packages.elasticsearch.org/elasticsearch/1.0/debian') } + + end + + end + + end + +end diff --git a/elasticsearch/spec/classes/002_elasticsearch_init_redhat_spec.rb b/elasticsearch/spec/classes/002_elasticsearch_init_redhat_spec.rb new file mode 100644 index 000000000..b01a6d51d --- /dev/null +++ b/elasticsearch/spec/classes/002_elasticsearch_init_redhat_spec.rb @@ -0,0 +1,210 @@ +require 'spec_helper' + +describe 'elasticsearch', :type => 'class' do + + default_params = { + :config => { 'node.name' => 'foo' } + } + + [ 'RedHat', 'CentOS', 'Fedora', 'Scientific', 'Amazon', 'OracleLinux', 'SLC' ].each do |distro| + + context "on #{distro} OS" do + + let :facts do { + :operatingsystem => distro, + :kernel => 'Linux', + :osfamily => 'RedHat' + } end + + let (:params) { + default_params + } + + context 'Main class' do + + it { should compile.with_all_deps } + # init.pp + it { should contain_anchor('elasticsearch::begin') } + it { should contain_class('elasticsearch::params') } + it { should contain_class('elasticsearch::package').that_requires('Anchor[elasticsearch::begin]') } + it { should contain_class('elasticsearch::config').that_requires('Class[elasticsearch::package]') } + + # Base directories + it { should contain_file('/etc/elasticsearch') } + it { should contain_exec('mkdir_templates_elasticsearch').with(:command => 'mkdir -p /etc/elasticsearch/templates_import', :creates => '/etc/elasticsearch/templates_import') } + it { should contain_file('/etc/elasticsearch/templates_import').with(:require => 'Exec[mkdir_templates_elasticsearch]') } + it { should contain_file('/usr/share/elasticsearch/plugins') } + + # file removal from package + it { should contain_file('/etc/init.d/elasticsearch').with(:ensure => 'absent') } + it { should contain_file('/etc/sysconfig/elasticsearch').with(:ensure => 'absent') } + it { should contain_file('/etc/elasticsearch/elasticsearch.yml').with(:ensure => 'absent') } + it { should contain_file('/etc/elasticsearch/logging.yml').with(:ensure => 'absent') } + + end + + context 'package installation' do + + context 'via repository' do + + context 'with default settings' do + + it { should contain_package('elasticsearch').with(:ensure => 'present') } + it { should_not contain_package('my-elasticsearch').with(:ensure => 'present') } + + end + + context 'with specified version' do + + let (:params) { + default_params.merge({ + :version => '1.0' + }) + } + + it { should contain_package('elasticsearch').with(:ensure => '1.0') } + end + + context 'with specified package name' do + + let (:params) { + default_params.merge({ + :package_name => 'my-elasticsearch' + }) + } + + it { should contain_package('my-elasticsearch').with(:ensure => 'present') } + it { should_not contain_package('elasticsearch').with(:ensure => 'present') } + end + + context 'with auto upgrade enabled' do + + let (:params) { + default_params.merge({ + :autoupgrade => true + }) + } + + it { should contain_package('elasticsearch').with(:ensure => 'latest') } + end + + end + + context 'when setting package version and package_url' do + + let (:params) { + default_params.merge({ + :version => '0.90.10', + :package_url => 'puppet:///path/to/some/elasticsearch-0.90.10.rpm' + }) + } + + it { expect { should raise_error(Puppet::Error) } } + + end + + context 'via package_url setting' do + + context 'using puppet:/// schema' do + + let (:params) { + default_params.merge({ + :package_url => 'puppet:///path/to/package.rpm' + }) + } + + it { should contain_file('/opt/elasticsearch/swdl/package.rpm').with(:source => 'puppet:///path/to/package.rpm', :backup => false) } + it { should contain_package('elasticsearch').with(:ensure => 'present', :source => '/opt/elasticsearch/swdl/package.rpm', :provider => 'rpm') } + end + + context 'using http:// schema' do + + let (:params) { + default_params.merge({ + :package_url => 'http://www.domain.com/path/to/package.rpm' + }) + } + + it { should contain_exec('create_package_dir_elasticsearch').with(:command => 'mkdir -p /opt/elasticsearch/swdl') } + it { should contain_file('/opt/elasticsearch/swdl').with(:purge => false, :force => false, :require => "Exec[create_package_dir_elasticsearch]") } + it { should contain_exec('download_package_elasticsearch').with(:command => 'wget --no-check-certificate -O /opt/elasticsearch/swdl/package.rpm http://www.domain.com/path/to/package.rpm 2> /dev/null', :require => 'File[/opt/elasticsearch/swdl]') } + it { should contain_package('elasticsearch').with(:ensure => 'present', :source => '/opt/elasticsearch/swdl/package.rpm', :provider => 'rpm') } + end + + context 'using https:// schema' do + + let (:params) { + default_params.merge({ + :package_url => 'https://www.domain.com/path/to/package.rpm' + }) + } + + it { should contain_exec('create_package_dir_elasticsearch').with(:command => 'mkdir -p /opt/elasticsearch/swdl') } + it { should contain_file('/opt/elasticsearch/swdl').with(:purge => false, :force => false, :require => 'Exec[create_package_dir_elasticsearch]') } + it { should contain_exec('download_package_elasticsearch').with(:command => 'wget --no-check-certificate -O /opt/elasticsearch/swdl/package.rpm https://www.domain.com/path/to/package.rpm 2> /dev/null', :require => 'File[/opt/elasticsearch/swdl]') } + it { should contain_package('elasticsearch').with(:ensure => 'present', :source => '/opt/elasticsearch/swdl/package.rpm', :provider => 'rpm') } + end + + context 'using ftp:// schema' do + + let (:params) { + default_params.merge({ + :package_url => 'ftp://www.domain.com/path/to/package.rpm' + }) + } + + it { should contain_exec('create_package_dir_elasticsearch').with(:command => 'mkdir -p /opt/elasticsearch/swdl') } + it { should contain_file('/opt/elasticsearch/swdl').with(:purge => false, :force => false, :require => 'Exec[create_package_dir_elasticsearch]') } + it { should contain_exec('download_package_elasticsearch').with(:command => 'wget --no-check-certificate -O /opt/elasticsearch/swdl/package.rpm ftp://www.domain.com/path/to/package.rpm 2> /dev/null', :require => 'File[/opt/elasticsearch/swdl]') } + it { should contain_package('elasticsearch').with(:ensure => 'present', :source => '/opt/elasticsearch/swdl/package.rpm', :provider => 'rpm') } + end + + context 'using file:// schema' do + + let (:params) { + default_params.merge({ + :package_url => 'file:/path/to/package.rpm' + }) + } + + it { should contain_exec('create_package_dir_elasticsearch').with(:command => 'mkdir -p /opt/elasticsearch/swdl') } + it { should contain_file('/opt/elasticsearch/swdl').with(:purge => false, :force => false, :require => 'Exec[create_package_dir_elasticsearch]') } + it { should contain_file('/opt/elasticsearch/swdl/package.rpm').with(:source => '/path/to/package.rpm', :backup => false) } + it { should contain_package('elasticsearch').with(:ensure => 'present', :source => '/opt/elasticsearch/swdl/package.rpm', :provider => 'rpm') } + end + + end + + end # package + + context 'when setting the module to absent' do + + let (:params) { + default_params.merge({ + :ensure => 'absent' + }) + } + + it { should contain_package('elasticsearch').with(:ensure => 'absent') } + + end + + context 'When managing the repository' do + + let (:params) { + default_params.merge({ + :manage_repo => true, + :repo_version => '1.0' + }) + } + + it { should contain_class('elasticsearch::repo').that_requires('Anchor[elasticsearch::begin]') } + it { should contain_yumrepo('elasticsearch').with(:baseurl => 'http://packages.elasticsearch.org/elasticsearch/1.0/centos', :gpgkey => 'http://packages.elasticsearch.org/GPG-KEY-elasticsearch', :enabled => 1) } + + end + + end + + end + +end diff --git a/elasticsearch/spec/classes/003_elasticsearch_init_opensuse_spec.rb b/elasticsearch/spec/classes/003_elasticsearch_init_opensuse_spec.rb new file mode 100644 index 000000000..0ff608068 --- /dev/null +++ b/elasticsearch/spec/classes/003_elasticsearch_init_opensuse_spec.rb @@ -0,0 +1,211 @@ +require 'spec_helper' + +describe 'elasticsearch', :type => 'class' do + + default_params = { + :config => { 'node.name' => 'foo' } + } + + [ 'OpenSuSE' ].each do |distro| + + context "on #{distro} OS" do + + let :facts do { + :operatingsystem => distro, + :kernel => 'Linux', + :osfamily => 'Suse' + } end + + let (:params) { + default_params + } + + context 'Main class' do + + it { should compile.with_all_deps } + # init.pp + it { should contain_anchor('elasticsearch::begin') } + it { should contain_class('elasticsearch::params') } + it { should contain_class('elasticsearch::package').that_requires('Anchor[elasticsearch::begin]') } + it { should contain_class('elasticsearch::config').that_requires('Class[elasticsearch::package]') } + + # Base directories + it { should contain_file('/etc/elasticsearch') } + it { should contain_exec('mkdir_templates_elasticsearch').with(:command => 'mkdir -p /etc/elasticsearch/templates_import', :creates => '/etc/elasticsearch/templates_import') } + it { should contain_file('/etc/elasticsearch/templates_import').with(:require => 'Exec[mkdir_templates_elasticsearch]') } + it { should contain_file('/usr/share/elasticsearch/plugins') } + + # file removal from package + it { should contain_file('/usr/lib/systemd/system/elasticsearch.service').with(:ensure => 'absent') } + it { should contain_file('/etc/sysconfig/elasticsearch').with(:ensure => 'absent') } + it { should contain_file('/etc/elasticsearch/elasticsearch.yml').with(:ensure => 'absent') } + it { should contain_file('/etc/elasticsearch/logging.yml').with(:ensure => 'absent') } + + end + + context 'package installation' do + + context 'via repository' do + + context 'with default settings' do + + it { should contain_package('elasticsearch').with(:ensure => 'present') } + it { should_not contain_package('my-elasticsearch').with(:ensure => 'present') } + + end + + context 'with specified version' do + + let (:params) { + default_params.merge({ + :version => '1.0' + }) + } + + it { should contain_package('elasticsearch').with(:ensure => '1.0') } + end + + context 'with specified package name' do + + let (:params) { + default_params.merge({ + :package_name => 'my-elasticsearch' + }) + } + + it { should contain_package('my-elasticsearch').with(:ensure => 'present') } + it { should_not contain_package('elasticsearch').with(:ensure => 'present') } + end + + context 'with auto upgrade enabled' do + + let (:params) { + default_params.merge({ + :autoupgrade => true + }) + } + + it { should contain_package('elasticsearch').with(:ensure => 'latest') } + end + + end + + context 'when setting package version and package_url' do + + let (:params) { + default_params.merge({ + :version => '0.90.10', + :package_url => 'puppet:///path/to/some/elasticsearch-0.90.10.rpm' + }) + } + + it { expect { should raise_error(Puppet::Error) } } + + end + + context 'via package_url setting' do + + context 'using puppet:/// schema' do + + let (:params) { + default_params.merge({ + :package_url => 'puppet:///path/to/package.rpm' + }) + } + + it { should contain_file('/opt/elasticsearch/swdl/package.rpm').with(:source => 'puppet:///path/to/package.rpm', :backup => false) } + it { should contain_package('elasticsearch').with(:ensure => 'present', :source => '/opt/elasticsearch/swdl/package.rpm', :provider => 'rpm') } + end + + context 'using http:// schema' do + + let (:params) { + default_params.merge({ + :package_url => 'http://www.domain.com/path/to/package.rpm' + }) + } + + it { should contain_exec('create_package_dir_elasticsearch').with(:command => 'mkdir -p /opt/elasticsearch/swdl') } + it { should contain_file('/opt/elasticsearch/swdl').with(:purge => false, :force => false, :require => "Exec[create_package_dir_elasticsearch]") } + it { should contain_exec('download_package_elasticsearch').with(:command => 'wget --no-check-certificate -O /opt/elasticsearch/swdl/package.rpm http://www.domain.com/path/to/package.rpm 2> /dev/null', :require => 'File[/opt/elasticsearch/swdl]') } + it { should contain_package('elasticsearch').with(:ensure => 'present', :source => '/opt/elasticsearch/swdl/package.rpm', :provider => 'rpm') } + end + + context 'using https:// schema' do + + let (:params) { + default_params.merge({ + :package_url => 'https://www.domain.com/path/to/package.rpm' + }) + } + + it { should contain_exec('create_package_dir_elasticsearch').with(:command => 'mkdir -p /opt/elasticsearch/swdl') } + it { should contain_file('/opt/elasticsearch/swdl').with(:purge => false, :force => false, :require => 'Exec[create_package_dir_elasticsearch]') } + it { should contain_exec('download_package_elasticsearch').with(:command => 'wget --no-check-certificate -O /opt/elasticsearch/swdl/package.rpm https://www.domain.com/path/to/package.rpm 2> /dev/null', :require => 'File[/opt/elasticsearch/swdl]') } + it { should contain_package('elasticsearch').with(:ensure => 'present', :source => '/opt/elasticsearch/swdl/package.rpm', :provider => 'rpm') } + end + + context 'using ftp:// schema' do + + let (:params) { + default_params.merge({ + :package_url => 'ftp://www.domain.com/path/to/package.rpm' + }) + } + + it { should contain_exec('create_package_dir_elasticsearch').with(:command => 'mkdir -p /opt/elasticsearch/swdl') } + it { should contain_file('/opt/elasticsearch/swdl').with(:purge => false, :force => false, :require => 'Exec[create_package_dir_elasticsearch]') } + it { should contain_exec('download_package_elasticsearch').with(:command => 'wget --no-check-certificate -O /opt/elasticsearch/swdl/package.rpm ftp://www.domain.com/path/to/package.rpm 2> /dev/null', :require => 'File[/opt/elasticsearch/swdl]') } + it { should contain_package('elasticsearch').with(:ensure => 'present', :source => '/opt/elasticsearch/swdl/package.rpm', :provider => 'rpm') } + end + + context 'using file:// schema' do + + let (:params) { + default_params.merge({ + :package_url => 'file:/path/to/package.rpm' + }) + } + + it { should contain_exec('create_package_dir_elasticsearch').with(:command => 'mkdir -p /opt/elasticsearch/swdl') } + it { should contain_file('/opt/elasticsearch/swdl').with(:purge => false, :force => false, :require => 'Exec[create_package_dir_elasticsearch]') } + it { should contain_file('/opt/elasticsearch/swdl/package.rpm').with(:source => '/path/to/package.rpm', :backup => false) } + it { should contain_package('elasticsearch').with(:ensure => 'present', :source => '/opt/elasticsearch/swdl/package.rpm', :provider => 'rpm') } + end + + end + + end # package + + context 'when setting the module to absent' do + + let (:params) { + default_params.merge({ + :ensure => 'absent' + }) + } + + it { should contain_package('elasticsearch').with(:ensure => 'absent') } + + end + + context 'When managing the repository' do + + let (:params) { + default_params.merge({ + :manage_repo => true, + :repo_version => '1.0' + }) + } + + it { should contain_class('elasticsearch::repo').that_requires('Anchor[elasticsearch::begin]') } + it { should contain_exec('elasticsearch_suse_import_gpg') } + it { should contain_zypprepo('elasticsearch').with(:baseurl => 'http://packages.elasticsearch.org/elasticsearch/1.0/centos') } + + end + + end + + end + +end diff --git a/elasticsearch/spec/classes/005_elasticsearch_repo_spec.rb b/elasticsearch/spec/classes/005_elasticsearch_repo_spec.rb new file mode 100644 index 000000000..24ec1a5fc --- /dev/null +++ b/elasticsearch/spec/classes/005_elasticsearch_repo_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +describe 'elasticsearch', :type => 'class' do + + context "Repo class" do + + let :facts do { + :operatingsystem => 'CentOS', + :kernel => 'Linux', + :osfamily => 'RedHat' + } end + + context "Use anchor type for ordering" do + + let :params do { + :config => { }, + :manage_repo => true, + :repo_version => '1.3' + } end + + it { should contain_class('elasticsearch::repo').that_requires('Anchor[elasticsearch::begin]') } + end + + + context "Use stage type for ordering" do + + let :params do { + :config => { }, + :manage_repo => true, + :repo_version => '1.3', + :repo_stage => 'setup' + } end + + it { should contain_stage('setup') } + it { should contain_class('elasticsearch::repo').with(:stage => 'setup') } + + end + + end + +end diff --git a/elasticsearch/spec/classes/010_elasticsearch_init_unkown_spec.rb b/elasticsearch/spec/classes/010_elasticsearch_init_unkown_spec.rb new file mode 100644 index 000000000..12bce8fef --- /dev/null +++ b/elasticsearch/spec/classes/010_elasticsearch_init_unkown_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe 'elasticsearch', :type => 'class' do + + context "on an unknown OS" do + + context "it should fail" do + let :facts do { + :operatingsystem => 'Windows' + } end + + it { expect { should raise_error(Puppet::Error) } } + + end + + end + +end diff --git a/elasticsearch/spec/classes/012_elasticsearch_java_spec.rb b/elasticsearch/spec/classes/012_elasticsearch_java_spec.rb new file mode 100644 index 000000000..4707e977e --- /dev/null +++ b/elasticsearch/spec/classes/012_elasticsearch_java_spec.rb @@ -0,0 +1,175 @@ +require 'spec_helper' + +describe 'elasticsearch', :type => 'class' do + + context "install java" do + + let :params do { + :java_install => true, + :config => { 'node' => { 'name' => 'test' } } + } end + + context "On Debian OS" do + + let :facts do { + :operatingsystem => 'Debian', + :kernel => 'Linux', + :osfamily => 'Debian' + + } end + + it { should contain_class('elasticsearch::java').that_requires('Anchor[elasticsearch::begin]') } + it { should contain_class('elasticsearch::package').that_requires('Class[elasticsearch::java]') } + it { should contain_package('openjdk-7-jre-headless') } + + end + + context "On Debian OS (6.0 Squeeze)" do + + let :facts do { + :operatingsystem => 'Debian', + :kernel => 'Linux', + :osfamily => 'Debian', + :lsbdistcodename => 'squeeze' + + } end + + it { should contain_class('elasticsearch::java').that_requires('Anchor[elasticsearch::begin]') } + it { should contain_class('elasticsearch::package').that_requires('Class[elasticsearch::java]') } + it { should contain_package('openjdk-6-jre-headless') } + + end + context "On Ubuntu OS" do + + let :facts do { + :operatingsystem => 'Ubuntu', + :kernel => 'Linux', + :osfamily => 'Debian' + + } end + + it { should contain_class('elasticsearch::java') } + it { should contain_class('elasticsearch::package').that_requires('Class[elasticsearch::java]') } + it { should contain_package('openjdk-7-jre-headless') } + + end + + context "On CentOS OS " do + + let :facts do { + :operatingsystem => 'CentOS', + :kernel => 'Linux', + :osfamily => 'RedHat' + + } end + + it { should contain_class('elasticsearch::java') } + it { should contain_class('elasticsearch::package').that_requires('Class[elasticsearch::java]') } + it { should contain_package('java-1.7.0-openjdk') } + + end + + context "On RedHat OS " do + + let :facts do { + :operatingsystem => 'Redhat', + :kernel => 'Linux', + :osfamily => 'RedHat' + + } end + + it { should contain_class('elasticsearch::java') } + it { should contain_class('elasticsearch::package').that_requires('Class[elasticsearch::java]') } + it { should contain_package('java-1.7.0-openjdk') } + + end + + context "On Fedora OS " do + + let :facts do { + :operatingsystem => 'Fedora', + :kernel => 'Linux', + :osfamily => 'RedHat' + } end + + it { should contain_class('elasticsearch::java') } + it { should contain_class('elasticsearch::package').that_requires('Class[elasticsearch::java]') } + it { should contain_package('java-1.7.0-openjdk') } + + end + + context "On Scientific OS " do + + let :facts do { + :operatingsystem => 'Scientific', + :kernel => 'Linux', + :osfamily => 'RedHat' + } end + + it { should contain_class('elasticsearch::java') } + it { should contain_class('elasticsearch::package').that_requires('Class[elasticsearch::java]') } + it { should contain_package('java-1.7.0-openjdk') } + + end + + context "On Amazon OS " do + + let :facts do { + :operatingsystem => 'Amazon', + :kernel => 'Linux', + :osfamily => 'RedHat' + } end + + it { should contain_class('elasticsearch::java') } + it { should contain_class('elasticsearch::package').that_requires('Class[elasticsearch::java]') } + it { should contain_package('java-1.7.0-openjdk') } + + end + + context "On OracleLinux OS " do + + let :facts do { + :operatingsystem => 'OracleLinux', + :kernel => 'Linux', + :osfamily => 'RedHat' + } end + + it { should contain_class('elasticsearch::java') } + it { should contain_class('elasticsearch::package').that_requires('Class[elasticsearch::java]') } + it { should contain_package('java-1.7.0-openjdk') } + + end + + context "On an unknown OS" do + + let :facts do { + :operatingsystem => 'Windows' + } end + + it { expect { should raise_error(Puppet::Error) } } + + end + + context "Custom java package" do + + let :facts do { + :operatingsystem => 'CentOS', + :kernel => 'Linux', + :osfamily => 'RedHat' + } end + + let :params do { + :java_install => true, + :java_package => 'java-1.6.0-openjdk', + :config => { 'node' => { 'name' => 'test' } } + } end + + it { should contain_class('elasticsearch::java') } + it { should contain_class('elasticsearch::package').that_requires('Class[elasticsearch::java]') } + it { should contain_package('java-1.6.0-openjdk') } + + end + + end + +end diff --git a/elasticsearch/spec/classes/099_coverage_spec.rb b/elasticsearch/spec/classes/099_coverage_spec.rb new file mode 100644 index 000000000..12513b83c --- /dev/null +++ b/elasticsearch/spec/classes/099_coverage_spec.rb @@ -0,0 +1 @@ +at_exit { RSpec::Puppet::Coverage.report! } diff --git a/elasticsearch/spec/defines/001_elasticsearch_python_spec.rb b/elasticsearch/spec/defines/001_elasticsearch_python_spec.rb new file mode 100644 index 000000000..cd442096a --- /dev/null +++ b/elasticsearch/spec/defines/001_elasticsearch_python_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +describe 'elasticsearch::python', :type => 'define' do + + let :facts do { + :operatingsystem => 'CentOS', + :kernel => 'Linux', + :osfamily => 'RedHat' + } end + + [ 'pyes', 'rawes', 'pyelasticsearch', 'ESClient', 'elasticutils', 'elasticsearch' ].each do |pythonlib| + + context "installation of library #{pythonlib}" do + + let(:title) { pythonlib } + + it { should contain_elasticsearch__python(pythonlib) } + it { should contain_package("python_#{pythonlib}").with(:provider => 'pip', :name => pythonlib) } + + end + + end + +end diff --git a/elasticsearch/spec/defines/002_elasticsearch_ruby_spec.rb b/elasticsearch/spec/defines/002_elasticsearch_ruby_spec.rb new file mode 100644 index 000000000..8eb6a587d --- /dev/null +++ b/elasticsearch/spec/defines/002_elasticsearch_ruby_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +describe 'elasticsearch::ruby', :type => 'define' do + + let :facts do { + :operatingsystem => 'CentOS', + :kernel => 'Linux', + :osfamily => 'RedHat' + } end + + [ 'tire', 'stretcher', 'elastic_searchable', 'elasticsearch', 'flex'].each do |rubylib| + + context "installation of library #{rubylib}" do + + let(:title) { rubylib } + + it { should contain_elasticsearch__ruby(rubylib) } + it { should contain_package("ruby_#{rubylib}").with(:provider => 'gem', :name => rubylib) } + + end + + end + +end diff --git a/elasticsearch/spec/defines/003_elasticsearch_template_spec.rb b/elasticsearch/spec/defines/003_elasticsearch_template_spec.rb new file mode 100644 index 000000000..a4ab9556c --- /dev/null +++ b/elasticsearch/spec/defines/003_elasticsearch_template_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +describe 'elasticsearch::template', :type => 'define' do + + let :facts do { + :operatingsystem => 'CentOS', + :kernel => 'Linux', + :osfamily => 'RedHat' + } end + + let(:title) { 'foo' } + let(:pre_condition) { 'class {"elasticsearch": config => { "node" => {"name" => "test" }}}'} + + context "Add a template" do + + let :params do { + :ensure => 'present', + :file => 'puppet:///path/to/foo.json', + } end + + it { should contain_elasticsearch__template('foo') } + it { should contain_file('/etc/elasticsearch/templates_import/elasticsearch-template-foo.json').with(:source => 'puppet:///path/to/foo.json', :notify => "Exec[delete_template_foo]", :require => "Exec[mkdir_templates_elasticsearch]") } + it { should contain_exec('insert_template_foo').with(:command => "curl -sL -w \"%{http_code}\\n\" -XPUT http://localhost:9200/_template/foo -d @/etc/elasticsearch/templates_import/elasticsearch-template-foo.json -o /dev/null | egrep \"(200|201)\" > /dev/null", :unless => 'test $(curl -s \'http://localhost:9200/_template/foo?pretty=true\' | wc -l) -gt 1') } + end + + context "Delete a template" do + + let :params do { + :ensure => 'absent' + } end + + it { should contain_elasticsearch__template('foo') } + it { should_not contain_file('/etc/elasticsearch/templates_import/elasticsearch-template-foo.json').with(:source => 'puppet:///path/to/foo.json') } + it { should_not contain_exec('insert_template_foo') } + it { should contain_exec('delete_template_foo').with(:command => 'curl -s -XDELETE http://localhost:9200/_template/foo', :notify => nil, :onlyif => 'test $(curl -s \'http://localhost:9200/_template/foo?pretty=true\' | wc -l) -gt 1' ) } + end + + context "Add template with alternative host and port" do + + let :params do { + :file => 'puppet:///path/to/foo.json', + :host => 'otherhost', + :port => '9201' + } end + + it { should contain_elasticsearch__template('foo') } + it { should contain_file('/etc/elasticsearch/templates_import/elasticsearch-template-foo.json').with(:source => 'puppet:///path/to/foo.json') } + it { should contain_exec('insert_template_foo').with(:command => "curl -sL -w \"%{http_code}\\n\" -XPUT http://otherhost:9201/_template/foo -d @/etc/elasticsearch/templates_import/elasticsearch-template-foo.json -o /dev/null | egrep \"(200|201)\" > /dev/null", :unless => 'test $(curl -s \'http://otherhost:9201/_template/foo?pretty=true\' | wc -l) -gt 1') } + end + +end diff --git a/elasticsearch/spec/defines/004_elasticsearch_plugin_spec.rb b/elasticsearch/spec/defines/004_elasticsearch_plugin_spec.rb new file mode 100644 index 000000000..581982900 --- /dev/null +++ b/elasticsearch/spec/defines/004_elasticsearch_plugin_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe 'elasticsearch::plugin', :type => 'define' do + + let(:title) { 'mobz/elasticsearch-head' } + let :facts do { + :operatingsystem => 'CentOS', + :kernel => 'Linux', + :osfamily => 'RedHat' + } end + let(:pre_condition) { 'class {"elasticsearch": config => { "node" => {"name" => "test" }}}'} + + context "Add a plugin" do + + let :params do { + :ensure => 'present', + :module_dir => 'head', + :instances => 'es-01' + } end + + it { should contain_elasticsearch__plugin('mobz/elasticsearch-head') } + it { should contain_exec('install_plugin_mobz/elasticsearch-head').with(:command => '/usr/share/elasticsearch/bin/plugin -install mobz/elasticsearch-head', :creates => '/usr/share/elasticsearch/plugins/head') } + end + + context "Remove a plugin" do + + let :params do { + :ensure => 'absent', + :module_dir => 'head', + :instances => 'es-01' + } end + + it { should contain_elasticsearch__plugin('mobz/elasticsearch-head') } + it { should contain_exec('remove_plugin_mobz/elasticsearch-head').with(:command => '/usr/share/elasticsearch/bin/plugin --remove head', :onlyif => 'test -d /usr/share/elasticsearch/plugins/head') } + end + +end diff --git a/elasticsearch/spec/defines/005_elasticsearch_instance_spec.rb b/elasticsearch/spec/defines/005_elasticsearch_instance_spec.rb new file mode 100644 index 000000000..16b0ab5f2 --- /dev/null +++ b/elasticsearch/spec/defines/005_elasticsearch_instance_spec.rb @@ -0,0 +1,317 @@ +require 'spec_helper' + +describe 'elasticsearch::instance', :type => 'define' do + + let :facts do { + :operatingsystem => 'CentOS', + :kernel => 'Linux', + :osfamily => 'RedHat', + :hostname => 'elasticsearch001' + } end + + let(:title) { 'es-01' } + let(:pre_condition) { 'class {"elasticsearch": }' } + + context "Config file" do + + context "with nothing set" do + + let :params do { + :config => { } + } end + + it { should contain_file('/etc/elasticsearch/es-01/elasticsearch.yml').with(:content => "### MANAGED BY PUPPET ###\n---\nnode: \n name: elasticsearch001-es-01\npath: \n data: /usr/share/elasticsearch/data/es-01\n") } + + end + + context "set a value" do + + let :params do { + :config => { 'node.name' => 'test' } + } end + + it { should contain_file('/etc/elasticsearch/es-01/elasticsearch.yml').with(:content => "### MANAGED BY PUPPET ###\n---\nnode: \n name: test\npath: \n data: /usr/share/elasticsearch/data/es-01\n") } + + end + + context "set a value to true" do + + let :params do { + :config => { 'node' => { 'master' => true } } + } end + + it { should contain_file('/etc/elasticsearch/es-01/elasticsearch.yml').with(:content => "### MANAGED BY PUPPET ###\n---\nnode: \n master: true\n name: elasticsearch001-es-01\npath: \n data: /usr/share/elasticsearch/data/es-01\n") } + + end + + context "set a value to false" do + + let :params do { + :config => { 'node' => { 'data' => false } } + } end + + it { should contain_file('/etc/elasticsearch/es-01/elasticsearch.yml').with(:content => "### MANAGED BY PUPPET ###\n---\nnode: \n data: false\n name: elasticsearch001-es-01\npath: \n data: /usr/share/elasticsearch/data/es-01\n") } + + end + + context "deeper hash and multiple keys" do + + let :params do { + :config => { 'index' => { 'routing' => { 'allocation' => { 'include' => 'tag1', 'exclude' => [ 'tag2', 'tag3' ] } } }, 'node.name' => 'somename' } + } end + + it { should contain_file('/etc/elasticsearch/es-01/elasticsearch.yml').with(:content => "### MANAGED BY PUPPET ###\n---\nindex: \n routing: \n allocation: \n exclude: \n - tag2\n - tag3\n include: tag1\nnode: \n name: somename\npath: \n data: /usr/share/elasticsearch/data/es-01\n") } + + end + + context "Combination of full hash and shorted write up keys" do + + let :params do { + :config => { 'node' => { 'name' => 'NodeName', 'rack' => 46 }, 'boostrap.mlockall' => true, 'cluster' => { 'name' => 'ClusterName', 'routing.allocation.awareness.attributes' => 'rack' }, 'discovery.zen' => { 'ping.unicast.hosts'=> [ "host1", "host2" ], 'minimum_master_nodes' => 3, 'ping.multicast.enabled' => false }, 'gateway' => { 'expected_nodes' => 4, 'recover_after_nodes' => 3 }, 'network.host' => '123.123.123.123' } + } end + + it { should contain_file('/etc/elasticsearch/es-01/elasticsearch.yml').with(:content => "### MANAGED BY PUPPET ###\n---\nboostrap: \n mlockall: true\ncluster: \n name: ClusterName\n routing: \n allocation: \n awareness: \n attributes: rack\ndiscovery: \n zen: \n minimum_master_nodes: 3\n ping: \n multicast: \n enabled: false\n unicast: \n hosts: \n - host1\n - host2\ngateway: \n expected_nodes: 4\n recover_after_nodes: 3\nnetwork: \n host: 123.123.123.123\nnode: \n name: NodeName\n rack: 46\npath: \n data: /usr/share/elasticsearch/data/es-01\n") } + + end + + context "merge Main class and instance configs together" do + let(:pre_condition) { 'class {"elasticsearch": config => { "cluster.name" => "somename"} }' } + let :params do { + :config => { 'node.name' => 'NodeName' } + } end + + it { should contain_file('/etc/elasticsearch/es-01/elasticsearch.yml').with(:content => "### MANAGED BY PUPPET ###\n---\ncluster: \n name: somename\nnode: \n name: NodeName\npath: \n data: /usr/share/elasticsearch/data/es-01\n") } + + end + + context "override main class confgi with instance config" do + let(:pre_condition) { 'class {"elasticsearch": config => { "cluster.name" => "somename" } }' } + let :params do { + :config => { 'node.name' => 'NodeName', 'cluster.name' => 'ClusterName' } + } end + + it { should contain_file('/etc/elasticsearch/es-01/elasticsearch.yml').with(:content => "### MANAGED BY PUPPET ###\n---\ncluster: \n name: ClusterName\nnode: \n name: NodeName\npath: \n data: /usr/share/elasticsearch/data/es-01\n") } + end + + context "service restarts" do + + let :facts do { + :operatingsystem => 'CentOS', + :kernel => 'Linux', + :osfamily => 'RedHat', + :hostname => 'elasticsearch001' + } end + + let(:title) { 'es-01' } + context "does not restart when restart_on_change is false" do + let :params do { + :config => { 'node' => { 'name' => 'test' } }, + } end + let(:pre_condition) { 'class {"elasticsearch": config => { }, restart_on_change => false }' } + + it { should contain_file('/etc/elasticsearch/es-01/elasticsearch.yml').without_notify } + end + + context "should happen restart_on_change is true (default)" do + let :params do { + :config => { 'node' => { 'name' => 'test' } }, + } end + let(:pre_condition) { 'class {"elasticsearch": config => { }}' } + + it { should contain_file('/etc/elasticsearch/es-01/elasticsearch.yml').with(:notify => "Elasticsearch::Service[es-01]") } + end + + end + + end + + context "Config dir" do + + let(:title) { 'es-01' } + + context "default" do + let(:pre_condition) { 'class {"elasticsearch": }' } + it { should contain_exec('mkdir_configdir_elasticsearch_es-01') } + it { should contain_file('/etc/elasticsearch/es-01').with(:ensure => 'directory') } + it { should contain_file('/etc/elasticsearch/es-01/elasticsearch.yml') } + it { should contain_file('/etc/elasticsearch/es-01/logging.yml') } + end + + context "Set in main class" do + let(:pre_condition) { 'class {"elasticsearch": configdir => "/etc/elasticsearch-config" }' } + + it { should contain_exec('mkdir_configdir_elasticsearch_es-01') } + it { should contain_file('/etc/elasticsearch-config').with(:ensure => 'directory') } + it { should contain_file('/etc/elasticsearch-config/templates_import').with(:ensure => 'directory') } + it { should contain_file('/etc/elasticsearch-config/es-01').with(:ensure => 'directory') } + it { should contain_file('/etc/elasticsearch-config/es-01/elasticsearch.yml') } + it { should contain_file('/etc/elasticsearch-config/es-01/logging.yml') } + end + + context "set in instance" do + let(:pre_condition) { 'class {"elasticsearch": }' } + let :params do { + :configdir => '/etc/elasticsearch-config/es-01' + } end + + it { should contain_exec('mkdir_configdir_elasticsearch_es-01') } + it { should contain_file('/etc/elasticsearch').with(:ensure => 'directory') } + it { should contain_file('/etc/elasticsearch-config/es-01').with(:ensure => 'directory') } + it { should contain_file('/etc/elasticsearch-config/es-01/elasticsearch.yml') } + it { should contain_file('/etc/elasticsearch-config/es-01/logging.yml') } + end + + end + + context "Service" do + let(:title) { 'es-01' } + let(:pre_condition) { 'class {"elasticsearch": }' } + + context "Debian based" do + let :facts do { + :operatingsystem => 'Debian', + :kernel => 'Linux', + :osfamily => 'Debian', + :hostname => 'elasticsearch001' + } end + + it { should contain_elasticsearch__service('es-01').with(:init_template => 'elasticsearch/etc/init.d/elasticsearch.Debian.erb', :init_defaults => {"CONF_DIR"=>"/etc/elasticsearch/es-01", "CONF_FILE"=>"/etc/elasticsearch/es-01/elasticsearch.yml", "LOG_DIR"=>"/var/log/elasticsearch/es-01", "ES_HOME"=>"/usr/share/elasticsearch"}) } + end + + context "Redhat based" do + let :facts do { + :operatingsystem => 'CentOS', + :kernel => 'Linux', + :osfamily => 'RedHat', + :hostname => 'elasticsearch001' + } end + + it { should contain_elasticsearch__service('es-01').with(:init_template => 'elasticsearch/etc/init.d/elasticsearch.RedHat.erb', :init_defaults => {"CONF_DIR"=>"/etc/elasticsearch/es-01", "CONF_FILE"=>"/etc/elasticsearch/es-01/elasticsearch.yml", "LOG_DIR"=>"/var/log/elasticsearch/es-01", "ES_HOME"=>"/usr/share/elasticsearch"}) } + end + + context "OpenSuse based" do + let :facts do { + :operatingsystem => 'OpenSuSE', + :kernel => 'Linux', + :osfamily => 'Suse', + :hostname => 'elasticsearch001' + } end + + it { should contain_elasticsearch__service('es-01').with(:init_template => 'elasticsearch/etc/init.d/elasticsearch.OpenSuSE.erb', :init_defaults => {"CONF_DIR"=>"/etc/elasticsearch/es-01", "CONF_FILE"=>"/etc/elasticsearch/es-01/elasticsearch.yml", "LOG_DIR"=>"/var/log/elasticsearch/es-01", "ES_HOME"=>"/usr/share/elasticsearch"}) } + end + + end + + context "data directory" do + let :facts do { + :operatingsystem => 'CentOS', + :kernel => 'Linux', + :osfamily => 'RedHat', + :hostname => 'elasticsearch001' + } end + + let(:title) { 'es-01' } + let(:pre_condition) { 'class {"elasticsearch": }' } + + context "default" do + it { should contain_exec('mkdir_datadir_elasticsearch_es-01') } + it { should contain_file('/etc/elasticsearch/es-01/elasticsearch.yml').with(:content => "### MANAGED BY PUPPET ###\n---\nnode: \n name: elasticsearch001-es-01\npath: \n data: /usr/share/elasticsearch/data/es-01\n" ) } + it { should contain_file('/usr/share/elasticsearch/data/es-01').with( :ensure => 'directory') } + end + + context "single from main config " do + let(:pre_condition) { 'class {"elasticsearch": datadir => "/var/lib/elasticsearch-data" }' } + it { should contain_exec('mkdir_datadir_elasticsearch_es-01') } + it { should contain_file('/etc/elasticsearch/es-01/elasticsearch.yml').with(:content => "### MANAGED BY PUPPET ###\n---\nnode: \n name: elasticsearch001-es-01\npath: \n data: /var/lib/elasticsearch-data/es-01\n" ) } + it { should contain_file('/var/lib/elasticsearch-data/es-01').with( :ensure => 'directory') } + end + + context "single from instance config" do + let(:pre_condition) { 'class {"elasticsearch": }' } + let :params do { + :datadir => '/var/lib/elasticsearch/data' + } end + + it { should contain_exec('mkdir_datadir_elasticsearch_es-01') } + it { should contain_file('/etc/elasticsearch/es-01/elasticsearch.yml').with(:content => "### MANAGED BY PUPPET ###\n---\nnode: \n name: elasticsearch001-es-01\npath: \n data: /var/lib/elasticsearch/data\n" ) } + it { should contain_file('/var/lib/elasticsearch/data').with( :ensure => 'directory') } + end + + context "multiple from main config" do + let(:pre_condition) { 'class {"elasticsearch": datadir => [ "/var/lib/elasticsearch-data01", "/var/lib/elasticsearch-data02"] }' } + it { should contain_exec('mkdir_datadir_elasticsearch_es-01') } + it { should contain_file('/etc/elasticsearch/es-01/elasticsearch.yml').with(:content => "### MANAGED BY PUPPET ###\n---\nnode: \n name: elasticsearch001-es-01\npath: \n data: \n - /var/lib/elasticsearch-data01/es-01\n - /var/lib/elasticsearch-data02/es-01\n" ) } + it { should contain_file('/var/lib/elasticsearch-data01/es-01').with( :ensure => 'directory') } + it { should contain_file('/var/lib/elasticsearch-data02/es-01').with( :ensure => 'directory') } + end + + context "multiple from instance config" do + let(:pre_condition) { 'class {"elasticsearch": }' } + let :params do { + :datadir => ['/var/lib/elasticsearch-data/01', '/var/lib/elasticsearch-data/02'] + } end + + it { should contain_exec('mkdir_datadir_elasticsearch_es-01') } + it { should contain_file('/etc/elasticsearch/es-01/elasticsearch.yml').with(:content => "### MANAGED BY PUPPET ###\n---\nnode: \n name: elasticsearch001-es-01\npath: \n data: \n - /var/lib/elasticsearch-data/01\n - /var/lib/elasticsearch-data/02\n" ) } + it { should contain_file('/var/lib/elasticsearch-data/01').with( :ensure => 'directory') } + it { should contain_file('/var/lib/elasticsearch-data/02').with( :ensure => 'directory') } + end + + end + + context "Logging" do + + let :facts do { + :operatingsystem => 'CentOS', + :kernel => 'Linux', + :osfamily => 'RedHat', + :hostname => 'elasticsearch001' + } end + + let(:pre_condition) { 'class {"elasticsearch": }' } + + context "default" do + it { should contain_file('/etc/elasticsearch/es-01/logging.yml').with_content(/^logger.index.search.slowlog: TRACE, index_search_slow_log_file$/).with(:source => nil) } + end + + context "from main class" do + + context "config" do + let(:pre_condition) { 'class {"elasticsearch": logging_config => { "index.search.slowlog" => "DEBUG, index_search_slow_log_file" } }' } + + it { should contain_file('/etc/elasticsearch/es-01/logging.yml').with_content(/^logger.index.search.slowlog: DEBUG, index_search_slow_log_file$/).with(:source => nil) } + end + + context "logging file " do + let(:pre_condition) { 'class {"elasticsearch": logging_file => "puppet:///path/to/logging.yml" }' } + + it { should contain_file('/etc/elasticsearch/es-01/logging.yml').with(:source => 'puppet:///path/to/logging.yml', :content => nil) } + end + + end + + context "from instance" do + + let(:pre_condition) { 'class {"elasticsearch": }' } + + context "config" do + let :params do { + :logging_config => { 'index.search.slowlog' => 'INFO, index_search_slow_log_file' } + } end + + it { should contain_file('/etc/elasticsearch/es-01/logging.yml').with_content(/^logger.index.search.slowlog: INFO, index_search_slow_log_file$/).with(:source => nil) } + end + + context "logging file " do + let :params do { + :logging_file => 'puppet:///path/to/logging.yml' + } end + + it { should contain_file('/etc/elasticsearch/es-01/logging.yml').with(:source => 'puppet:///path/to/logging.yml', :content => nil) } + end + + end + + end + +end diff --git a/elasticsearch/spec/defines/010_elasticsearch_service_init_spec.rb b/elasticsearch/spec/defines/010_elasticsearch_service_init_spec.rb new file mode 100644 index 000000000..a35b4fa82 --- /dev/null +++ b/elasticsearch/spec/defines/010_elasticsearch_service_init_spec.rb @@ -0,0 +1,125 @@ +require 'spec_helper' + +describe 'elasticsearch::service::init', :type => 'define' do + + let :facts do { + :operatingsystem => 'CentOS', + :kernel => 'Linux', + :osfamily => 'RedHat' + } end + + let(:title) { 'es-01' } + let(:pre_condition) { 'class {"elasticsearch": config => { "node" => {"name" => "test" }}}' } + + context "Setup service" do + + let :params do { + :ensure => 'present', + :status => 'enabled' + } end + + it { should contain_elasticsearch__service__init('es-01') } + it { should contain_service('es-01').with(:ensure => 'running', :enable => true) } + end + + context "Remove service" do + + let :params do { + :ensure => 'absent' + } end + + it { should contain_elasticsearch__service__init('es-01') } + it { should contain_service('es-01').with(:ensure => 'stopped', :enable => false) } + end + + context "unmanaged" do + let :params do { + :ensure => 'present', + :status => 'unmanaged' + } end + + it { should contain_elasticsearch__service__init('es-01') } + it { should_not contain_service('es-01') } + it { should_not contain_file('/etc/init.d/elasticsearch-es-01') } + it { should_not contain_file('/etc/sysconfig/elasticsearch-es-01') } + + end + + context "Defaults file" do + + context "Set via file" do + let :params do { + :ensure => 'present', + :status => 'enabled', + :init_defaults_file => 'puppet:///path/to/initdefaultsfile' + } end + + it { should contain_file('/etc/sysconfig/elasticsearch-es-01').with(:source => 'puppet:///path/to/initdefaultsfile', :notify => 'Service[es-01]', :before => 'Service[es-01]') } + end + + context "Set via hash" do + let :params do { + :ensure => 'present', + :status => 'enabled', + :init_defaults => {'ES_HOME' => '/usr/share/elasticsearch' } + } end + + it { should contain_augeas('defaults_es-01').with(:incl => '/etc/sysconfig/elasticsearch-es-01', :changes => "set ES_GROUP 'elasticsearch'\nset ES_HOME '/usr/share/elasticsearch'\nset ES_USER 'elasticsearch'\n", :notify => 'Service[es-01]', :before => 'Service[es-01]') } + end + + context "No restart when 'restart_on_change' is false" do + let(:pre_condition) { 'class {"elasticsearch": config => { "node" => {"name" => "test" }}, restart_on_change => false } ' } + + context "Set via file" do + let :params do { + :ensure => 'present', + :status => 'enabled', + :init_defaults_file => 'puppet:///path/to/initdefaultsfile' + } end + + it { should contain_file('/etc/sysconfig/elasticsearch-es-01').with(:source => 'puppet:///path/to/initdefaultsfile', :notify => nil, :before => 'Service[es-01]') } + end + + context "Set via hash" do + let :params do { + :ensure => 'present', + :status => 'enabled', + :init_defaults => {'ES_HOME' => '/usr/share/elasticsearch' } + } end + + it { should contain_augeas('defaults_es-01').with(:incl => '/etc/sysconfig/elasticsearch-es-01', :changes => "set ES_GROUP 'elasticsearch'\nset ES_HOME '/usr/share/elasticsearch'\nset ES_USER 'elasticsearch'\n", :notify => nil, :before => 'Service[es-01]') } + end + + end + + end + + context "Init file" do + let(:pre_condition) { 'class {"elasticsearch": config => { "node" => {"name" => "test" }} } ' } + + context "Via template" do + let :params do { + :ensure => 'present', + :status => 'enabled', + :init_template => 'elasticsearch/etc/init.d/elasticsearch.RedHat.erb' + } end + + it { should contain_file('/etc/init.d/elasticsearch-es-01').with(:notify => 'Service[es-01]', :before => 'Service[es-01]') } + end + + context "No restart when 'restart_on_change' is false" do + let(:pre_condition) { 'class {"elasticsearch": config => { "node" => {"name" => "test" }}, restart_on_change => false } ' } + + let :params do { + :ensure => 'present', + :status => 'enabled', + :init_template => 'elasticsearch/etc/init.d/elasticsearch.RedHat.erb' + } end + + it { should contain_file('/etc/init.d/elasticsearch-es-01').with(:notify => nil, :before => 'Service[es-01]') } + + end + + end + +end diff --git a/elasticsearch/spec/defines/011_elasticsearch_service_system_spec.rb b/elasticsearch/spec/defines/011_elasticsearch_service_system_spec.rb new file mode 100644 index 000000000..beaf4999f --- /dev/null +++ b/elasticsearch/spec/defines/011_elasticsearch_service_system_spec.rb @@ -0,0 +1,127 @@ +require 'spec_helper' + +describe 'elasticsearch::service::systemd', :type => 'define' do + + let :facts do { + :operatingsystem => 'OpenSuSE', + :kernel => 'Linux', + :osfamily => 'Suse' + } end + + let(:title) { 'es-01' } + let(:pre_condition) { 'class {"elasticsearch": config => { "node" => {"name" => "test" }}}' } + + context "Setup service" do + + let :params do { + :ensure => 'present', + :status => 'enabled' + } end + + it { should contain_elasticsearch__service__systemd('es-01') } + it { should contain_exec('systemd_reload').with(:command => '/bin/systemctl daemon-reload') } + it { should contain_service('es-01').with(:ensure => 'running', :enable => true, :provider => 'systemd') } + end + + context "Remove service" do + + let :params do { + :ensure => 'absent' + } end + + it { should contain_elasticsearch__service__systemd('es-01') } + it { should contain_exec('systemd_reload').with(:command => '/bin/systemctl daemon-reload') } + it { should contain_service('es-01').with(:ensure => 'stopped', :enable => false, :provider => 'systemd') } + end + + context "unmanaged" do + let :params do { + :ensure => 'present', + :status => 'unmanaged' + } end + + it { should contain_elasticsearch__service__systemd('es-01') } + it { should_not contain_service('es-01') } + it { should_not contain_file('/usr/lib/systemd/system/elasticsearch-es-01.service') } + it { should_not contain_file('/etc/sysconfig/elasticsearch-es-01') } + + end + + context "Defaults file" do + + context "Set via file" do + let :params do { + :ensure => 'present', + :status => 'enabled', + :init_defaults_file => 'puppet:///path/to/initdefaultsfile' + } end + + it { should contain_file('/etc/sysconfig/elasticsearch-es-01').with(:source => 'puppet:///path/to/initdefaultsfile', :before => 'Service[es-01]') } + end + + context "Set via hash" do + let :params do { + :ensure => 'present', + :status => 'enabled', + :init_defaults => {'ES_HOME' => '/usr/share/elasticsearch' } + } end + + it { should contain_augeas('defaults_es-01').with(:incl => '/etc/sysconfig/elasticsearch-es-01', :changes => "set ES_GROUP 'elasticsearch'\nset ES_HOME '/usr/share/elasticsearch'\nset ES_USER 'elasticsearch'\n", :before => 'Service[es-01]') } + end + + context "No restart when 'restart_on_change' is false" do + let(:pre_condition) { 'class {"elasticsearch": config => { "node" => {"name" => "test" }}, restart_on_change => false } ' } + + context "Set via file" do + let :params do { + :ensure => 'present', + :status => 'enabled', + :init_defaults_file => 'puppet:///path/to/initdefaultsfile' + } end + + it { should contain_file('/etc/sysconfig/elasticsearch-es-01').with(:source => 'puppet:///path/to/initdefaultsfile', :notify => 'Exec[systemd_reload]', :before => 'Service[es-01]') } + end + + context "Set via hash" do + let :params do { + :ensure => 'present', + :status => 'enabled', + :init_defaults => {'ES_HOME' => '/usr/share/elasticsearch' } + } end + + it { should contain_augeas('defaults_es-01').with(:incl => '/etc/sysconfig/elasticsearch-es-01', :changes => "set ES_GROUP 'elasticsearch'\nset ES_HOME '/usr/share/elasticsearch'\nset ES_USER 'elasticsearch'\n", :notify => 'Exec[systemd_reload]', :before => 'Service[es-01]') } + end + + end + + end + + context "Init file" do + let(:pre_condition) { 'class {"elasticsearch": config => { "node" => {"name" => "test" }} } ' } + + context "Via template" do + let :params do { + :ensure => 'present', + :status => 'enabled', + :init_template => 'elasticsearch/etc/init.d/elasticsearch.OpenSuSE.erb' + } end + + it { should contain_file('/usr/lib/systemd/system/elasticsearch-es-01.service').with(:before => 'Service[es-01]') } + end + + context "No restart when 'restart_on_change' is false" do + let(:pre_condition) { 'class {"elasticsearch": config => { "node" => {"name" => "test" }}, restart_on_change => false } ' } + + let :params do { + :ensure => 'present', + :status => 'enabled', + :init_template => 'elasticsearch/etc/init.d/elasticsearch.OpenSuSE.erb' + } end + + it { should contain_file('/usr/lib/systemd/system/elasticsearch-es-01.service').with(:notify => 'Exec[systemd_reload]', :before => 'Service[es-01]') } + + end + + end + +end diff --git a/elasticsearch/spec/spec_helper.rb b/elasticsearch/spec/spec_helper.rb new file mode 100644 index 000000000..f3a9f9620 --- /dev/null +++ b/elasticsearch/spec/spec_helper.rb @@ -0,0 +1,6 @@ +require 'rubygems' +require 'puppetlabs_spec_helper/module_spec_helper' + +RSpec.configure do |c| + c.parser = 'future' if ENV['FUTURE_PARSER'] == 'true' +end diff --git a/elasticsearch/spec/spec_helper_acceptance.rb b/elasticsearch/spec/spec_helper_acceptance.rb new file mode 100644 index 000000000..ae46b79cd --- /dev/null +++ b/elasticsearch/spec/spec_helper_acceptance.rb @@ -0,0 +1,87 @@ +require 'beaker-rspec' +require 'pry' +require 'securerandom' + +files_dir = ENV['files_dir'] || '/home/jenkins/puppet' + +proxy_host = ENV['proxy_host'] || '' + +gem_proxy = '' +gem_proxy = "http_proxy=http://#{proxy_host}" unless proxy_host.empty? + +if !proxy_host.empty? + hosts.each do |host| + case host['platform'] + when /ubuntu/, /debian/ + on host, "echo 'Acquire::http::Proxy \"http://#{proxy_host}/\";' >> /etc/apt/apt.conf.d/10proxy" + when /^el-/, /centos/, /fedora/, /redhat/ + on host, "echo 'proxy=http://#{proxy_host}/' >> /etc/yum.conf" + end + end +end + +hosts.each do |host| + # Install Puppet + if host.is_pe? + install_pe + else + puppetversion = ENV['VM_PUPPET_VERSION'] + on host, "#{gem_proxy} gem install puppet --no-ri --no-rdoc --version '~> #{puppetversion}'" + on host, "mkdir -p #{host['distmoduledir']}" + + if fact('osfamily') == 'Suse' + install_package host, 'rubygems ruby-devel augeas-devel libxml2-devel' + on host, "#{gem_proxy} gem install ruby-augeas --no-ri --no-rdoc" + end + + end + + # Copy over some files + if fact('osfamily') == 'Debian' + scp_to(host, "#{files_dir}/elasticsearch-1.1.0.deb", '/tmp/elasticsearch-1.1.0.deb') + end + + if fact('osfamily') == 'RedHat' + scp_to(host, "#{files_dir}/elasticsearch-1.1.0.noarch.rpm", '/tmp/elasticsearch-1.1.0.noarch.rpm') + end + + if fact('osfamily') == 'Suse' + scp_to(host, "#{files_dir}/elasticsearch-1.1.0.noarch.rpm", '/tmp/elasticsearch-1.1.0.noarch.rpm') + end + + # on debian/ubuntu nodes ensure we get the latest info + # Can happen we have stalled data in the images + if fact('osfamily') == 'Debian' + on host, "apt-get update" + end + +end + +RSpec.configure do |c| + # Project root + proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) + + # Readable test descriptions + c.formatter = :documentation + + # Configure all nodes in nodeset + c.before :suite do + # Install module and dependencies + puppet_module_install(:source => proj_root, :module_name => 'elasticsearch') + hosts.each do |host| + + if !host.is_pe? + scp_to(host, "#{files_dir}/puppetlabs-stdlib-3.2.0.tar.gz", '/tmp/puppetlabs-stdlib-3.2.0.tar.gz') + on host, puppet('module','install','/tmp/puppetlabs-stdlib-3.2.0.tar.gz'), { :acceptable_exit_codes => [0,1] } + end + if fact('osfamily') == 'Debian' + scp_to(host, "#{files_dir}/puppetlabs-apt-1.4.2.tar.gz", '/tmp/puppetlabs-apt-1.4.2.tar.gz') + on host, puppet('module','install','/tmp/puppetlabs-apt-1.4.2.tar.gz'), { :acceptable_exit_codes => [0,1] } + end + if fact('osfamily') == 'Suse' + on host, puppet('module','install','darin-zypprepo'), { :acceptable_exit_codes => [0,1] } + end + + end + end +end diff --git a/elasticsearch/templates/etc/elasticsearch/elasticsearch.yml.erb b/elasticsearch/templates/etc/elasticsearch/elasticsearch.yml.erb new file mode 100644 index 000000000..00a7220cb --- /dev/null +++ b/elasticsearch/templates/etc/elasticsearch/elasticsearch.yml.erb @@ -0,0 +1,93 @@ +<%- + + # Function to make a structured and sorted yaml representation out of a hash + def recursive_hash_to_yml_string(hash, depth=0) + spacer = "" + depth.times { spacer += " "} + hash.keys.sort.each do |sorted_key| + @yml_string += spacer + sorted_key + ": " + if hash[sorted_key].is_a?(Array) + keyspacer = "" + sorted_key.length.times { keyspacer += " " } + @yml_string += "\n" + hash[sorted_key].each do |item| + @yml_string += spacer + keyspacer + "- " + item +"\n" + end + elsif hash[sorted_key].is_a?(Hash) + @yml_string += "\n" + recursive_hash_to_yml_string(hash[sorted_key], depth+1) + else + @yml_string += "#{hash[sorted_key].to_s}\n" + end + end + end + + # Function to transform shorted write up of the keys into full hash representation + def transform(hash) + return_vals = [] + + hash.each do |key,val| + if m = /^([^.]+)\.(.*)$/.match(key) + temp = { m[1] => { m[2] => val } } + transform(temp).each do |stuff| + return_vals << stuff + end + else + if val.is_a?(Hash) + transform(val).each do |stuff| + return_vals << { key => stuff } + end + else + return_vals << { key => val } + end + end + end + + return_vals + end + + # Function to deep merge hashes with same keys + class ::Hash + def deep_merge_with_array_values_concatenated(hash) + target = dup + + hash.keys.each do |key| + if hash[key].is_a? Hash and self[key].is_a? Hash + target[key] = target[key].deep_merge_with_array_values_concatenated(hash[key]) + next + end + + if hash[key].is_a?(Array) && target[key].is_a?(Array) + target[key] = target[key] + hash[key] + else + target[key] = hash[key] + end + end + + target + end + end + + # initial string + @yml_string = "### MANAGED BY PUPPET ###\n" + + if !@instance_conf.empty? + + @yml_string += "---\n" + + ## Transform shorted keys into full write up + transformed_config = transform(@instance_conf) + + # Merge it back into a hash + tmphash = { } + transformed_config.each do |subhash| + tmphash = tmphash.deep_merge_with_array_values_concatenated(subhash) + end + + # Transform it into yaml + recursive_hash_to_yml_string(tmphash) + + end + +-%> +<%= @yml_string -%> diff --git a/elasticsearch/templates/etc/elasticsearch/logging.yml.erb b/elasticsearch/templates/etc/elasticsearch/logging.yml.erb new file mode 100644 index 000000000..e24d41426 --- /dev/null +++ b/elasticsearch/templates/etc/elasticsearch/logging.yml.erb @@ -0,0 +1,52 @@ +# This file is managed by Puppet, do not edit manually, your changes *will* be overwritten! +# +# Please see the source file for context and more information: +# +# https://github.com/elasticsearch/elasticsearch/blob/master/config/logging.yml +# + +es.logger.level: <%= @logging_level %> +rootLogger: <%= @logging_level %>, console, file + +# ----- Configuration set by Puppet --------------------------------------------- + +<% @logging_hash.sort.each do |key,value| %> +logger.<%= key %>: <%= value %> +<% end %> + +# ------------------------------------------------------------------------------- + +additivity: + index.search.slowlog: false + index.indexing.slowlog: false + +appender: + console: + type: console + layout: + type: consolePattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" + + file: + type: dailyRollingFile + file: ${path.logs}/${cluster.name}.log + datePattern: "'.'yyyy-MM-dd" + layout: + type: pattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" + + index_search_slow_log_file: + type: dailyRollingFile + file: ${path.logs}/${cluster.name}_index_search_slowlog.log + datePattern: "'.'yyyy-MM-dd" + layout: + type: pattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" + + index_indexing_slow_log_file: + type: dailyRollingFile + file: ${path.logs}/${cluster.name}_index_indexing_slowlog.log + datePattern: "'.'yyyy-MM-dd" + layout: + type: pattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" diff --git a/elasticsearch/templates/etc/init.d/elasticsearch.Debian.erb b/elasticsearch/templates/etc/init.d/elasticsearch.Debian.erb new file mode 100644 index 000000000..ede849536 --- /dev/null +++ b/elasticsearch/templates/etc/init.d/elasticsearch.Debian.erb @@ -0,0 +1,203 @@ +#!/bin/sh +# +# /etc/init.d/elasticsearch-<%= @name %> -- startup script for Elasticsearch +# +# Written by Miquel van Smoorenburg . +# Modified for Debian GNU/Linux by Ian Murdock . +# Modified for Tomcat by Stefan Gybas . +# Modified for Tomcat6 by Thierry Carrez . +# Additional improvements by Jason Brittain . +# Modified by Nicolas Huray for Elasticsearch . +# +### BEGIN INIT INFO +# Provides: elasticsearch-<%= @name %> +# Required-Start: $network $remote_fs $named +# Required-Stop: $network $remote_fs $named +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Starts elasticsearch-<%= @name %> +# Description: Starts elasticsearch-<%= @name %> using start-stop-daemon +### END INIT INFO + +PATH=/bin:/usr/bin:/sbin:/usr/sbin +NAME=elasticsearch-<%= @name %> +DESC="Elasticsearch Server <%= @name %>" +DEFAULT=/etc/default/$NAME + +if [ `id -u` -ne 0 ]; then + echo "You need root privileges to run this script" + exit 1 +fi + + +. /lib/lsb/init-functions + +if [ -r /etc/default/rcS ]; then + . /etc/default/rcS +fi + + +# The following variables can be overwritten in $DEFAULT + +# Run Elasticsearch as this user ID and group ID +ES_USER=elasticsearch +ES_GROUP=elasticsearch + +# The first existing directory is used for JAVA_HOME (if JAVA_HOME is not defined in $DEFAULT) +JDK_DIRS="/usr/lib/jvm/java-7-oracle /usr/lib/jvm/java-7-openjdk /usr/lib/jvm/java-7-openjdk-amd64/ /usr/lib/jvm/java-7-openjdk-armhf /usr/lib/jvm/java-7-openjdk-i386/ /usr/lib/jvm/java-6-sun /usr/lib/jvm/java-6-openjdk /usr/lib/jvm/java-6-openjdk-amd64 /usr/lib/jvm/java-6-openjdk-armhf /usr/lib/jvm/java-6-openjdk-i386 /usr/lib/jvm/default-java" + +# Look for the right JVM to use +for jdir in $JDK_DIRS; do + if [ -r "$jdir/bin/java" -a -z "${JAVA_HOME}" ]; then + JAVA_HOME="$jdir" + fi +done +export JAVA_HOME + +# Directory where the Elasticsearch binary distribution resides +ES_HOME=/usr/share/$NAME + +# Heap Size (defaults to 256m min, 1g max) +#ES_HEAP_SIZE=2g + +# Heap new generation +#ES_HEAP_NEWSIZE= + +# max direct memory +#ES_DIRECT_SIZE= + +# Additional Java OPTS +#ES_JAVA_OPTS= + +# Maximum number of open files +MAX_OPEN_FILES=65535 + +# Maximum amount of locked memory +#MAX_LOCKED_MEMORY= + +# Elasticsearch log directory +LOG_DIR=/var/log/$NAME + +# Elasticsearch data directory +DATA_DIR=/var/lib/$NAME + +# Elasticsearch work directory +WORK_DIR=/tmp/$NAME + +# Elasticsearch configuration directory +CONF_DIR=/etc/$NAME + +# Elasticsearch configuration file (elasticsearch.yml) +CONF_FILE=$CONF_DIR/elasticsearch.yml + +# Maximum number of VMA (Virtual Memory Areas) a process can own +MAX_MAP_COUNT=262144 + +# End of variables that can be overwritten in $DEFAULT + +# overwrite settings from default file +if [ -f "$DEFAULT" ]; then + . "$DEFAULT" +fi + +# Define other required variables +PID_FILE=/var/run/$NAME.pid +DAEMON=$ES_HOME/bin/elasticsearch +DAEMON_OPTS="-d -p $PID_FILE -Des.default.config=$CONF_FILE -Des.default.path.home=$ES_HOME -Des.default.path.logs=$LOG_DIR -Des.default.path.data=$DATA_DIR -Des.default.path.work=$WORK_DIR -Des.default.path.conf=$CONF_DIR" + +export ES_HEAP_SIZE +export ES_HEAP_NEWSIZE +export ES_DIRECT_SIZE +export ES_JAVA_OPTS + +# Check DAEMON exists +test -x $DAEMON || exit 0 + +checkJava() { + if [ -x "$JAVA_HOME/bin/java" ]; then + JAVA="$JAVA_HOME/bin/java" + else + JAVA=`which java` + fi + + if [ ! -x "$JAVA" ]; then + echo "Could not find any executable java binary. Please install java in your PATH or set JAVA_HOME" + exit 1 + fi +} + +case "$1" in + start) + checkJava + + if [ -n "$MAX_LOCKED_MEMORY" -a -z "$ES_HEAP_SIZE" ]; then + log_failure_msg "MAX_LOCKED_MEMORY is set - ES_HEAP_SIZE must also be set" + exit 1 + fi + + log_daemon_msg "Starting $DESC" + + pid=`pidofproc -p $PID_FILE elasticsearch` + if [ -n "$pid" ] ; then + log_begin_msg "Already running." + log_end_msg 0 + exit 0 + fi + + # Prepare environment + mkdir -p "$LOG_DIR" "$DATA_DIR" "$WORK_DIR" && chown "$ES_USER":"$ES_GROUP" "$LOG_DIR" "$DATA_DIR" "$WORK_DIR" + touch "$PID_FILE" && chown "$ES_USER":"$ES_GROUP" "$PID_FILE" + + if [ -n "$MAX_OPEN_FILES" ]; then + ulimit -n $MAX_OPEN_FILES + fi + + if [ -n "$MAX_LOCKED_MEMORY" ]; then + ulimit -l $MAX_LOCKED_MEMORY + fi + + if [ -n "$MAX_MAP_COUNT" ]; then + sysctl -q -w vm.max_map_count=$MAX_MAP_COUNT + fi + + # Start Daemon + start-stop-daemon --start -b --user "$ES_USER" -c "$ES_USER" --pidfile "$PID_FILE" --exec $DAEMON -- $DAEMON_OPTS + log_end_msg $? + ;; + stop) + log_daemon_msg "Stopping $DESC" + + if [ -f "$PID_FILE" ]; then + start-stop-daemon --stop --pidfile "$PID_FILE" \ + --user "$ES_USER" \ + --retry=TERM/20/KILL/5 >/dev/null + if [ $? -eq 1 ]; then + log_progress_msg "$DESC is not running but pid file exists, cleaning up" + elif [ $? -eq 3 ]; then + PID="`cat $PID_FILE`" + log_failure_msg "Failed to stop $DESC (pid $PID)" + exit 1 + fi + rm -f "$PID_FILE" + else + log_progress_msg "(not running)" + fi + log_end_msg 0 + ;; + status) + status_of_proc -p $PID_FILE elasticsearch elasticsearch && exit 0 || exit $? + ;; + restart|force-reload) + if [ -f "$PID_FILE" ]; then + $0 stop + sleep 1 + fi + $0 start + ;; + *) + log_success_msg "Usage: $0 {start|stop|restart|force-reload|status}" + exit 1 + ;; +esac + +exit 0 diff --git a/elasticsearch/templates/etc/init.d/elasticsearch.OpenSuSE.erb b/elasticsearch/templates/etc/init.d/elasticsearch.OpenSuSE.erb new file mode 100644 index 000000000..5bdb6ba4c --- /dev/null +++ b/elasticsearch/templates/etc/init.d/elasticsearch.OpenSuSE.erb @@ -0,0 +1,20 @@ +[Unit] +Description=Starts and stops a single elasticsearch instance on this system +Documentation=http://www.elasticsearch.org + +[Service] +Type=forking +EnvironmentFile=/etc/sysconfig/elasticsearch-<%= @name %> +User=<%= @user %> +Group=<%= @group %> +PIDFile=/var/run/elasticsearch/elasticsearch-<%= @name %>.pid +ExecStart=/usr/share/elasticsearch/bin/elasticsearch -d -p /var/run/elasticsearch/elasticsearch-<%= @name %>.pid -Des.default.config=$CONF_FILE -Des.default.path.home=$ES_HOME -Des.default.path.logs=$LOG_DIR -Des.default.path.data=$DATA_DIR -Des.default.path.work=$WORK_DIR -Des.default.path.conf=$CONF_DIR +# See MAX_OPEN_FILES in sysconfig +LimitNOFILE=65535 +# See MAX_LOCKED_MEMORY in sysconfig, use "infinity" when MAX_LOCKED_MEMORY=unlimited and using bootstrap.mlockall: true +#LimitMEMLOCK=infinity +# Shutdown delay in seconds, before process is tried to be killed with KILL (if configured) +TimeoutStopSec=20 + +[Install] +WantedBy=multi-user.target diff --git a/elasticsearch/templates/etc/init.d/elasticsearch.RedHat.erb b/elasticsearch/templates/etc/init.d/elasticsearch.RedHat.erb new file mode 100644 index 000000000..3a0dac771 --- /dev/null +++ b/elasticsearch/templates/etc/init.d/elasticsearch.RedHat.erb @@ -0,0 +1,158 @@ +#!/bin/sh +# +# elasticsearch<%= @name %> +# +# chkconfig: 2345 80 20 +# description: Starts and stops a single elasticsearch instance on this system +# + +### BEGIN INIT INFO +# Provides: Elasticsearch-<%= @name %> +# Required-Start: $network $named +# Required-Stop: $network $named +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: This service manages the elasticsearch daemon +# Description: Elasticsearch is a very scalable, schema-free and high-performance search solution supporting multi-tenancy and near realtime search. +### END INIT INFO + +# +# init.d / servicectl compatibility (openSUSE) +# +if [ -f /etc/rc.status ]; then + . /etc/rc.status + rc_reset +fi + +# +# Source function library. +# +if [ -f /etc/rc.d/init.d/functions ]; then + . /etc/rc.d/init.d/functions +fi + +exec="/usr/share/elasticsearch/bin/elasticsearch" +prog="elasticsearch-<%= @name %>" +pidfile=/var/run/elasticsearch/${prog}.pid + +[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog + +export ES_HEAP_SIZE +export ES_HEAP_NEWSIZE +export ES_DIRECT_SIZE +export ES_JAVA_OPTS + +lockfile=/var/lock/subsys/$prog + +# backwards compatibility for old config sysconfig files, pre 0.90.1 +if [ -n $USER ] && [ -z $ES_USER ] ; then + ES_USER=$USER +fi + +checkJava() { + if [ -x "$JAVA_HOME/bin/java" ]; then + JAVA="$JAVA_HOME/bin/java" + else + JAVA=`which java` + fi + + if [ ! -x "$JAVA" ]; then + echo "Could not find any executable java binary. Please install java in your PATH or set JAVA_HOME" + exit 1 + fi +} + +start() { + checkJava + [ -x $exec ] || exit 5 + [ -f $CONF_FILE ] || exit 6 + if [ -n "$MAX_LOCKED_MEMORY" -a -z "$ES_HEAP_SIZE" ]; then + echo "MAX_LOCKED_MEMORY is set - ES_HEAP_SIZE must also be set" + return 7 + fi + if [ -n "$MAX_OPEN_FILES" ]; then + ulimit -n $MAX_OPEN_FILES + fi + if [ -n "$MAX_LOCKED_MEMORY" ]; then + ulimit -l $MAX_LOCKED_MEMORY + fi + if [ -n "$MAX_MAP_COUNT" ]; then + sysctl -q -w vm.max_map_count=$MAX_MAP_COUNT + fi + if [ -n "$WORK_DIR" ]; then + mkdir -p "$WORK_DIR" + chown "$ES_USER":"$ES_GROUP" "$WORK_DIR" + fi + echo -n $"Starting $prog: " + # if not running, start it up here, usually something like "daemon $exec" + daemon --user $ES_USER --pidfile $pidfile $exec -p $pidfile -d -Des.default.path.home=$ES_HOME -Des.default.path.logs=$LOG_DIR -Des.default.path.data=$DATA_DIR -Des.default.path.work=$WORK_DIR -Des.default.path.conf=$CONF_DIR + retval=$? + echo + [ $retval -eq 0 ] && touch $lockfile + return $retval +} + +stop() { + echo -n $"Stopping $prog: " + # stop it here, often "killproc $prog" + killproc -p $pidfile -d 20 $prog + retval=$? + echo + [ $retval -eq 0 ] && rm -f $lockfile + return $retval +} + +restart() { + stop + start +} + +reload() { + restart +} + +force_reload() { + restart +} + +rh_status() { + # run checks to determine if the service is running or use generic status + status -p $pidfile $prog +} + +rh_status_q() { + rh_status >/dev/null 2>&1 +} + + +case "$1" in + start) + rh_status_q && exit 0 + $1 + ;; + stop) + rh_status_q || exit 0 + $1 + ;; + restart) + $1 + ;; + reload) + rh_status_q || exit 7 + $1 + ;; + force-reload) + force_reload + ;; + status) + rh_status + ;; + condrestart|try-restart) + rh_status_q || exit 0 + restart + ;; + *) + echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" + exit 2 +esac +exit $? diff --git a/elasticsearch/templates/etc/sysconfig/defaults.erb b/elasticsearch/templates/etc/sysconfig/defaults.erb new file mode 100644 index 000000000..7dad4e078 --- /dev/null +++ b/elasticsearch/templates/etc/sysconfig/defaults.erb @@ -0,0 +1,3 @@ +<% @new_init_defaults.sort.map do |key, value| -%> +set <%= key %> '<%= value %>' +<% end -%> diff --git a/firewall/CHANGELOG.md b/firewall/CHANGELOG.md index cc85c88c0..c8c3f3ebf 100644 --- a/firewall/CHANGELOG.md +++ b/firewall/CHANGELOG.md @@ -1,26 +1,3 @@ -##2014-11-04 - Supported Release 1.2.0 -###Summary - -This release has a number of new features and bugfixes, including rule inversion, future parser support, improved EL7 support, and the ability to purge ip6tables rules. - -####Features -- Documentation updates! -- Test updates! -- Add ipset support -- Enable rule inversion -- Future parser support -- Improved support for EL7 -- Support netfilter-persistent -- Add support for statistics module -- Add support for mac address source rules -- Add cbt protocol - -####Bugfixes -- Incorrect use of `source => :iptables` in the ip6tables provider was making it impossible to purge ip6tables rules (MODULES-41) -- Don't require `toports` when `jump => 'REDIRECT'` (MODULES-1086) -- Don't limit which chains iniface and outiface parameters can be used in -- Don't fail on rules added with ipsec/strongswan (MODULES-796) - ##2014-07-08 - Supported Release 1.1.3 ###Summary This is a supported release with test coverage enhancements. diff --git a/firewall/lib/puppet/util/firewall.rb b/firewall/lib/puppet/util/firewall.rb index c5a78b859..9982bed83 100644 --- a/firewall/lib/puppet/util/firewall.rb +++ b/firewall/lib/puppet/util/firewall.rb @@ -169,7 +169,7 @@ def persist_iptables(proto) end # RHEL 7 and newer also use systemd to persist iptable rules - if os_key == 'RedHat' && ['RedHat','CentOS','Scientific','SL','SLC','Ascendos','CloudLinux','PSBM','OracleLinux','OVS','OEL','Amazon','XenServer'].include?(Facter.value(:operatingsystem)) && Facter.value(:operatingsystemrelease).to_i >= 7 + if os_key == 'RedHat' && Facter.value(:operatingsystem) == 'RedHat' && Facter.value(:operatingsystemrelease).to_i >= 7 os_key = 'Fedora' end diff --git a/firewall/manifests/linux/redhat.pp b/firewall/manifests/linux/redhat.pp index c808c7e43..b7a4d0e3f 100644 --- a/firewall/manifests/linux/redhat.pp +++ b/firewall/manifests/linux/redhat.pp @@ -22,10 +22,9 @@ # lib/puppet/util/firewall.rb. if ($::operatingsystem != 'Fedora' and versioncmp($::operatingsystemrelease, '7.0') >= 0) or ($::operatingsystem == 'Fedora' and versioncmp($::operatingsystemrelease, '15') >= 0) { - service { "firewalld": - ensure => stopped, - enable => false, - before => Package['iptables-services'] + package { 'firewalld': + ensure => absent, + before => Package['iptables-services'], } package { 'iptables-services': diff --git a/firewall/metadata.json b/firewall/metadata.json index a0ad4523e..eae9ac8e9 100644 --- a/firewall/metadata.json +++ b/firewall/metadata.json @@ -1,12 +1,12 @@ { "name": "puppetlabs-firewall", - "version": "1.2.0", + "version": "1.1.3", "author": "Puppet Labs", "summary": "Manages Firewalls such as iptable", "license": "Apache-2.0", "source": "https://github.com/puppetlabs/puppetlabs-firewall", - "project_page": "http://github.com/puppetlabs/puppetlabs-firewall", - "issues_url": "https://tickets.puppetlabs.com/browse/MODULES", + "project_page": "http://forge.puppetlabs.com/puppetlabs/firewall", + "issues_url": "https://github.com/puppetlabs/puppetlabs-firewall/issues", "operatingsystem_support": [ { "operatingsystem": "RedHat", diff --git a/firewall/spec/acceptance/purge_spec.rb b/firewall/spec/acceptance/purge_spec.rb index 73582b878..e25548933 100644 --- a/firewall/spec/acceptance/purge_spec.rb +++ b/firewall/spec/acceptance/purge_spec.rb @@ -130,7 +130,7 @@ class { 'firewall': } expect(shell('iptables-save').stdout).to match(/-A INPUT -s 1\.2\.1\.1(\/32)? -p tcp\s?\n-A INPUT -s 1\.2\.1\.1(\/32)? -p udp/) end end - context 'ipv6 chain purge', :unless => (fact('osfamily') == 'RedHat' and fact('operatingsystemmajrelease') == '5') do + context('ipv6 chain purge') do after(:all) do ip6tables_flush_all_tables end @@ -166,10 +166,9 @@ class { 'firewall': } purge => true, } firewall { '010 output-1::50': - chain => 'OUTPUT', - proto => 'all', - source => '1::50', - provider => 'ip6tables', + chain => 'OUTPUT', + proto => 'all', + source => '1::50', } EOS diff --git a/firewall/spec/unit/classes/firewall_linux_redhat_spec.rb b/firewall/spec/unit/classes/firewall_linux_redhat_spec.rb index 9ffab4444..43263fa67 100644 --- a/firewall/spec/unit/classes/firewall_linux_redhat_spec.rb +++ b/firewall/spec/unit/classes/firewall_linux_redhat_spec.rb @@ -12,7 +12,7 @@ :operatingsystemrelease => osrel }} - it { should_not contain_service('firewalld') } + it { should_not contain_package('firewalld') } it { should_not contain_package('iptables-services') } end end @@ -24,9 +24,8 @@ :operatingsystemrelease => osrel }} - it { should contain_service('firewalld').with( - :ensure => 'stopped', - :enable => false, + it { should contain_package('firewalld').with( + :ensure => 'absent', :before => 'Package[iptables-services]' )} diff --git a/firewall/spec/unit/puppet/util/firewall_spec.rb b/firewall/spec/unit/puppet/util/firewall_spec.rb index 4d6f92c66..e5864879c 100644 --- a/firewall/spec/unit/puppet/util/firewall_spec.rb +++ b/firewall/spec/unit/puppet/util/firewall_spec.rb @@ -143,22 +143,13 @@ subject.persist_iptables(proto) end - it 'should exec for CentOS 6 identified from operatingsystem and operatingsystemrelease' do + it 'should exec for CentOS identified from operatingsystem' do allow(Facter.fact(:osfamily)).to receive(:value).and_return(nil) allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('CentOS') - allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return('6.5') expect(subject).to receive(:execute).with(%w{/sbin/service iptables save}) subject.persist_iptables(proto) end - it 'should exec for CentOS 7 identified from operatingsystem and operatingsystemrelease' do - allow(Facter.fact(:osfamily)).to receive(:value).and_return(nil) - allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('CentOS') - allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return('7.0.1406') - expect(subject).to receive(:execute).with(%w{/usr/libexec/iptables/iptables.init save}) - subject.persist_iptables(proto) - end - it 'should exec for Archlinux identified from osfamily' do allow(Facter.fact(:osfamily)).to receive(:value).and_return('Archlinux') expect(subject).to receive(:execute).with(['/bin/sh', '-c', '/usr/sbin/iptables-save > /etc/iptables/iptables.rules']) diff --git a/fluentd/.fixtures.yml b/fluentd/.fixtures.yml new file mode 100644 index 000000000..779d63a00 --- /dev/null +++ b/fluentd/.fixtures.yml @@ -0,0 +1,8 @@ +fixtures: + repositories: + apt: "https://github.com/puppetlabs/puppetlabs-apt.git" + concat: "https://github.com/puppetlabs/puppetlabs-concat.git" + stdlib: "https://github.com/puppetlabs/puppetlabs-stdlib.git" + symlinks: + "fluentd": "#{source_dir}" + diff --git a/fluentd/.gemfile b/fluentd/.gemfile new file mode 100644 index 000000000..e7ea9dbb9 --- /dev/null +++ b/fluentd/.gemfile @@ -0,0 +1,16 @@ +source 'https://rubygems.org' + +group :development, :test do + gem 'rake', :require => false + gem 'rspec-puppet', :require => false + gem 'puppetlabs_spec_helper', :require => false + gem 'rspec-system', :require => false + gem 'rspec-system-puppet', :require => false + gem 'rspec-system-serverspec', :require => false +end + +if puppetversion = ENV['PUPPET_GEM_VERSION'] + gem 'puppet', puppetversion, :require => false +else + gem 'puppet', :require => false +end diff --git a/fluentd/.gitignore b/fluentd/.gitignore new file mode 100644 index 000000000..e7beff00f --- /dev/null +++ b/fluentd/.gitignore @@ -0,0 +1,7 @@ +pkg/ +Gemfile.lock +vendor/ +spec/fixtures/modules/ +.vagrant/ +.bundle/ +coverage/ diff --git a/fluentd/.travis.yml b/fluentd/.travis.yml new file mode 100644 index 000000000..6b43b295a --- /dev/null +++ b/fluentd/.travis.yml @@ -0,0 +1,28 @@ +--- +branches: + only: + - master +language: ruby +bundler_args: --without development +script: bundle exec rake spec SPEC_OPTS='--format documentation' +rvm: + - 1.9.3 + - 2.0.0 +gemfile: .gemfile +env: + matrix: + - PUPPET_GEM_VERSION="~> 2.7.0" + - PUPPET_GEM_VERSION="~> 3.1.0" + - PUPPET_GEM_VERSION="~> 3.2.0" + - PUPPET_GEM_VERSION="~> 3.3.0" + - PUPPET_GEM_VERSION="~> 3.4.0" +matrix: + exclude: + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 2.7.0" + gemfile: .gemfile + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 3.1.0" + gemfile: .gemfile +notifications: + email: gianni.carafa@srf.ch diff --git a/fluentd/CHANGELOG b/fluentd/CHANGELOG new file mode 100644 index 000000000..c1e5eb502 --- /dev/null +++ b/fluentd/CHANGELOG @@ -0,0 +1,23 @@ +## puppet-fluentd changelog + +Release notes for the srfmmz/puppet-fluentd module. + +------------------------------------------ + +#### 0.1.4 - 2014/07/21 + +* added filter functionality +* restart td-agent daemon after config change +* configfile syntax fix for td-agent v2 + +#### 0.1.3 - 2014/07/06 + +Please update to get the new repository of Treasuredata. + +Detailed Changes: + +* FEATURE : added time_format for source configuration +* CHANGE : Added new treasuredata repositories. + - for more info see here : https://groups.google.com/forum/#!topic/fluentd/46FnWIoLTrU +* CHANGE : Restart td-agent when config changed +* FEATURE : Startetd this changelog \ No newline at end of file diff --git a/fluentd/Gemfile b/fluentd/Gemfile new file mode 100644 index 000000000..e30fed0ea --- /dev/null +++ b/fluentd/Gemfile @@ -0,0 +1,21 @@ +# Gemfile to run bundler command +# usagen: bundler exec rake spec + +source 'https://rubygems.org' + +puppetversion = ENV.key?('PUPPET_VERSION') ? "~> #{ENV['PUPPET_VERSION']}" : ['>= 3.2.1'] +gem 'puppet', puppetversion + +if puppetversion =~ /^3/ + ## rspec-hiera-puppet is puppet 3 only + gem 'rspec-hiera-puppet', '>=1.0.0' +end + +facterversion = ENV.key?('FACTER_VERSION') ? "~> #{ENV['FACTER_VERSION']}" : ['>= 1.7.1'] + +gem 'facter', facterversion + +gem 'rake' +gem 'rspec' +gem 'rspec-puppet', '>=0.1.6' +gem 'puppetlabs_spec_helper', '>=0.4.1' diff --git a/fluentd/LICENSE b/fluentd/LICENSE new file mode 100644 index 000000000..e06d20818 --- /dev/null +++ b/fluentd/LICENSE @@ -0,0 +1,202 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. + diff --git a/fluentd/Modulefile b/fluentd/Modulefile new file mode 100644 index 000000000..520e5e410 --- /dev/null +++ b/fluentd/Modulefile @@ -0,0 +1,13 @@ +name 'srf-fluentd' +version '0.1.4' + +dependency 'puppetlabs/apt', '>= 1.4.0' +dependency 'puppetlabs/concat', '>= 1.0.0' +dependency 'puppetlabs/stdlib', '>= 4.1.0' + +summary 'fluentd module' +description 'Manages Fluentd installation and configuration with Puppet using the td-agent.' +source 'https://github.com/mmz-srf/puppet-fluentd.git' +project_page 'https://github.com/mmz-srf/puppet-fluentd' +author 'Gianni Carafa' +license 'Apache License, Version 2.0' diff --git a/fluentd/README.md b/fluentd/README.md new file mode 100644 index 000000000..cf06b48da --- /dev/null +++ b/fluentd/README.md @@ -0,0 +1,178 @@ +puppet-fluentd +============== + +[![Build Status](https://travis-ci.org/mmz-srf/puppet-fluentd.png?branch=master)](https://travis-ci.org/mmz-srf/puppet-fluentd) + +Manage Fluentd installation, configuration and Plugin-management with Puppet using the td-agent. + +## Supported Operating Systems +- Debian (tested on Debian 7.5) +- Ubuntu +- Redhat +- CentOS (tested on CentOS 6.4) + +## Used Modules +- apt: "https://github.com/puppetlabs/puppetlabs-apt.git" (Only for Debian) +- concat: "https://github.com/puppetlabs/puppetlabs-concat.git" +- stdlib: "https://github.com/puppetlabs/puppetlabs-stdlib.git" + +## Contributing +* Fork it +* Create a feature branch (`git checkout -b my-new-feature`) +* Run rspec tests (`bundle exec rake spec` or `rake spec`) +* Commit your changes (`git commit -am 'Added some feature'`) +* Push to the branch (`git push origin my-new-feature`) +* Create new Pull Request + +### Todo's +- Ouput copy and roundrobin to multiple stores +- ~~No RedHat suport yet~~ (THX to [pranav](https://github.com/pranav) ) +- ~~Automatic installation of td-agent Plugins~~ (THX to [darktim](https://github.com/darktim) ) +- ~~Monitor/Restart Service~~ (THX to [darktim](https://github.com/darktim) ) +- ~~Logrotate td-agent logs~~ (Wont Fix. td-agent handels it now by it self) + + +## Configuration +How to configure a Agent to send data to a centralised Fluentd-Server + +### Install a Plugin +Install your fluentd plugin. (check here for the right pluginname : http://fluentd.org/plugin/ ). + +You can choose from a file or gem based instalation. +``` + include ::fluentd + + fluentd::install_plugin { 'elasticsearch': + plugin_type => 'gem', + plugin_name => 'fluent-plugin-elasticsearch', + } +``` + +### Create a Agent +The Agent watches over your logfiles and sends its content to the Collector. +``` + include ::fluentd + + fluentd::configfile { 'apache': } + fluentd::source { 'apache_main': + configfile => 'apache' + type => 'tail', + format => 'apache2', + tag => 'apache.access_log', + config => { + 'path' => '/var/log/apache2/access.log', + 'pos_file' => '/var/tmp/fluentd.pos', + }, + notify => Class['fluentd::service'] + } + + fluentd::configfile { 'syslog': } + fluentd::source { 'syslog_main': + configfile => 'syslog', + type => 'tail', + format => 'syslog', + tag => 'system.syslog', + config => { + 'path' => '/var/log/syslog', + 'pos_file' => '/tmp/td-agent.syslog.pos', + }, + notify => Class['fluentd::service'] + } + + fluentd::configfile { 'forward': } + fluentd::match { 'forward_main': + configfile => 'forward' + pattern => '**', + type => 'forward', + servers => [ + {'host' => 'PUT_YOUR_HOST_HERE', 'port' => '24224'} + ], + notify => Class['fluentd::service'] + } +``` +#### creates on the Agent side following files : +``` +/etc/td-agent/ + ├── config.d + │   ├── apache.conf + │   ├── syslog.conf + │   └── forward.conf + ├── ... + ... +``` + +### Create a Collector +The Collector collects all the data from the Agents. He now stores the data in files, Elasticsearch or elsewhere. +``` + include ::fluentd + + fluentd::configfile { 'collector': } + fluentd::source { 'collector_main': + configfile => 'collector', + type => 'forward', + notify => Class['fluentd::service'] + } + + fluentd::match { 'collector_main': + configfile => 'collector', + pattern => '**', + type => 'elasticsearch', + config => { + 'logstash_format' => true, + }, + notify => Class['fluentd::service'] + } + + # receive syslog messages on port 5140 + # all rsyslog daemons on the clients sends theire messages to 5140 + fluentd::configfile { 'rsyslog': } + fluentd::source { 'rsyslog_main': + configfile => 'rsyslog', + type => 'syslog', + tag => 'system.local', + config => { + 'port' => '5140', + 'bind' => '0.0.0.0', + }, + notify => Class['fluentd::service'] + } +``` + +#### creates on the Collectors side following files : +``` +/etc/td-agent/ + ├── config.d + │   └── collector.conf + ├── ... + ... +``` + +### copy ouput to multiple stores +```` + $logger=[ { 'host' => 'logger-sample01', 'port' => '24224'}, + { 'host' => 'logger-example01', 'port' => '24224', 'standby' => ''} ] + + fluentd::match { 'forward_to_logger': + configfile => 'sample_tail', + pattern => 'alocal', + type => 'copy', + config => [ + { + 'type' => 'forward', + 'send_timeout' => '60s', + 'recover_wait' => '10s', + 'heartbeat_interval' => '1s', + 'phi_threshold' => 8, + 'hard_timeout' => '60s', + 'flush_interval' => '5s', + 'servers' => $logger, + }, + { + 'type' => 'stdout', + 'output_type' => 'json', + } + ], + notify => Class['fluentd::service'] + } + +``` diff --git a/fluentd/Rakefile b/fluentd/Rakefile new file mode 100644 index 000000000..bbfcc8b9c --- /dev/null +++ b/fluentd/Rakefile @@ -0,0 +1,2 @@ +require 'rubygems' +require 'puppetlabs_spec_helper/rake_tasks' \ No newline at end of file diff --git a/fluentd/files/packages.treasure-data.com.key b/fluentd/files/packages.treasure-data.com.key new file mode 100644 index 000000000..c86c35489 --- /dev/null +++ b/fluentd/files/packages.treasure-data.com.key @@ -0,0 +1,18 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.5 (GNU/Linux) + +mQGiBFKuy6MRBACJccrkWHfYSKSbchD4ywYLGkIKITSLzEXrlpy4Sp6Mr9G1OGIv +buIUIxksnev6h6wlFgbFYTLngpod+Jn4DpcGgXqzZ3lhOY1fvAaQ4SJ4RUELm1JJ +SEWE0uYHuoI4+TrLtod3U02ETD+Lf6LWO3IfU1AYsA467tDYpVXR0GHeAwCgqU/V +M+ypNOYFHVs1/aX83wdg0K8D+wf+tcvTHb/i2lND5DVfTWtC54WYNIG6QO8DgGdu +EYregcMKRRNM4nSfUISzCgrjnpLGjvB9NKONCrimDiuz4TH7KDcEsKGA+zTzJLCy +mcE6tKEHV5yS7cK0inim4nxAKFB70306CkhHyL2TrB42ppNFJwG1igO4BJ+55b9X +Dd03A/9o6ONFle6khG+UE82WYTayy2pAshKTSLnOlB7CIxwt3SrzFXTuNosajVgZ +S0EIB8c6SjFN6P1jHjd7PYOmBxd2xptERTg8CJMoTUI+P7LypWuvmagIdEwe571d +ZMQ26HNuHg7/IMg+FQxhl/DFU6Aq+2CBJ3q8gO8Us4/UYc1PhrRUVHJlYXN1cmUg +RGF0YSwgSW5jIChUcmVhc3VyZSBBZ2VudCBPZmZpY2lhbCBTaWduaW5nIGtleSkg +PHN1cHBvcnRAdHJlYXN1cmUtZGF0YS5jb20+iGAEExECACAFAlKuy6MCGwMGCwkI +BwMCBBUCCAMEFgIDAQIeAQIXgAAKCRAQk9tFoS4gb3HgAKCBCDNT/cWzkKRQxvnL +LiGvPetVSACeLLKnt2twfiPc9ZEA/X8MKkPjWqc= +=e5Fe +-----END PGP PUBLIC KEY BLOCK----- diff --git a/fluentd/manifests/config.pp b/fluentd/manifests/config.pp new file mode 100644 index 000000000..f9d62526c --- /dev/null +++ b/fluentd/manifests/config.pp @@ -0,0 +1,19 @@ +# Class: fluentd::config() +# +# +class fluentd::config() { + file { '/etc/td-agent/td-agent.conf' : + ensure => file, + owner => 'root', + group => 'root', + content => template('fluentd/td-agent.conf.erb'), + notify => Class['fluentd::service'], + } + + file {'/etc/td-agent/config.d': + ensure => 'directory', + owner => 'td-agent', + group => 'td-agent', + mode => '0750', + } +} \ No newline at end of file diff --git a/fluentd/manifests/configfile.pp b/fluentd/manifests/configfile.pp new file mode 100644 index 000000000..509cf4eef --- /dev/null +++ b/fluentd/manifests/configfile.pp @@ -0,0 +1,15 @@ +# == definition fluentd::configfile +define fluentd::configfile { + $source_conf = "/etc/td-agent/config.d/${title}.conf" + if ! defined(Class['fluentd']) { + fail('You must include the fluentd base class before using any fluentd defined resources') + } + concat{$source_conf: + owner => 'td-agent', + group => 'td-agent', + mode => '0644', + require => Class['Fluentd::Packages'], + notify => Class['Fluentd::Service'], + } +} + diff --git a/fluentd/manifests/filter.pp b/fluentd/manifests/filter.pp new file mode 100644 index 000000000..1d55517d7 --- /dev/null +++ b/fluentd/manifests/filter.pp @@ -0,0 +1,35 @@ +# == definition fluentd::filter +define fluentd::filter ( + $configfile, + $pattern, + $type = 'grep', + $input_key = '', + $regexp = '', + $exclude = '', + $config = {}, + $output_tag = '', + $add_tag_prefix = '', + $remove_tag_prefix = '', + $add_tag_suffix = '', + $remove_tag_suffix = '', +) { + + if ($type == 'grep') { + if !defined(Fluentd::Install_plugin['fluent-plugin-grep']) { + fluentd::install_plugin{'fluent-plugin-grep': + plugin_type => 'gem' + } + } + } + + if ((($regexp != '') or ($exclude != '')) and ($input_key == '')) or (($input_key != '') and ($regexp == '') and ($exclude == '')) { + fail ('regexp, exlude and input_key must be used in conjuction') + } + + concat::fragment { 'filter': + target => "/etc/td-agent/config.d/${configfile}.conf", + require => Class['Fluentd::Packages'], + content => template('fluentd/filter.erb'), + } + +} diff --git a/fluentd/manifests/forest_match.pp b/fluentd/manifests/forest_match.pp new file mode 100644 index 000000000..09ca9a996 --- /dev/null +++ b/fluentd/manifests/forest_match.pp @@ -0,0 +1,15 @@ +# == definition fluentd::forest_match +define fluentd::forest_match ( + $configfile, + $type, + $pattern, + $config = {}, + $servers = [], +) { + + concat::fragment { "match_${title}": + target => "/etc/td-agent/config.d/${configfile}.conf", + require => Package["${fluentd::package_name}"], + content => template('fluentd/forest_match.erb'), + } +} diff --git a/fluentd/manifests/init.pp b/fluentd/manifests/init.pp new file mode 100644 index 000000000..f0e095634 --- /dev/null +++ b/fluentd/manifests/init.pp @@ -0,0 +1,17 @@ +# == class fluentd +class fluentd ( + $version = '1', + $package_name = $fluentd::params::package_name, + $install_repo = $fluentd::params::install_repo, + $package_ensure = $fluentd::params::package_ensure, + $service_enable = $fluentd::params::service_enable, + $service_ensure = $fluentd::params::service_ensure +) inherits fluentd::params { + class{'fluentd::packages': } + class{'fluentd::config': } + class{'fluentd::service': } + + validate_bool($install_repo, $service_enable) + + Class['Fluentd::Packages'] -> Class['Fluentd::Config'] -> Class['Fluentd::Service'] +} diff --git a/fluentd/manifests/install_plugin.pp b/fluentd/manifests/install_plugin.pp new file mode 100644 index 000000000..a395b7fde --- /dev/null +++ b/fluentd/manifests/install_plugin.pp @@ -0,0 +1,40 @@ +# == fluentd::install_plugin +# +# install a plugin with either /usr/lib/fluent/ruby/bin/fluent-gem or a file +# +# you need to sepecify wich one of theese options plus the name. +# +# Parameters: +# the name of this ressource reflects the either the filename of the plugin, which must +# be copied to fluentd/files or the name of the gem +# +# plugin_type: specify "file" to copy that file to /etc/fluentd/plugins +# specify "gem" to try to install the plugin with fluent-gem +# +# ensure: "present"(default) or "absent", install or uninstall a plugin +# +define fluentd::install_plugin ( + $plugin_type, + $ensure = 'present', + $plugin_name = $name, +) { + case $plugin_type { + 'file': { + fluentd::install_plugin::file { + [$plugin_name]: + ensure => $ensure, + require => Class['Fluentd::Packages'] + } + } + 'gem': { + fluentd::install_plugin::gem { + [$plugin_name]: + ensure => $ensure, + require => Class['Fluentd::Packages'] + } + } + default: { + fail("plugin_type => ${plugin_type} is currently not supportet by this module") + } + } +} diff --git a/fluentd/manifests/install_plugin/file.pp b/fluentd/manifests/install_plugin/file.pp new file mode 100644 index 000000000..47d3ab155 --- /dev/null +++ b/fluentd/manifests/install_plugin/file.pp @@ -0,0 +1,24 @@ +# == fluentd::install_plugin::file +# +# install a plugin with by copying a file to /etc/td-agent/plugins +# +# Parameters: +# the name of this ressource reflects the filename of the plugin, which must +# be copied to fluentd/files +# +# ensure: "present"(default) or "absent", install or uninstall a plugin +# +define fluentd::install_plugin::file ( + $ensure = 'present', + $plugin_name = $name, +) { + file { + "/etc/td-agent/plugin/${plugin_name}": + ensure => $ensure, + owner => td-agent, + group => td-agent, + mode => '0640', + source => "puppet:///fluentd/plugins/${plugin_name}", + notify => Service["${::fluentd::service_name}"]; + } +} diff --git a/fluentd/manifests/install_plugin/gem.pp b/fluentd/manifests/install_plugin/gem.pp new file mode 100644 index 000000000..bd8e14508 --- /dev/null +++ b/fluentd/manifests/install_plugin/gem.pp @@ -0,0 +1,21 @@ +# == fluentd::install_plugin::gem +# +# install a plugin with /usr/lib/fluent/ruby/bin/fluent-gem +# +# Parameters: +# the name of this ressource reflects the name of the gem +# +# ensure: "present"(default) or "absent", install or uninstall a plugin +# +define fluentd::install_plugin::gem ( + $ensure = 'latest', + $plugin_name = $name, +) { + + package { $plugin_name: + ensure => $ensure, + provider => 'fluentgem', + notify => Service["${fluentd::service_name}"]; + } + +} diff --git a/fluentd/manifests/install_repo/apt.pp b/fluentd/manifests/install_repo/apt.pp new file mode 100644 index 000000000..e2eadbfc1 --- /dev/null +++ b/fluentd/manifests/install_repo/apt.pp @@ -0,0 +1,24 @@ +##apt.pp + +# Class: fluentd::install_repo::apt () +# +# +class fluentd::install_repo::apt () { + + apt::source { 'treasure-data': + location => "http://packages.treasuredata.com/debian", + release => "lucid", + repos => "contrib", + include_src => false, + } + + file { '/tmp/packages.treasure-data.com.key': + ensure => file, + source => 'puppet:///modules/fluentd/packages.treasure-data.com.key' + }-> + exec { "import gpg key Treasure Data": + command => "/bin/cat /tmp/packages.treasure-data.com.key | apt-key add -", + unless => "/usr/bin/apt-key list | grep -q 'Treasure Data'", + notify => Class['::apt::update'], + } +} diff --git a/fluentd/manifests/install_repo/yum.pp b/fluentd/manifests/install_repo/yum.pp new file mode 100644 index 000000000..790781465 --- /dev/null +++ b/fluentd/manifests/install_repo/yum.pp @@ -0,0 +1,19 @@ +# yum.pp + +# Class: fluentd::install_repo::yum () +# +# +class fluentd::install_repo::yum ( + $key = $fluentd::params::yum_key_url, + ) { + + # Sorry for the different naming of the Repository between debian and redhat. + # But I dont want rename it to avoid a duplication. + yumrepo { 'treasuredata': + descr => 'Treasure Data', + baseurl => 'http://packages.treasuredata.com/redhat/$basearch', + gpgkey => 'http://packages.treasuredata.com/redhat/RPM-GPG-KEY-td-agent', + gpgcheck => 1, + } + +} diff --git a/fluentd/manifests/match.pp b/fluentd/manifests/match.pp new file mode 100644 index 000000000..61c4e624f --- /dev/null +++ b/fluentd/manifests/match.pp @@ -0,0 +1,15 @@ +# == definition fluentd::match +define fluentd::match ( + $configfile, + $type, + $pattern, + $config = {}, + $servers = [], +) { + + concat::fragment { "match_${title}": + target => "/etc/td-agent/config.d/${configfile}.conf", + require => Package["${fluentd::package_name}"], + content => template('fluentd/match.erb'), + } +} diff --git a/fluentd/manifests/packages.pp b/fluentd/manifests/packages.pp new file mode 100644 index 000000000..e48793579 --- /dev/null +++ b/fluentd/manifests/packages.pp @@ -0,0 +1,50 @@ +# == class fluentd::packages +class fluentd::packages ( + $package_name = $fluentd::package_name, + $install_repo = $fluentd::install_repo, + $package_ensure = $fluentd::package_ensure +){ + if $install_repo { + case $::osfamily { + 'redhat': { + class{'fluentd::install_repo::yum': + before => Package[$package_name], + } + } + 'debian': { + class{'fluentd::install_repo::apt': + before => Package[$package_name], + } + } + default: { + fail("Unsupported osfamily ${::osfamily}") + } + } + } + package { "$package_name": + ensure => $package_ensure + } + +# extra bits... why this is required isn't quite clear. + case $::osfamily { + 'debian': { + package{[ + 'libxslt1.1', + 'libyaml-0-2', + ]: + before => Package[$package_name], + ensure => $package_ensure + } + exec {'add user td-agent to group adm': + provider => shell, + unless => '/bin/grep -q "adm\S*td-agent" /etc/group', + command => '/usr/sbin/usermod -aG adm td-agent', + subscribe => Package[$package_name], + } + } + default: { + info("No required fluentd::packages extra bits for ${::osfamily}") + } + } + +} diff --git a/fluentd/manifests/params.pp b/fluentd/manifests/params.pp new file mode 100644 index 000000000..aed3b9a31 --- /dev/null +++ b/fluentd/manifests/params.pp @@ -0,0 +1,12 @@ +# Class: fluentd::params +# +# +class fluentd::params { + $package_name = 'td-agent' + $package_ensure = 'installed' + $install_repo = true + $service_ensure = 'running' + $service_enable = true + $service_name = 'td-agent' + $yum_key_url = "http://packages.treasure-data.com/redhat/RPM-GPG-KEY-td-agent" +} \ No newline at end of file diff --git a/fluentd/manifests/service.pp b/fluentd/manifests/service.pp new file mode 100644 index 000000000..a539f0978 --- /dev/null +++ b/fluentd/manifests/service.pp @@ -0,0 +1,12 @@ +# == class fluentd::service +class fluentd::service ( + $service_ensure = $fluentd::service_ensure, + $service_enable = $fluentd::service_enable, +) { + include fluentd::params + service {"${fluentd::params::service_name}": + ensure => $service_ensure, + enable => $service_enable, + hasstatus => true + } +} diff --git a/fluentd/manifests/source.pp b/fluentd/manifests/source.pp new file mode 100644 index 000000000..cff1b3560 --- /dev/null +++ b/fluentd/manifests/source.pp @@ -0,0 +1,16 @@ +# == definition fluentd::source +define fluentd::source ( + $configfile, + $type, + $tag = false, + $format = false, + $time_format = false, + $config = {}, +) { + + concat::fragment { "source_${title}": + target => "/etc/td-agent/config.d/${configfile}.conf", + require => Package["${fluentd::package_name}"], + content => template('fluentd/source.erb'), + } +} diff --git a/fluentd/spec/classes/fluentd_config_spec.rb b/fluentd/spec/classes/fluentd_config_spec.rb new file mode 100644 index 000000000..cb744b569 --- /dev/null +++ b/fluentd/spec/classes/fluentd_config_spec.rb @@ -0,0 +1,58 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +describe 'fluentd::config', :type => :class do + context "On a Debian OS, config files should be written" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '7', + :concat_basedir => '/tmp', + :lsbdistid => 'Debian', + } + end + + it "/etc/td-agent/td-agent.conf should be in place" do + should contain_file("/etc/td-agent/td-agent.conf").with( + 'ensure' => 'file', + 'owner' => 'root', + 'group' => 'root', + 'notify' => 'Class[Fluentd::Service]' + ).with_content(/^# Include.*config.d.*/m) + end + it "/etc/td-agent/config.d is created" do + should contain_file("/etc/td-agent/config.d").with( + 'ensure' => 'directory', + 'owner' => 'td-agent', + 'group' => 'td-agent', + 'mode' => '0750' + ) + end + end + context "On a Redhat OS, config files should be written" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6.5', + :concat_basedir => '/tmp', + :lsbdistid => 'CentOS', + } + end + it "/etc/td-agent/td-agent.conf should be in place" do + should contain_file("/etc/td-agent/td-agent.conf").with( + 'ensure' => 'file', + 'owner' => 'root', + 'group' => 'root', + 'notify' => 'Class[Fluentd::Service]' + ).with_content(/^# Include.*config.d.*/m) + end + it "/etc/td-agent/config.d is created" do + should contain_file("/etc/td-agent/config.d").with( + 'ensure' => 'directory', + 'owner' => 'td-agent', + 'group' => 'td-agent', + 'mode' => '0750' + ) + end + end +end diff --git a/fluentd/spec/classes/fluentd_packages_spec.rb b/fluentd/spec/classes/fluentd_packages_spec.rb new file mode 100644 index 000000000..bad912abb --- /dev/null +++ b/fluentd/spec/classes/fluentd_packages_spec.rb @@ -0,0 +1,70 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +describe 'fluentd::packages', :type => :class do + let (:params) {{:package_name => 'td-agent', :package_ensure => 'installed'}} + context "On a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '7', + :lsbdistid => 'Debian', + } + end + + context "with install_repo=>true" do + let(:params) { {:install_repo => true} } + it do + should contain_apt__source("treasure-data").with( + 'location' => 'http://packages.treasuredata.com/debian' + ) + end + end + it { should contain_package("libxslt1.1").with( + 'ensure' => 'installed' + ) + } + it { should contain_package("libyaml-0-2").with( + 'ensure' => 'installed' + ) + } + it { should contain_package("td-agent").with( + 'ensure' => 'installed' + ) + } + end + + context "On a RedHat/Centos OS" do + + let :params do + { + :package_name => 'td-agent', + :package_ensure => 'running', + } + end + + let :facts do + { + :osfamily => 'Redhat', + } + end + + it { should contain_class('fluentd::packages')} + + context "with install_repo=>true" do + let(:params) { {:install_repo => true} } + it do + should contain_yumrepo('treasuredata').with( + 'baseurl' => 'http://packages.treasuredata.com/redhat/$basearch', + 'gpgkey' => 'http://packages.treasuredata.com/redhat/RPM-GPG-KEY-td-agent', + 'gpgcheck' => 1 + ) + end + end + + it { should contain_package("td-agent").with( + 'ensure' => 'running' + ) + } + end +end diff --git a/fluentd/spec/classes/fluentd_service_spec.rb b/fluentd/spec/classes/fluentd_service_spec.rb new file mode 100644 index 000000000..c9ec2bbaf --- /dev/null +++ b/fluentd/spec/classes/fluentd_service_spec.rb @@ -0,0 +1,79 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +describe 'fluentd::service', :type => :class do + context "On a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '7', + :concat_basedir => '/tmp', + :lsbdistid => 'Debian', + } + end + context "td-agent running and enabled" do + let (:params) {{:service_ensure => 'running', :service_enable => true}} + it { should contain_service("td-agent").with( + 'ensure' => 'running', + 'enable' => 'true', + 'hasstatus' => 'true' + ) + } + end + context "td-agent stopped but enabled" do + let (:params) {{:service_ensure => 'stopped', :service_enable => true}} + it { should contain_service("td-agent").with( + 'ensure' => 'stopped', + 'enable' => 'true', + 'hasstatus' => 'true' + ) + } + end + context "td-agent stopped and disabled" do + let (:params) {{:service_ensure => 'stopped', :service_enable => false}} + it { should contain_service("td-agent").with( + 'ensure' => 'stopped', + 'enable' => 'false', + 'hasstatus' => 'true' + ) + } + end + end + context "On a Redhat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6.5', + :concat_basedir => '/tmp', + :lsbdistid => 'CentOs', + } + end + context "td-agent running and enabled" do + let (:params) {{:service_ensure => 'running', :service_enable => true}} + it { should contain_service("td-agent").with( + 'ensure' => 'running', + 'enable' => 'true', + 'hasstatus' => 'true' + ) + } + end + context "td-agent stopped but enabled" do + let (:params) {{:service_ensure => 'stopped', :service_enable => true}} + it { should contain_service("td-agent").with( + 'ensure' => 'stopped', + 'enable' => 'true', + 'hasstatus' => 'true' + ) + } + end + context "td-agent stopped and disabled" do + let (:params) {{:service_ensure => 'stopped', :service_enable => false}} + it { should contain_service("td-agent").with( + 'ensure' => 'stopped', + 'enable' => 'false', + 'hasstatus' => 'true' + ) + } + end + end +end diff --git a/fluentd/spec/classes/fluentd_spec.rb b/fluentd/spec/classes/fluentd_spec.rb new file mode 100644 index 000000000..0d5c7cb4d --- /dev/null +++ b/fluentd/spec/classes/fluentd_spec.rb @@ -0,0 +1,233 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +describe 'fluentd', :type => :class do + context "On a Debian OS " do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '7', + :concat_basedir => '/tmp', + :lsbdistid => 'Debian', + } + end + context "Debian with repo installed" do + let (:params) {{ + :package_name => 'td-agent', + :package_ensure => 'installed', + :install_repo => true, + :service_enable => true, + :service_ensure => 'running' + }} + it "has fluentd::packages" do + should contain_class('fluentd::packages') + end + it "has fluentd::config class" do + should contain_class('fluentd::config') + end + it "has fluentd::service class" do + should contain_class('fluentd::service') + end + it "the apt repo file exists" do + should contain_apt__source("treasure-data").with( + 'location' => 'http://packages.treasuredata.com/debian' + ) + end + it "/etc/td-agent/td-agent.conf should be in place" do + should contain_file("/etc/td-agent/td-agent.conf").with( + 'ensure' => 'file', + 'owner' => 'root', + 'group' => 'root', + 'notify' => 'Class[Fluentd::Service]' + ).with_content(/^# Include.*config.d.*/m) + end + it "/etc/td-agent/config.d is created" do + should contain_file("/etc/td-agent/config.d").with( + 'ensure' => 'directory', + 'owner' => 'td-agent', + 'group' => 'td-agent', + 'mode' => '0750' + ) + end + it "td-agent package should be installed" do + should contain_package("td-agent").with( + 'ensure' => 'installed' + ) + end + it "td-agent is running" do + should contain_service("td-agent").with( + 'ensure' => 'running', + 'enable' => 'true', + 'hasstatus' => 'true' + ) + end + end + context "Debian without repo installed" do + let (:params) {{ + :package_name => 'td-agent', + :package_ensure => 'installed', + :install_repo => false, + :service_enable => true, + :service_ensure => 'running' + }} + it "has fluentd::packages" do + should contain_class('fluentd::packages') + end + it "has fluentd::config class" do + should contain_class('fluentd::config') + end + it "has fluentd::service class" do + should contain_class('fluentd::service') + end + it "the apt repo file DOES NOT exists" do + should_not contain_apt__source("treasure-data").with( + 'location' => 'http://packages.treasure-data.com/debian' + ) + end + it "/etc/td-agent/td-agent.conf should be in place" do + should contain_file("/etc/td-agent/td-agent.conf").with( + 'ensure' => 'file', + 'owner' => 'root', + 'group' => 'root', + 'notify' => 'Class[Fluentd::Service]' + ).with_content(/^# Include.*config.d.*/m) + end + it "/etc/td-agent/config.d is created" do + should contain_file("/etc/td-agent/config.d").with( + 'ensure' => 'directory', + 'owner' => 'td-agent', + 'group' => 'td-agent', + 'mode' => '0750' + ) + end + it "td-agent package should be installed" do + should contain_package("td-agent").with( + 'ensure' => 'installed' + ) + end + it "td-agent is running" do + should contain_service("td-agent").with( + 'ensure' => 'running', + 'enable' => 'true', + 'hasstatus' => 'true' + ) + end + end + end + context "On a Redhat-like OS with repo installed" do + let (:params) {{ + :package_name => 'td-agent', + :package_ensure => 'installed', + :install_repo => true, + :service_enable => true, + :service_ensure => 'running' + }} + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6.5', + :concat_basedir => '/tmp', + :lsbdistid => 'CentOs', + } + end + + it "has fluentd::packages" do + should contain_class('fluentd::packages') + end + it "has fluentd::config class" do + should contain_class('fluentd::config') + end + it "has fluentd::service class" do + should contain_class('fluentd::service') + end + it "the yum repo file exists" do + should contain_yumrepo('treasuredata') + end + it "/etc/td-agent/td-agent.conf should be in place" do + should contain_file("/etc/td-agent/td-agent.conf").with( + 'ensure' => 'file', + 'owner' => 'root', + 'group' => 'root', + 'notify' => 'Class[Fluentd::Service]' + ).with_content(/^# Include.*config.d.*/m) + end + it "/etc/td-agent/config.d is created" do + should contain_file("/etc/td-agent/config.d").with( + 'ensure' => 'directory', + 'owner' => 'td-agent', + 'group' => 'td-agent', + 'mode' => '0750' + ) + end + it "td-agent package should be installed" do + should contain_package("td-agent").with( + 'ensure' => 'installed' + ) + end + it "td-agent is running" do + should contain_service("td-agent").with( + 'ensure' => 'running', + 'enable' => 'true', + 'hasstatus' => 'true' + ) + end + end + context "On a Redhat-like OS without repo installed" do + let (:params) {{ + :package_name => 'td-agent', + :package_ensure => 'installed', + :install_repo => false, + :service_enable => true, + :service_ensure => 'running' + }} + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6.5', + :concat_basedir => '/tmp', + :lsbdistid => 'CentOs', + } + end + + it "has fluentd::packages" do + should contain_class('fluentd::packages') + end + it "has fluentd::config class" do + should contain_class('fluentd::config') + end + it "has fluentd::service class" do + should contain_class('fluentd::service') + end + it "the yum repo file DOES NOT exists" do + should_not contain_file('/etc/yum.repos.d/td.repo').with_content(/^name=TreasureData$/) + end + it "/etc/td-agent/td-agent.conf should be in place" do + should contain_file("/etc/td-agent/td-agent.conf").with( + 'ensure' => 'file', + 'owner' => 'root', + 'group' => 'root', + 'notify' => 'Class[Fluentd::Service]' + ).with_content(/^# Include.*config.d.*/m) + end + it "/etc/td-agent/config.d is created" do + should contain_file("/etc/td-agent/config.d").with( + 'ensure' => 'directory', + 'owner' => 'td-agent', + 'group' => 'td-agent', + 'mode' => '0750' + ) + end + it "td-agent package should be installed" do + should contain_package("td-agent").with( + 'ensure' => 'installed' + ) + end + it "td-agent is running" do + should contain_service("td-agent").with( + 'ensure' => 'running', + 'enable' => 'true', + 'hasstatus' => 'true' + ) + end + end +end diff --git a/fluentd/spec/defines/fluent_filter_spec.rb b/fluentd/spec/defines/fluent_filter_spec.rb new file mode 100644 index 000000000..39827cbdf --- /dev/null +++ b/fluentd/spec/defines/fluent_filter_spec.rb @@ -0,0 +1,39 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +describe 'fluentd::filter' do + let(:title) {'bar'} + + let (:facts) {{ + :osfamily => 'Debian', + :concat_basedir => '/dne', + :lsbdistid => 'Debian', + }} + + context "when type is grep" do + let(:params) {{ + :configfile => 'foo', + :pattern => 'baz', + :type => 'grep', + :input_key => 'input_key', + :regexp => '/regex/', + :exclude => 'exclude', + :config => {}, + :output_tag => 'output_tag', + :add_tag_prefix => 'add_tag_prefix', + :remove_tag_prefix => 'remove_tag_prefix', + :add_tag_suffix => 'add_tag_suffix', + :remove_tag_suffix => 'remove_tag_suffix', + }} + + it "should install the grep plugin" do + should contain_fluentd__install_plugin('fluent-plugin-grep') + end + + + it "should create matcher single segment" do + should contain_concat__fragment('filter').with_content(/.*type.*input_key.*regexp.*exclude.*output_tag.*add_tag_prefix.*remove_tag_prefix.*add_tag_suffix.*remove_tag_suffix.*<\/match>/m) + end + end + +end \ No newline at end of file diff --git a/fluentd/spec/defines/fluentd_configfile_spec.rb b/fluentd/spec/defines/fluentd_configfile_spec.rb new file mode 100644 index 000000000..6f3bb2675 --- /dev/null +++ b/fluentd/spec/defines/fluentd_configfile_spec.rb @@ -0,0 +1,30 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +describe 'fluentd::configfile', :type => :define do + let :pre_condition do + 'include fluentd' + end + ['Debian','RedHat'].each do |osfam| + context "When on an #{osfam} system" do + let (:facts) {{ + :osfamily => osfam, + :concat_basedir => '/dne', + :lsbdistid => 'Debian', # Concatlib needs this value. Works for RedHat too. + }} + context 'when fed no parameters' do + let (:title) { 'MyBaconBringsAllTheBoysToTheYard'} + context 'provides a stub config' do + it { should contain_class('fluentd') } + it { should contain_concat("/etc/td-agent/config.d/#{title}.conf").with( + 'owner' => 'td-agent', + 'group' => 'td-agent', + 'mode' => '0644', + 'require' => 'Class[Fluentd::Packages]' + ) + } + end + end + end + end +end \ No newline at end of file diff --git a/fluentd/spec/defines/fluentd_match_spec.rb b/fluentd/spec/defines/fluentd_match_spec.rb new file mode 100644 index 000000000..2c459add6 --- /dev/null +++ b/fluentd/spec/defines/fluentd_match_spec.rb @@ -0,0 +1,76 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +describe 'fluentd::match' do + let(:title) {'bar'} + + let (:facts) {{ + :osfamily => 'Debian', + :concat_basedir => '/dne', + :lsbdistid => 'Debian', + }} + + context "when no servers or out_copy" do + let(:params) {{ + :configfile => 'foo', + :pattern => 'baz', + :type => 'file', + :config => { + 'time_slice_wait' => '10m', + 'compress' => 'gzip', + } + }} + + it "should create matcher single segment" do + should contain_concat__fragment('match_bar').with_content(/.*type.*file.*compress.*gzip.*time_slice_wait.*10m.*<\/match>/m) + should_not contain_concat__fragment('match_bar').with_content(/server/) + should_not contain_concat__fragment('match_bar').with_content(/store/) + end + end + + context "when servers but no out_copy" do + let(:params) {{ + :configfile => 'foo', + :pattern => 'baz', + :type => 'file', + :servers => [{ 'host' => 'kelis', 'port' => '24224'}, { 'host' => 'bossy', 'port' => '24224'}], + :config => { + 'time_slice_wait' => '10m', + 'compress' => 'gzip', + } + }} + + it "should create matcher with server" do + should contain_concat__fragment('match_bar').with_content(/.*type.*file.*compress.*gzip.*time_slice_wait.*10m.*.*host kelis.*port.*24224.*<\/server>.*.*host.*bossy.*port.*24224.*<\/server>.*<\/match>/m) + should contain_concat__fragment('match_bar').with_content(/server/) + should_not contain_concat__fragment('match_bar').with_content(/store/) + end + end + + context "when out_copy" do + let(:params) {{ + :configfile => 'foo', + :pattern => 'baz', + :type => 'copy', + :config => [ + { + 'type' => 'file', + 'compress' => 'gzip', + 'servers' => [{ 'host' => 'kelis', 'port' => '24224'}, { 'host' => 'bossy', 'port' => '24224'}], + }, + { + 'type' => 'mongo', + 'database' => 'dummy', + } + ] + }} + + it "should create matcher with server" do + should contain_concat__fragment('match_bar').with_content(/.*type.*copy.*.*compress.*gzip.*.*host.*kelis.*port.*24224.*<\/server>.*.*host.*bossy.*port.*24224.*<\/server>.*type.*file.*<\/store>.*.*database.*dummy.*type.*mongo.*<\/store>.*<\/match>/m) + end + end + + +end + + diff --git a/fluentd/spec/fixtures/unit/provider/fluentgem/gem-list-single-package b/fluentd/spec/fixtures/unit/provider/fluentgem/gem-list-single-package new file mode 100644 index 000000000..41576e3e6 --- /dev/null +++ b/fluentd/spec/fixtures/unit/provider/fluentgem/gem-list-single-package @@ -0,0 +1,4 @@ + +*** REMOTE GEMS *** + +bundler (1.6.2) diff --git a/fluentd/spec/fixtures/unit/provider/fluentgem/line-with-1.8.5-warning b/fluentd/spec/fixtures/unit/provider/fluentgem/line-with-1.8.5-warning new file mode 100644 index 000000000..07a27e7b8 --- /dev/null +++ b/fluentd/spec/fixtures/unit/provider/fluentgem/line-with-1.8.5-warning @@ -0,0 +1,14 @@ +/home/jenkins/.rvm/gems/ruby-1.8.5-p231@global/gems/rubygems-bundler-0.9.0/lib/rubygems-bundler/regenerate_binstubs_command.rb:34: warning: parenthesize argument(s) for future version + +*** LOCAL GEMS *** + +columnize (0.3.2) +diff-lcs (1.1.3) +metaclass (0.0.1) +mocha (0.10.5) +rake (0.8.7) +rspec-core (2.9.0) +rspec-expectations (2.9.1) +rspec-mocks (2.9.0) +rubygems-bundler (0.9.0) +rvm (1.11.3.3) diff --git a/fluentd/spec/spec.opts b/fluentd/spec/spec.opts new file mode 100644 index 000000000..de653df4b --- /dev/null +++ b/fluentd/spec/spec.opts @@ -0,0 +1,4 @@ +--format s +--colour +--loadby mtime +--backtrace diff --git a/fluentd/spec/spec_helper.rb b/fluentd/spec/spec_helper.rb new file mode 100644 index 000000000..479ed6922 --- /dev/null +++ b/fluentd/spec/spec_helper.rb @@ -0,0 +1,2 @@ +require 'rubygems' +require 'puppetlabs_spec_helper/module_spec_helper' \ No newline at end of file diff --git a/fluentd/spec/unit/provider/fluentgem_spec.rb b/fluentd/spec/unit/provider/fluentgem_spec.rb new file mode 100644 index 000000000..b66076fd4 --- /dev/null +++ b/fluentd/spec/unit/provider/fluentgem_spec.rb @@ -0,0 +1,176 @@ +#! /usr/bin/env ruby +require 'spec_helper' + +provider_class = Puppet::Type.type(:package).provider(:gem) + +describe provider_class do + let(:resource) do + Puppet::Type.type(:package).new( + :name => 'myresource', + :ensure => :installed + ) + end + + let(:provider) do + provider = provider_class.new + provider.resource = resource + provider + end + + before :each do + resource.provider = provider + end + + describe "when installing" do + it "should use the path to the gem" do + provider_class.stubs(:command).with(:gemcmd).returns "/my/gem" + provider.expects(:execute).with { |args| args[0] == "/my/gem" }.returns "" + provider.install + end + + it "should specify that the gem is being installed" do + provider.expects(:execute).with { |args| args[1] == "install" }.returns "" + provider.install + end + + it "should specify that documentation should not be included" do + provider.expects(:execute).with { |args| args[2] == "--no-rdoc" }.returns "" + provider.install + end + + it "should specify that RI should not be included" do + provider.expects(:execute).with { |args| args[3] == "--no-ri" }.returns "" + provider.install + end + + it "should specify the package name" do + provider.expects(:execute).with { |args| args[4] == "myresource" }.returns "" + provider.install + end + + describe "when a source is specified" do + describe "as a normal file" do + it "should use the file name instead of the gem name" do + resource[:source] = "/my/file" + provider.expects(:execute).with { |args| args[2] == "/my/file" }.returns "" + provider.install + end + end + describe "as a file url" do + it "should use the file name instead of the gem name" do + resource[:source] = "file:///my/file" + provider.expects(:execute).with { |args| args[2] == "/my/file" }.returns "" + provider.install + end + end + describe "as a puppet url" do + it "should fail" do + resource[:source] = "puppet://my/file" + lambda { provider.install }.should raise_error(Puppet::Error) + end + end + describe "as a non-file and non-puppet url" do + it "should treat the source as a gem repository" do + resource[:source] = "http://host/my/file" + provider.expects(:execute).with { |args| args[2..4] == ["--source", "http://host/my/file", "myresource"] }.returns "" + provider.install + end + end + describe "with an invalid uri" do + it "should fail" do + URI.expects(:parse).raises(ArgumentError) + resource[:source] = "http:::::uppet:/:/my/file" + lambda { provider.install }.should raise_error(Puppet::Error) + end + end + end + end + + describe "#latest" do + it "should return a single value for 'latest'" do + #gemlist is used for retrieving both local and remote version numbers, and there are cases + # (particularly local) where it makes sense for it to return an array. That doesn't make + # sense for '#latest', though. + provider.class.expects(:gemlist).with({ :justme => 'myresource'}).returns({ + :name => 'myresource', + :ensure => ["3.0"], + :provider => :gem, + }) + provider.latest.should == "3.0" + end + + it "should list from the specified source repository" do + resource[:source] = "http://foo.bar.baz/gems" + provider.class.expects(:gemlist). + with({:justme => 'myresource', :source => "http://foo.bar.baz/gems"}). + returns({ + :name => 'myresource', + :ensure => ["3.0"], + :provider => :gem, + }) + provider.latest.should == "3.0" + end + end + + describe "#instances" do + before do + provider_class.stubs(:command).with(:gemcmd).returns "/my/gem" + end + + it "should return an empty array when no gems installed" do + provider_class.expects(:execute).with(%w{/my/gem list --local}).returns("\n") + provider_class.instances.should == [] + end + + it "should return ensure values as an array of installed versions" do + provider_class.expects(:execute).with(%w{/my/gem list --local}).returns <<-HEREDOC.gsub(/ /, '') + systemu (1.2.0) + vagrant (0.8.7, 0.6.9) + HEREDOC + + provider_class.instances.map {|p| p.properties}.should == [ + {:ensure => ["1.2.0"], :provider => :gem, :name => 'systemu'}, + {:ensure => ["0.8.7", "0.6.9"], :provider => :gem, :name => 'vagrant'} + ] + end + + it "should ignore platform specifications" do + provider_class.expects(:execute).with(%w{/my/gem list --local}).returns <<-HEREDOC.gsub(/ /, '') + systemu (1.2.0) + nokogiri (1.6.1 ruby java x86-mingw32 x86-mswin32-60, 1.4.4.1 x86-mswin32) + HEREDOC + + provider_class.instances.map {|p| p.properties}.should == [ + {:ensure => ["1.2.0"], :provider => :gem, :name => 'systemu'}, + {:ensure => ["1.6.1 ruby java x86-mingw32 x86-mswin32-60", "1.4.4.1 x86-mswin32"], :provider => :gem, :name => 'nokogiri'} + ] + end + + it "should not fail when an unmatched line is returned" do + provider_class.expects(:execute).with(%w{/my/gem list --local}). + returns(File.read(my_fixture('line-with-1.8.5-warning'))) + + provider_class.instances.map {|p| p.properties}. + should == [{:provider=>:gem, :ensure=>["0.3.2"], :name=>"columnize"}, + {:provider=>:gem, :ensure=>["1.1.3"], :name=>"diff-lcs"}, + {:provider=>:gem, :ensure=>["0.0.1"], :name=>"metaclass"}, + {:provider=>:gem, :ensure=>["0.10.5"], :name=>"mocha"}, + {:provider=>:gem, :ensure=>["0.8.7"], :name=>"rake"}, + {:provider=>:gem, :ensure=>["2.9.0"], :name=>"rspec-core"}, + {:provider=>:gem, :ensure=>["2.9.1"], :name=>"rspec-expectations"}, + {:provider=>:gem, :ensure=>["2.9.0"], :name=>"rspec-mocks"}, + {:provider=>:gem, :ensure=>["0.9.0"], :name=>"rubygems-bundler"}, + {:provider=>:gem, :ensure=>["1.11.3.3"], :name=>"rvm"}] + end + end + + #describe "listing gems" do + # describe "searching for a single package" do + # it "searches for an exact match" do + # provider_class.expects(:execute).with(includes('^bundler$')).returns(File.read(my_fixture('gem-list-single-package'))) + # expected = {:name => 'bundler', :ensure => %w[1.6.2], :provider => :fluentgem} + # expect(provider_class.gemlist({:justme => 'bundler'})).to eq(expected) + # end + # end + #end +end diff --git a/fluentd/spec_helper.rb b/fluentd/spec_helper.rb new file mode 100644 index 000000000..3d9200524 --- /dev/null +++ b/fluentd/spec_helper.rb @@ -0,0 +1 @@ +require 'puppetlabs_spec_helper/module_spec_helper' \ No newline at end of file diff --git a/fluentd/templates/filter.erb b/fluentd/templates/filter.erb new file mode 100644 index 000000000..b5b059eef --- /dev/null +++ b/fluentd/templates/filter.erb @@ -0,0 +1,30 @@ +> + type <%= @type %> +<%- if @input_key != '' -%> + input_key <%= @input_key %> +<%- if @regexp != '' -%> + regexp <%= @regexp %> +<%- end -%> +<%- if @exclude != '' -%> + exclude <%= @exclude %> +<%- end -%> +<%- end -%> +<%- @config.sort_by{|key,val|key}.each do |key, val| -%> + <%= key %> <%= val %> +<%- end -%> +<%- if @output_tag != '' -%> + tag <%= @output_tag %> +<%- end -%> +<%- if @add_tag_prefix != '' -%> + add_tag_prefix <%= @add_tag_prefix %> +<%- end -%> +<%- if @remove_tag_prefix != '' -%> + remove_tag_prefix <%= @remove_tag_prefix %> +<%- end -%> +<%- if @add_tag_suffix != '' -%> + add_tag_suffix <%= @add_tag_suffix %> +<%- end -%> +<%- if @remove_tag_suffix != '' -%> + remove_tag_suffix <%= @remove_tag_suffix %> +<%- end -%> + diff --git a/fluentd/templates/forest_match.erb b/fluentd/templates/forest_match.erb new file mode 100644 index 000000000..2b7053b25 --- /dev/null +++ b/fluentd/templates/forest_match.erb @@ -0,0 +1,21 @@ +> + type <%= @type %> +<% @config.sort_by{|key,val|key}.each do |key, val| -%> + <% if val.class == Hash && key == 'template' %> + + <% else -%> + <%= key %> <%= val %> + <% end -%> +<% end -%> +<% @servers.each do |server| -%> + +<% @server.sort_by{|key,val|key}.each do |key, val| -%> + <%= key %> <%= val %> +<% end -%> + +<% end -%> + \ No newline at end of file diff --git a/fluentd/templates/match.erb b/fluentd/templates/match.erb new file mode 100644 index 000000000..1fc09f9c5 --- /dev/null +++ b/fluentd/templates/match.erb @@ -0,0 +1,33 @@ +> + type <%= @type %> + <%- if @type == 'copy' -%> + <%- @config.each do |outcopy| -%> + + <%- outcopy.sort_by{|key,val|key}.each do |key, val| -%> + <%- if key == 'servers' -%> + <%- val.each do |server| -%> + + <%- server.sort_by{|k,v|k}.each do |k,v| -%> + <%= k %> <%= v %> + <%- end -%> + + <%- end -%> + <%- else -%> + <%= key %> <%= val %> + <%- end -%> + <%- end -%> + + <%- end -%> + <%- else -%> + <%- @config.sort_by{|key,val|key}.each do |key, val| -%> + <%= key %> <%= val %> + <%- end -%> + <%- end -%> + <%- @servers.each do |server| -%> + + <%- server.sort_by{|key,val|key}.each do |key, val| -%> + <%= key %> <%= val %> + <%- end -%> + + <%- end -%> + diff --git a/fluentd/templates/source.erb b/fluentd/templates/source.erb new file mode 100644 index 000000000..19002d69d --- /dev/null +++ b/fluentd/templates/source.erb @@ -0,0 +1,9 @@ + + type <%= @type %> +<% @config.sort_by{|key,val|key}.each do |key, val| -%> + <%= key %> <%= val %> +<% end -%> +<% if @format != false -%> format <%= @format %><% end %> +<% if @time_format != false -%> time_format <%= @time_format %><% end %> +<% if @tag != false -%> tag <%= @tag %><% end %> + diff --git a/fluentd/templates/td-agent.conf.erb b/fluentd/templates/td-agent.conf.erb new file mode 100644 index 000000000..dfced4343 --- /dev/null +++ b/fluentd/templates/td-agent.conf.erb @@ -0,0 +1,2 @@ +# Include config files in the ./config.d directory +<% if @version == '2' -%>@<% end %>include config.d/*.conf diff --git a/galera/.fixtures.yml b/galera/.fixtures.yml deleted file mode 100644 index 67f800fa3..000000000 --- a/galera/.fixtures.yml +++ /dev/null @@ -1,7 +0,0 @@ -fixtures: - repositories: - "stdlib": "git://github.com/puppetlabs/puppetlabs-stdlib" - "xinetd": "git://github.com/packstack/puppetlabs-xinetd" - "mysql": "git://github.com/packstack/puppetlabs-mysql" - symlinks: - "galera": "#{source_dir}" diff --git a/galera/.gitignore b/galera/.gitignore deleted file mode 100644 index 05823c3ae..000000000 --- a/galera/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -Gemfile.lock -.vagrant -vendor -spec/fixtures/modules -spec/fixtures/manifests/site.pp diff --git a/galera/Gemfile b/galera/Gemfile deleted file mode 100644 index fd321c0f3..000000000 --- a/galera/Gemfile +++ /dev/null @@ -1,20 +0,0 @@ -source 'https://rubygems.org' - -group :development, :test do - gem 'rake', :require => false - gem 'rspec-puppet', :require => false - gem 'puppetlabs_spec_helper', :require => false - gem 'rspec-system', :require => false - gem 'rspec-system-puppet', :require => false - gem 'rspec-system-serverspec', :require => false - gem 'serverspec', :require => false - gem 'puppet-lint', :require => false -end - -if puppetversion = ENV['PUPPET_GEM_VERSION'] - gem 'puppet', puppetversion, :require => false -else - gem 'puppet', :require => false -end - -# vim:ft=ruby diff --git a/galera/LICENSE b/galera/LICENSE deleted file mode 100644 index 8d968b6cb..000000000 --- a/galera/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/galera/Modulefile b/galera/Modulefile deleted file mode 100644 index e03c12a44..000000000 --- a/galera/Modulefile +++ /dev/null @@ -1,11 +0,0 @@ -name 'puppet-galera' -version '0.0.2' -source 'https://github.com/rohara/puppet-galera' -author 'Ryan O\'Hara' -license 'Apache License 2.0' -summary 'Install/configure MariaDB with Galera' -description 'Install/configure MariaDB with Galera' -project_page 'https://github.com/rohara/puppet-galera' - -dependency 'puppetlabs/mysql', '>= 0.5.0' -dependency 'puppetlabs/xinetd', '>= 1.2.0' diff --git a/galera/README.md b/galera/README.md deleted file mode 100644 index c07953245..000000000 --- a/galera/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# Galera module - -This is a module for installing and confiuring galera. - -It depends on the mysql module from puppetlabs as well as xinetd. - -## Usage - -### galera::server - - Used to deploy and manage a MariaDB Galera server cluster. Installs - mariadb-galera-server and galera packages, configures galera.cnf and - starts mysqld service: - - class { 'galera::server': - config_hash => { - bind_address => '0.0.0.0', - default_engine => 'InnoDB', - root_password => 'root_pass', - }, - wsrep_cluster_name => 'galera_cluster', - wsrep_sst_method => 'rsync' - wsrep_sst_username => 'ChangeMe', - wsrep_sst_password => 'ChangeMe', - } - -### galera::monitor - - Used to monitor a MariaDB Galera cluster server. The class is meant - to be used in a server load-balancer environment. - - class {'galera::monitor': - monitor_username => 'mon_user', - monitor_password => 'mon_pass' - } - - Here is a sample 3-node HAProxy Configuration: - - listen galera 192.168.220.40:3306 - balance leastconn - mode tcp - option tcpka - option httpchk - server control01 192.168.220.41:3306 check port 9200 inter 2000 rise 2 fall 5 - server control02 192.168.220.42:3306 check port 9200 inter 2000 rise 2 fall 5 - server control03 192.168.220.43:3306 check port 9200 inter 2000 rise 2 fall 5 - -## Authors - -Daneyon Hansen, Ryan O'Hara diff --git a/galera/lib/puppet/parser/functions/wsrep_options.rb b/galera/lib/puppet/parser/functions/wsrep_options.rb deleted file mode 100644 index f09049026..000000000 --- a/galera/lib/puppet/parser/functions/wsrep_options.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Puppet::Parser::Functions - newfunction(:wsrep_options, :type => :rvalue) do |args| - opts = args[0] - opts.delete_if {|key, val| val.equal? :undef} - opts.sort.map do |k,v| - String(k) + "=" + String(v) - end - end -end diff --git a/galera/manifests/monitor.pp b/galera/manifests/monitor.pp deleted file mode 100644 index 75deb4d01..000000000 --- a/galera/manifests/monitor.pp +++ /dev/null @@ -1,65 +0,0 @@ -# Class galera::monitor -# -# Parameters: -# [*mysql_username*] - Username of the service account used for the clustercheck script. -# [*mysql_password*] - Password of the service account used for the clustercheck script. -# [*mysql_host*] - Hostname/IP address of mysql server to monitor. Defaults to 127.0.0.1. -# [*mysql_port] - Port used by mysql service. Defaults to 3306. -# [*monitor_port*] - Port used by galera monitor service. Defaults to 9200. -# [*monitor_script*] - Full path to monitor script. Defaults to '/usr/bin/clustercheck'. -# [*enabled*] - Enable/Disable galera monitor xinetd::service. Defaults to true. -# -# Actions: -# -# Requires: -# -# Sample usage: -# class { 'galera::monitor': -# mysql_username => 'mon_user', -# mysql_password => 'mon_pass' -# } -# -class galera::monitor ( - $mysql_username = 'monitor_user', - $mysql_password = 'monitor_pass', - $mysql_host = '127.0.0.1', - $mysql_port = '3306', - $monitor_port = '9200', - $monitor_script = '/usr/bin/clustercheck', - $enabled = true, -) { - - Class['galera::server'] -> Class['galera::monitor'] - - if $enabled { - $monitor_disable = 'no' - } else { - $monitor_disable = 'yes' - } - - file { '/etc/sysconfig/clustercheck': - mode => '0640', - content => template("galera/clustercheck.erb"), - owner => 'root', - group => 'root', - } - - xinetd::service { 'galera-monitor': - disable => $monitor_disable, - port => $monitor_port, - server => $monitor_script, - flags => 'REUSE', - per_source => 'UNLIMITED', - service_type => 'UNLISTED', - log_on_success => '', - log_on_success_operator => '=', - log_on_failure => 'HOST', - log_on_failure_operator => '=', - } - - database_user { "${mysql_username}@${mysql_host}": - ensure => present, - password_hash => mysql_password($mysql_password), - require => [File['/root/.my.cnf'],Service['galera']], - } -} diff --git a/galera/manifests/server.pp b/galera/manifests/server.pp deleted file mode 100644 index 57a1718e8..000000000 --- a/galera/manifests/server.pp +++ /dev/null @@ -1,102 +0,0 @@ -# Class: galera::server -# -# manages the installation of the galera server. -# manages the package, service, galera.cnf -# -# Parameters: -# [*config_hash*] - Hash of config parameters that need to be set. -# [*bootstrap*] - Defaults to false, boolean to set cluster boostrap. -# [*package_name*] - The name of the galera package. -# [*package_ensure*] - Ensure state for package. Can be specified as version. -# [*service_name*] - The name of the galera service. -# [*service_enable*] - Defaults to true, boolean to set service enable. -# [*service_ensure*] - Defaults to running, needed to set root password. -# [*service_provider*] - What service provider to use. -# [*wsrep_bind_address*] - Address to bind galera service. -# [*wsrep_node_address*] - Address of local galera node. -# [*wsrep_provider*] - Full path to wsrep provider library or 'none'. -# [*wsrep_cluster_name*] - Logical cluster name. Should be the same for all nodes. -# [*wsrep_cluster_members*] - List of cluster members, IP addresses or hostnames. -# [*wsrep_sst_method*] - State snapshot transfer method. -# [*wsrep_sst_username*] - Username used by the wsrep_sst_auth authentication string. -# [*wsrep_sst_password*] - Password used by the wsrep_sst_auth authentication string. -# [*wsrep_ssl*] - Boolean to disable SSL even if certificate and key are configured. -# [*wsrep_ssl_key*] - Private key for the certificate above, unencrypted, in PEM format. -# [*wsrep_ssl_cert*] - Certificate file in PEM format. -# -# Actions: -# -# Requires: -# -# Sample Usage: -# class { 'galera::server': -# config_hash => { -# bind_address => '0.0.0.0', -# default_engine => 'InnoDB', -# root_password => 'root_pass', -# }, -# wsrep_cluster_name => 'galera_cluster', -# wsrep_sst_method => 'rsync' -# wsrep_sst_username => 'ChangeMe', -# wsrep_sst_password => 'ChangeMe', -# } -# -class galera::server ( - $config_hash = {}, - $bootstrap = false, - $debug = false, - $package_name = 'mariadb-galera-server', - $package_ensure = 'present', - $service_name = $mysql::params::service_name, - $service_enable = true, - $service_ensure = 'running', - $service_provider = $mysql::params::service_provider, - $wsrep_bind_address = '0.0.0.0', - $wsrep_node_address = undef, - $wsrep_provider = '/usr/lib64/galera/libgalera_smm.so', - $wsrep_cluster_name = 'galera_cluster', - $wsrep_cluster_members = [ $::ipaddress ], - $wsrep_sst_method = 'rsync', - $wsrep_sst_username = 'root', - $wsrep_sst_password = undef, - $wsrep_ssl = false, - $wsrep_ssl_key = undef, - $wsrep_ssl_cert = undef, -) inherits mysql { - - $config_class = { 'mysql::config' => $config_hash } - - create_resources( 'class', $config_class ) - - package { 'galera': - name => $package_name, - ensure => $package_ensure, - } - - $wsrep_provider_options = wsrep_options({ - 'socket.ssl' => $wsrep_ssl, - 'socket.ssl_key' => $wsrep_ssl_key, - 'socket.ssl_cert' => $wsrep_ssl_cert, - }) - - $wsrep_debug = bool2num($debug) - - file { '/etc/my.cnf.d/galera.cnf': - ensure => present, - mode => '0644', - owner => 'root', - group => 'root', - content => template('galera/wsrep.cnf.erb'), - notify => Service['galera'], - } - - Service['galera'] -> Exec<| title == 'set_mysql_rootpw' |> - - service { 'galera': - name => $service_name, - enable => $service_enable, - ensure => $service_ensure, - require => Package['galera'], - provider => $service_provider, - } -} diff --git a/galera/spec/classes/galera_monitor_spec.rb b/galera/spec/classes/galera_monitor_spec.rb deleted file mode 100644 index 47e5a1d36..000000000 --- a/galera/spec/classes/galera_monitor_spec.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'spec_helper' -describe 'galera::monitor' do - let :facts do - { :osfamily => 'RedHat' } - end - let :pre_condition do - "include 'galera::server'" - end - let :params do - { - :monitor_username => 'monitor_user', - :monitor_password => 'monitor_pass', - :monitor_hostname => '127.0.0.1', - :mysql_port => '3306', - :mysql_path => '/usr/bin/mysql', - :script_dir => '/usr/local/bin', - :enabled => true - } - end - - it { should contain_service('xinetd').with( - :ensure => 'running', - :enable => 'true' - )} - - it { should contain_file('/usr/local/bin/galera_chk')} - it { should contain_database_user("monitor_user@127.0.0.1")} - -end diff --git a/galera/spec/classes/galera_server_spec.rb b/galera/spec/classes/galera_server_spec.rb deleted file mode 100644 index 6abff142b..000000000 --- a/galera/spec/classes/galera_server_spec.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'spec_helper' -describe 'galera::server' do - let :facts do - { :osfamily => 'RedHat' } - end - let :params do - { - :package_name => 'mariadb-galera-server', - :package_ensure => 'present', - :service_name => 'mariadb', - :service_enable => false, - :service_ensure => 'running', - :wsrep_bind_address => '0.0.0.0', - :wsrep_provider => '/usr/lib64/galera/libgalera_smm.so', - :wsrep_cluster_name => 'galera_cluster', - :wsrep_cluster_address => 'gcomm://', - :wsrep_sst_method => 'rsync', - :wsrep_sst_username => 'wsrep_user', - :wsrep_sst_password => 'wsrep_pass', - } - end - - it { should contain_package('galera')} - it { should contain_file('/etc/my.cnf.d/galera.cnf')} - it { should contain_service('galera')} - -end diff --git a/galera/templates/clustercheck.erb b/galera/templates/clustercheck.erb deleted file mode 100644 index 3abac52c5..000000000 --- a/galera/templates/clustercheck.erb +++ /dev/null @@ -1,7 +0,0 @@ -# This file is being maintained by Puppet. -# DO NOT EDIT - -MYSQL_USERNAME="<%= @mysql_username %>" -MYSQL_PASSWORD="<%= @mysql_password %>" -MYSQL_HOST="<%= @mysql_host %>" -MYSQL_PORT="<%= @mysql_port %>" diff --git a/galera/templates/wsrep.cnf.erb b/galera/templates/wsrep.cnf.erb deleted file mode 100644 index 287584f8a..000000000 --- a/galera/templates/wsrep.cnf.erb +++ /dev/null @@ -1,137 +0,0 @@ -# This file contains wsrep-related mysqld options. It should be included -# in the main MySQL configuration file. -# -# Options that need to be customized: -# - wsrep_provider -# - wsrep_cluster_address -# - wsrep_sst_auth -# The rest of defaults should work out of the box. - -## -## mysqld options _MANDATORY_ for correct opration of the cluster -## -[mysqld] - -# (This must be substituted by wsrep_format) -binlog_format=ROW - -# Currently only InnoDB storage engine is supported -default-storage-engine=innodb - -# to avoid issues with 'bulk mode inserts' using autoinc -innodb_autoinc_lock_mode=2 - -# This is a must for paralell applying -innodb_locks_unsafe_for_binlog=1 - -# Query Cache is not supported with wsrep -query_cache_size=0 -query_cache_type=0 - -# Override bind-address -# In some systems bind-address defaults to 127.0.0.1, and with mysqldump SST -# it will have (most likely) disastrous consequences on donor node -bind-address=<%= @wsrep_bind_address %> - -## -## WSREP options -## - -# Full path to wsrep provider library or 'none' -wsrep_provider=<%= @wsrep_provider %> - -# Provider specific configuration options -wsrep_provider_options="<%= @wsrep_provider_options.join '; ' %>" - -# Logical cluster name. Should be the same for all nodes. -wsrep_cluster_name="<%= @wsrep_cluster_name %>" - -# Group communication system handle -<% if @bootstrap -%> -wsrep_cluster_address="gcomm://" -<% elsif !@wsrep_cluster_members.empty? -%> -wsrep_cluster_address="gcomm://<%= @wsrep_cluster_members.join ',' %>" -<% end -%> - -# Human-readable node name (non-unique). Hostname by default. -#wsrep_node_name= - -# Base replication [:port] of the node. -# The values supplied will be used as defaults for state transfer receiving, -# listening ports and so on. Default: address of the first network interface. -<% if @wsrep_node_address -%> -wsrep_node_address=<%= @wsrep_node_address %> -<% else -%> -#wsrep_node_address= -<% end -%> - -# Address for incoming client connections. Autodetect by default. -#wsrep_node_incoming_address= - -# How many threads will process writesets from other nodes -wsrep_slave_threads=1 - -# DBUG options for wsrep provider -#wsrep_dbug_option - -# Generate fake primary keys for non-PK tables (required for multi-master -# and parallel applying operation) -wsrep_certify_nonPK=1 - -# Maximum number of rows in write set -wsrep_max_ws_rows=131072 - -# Maximum size of write set -wsrep_max_ws_size=1073741824 - -# to enable debug level logging, set this to 1 -wsrep_debug=<%= @wsrep_debug %> - -# convert locking sessions into transactions -wsrep_convert_LOCK_to_trx=0 - -# how many times to retry deadlocked autocommits -wsrep_retry_autocommit=1 - -# change auto_increment_increment and auto_increment_offset automatically -wsrep_auto_increment_control=1 - -# retry autoinc insert, which failed for duplicate key error -wsrep_drupal_282555_workaround=0 - -# enable "strictly synchronous" semantics for read operations -wsrep_causal_reads=0 - -# Command to call when node status or cluster membership changes. -# Will be passed all or some of the following options: -# --status - new status of this node -# --uuid - UUID of the cluster -# --primary - whether the component is primary or not ("yes"/"no") -# --members - comma-separated list of members -# --index - index of this node in the list -wsrep_notify_cmd= - -## -## WSREP State Transfer options -## - -# State Snapshot Transfer method -wsrep_sst_method=<%= @wsrep_sst_method %> - -# Address which donor should send State Snapshot to. -# Should be the address of THIS node. DON'T SET IT TO DONOR ADDRESS!!! -# (SST method dependent. Defaults to the first IP of the first interface) -#wsrep_sst_receive_address= - -# SST authentication string. This will be used to send SST to joining nodes. -# Depends on SST method. For mysqldump method it is root: -wsrep_sst_auth=<%= @wsrep_sst_username %>:<%= @wsrep_sst_password %> - -# Desired SST donor name. -#wsrep_sst_donor= - -# Reject client queries when donating SST (false) -#wsrep_sst_donor_rejects_queries=0 - -# Protocol version to use -# wsrep_protocol_version= diff --git a/gcc/.gitignore b/gcc/.gitignore new file mode 100644 index 000000000..d3ba99b63 --- /dev/null +++ b/gcc/.gitignore @@ -0,0 +1,4 @@ +REVISION +metadata.json +pkg/ +*.swp diff --git a/gcc/CHANGELOG b/gcc/CHANGELOG new file mode 100644 index 000000000..27f67619c --- /dev/null +++ b/gcc/CHANGELOG @@ -0,0 +1,21 @@ +2014-06-03 - Release 0.2.0 + +Summary: + +Just a few small tweaks. + +Features: +- Add g++ for RHEL. +- Fail on unsupported distributions. + +Fixes: + +2013-08-14 - Release 0.1.0 + +Features: +- Support osfamily instead of using `$operatingsystem`. Note that +Amazon Linux is RedHat osfamily on facter version 1.7 + +2011-06-03 - Dan Bode - 0.0.3 +* committed source to git +* added tests diff --git a/gcc/LICENSE b/gcc/LICENSE new file mode 100644 index 000000000..297f85cfa --- /dev/null +++ b/gcc/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2013 Puppet Labs + + 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. diff --git a/gcc/Modulefile b/gcc/Modulefile new file mode 100644 index 000000000..7fbd33a09 --- /dev/null +++ b/gcc/Modulefile @@ -0,0 +1,10 @@ +name 'puppetlabs-gcc' +version '0.2.0' +source 'git://github.com/puppetlabs/puppetlabs-gcc.git' +author 'puppetlabs' +license 'Apache 2.0' +summary 'module for installing gcc build utils' +description 'module for installing gcc build utils' +project_page 'https://github.com/puppetlabs/puppetlabs-gcc/' +dependency 'puppetlabs/stdlib', '>=2.0' + diff --git a/gcc/manifests/init.pp b/gcc/manifests/init.pp new file mode 100644 index 000000000..31debf4b5 --- /dev/null +++ b/gcc/manifests/init.pp @@ -0,0 +1,18 @@ +# Class: gcc +# +# This class installs gcc +# +# Parameters: +# +# Actions: +# - Install the gcc package +# +# Requires: +# +# Sample Usage: +# +class gcc( + $gcc_packages = $gcc::params::gcc_packages, +) inherits gcc::params { + ensure_packages($gcc_packages) +} diff --git a/gcc/manifests/params.pp b/gcc/manifests/params.pp new file mode 100644 index 000000000..1a5498fe1 --- /dev/null +++ b/gcc/manifests/params.pp @@ -0,0 +1,25 @@ +# Class: gcc::params +# +# This class manages parameters for the gcc module +# +# Parameters: +# +# Actions: +# +# Requires: +# +# Sample Usage: +# +class gcc::params { + case $::osfamily { + 'RedHat': { + $gcc_packages = [ 'gcc', 'gcc-c++' ] + } + 'Debian': { + $gcc_packages = [ 'gcc', 'build-essential' ] + } + default: { + fail("Class['gcc::params']: Unsupported osfamily: ${::osfamily}") + } + } +} diff --git a/gcc/tests/init.pp b/gcc/tests/init.pp new file mode 100644 index 000000000..03318607e --- /dev/null +++ b/gcc/tests/init.pp @@ -0,0 +1 @@ +include gcc diff --git a/gcc/tests/params.pp b/gcc/tests/params.pp new file mode 100644 index 000000000..1472ddb56 --- /dev/null +++ b/gcc/tests/params.pp @@ -0,0 +1 @@ +include gcc::params diff --git a/git/.fixtures.yml b/git/.fixtures.yml new file mode 100644 index 000000000..2aa9e3d77 --- /dev/null +++ b/git/.fixtures.yml @@ -0,0 +1,5 @@ +fixtures: + repositories: + vcsrepo: git://github.com/puppetlabs/puppetlabs-vcsrepo.git + symlinks: + git: "#{source_dir}" diff --git a/git/.gitignore b/git/.gitignore new file mode 100644 index 000000000..444fe1a3a --- /dev/null +++ b/git/.gitignore @@ -0,0 +1,2 @@ +*.swp +pkg/ diff --git a/git/CHANGELOG b/git/CHANGELOG new file mode 100644 index 000000000..2ccd9d067 --- /dev/null +++ b/git/CHANGELOG @@ -0,0 +1,15 @@ +##2014-06-25 - Release 0.1.0 + +This release adds git::subtree and git::config, as well as fixes up the +documentation and unit tests. + +####Features +- README improvements. +- Add git::subtree class to install git-subtree. +- Add git::config resource + +####Bugs +- Fix git_version fact. + +2011-06-03 - Dan Bode - 0.0.1 +* initial commit diff --git a/git/LICENSE b/git/LICENSE new file mode 100644 index 000000000..297f85cfa --- /dev/null +++ b/git/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2013 Puppet Labs + + 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. diff --git a/git/Modulefile b/git/Modulefile new file mode 100644 index 000000000..4d7b6f219 --- /dev/null +++ b/git/Modulefile @@ -0,0 +1,10 @@ +name 'puppetlabs-git' +version '0.1.0' +source 'git://github.com/puppetlabs/puppetlabs-git.git' +author 'puppetlabs' +license 'Apache 2.0' +summary 'module for installing git' +description 'module for installing git' +project_page 'https://github.com/puppetlabs/puppetlabs-git/' +dependency 'puppetlabs/vcsrepo', '>=0.1.0' + diff --git a/git/README.md b/git/README.md new file mode 100644 index 000000000..293fa76d0 --- /dev/null +++ b/git/README.md @@ -0,0 +1,87 @@ +#git + +####Table of Contents + +1. [Overview - What is the [Modulename] module?](#overview) +2. [Module Description - What does the module do?](#module-description) +3. [Setup - The basics of getting started with [Modulename]](#setup) + * [What [Modulename] affects](#what-registry-affects) +4. [Usage - Configuration options and additional functionality](#usage) +6. [Limitations - OS compatibility, etc.](#limitations) +7. [Development - Guide for contributing to the module](#development) + +##Overview + +Simple module that can install git or gitosis + +##Module Description + +This module installs the git revision control system on a target node. It does not manage a git server or any associated services; it simply ensures a bare minimum set of features (e.g. just a package) to use git. + +##Setup + +###What git affects + +* Package['git'] + +The specifics managed by the module may vary depending on the platform. + +##Usage + +###I just want `git` installed +Simply include the `git` class. + +```puppet +include git +``` + +###I want to use `git subtree` with bash completion + +```puppet +include git::subtree +``` + +###I want to set my user.name and user.email + +```puppet +git::config { 'user.name': + value => 'John Doe', +} + +git::config { 'user.email': + value => 'john.doe@example.com', +} +``` + +##Reference + +###Classes + +* `git`: Installs the git client package. +* `gitosis`: Installs the gitosis package. No configuration +* `subtree`: Installs and configures git-subtree for git 1.7 and up. + +###Resources + +* `git::config`: Set git global configuration for the user running puppet. + +###Facts + +* `git_exec_path`: Path to the directory containing all `git-*` commands. +* `git_version`: Version of git that is installed. Undefined if not installed. + +##Limitations + +This module is known to work with the following operating system families: + + - RedHat 5, 6 + - Debian 6.0.7 or newer + - Ubuntu 12.04 or newer + +##Development + +Puppet Labs modules on the Puppet Forge are open projects, and community contributions are essential for keeping them great. We can’t access the huge number of platforms and myriad of hardware, software, and deployment configurations that Puppet is intended to serve. + +We want to keep it as easy as possible to contribute changes so that our modules work in your environment. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things. + +You can read the complete module contribution guide [on the Puppet Labs wiki.](http://projects.puppetlabs.com/projects/module-site/wiki/Module_contributing) diff --git a/git/Rakefile b/git/Rakefile new file mode 100644 index 000000000..cd3d37995 --- /dev/null +++ b/git/Rakefile @@ -0,0 +1 @@ +require 'puppetlabs_spec_helper/rake_tasks' diff --git a/git/files/subtree/bash_completion.sh b/git/files/subtree/bash_completion.sh new file mode 100644 index 000000000..f2683e449 --- /dev/null +++ b/git/files/subtree/bash_completion.sh @@ -0,0 +1,25 @@ +#!bash +# +# bash completion support for Git subtree. +# +# To use this routine: +# +# 1) Make sure you have installed and configured the core Git completion script, which is required to make this script work; +# 2) Copy this file to somewhere (e.g. ~/.git-subtree-completion.sh); +# 3) Added the following line to your .bashrc: +# source ~/.git-subtree-completion.sh +# + +_git_subtree () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + + if [ $COMP_CWORD -eq 2 ]; then + __gitcomp "add merge pull push split" + return + elif [ $COMP_CWORD -eq 3 ]; then + __gitcomp "--prefix=" + return + fi + __gitcomp "$(__git_remotes)" +} diff --git a/git/lib/facter/git_exec_path.rb b/git/lib/facter/git_exec_path.rb new file mode 100644 index 000000000..23d06e0b0 --- /dev/null +++ b/git/lib/facter/git_exec_path.rb @@ -0,0 +1,4 @@ +# git_exec_path.rb +Facter.add('git_exec_path') do + setcode 'git --exec-path 2>/dev/null' +end diff --git a/git/lib/facter/git_html_path.rb b/git/lib/facter/git_html_path.rb new file mode 100644 index 000000000..705ab5a61 --- /dev/null +++ b/git/lib/facter/git_html_path.rb @@ -0,0 +1,4 @@ +# git_html_path.rb +Facter.add('git_html_path') do + setcode 'git --html-path 2>/dev/null' +end diff --git a/git/lib/facter/git_version.rb b/git/lib/facter/git_version.rb new file mode 100644 index 000000000..9741da1e8 --- /dev/null +++ b/git/lib/facter/git_version.rb @@ -0,0 +1,10 @@ +# git_version +output = %x{git --version 2>&1} + +if $?.exitstatus and output.match(/git version ((\d+\.){2,}\d+).*/) + Facter.add('git_version') do + setcode do + $1 + end + end +end diff --git a/git/manifests/config.pp b/git/manifests/config.pp new file mode 100644 index 000000000..0e71d54db --- /dev/null +++ b/git/manifests/config.pp @@ -0,0 +1,11 @@ +define git::config( + $value, + $section = regsubst($name, '^([^\.]+)\.([^\.]+)$','\1'), + $key = regsubst($name, '^([^\.]+)\.([^\.]+)$','\2'), +) { + exec{"git config --global ${section}.${key} '${value}'": + environment => inline_template('<%= "HOME=" + ENV["HOME"] %>'), + path => ['/usr/bin', '/bin'], + unless => "git config --global --get ${section}.${key} '${value}'", + } +} diff --git a/git/manifests/gitosis.pp b/git/manifests/gitosis.pp new file mode 100644 index 000000000..64b7b2df3 --- /dev/null +++ b/git/manifests/gitosis.pp @@ -0,0 +1,13 @@ +# Class: gitosis +# +# This installs and configures gitosis +# +# Requires: +# - Class[git] +# +class git::gitosis { + include ::git + package {'gitosis': + ensure => present + } +} diff --git a/git/manifests/init.pp b/git/manifests/init.pp new file mode 100644 index 000000000..3a93669ee --- /dev/null +++ b/git/manifests/init.pp @@ -0,0 +1,15 @@ +# Class: git +# +# This class installs git +# +# Actions: +# - Install the git package +# +# Sample Usage: +# class { 'git': } +# +class git { + package { 'git': + ensure => installed, + } +} diff --git a/git/manifests/subtree.pp b/git/manifests/subtree.pp new file mode 100644 index 000000000..910d7ff6a --- /dev/null +++ b/git/manifests/subtree.pp @@ -0,0 +1,52 @@ +# == Class: git::subtree +# +# Installs and configure git-subtree +# +class git::subtree { + + include ::git + + Package['git'] -> Class['git::subtree'] + + if (versioncmp('1.7.0', $::git_version) > 0) { + fail 'git-subtree requires git 1.7 or later!' + } + + if (versioncmp('1.7.11', $::git_version) > 0) { + $source_dir = '/usr/src/git-subtree' + vcsrepo { $source_dir: + ensure => present, + source => 'https://github.com/apenwarr/git-subtree.git', + provider => 'git', + revision => '2793ee6ba', + before => Exec['Build git-subtree'], + } + } else { + $source_dir = "${::git_html_path}/contrib/subtree" + } + + exec { 'Build git-subtree': + command => "/usr/bin/make prefix=/usr libexecdir=${::git_exec_path}", + creates => "${source_dir}/git-subtree", + cwd => $source_dir, + } + -> + package { [ 'asciidoc', 'xmlto', ]: + ensure => present, + } + -> + exec { "/usr/bin/make prefix=/usr libexecdir=${::git_exec_path} install": + onlyif => [ + "test ! -f ${::git_exec_path}/git-subtree", + 'test ! -f /usr/share/man/man1/git-subtree.1', + ], + cwd => $source_dir, + } + + file { '/etc/bash_completion.d/git-subtree': + ensure => file, + source => 'puppet:///modules/git/subtree/bash_completion.sh', + mode => '0644', + } + +} diff --git a/git/metadata.json b/git/metadata.json new file mode 100644 index 000000000..0936fcc09 --- /dev/null +++ b/git/metadata.json @@ -0,0 +1,19 @@ +{ + "project_page": "https://github.com/puppetlabs/puppetlabs-git", + "license": "Apache License, Version 2.0", + "source": "https://github.com/puppetlabs/puppetlabs-git", + "dependencies": [ + { + "name": "puppetlabs/vcsrepo", + "version_requirement": ">=0.1.0" + } + ], + "types": [ + + ], + "description": "Module for installing git.", + "summary": "Module for installing git.", + "name": "puppetlabs-git", + "author": "Puppet Labs", + "version": "0.1.0" +} diff --git a/git/spec/classes/git_subtree_spec.rb b/git/spec/classes/git_subtree_spec.rb new file mode 100644 index 000000000..484c43d92 --- /dev/null +++ b/git/spec/classes/git_subtree_spec.rb @@ -0,0 +1,70 @@ +require 'spec_helper' + +describe 'git::subtree' do + + context 'when git version < 1.7.0' do + let(:facts) { { :git_version => '1.6.0' } } + + it 'should fail' do + expect { should create_class('git::subtree') }.to raise_error(Puppet::Error, /git-subtree requires git 1.7 or later!/) + end + end + + context 'when git version > 1.7.0 and < 1.7.11' do + let(:facts) { { + :git_version => '1.7.0', + :git_exec_path => '/usr/lib/git-core', + } } + + it { should create_class('git') } + + it { should create_vcsrepo('/usr/src/git-subtree').with({ + :ensure => 'present', + :source => 'https://github.com/apenwarr/git-subtree.git', + :provider => 'git', + :revision => '2793ee6ba', + })} + + it { should create_exec('/usr/bin/make prefix=/usr libexecdir=/usr/lib/git-core').with({ + :creates => '/usr/src/git-subtree/git-subtree', + :cwd => '/usr/src/git-subtree', + })} + + it { should create_exec('/usr/bin/make prefix=/usr libexecdir=/usr/lib/git-core install').with({ + :creates => '/usr/lib/git-core/git-subtree', + :cwd => '/usr/src/git-subtree', + })} + + it { should create_file('/etc/bash_completion.d/git-subtree').with({ + :ensure => 'file', + :source => 'puppet:///modules/git/subtree/bash_completion.sh', + :mode => '0644', + })} + end + + context 'when git version >= 1.7.11' do + let(:facts) { { + :git_version => '1.7.11', + :git_exec_path => '/usr/lib/git-core', + } } + + it { should create_class('git') } + + it { should create_exec('/usr/bin/make prefix=/usr libexecdir=/usr/lib/git-core').with({ + :creates => '/usr/share/doc/git/contrib/subtree/git-subtree', + :cwd => '/usr/share/doc/git/contrib/subtree', + })} + + it { should create_exec('/usr/bin/make prefix=/usr libexecdir=/usr/lib/git-core install').with({ + :creates => '/usr/lib/git-core/git-subtree', + :cwd => '/usr/share/doc/git/contrib/subtree', + })} + + it { should create_file('/etc/bash_completion.d/git-subtree').with({ + :ensure => 'file', + :source => 'puppet:///modules/git/subtree/bash_completion.sh', + :mode => '0644', + })} + end + +end diff --git a/git/spec/spec_helper.rb b/git/spec/spec_helper.rb new file mode 100644 index 000000000..2c6f56649 --- /dev/null +++ b/git/spec/spec_helper.rb @@ -0,0 +1 @@ +require 'puppetlabs_spec_helper/module_spec_helper' diff --git a/git/tests/gitosis.pp b/git/tests/gitosis.pp new file mode 100644 index 000000000..e6240ae21 --- /dev/null +++ b/git/tests/gitosis.pp @@ -0,0 +1 @@ +class { 'git::gitosis': } diff --git a/git/tests/init.pp b/git/tests/init.pp new file mode 100644 index 000000000..c23290471 --- /dev/null +++ b/git/tests/init.pp @@ -0,0 +1 @@ +class { 'git': } diff --git a/glance/Modulefile b/glance/Modulefile new file mode 100644 index 000000000..cbd31e7dd --- /dev/null +++ b/glance/Modulefile @@ -0,0 +1,13 @@ +name 'puppetlabs-glance' +version '4.0.0' +author 'Puppet Labs and StackForge Contributors' +license 'Apache License 2.0' +summary 'Puppet module for OpenStack Glance' +description 'Installs and configures OpenStack Glance (Image Service).' +project_page 'https://launchpad.net/puppet-glance' +source 'https://github.com/stackforge/puppet-glance' + +dependency 'puppetlabs/inifile', '>=1.0.0 <2.0.0' +dependency 'puppetlabs/keystone', '>=4.0.0 <5.0.0' +dependency 'puppetlabs/stdlib', '>= 3.2.0' +dependency 'stackforge/openstacklib', '>=5.0.0' diff --git a/glance/lib/puppet/provider/glance.rb b/glance/lib/puppet/provider/glance.rb index ec4e1c08e..d3791b6b4 100644 --- a/glance/lib/puppet/provider/glance.rb +++ b/glance/lib/puppet/provider/glance.rb @@ -15,8 +15,7 @@ def self.get_glance_credentials glance_file['keystone_authtoken']['auth_protocol'] and glance_file['keystone_authtoken']['admin_tenant_name'] and glance_file['keystone_authtoken']['admin_user'] and - glance_file['keystone_authtoken']['admin_password'] and - glance_file['DEFAULT']['os_region_name'] + glance_file['keystone_authtoken']['admin_password'] g = {} g['auth_host'] = glance_file['keystone_authtoken']['auth_host'].strip @@ -25,7 +24,6 @@ def self.get_glance_credentials g['admin_tenant_name'] = glance_file['keystone_authtoken']['admin_tenant_name'].strip g['admin_user'] = glance_file['keystone_authtoken']['admin_user'].strip g['admin_password'] = glance_file['keystone_authtoken']['admin_password'].strip - g['os_region_name'] = glance_file['DEFAULT']['os_region_name'].strip # auth_admin_prefix not required to be set. g['auth_admin_prefix'] = (glance_file['keystone_authtoken']['auth_admin_prefix'] || '').strip @@ -74,11 +72,11 @@ def glance_hash def self.auth_glance(*args) begin g = glance_credentials - remove_warnings(glance('--os-tenant-name', g['admin_tenant_name'], '--os-username', g['admin_user'], '--os-password', g['admin_password'], '--os-region-name', g['os_region_name'], '--os-auth-url', auth_endpoint, args)) + remove_warnings(glance('-T', g['admin_tenant_name'], '-I', g['admin_user'], '-K', g['admin_password'], '-N', auth_endpoint, args)) rescue Exception => e if (e.message =~ /\[Errno 111\] Connection refused/) or (e.message =~ /\(HTTP 400\)/) or (e.message =~ /HTTP Unable to establish connection/) sleep 10 - remove_warnings(glance('--os-tenant-name', g['admin_tenant_name'], '--os-username', g['admin_user'], '--os-password', g['admin_password'], '--os-region-name', g['os_region_name'], '--os-auth-url', auth_endpoint, args)) + remove_warnings(glance('-T', g['admin_tenant_name'], '-I', g['admin_user'], '-K', g['admin_password'], '-N', auth_endpoint, args)) else raise(e) end @@ -92,7 +90,7 @@ def auth_glance(*args) def self.auth_glance_stdin(*args) begin g = glance_credentials - command = "glance --os-tenant-name #{g['admin_tenant_name']} --os-username #{g['admin_user']} --os-password #{g['admin_password']} --os-region-name #{g['os_region_name']} --os-auth-url #{auth_endpoint} #{args.join(' ')}" + command = "glance -T #{g['admin_tenant_name']} -I #{g['admin_user']} -K #{g['admin_password']} -N #{auth_endpoint} #{args.join(' ')}" # This is a horrible, horrible hack # Redirect stderr to stdout in order to report errors @@ -111,14 +109,14 @@ def auth_glance_stdin(*args) private def self.list_glance_images ids = [] - (auth_glance('image-list').split("\n")[3..-2] || []).collect do |line| - ids << line.split('|')[1].strip() + (auth_glance('index').split("\n")[2..-1] || []).collect do |line| + ids << line.split[0] end return ids end def self.get_glance_image_attr(id, attr) - (auth_glance('image-show', id).split("\n") || []).collect do |line| + (auth_glance('show', id).split("\n") || []).collect do |line| if line =~ /^#{attr}:/ return line.split(': ')[1..-1] end @@ -127,8 +125,8 @@ def self.get_glance_image_attr(id, attr) def self.get_glance_image_attrs(id) attrs = {} - (auth_glance('image-show', id).split("\n")[3..-2] || []).collect do |line| - attrs[line.split('|')[1].strip()] = line.split('|')[2].strip() + (auth_glance('show', id).split("\n") || []).collect do |line| + attrs[line.split(': ').first.downcase] = line.split(': ')[1..-1].pop end return attrs end diff --git a/glance/lib/puppet/provider/glance_image/glance.rb b/glance/lib/puppet/provider/glance_image/glance.rb index 7be1ace9d..53ebd61bc 100644 --- a/glance/lib/puppet/provider/glance_image/glance.rb +++ b/glance/lib/puppet/provider/glance_image/glance.rb @@ -21,10 +21,10 @@ def self.instances new( :ensure => :present, :name => attrs['name'], - :is_public => attrs['is_public'], - :container_format => attrs['container_format'], + :is_public => attrs['public'], + :container_format => attrs['container format'], :id => attrs['id'], - :disk_format => attrs['disk_format'] + :disk_format => attrs['disk format'] ) end end @@ -43,10 +43,12 @@ def exists? end def create + stdin = nil if resource[:source] # copy_from cannot handle file:// if resource[:source] =~ /^\// # local file - location = "--file=#{resource[:source]}" + location = "< #{resource[:source]}" + stdin = true else location = "--copy-from=#{resource[:source]}" end @@ -57,7 +59,11 @@ def create else raise(Puppet::Error, "Must specify either source or location") end - results = auth_glance('image-create', "--name=#{resource[:name]}", "--is-public=#{resource[:is_public]}", "--container-format=#{resource[:container_format]}", "--disk-format=#{resource[:disk_format]}", location) + if stdin + result = auth_glance_stdin('image-create', "--name=#{resource[:name]}", "--is-public=#{resource[:is_public]}", "--container-format=#{resource[:container_format]}", "--disk-format=#{resource[:disk_format]}", location) + else + results = auth_glance('image-create', "--name=#{resource[:name]}", "--is-public=#{resource[:is_public]}", "--container-format=#{resource[:container_format]}", "--disk-format=#{resource[:disk_format]}", location) + end id = nil diff --git a/glance/manifests/api.pp b/glance/manifests/api.pp index b033103b0..9b6869562 100644 --- a/glance/manifests/api.pp +++ b/glance/manifests/api.pp @@ -96,10 +96,6 @@ # (optional) User to authenticate as with keystone. # Defaults to 'glance'. # -# [*manage_service*] -# (optional) If Puppet should manage service startup / shutdown. -# Defaults to true. -# # [*enabled*] # (optional) Whether to enable services. # Defaults to true. @@ -162,9 +158,6 @@ # (optional) Base directory that the Image Cache uses. # Defaults to '/var/lib/glance/image-cache'. # -# [*os_region_name*] -# (optional) Sets the keystone region to use. -# Defaults to 'RegionOne'. class glance::api( $keystone_password, $verbose = false, @@ -188,7 +181,6 @@ $pipeline = 'keystone+cachemanagement', $keystone_tenant = 'services', $keystone_user = 'glance', - $manage_service = true, $enabled = true, $use_syslog = false, $log_facility = 'LOG_USER', @@ -201,14 +193,12 @@ $database_connection = 'sqlite:///var/lib/glance/glance.sqlite', $database_idle_timeout = 3600, $image_cache_dir = '/var/lib/glance/image-cache', - $os_region_name = 'RegionOne', # DEPRECATED PARAMETERS $mysql_module = undef, $sql_idle_timeout = false, $sql_connection = false, ) inherits glance { - include glance::policy require keystone::python if $mysql_module { @@ -220,7 +210,6 @@ } Package[$glance::params::api_package_name] -> File['/etc/glance/'] - Package[$glance::params::api_package_name] -> Class['glance::policy'] Package[$glance::params::api_package_name] -> Glance_api_config<||> Package[$glance::params::api_package_name] -> Glance_cache_config<||> @@ -231,8 +220,6 @@ Exec<| title == 'glance-manage db_sync' |> ~> Service['glance-api'] Glance_api_config<||> ~> Service['glance-api'] Glance_cache_config<||> ~> Service['glance-api'] - Class['glance::policy'] ~> Service['glance-api'] - Service['glance-api'] ~> Glance_image<||> File { ensure => present, @@ -284,7 +271,6 @@ 'DEFAULT/workers': value => $workers; 'DEFAULT/show_image_direct_url': value => $show_image_direct_url; 'DEFAULT/image_cache_dir': value => $image_cache_dir; - 'DEFAULT/os_region_name': value => $os_region_name; } # known_stores config @@ -299,9 +285,8 @@ } glance_cache_config { - 'DEFAULT/verbose': value => $verbose; - 'DEFAULT/debug': value => $debug; - 'DEFAULT/os_region_name': value => $os_region_name; + 'DEFAULT/verbose': value => $verbose; + 'DEFAULT/debug': value => $debug; } # configure api service to connect registry service @@ -438,12 +423,10 @@ '/etc/glance/glance-cache.conf']: } - if $manage_service { - if $enabled { - $service_ensure = 'running' - } else { - $service_ensure = 'stopped' - } + if $enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' } service { 'glance-api': diff --git a/glance/manifests/backend/cinder.pp b/glance/manifests/backend/cinder.pp index adb680096..db9593338 100644 --- a/glance/manifests/backend/cinder.pp +++ b/glance/manifests/backend/cinder.pp @@ -33,9 +33,9 @@ # Defaults to 'undef' # # [*os_region_name*] -# (optional) The os_region_name parameter is deprecated and has no effect. -# Use glance::api::os_region_name instead. -# Defaults to 'undef' +# (optional) Region name of this node. +# Should be a valid region name +# Defaults to 'RegionOne' # # [*cinder_ca_certificates_file*] # (optional) Location of ca certicate file to use for cinder client requests. @@ -54,7 +54,7 @@ # class glance::backend::cinder( - $os_region_name = undef, + $os_region_name = 'RegionOne', $cinder_ca_certificates_file = undef, $cinder_api_insecure = false, $cinder_catalog_info = 'volume:cinder:publicURL', @@ -63,21 +63,19 @@ ) { - if $os_region_name { - notice('The os_region_name parameter is deprecated and has no effect. Use glance::api::os_region_name instead.') - } - glance_api_config { 'DEFAULT/cinder_api_insecure': value => $cinder_api_insecure; 'DEFAULT/cinder_catalog_info': value => $cinder_catalog_info; 'DEFAULT/cinder_http_retries': value => $cinder_http_retries; 'DEFAULT/default_store': value => 'cinder'; + 'DEFAULT/os_region_name': value => $os_region_name; } glance_cache_config { 'DEFAULT/cinder_api_insecure': value => $cinder_api_insecure; 'DEFAULT/cinder_catalog_info': value => $cinder_catalog_info; 'DEFAULT/cinder_http_retries': value => $cinder_http_retries; + 'DEFAULT/os_region_name': value => $os_region_name; } if $cinder_endpoint_template { diff --git a/glance/manifests/backend/swift.pp b/glance/manifests/backend/swift.pp index 883125785..c02e65c3b 100644 --- a/glance/manifests/backend/swift.pp +++ b/glance/manifests/backend/swift.pp @@ -13,15 +13,12 @@ # $swift_store_auth_version - Optional. Default: '2' # # $swift_store_create_container_on_put - Optional. Default: 'False' -# -# $swift_store_large_object_size - Optional. Default: '5120' class glance::backend::swift( $swift_store_user, $swift_store_key, $swift_store_auth_address = '127.0.0.1:5000/v2.0/', $swift_store_container = 'glance', $swift_store_auth_version = '2', - $swift_store_large_object_size = '5120', $swift_store_create_container_on_put = false ) { @@ -34,8 +31,6 @@ 'DEFAULT/swift_store_auth_version': value => $swift_store_auth_version; 'DEFAULT/swift_store_create_container_on_put': value => $swift_store_create_container_on_put; - 'DEFAULT/swift_store_large_object_size': - value => $swift_store_large_object_size; } glance_cache_config { @@ -46,8 +41,6 @@ 'DEFAULT/swift_store_auth_version': value => $swift_store_auth_version; 'DEFAULT/swift_store_create_container_on_put': value => $swift_store_create_container_on_put; - 'DEFAULT/swift_store_large_object_size': - value => $swift_store_large_object_size; } } diff --git a/glance/manifests/backend/vsphere.pp b/glance/manifests/backend/vsphere.pp deleted file mode 100644 index 133b0986e..000000000 --- a/glance/manifests/backend/vsphere.pp +++ /dev/null @@ -1,83 +0,0 @@ -# -# Copyright (C) 2014 Mirantis -# -# Author: Steapn Rogov -# -# 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: glance::backend::vsphere -# -# Setup Glance to backend images into VMWare vCenter/ESXi -# -# === Parameters -# -# [*vcenter_api_insecure*] -# (optional) Allow to perform insecure SSL requests to vCenter/ESXi. -# Should be a valid string boolean value -# Defaults to 'False' -# -# [*vcenter_host*] -# (required) vCenter/ESXi Server target system. -# Should be a valid an IP address or a DNS name. -# -# [*vcenter_user*] -# (required) Username for authenticating with vCenter/ESXi server. -# -# [*vcenter_password*] -# (required) Password for authenticating with vCenter/ESXi server. -# -# [*vcenter_datacenter*] -# (required) Inventory path to a datacenter. -# If you want to use ESXi host as datastore,it should be "ha-datacenter". -# -# [*vcenter_datastore*] -# (required) Datastore associated with the datacenter. -# -# [*vcenter_image_dir*] -# (required) The name of the directory where the glance images will be stored -# in the VMware datastore. -# -# [*vcenter_task_poll_interval*] -# (optional) The interval used for polling remote tasks invoked on -# vCenter/ESXi server. -# Defaults to '5' -# -# [*vcenter_api_retry_count*] -# (optional) Number of times VMware ESX/VC server API must be retried upon -# connection related issues. -# Defaults to '10' -# -class glance::backend::vsphere( - $vcenter_host, - $vcenter_user, - $vcenter_password, - $vcenter_datacenter, - $vcenter_datastore, - $vcenter_image_dir, - $vcenter_api_insecure = 'False', - $vcenter_task_poll_interval = '5', - $vcenter_api_retry_count = '10', -) { - glance_api_config { - 'DEFAULT/default_store': value => 'vsphere'; - 'DEFAULT/vmware_api_insecure': value => $vcenter_api_insecure; - 'DEFAULT/vmware_server_host': value => $vcenter_host; - 'DEFAULT/vmware_server_username': value => $vcenter_user; - 'DEFAULT/vmware_server_password': value => $vcenter_password; - 'DEFAULT/vmware_datastore_name': value => $vcenter_datastore; - 'DEFAULT/vmware_store_image_dir': value => $vcenter_image_dir; - 'DEFAULT/vmware_task_poll_interval': value => $vcenter_task_poll_interval; - 'DEFAULT/vmware_api_retry_count': value => $vcenter_api_retry_count; - 'DEFAULT/vmware_datacenter_path': value => $vcenter_datacenter; - } -} diff --git a/glance/manifests/cache/cleaner.pp b/glance/manifests/cache/cleaner.pp index f05258db2..9ef5dbe53 100644 --- a/glance/manifests/cache/cleaner.pp +++ b/glance/manifests/cache/cleaner.pp @@ -19,24 +19,18 @@ # [*weekday*] # (optional) Defaults to '*'. # -# [*command_options*] -# command options to add to the cronjob -# (eg. point to config file, or redirect output) -# (optional) Defaults to ''. -# class glance::cache::cleaner ( - $minute = 1, - $hour = 0, - $monthday = '*', - $month = '*', - $weekday = '*', - $command_options = '', + $minute = 1, + $hour = 0, + $monthday = '*', + $month = '*', + $weekday = '*', ) { include glance::params cron { 'glance-cache-cleaner': - command => "${glance::params::cache_cleaner_command} ${command_options}", + command => $glance::params::cache_cleaner_command, environment => 'PATH=/bin:/usr/bin:/usr/sbin', user => 'glance', minute => $minute, diff --git a/glance/manifests/cache/pruner.pp b/glance/manifests/cache/pruner.pp index 96e135fb2..2cee4381c 100644 --- a/glance/manifests/cache/pruner.pp +++ b/glance/manifests/cache/pruner.pp @@ -19,24 +19,18 @@ # [*weekday*] # (optional) Defaults to '*'. # -# [*command_options*] -# command options to add to the cronjob -# (eg. point to config file, or redirect output) -# (optional) Defaults to ''. -# class glance::cache::pruner ( - $minute = '*/30', - $hour = '*', - $monthday = '*', - $month = '*', - $weekday = '*', - $command_options = '', + $minute = '*/30', + $hour = '*', + $monthday = '*', + $month = '*', + $weekday = '*', ) { include glance::params cron { 'glance-cache-pruner': - command => "${glance::params::cache_pruner_command} ${command_options}", + command => $glance::params::cache_pruner_command, environment => 'PATH=/bin:/usr/bin:/usr/sbin', user => 'glance', minute => $minute, diff --git a/glance/manifests/keystone/auth.pp b/glance/manifests/keystone/auth.pp index d325acef1..c98860b62 100644 --- a/glance/manifests/keystone/auth.pp +++ b/glance/manifests/keystone/auth.pp @@ -6,9 +6,6 @@ # $auth_name :: identifier used for all keystone objects related to glance. # Optional. Defaults to glance. # $password :: password for glance user. Optional. Defaults to glance_password. -# $configure_user :: Whether to configure a service user. Optional. Defaults to true. -# $configure_user_role :: Whether to configure the admin role for the service user. -# Optional. Defaults to true. # $service_name :: name of the service. Optional. Defaults to value of auth_name. # $service_type :: type of service to create. Optional. Defaults to image. # $public_address :: Public address for endpoint. Optional. Defaults to 127.0.0.1. @@ -23,22 +20,20 @@ # class glance::keystone::auth( $password, - $email = 'glance@localhost', - $auth_name = 'glance', - $configure_endpoint = true, - $configure_user = true, - $configure_user_role = true, - $service_name = undef, - $service_type = 'image', - $public_address = '127.0.0.1', - $admin_address = '127.0.0.1', - $internal_address = '127.0.0.1', - $port = '9292', - $region = 'RegionOne', - $tenant = 'services', - $public_protocol = 'http', - $admin_protocol = 'http', - $internal_protocol = 'http' + $email = 'glance@localhost', + $auth_name = 'glance', + $configure_endpoint = true, + $service_name = undef, + $service_type = 'image', + $public_address = '127.0.0.1', + $admin_address = '127.0.0.1', + $internal_address = '127.0.0.1', + $port = '9292', + $region = 'RegionOne', + $tenant = 'services', + $public_protocol = 'http', + $admin_protocol = 'http', + $internal_protocol = 'http' ) { if $service_name == undef { @@ -47,25 +42,20 @@ $real_service_name = $service_name } + Keystone_user_role["${auth_name}@${tenant}"] ~> Service <| name == 'glance-registry' |> + Keystone_user_role["${auth_name}@${tenant}"] ~> Service <| name == 'glance-api' |> Keystone_endpoint["${region}/${real_service_name}"] ~> Service <| name == 'glance-api' |> - if $configure_user { - keystone_user { $auth_name: - ensure => present, - password => $password, - email => $email, - tenant => $tenant, - } + keystone_user { $auth_name: + ensure => present, + password => $password, + email => $email, + tenant => $tenant, } - if $configure_user_role { - Keystone_user_role["${auth_name}@${tenant}"] ~> Service <| name == 'glance-registry' |> - Keystone_user_role["${auth_name}@${tenant}"] ~> Service <| name == 'glance-api' |> - - keystone_user_role { "${auth_name}@${tenant}": - ensure => present, - roles => 'admin', - } + keystone_user_role { "${auth_name}@${tenant}": + ensure => present, + roles => 'admin', } keystone_service { $real_service_name: diff --git a/glance/manifests/policy.pp b/glance/manifests/policy.pp deleted file mode 100644 index 34ce39913..000000000 --- a/glance/manifests/policy.pp +++ /dev/null @@ -1,29 +0,0 @@ -# == Class: glance::policy -# -# Configure the glance policies -# -# === Parameters -# -# [*policies*] -# (optional) Set of policies to configure for glance -# Example : { 'glance-context_is_admin' => {'context_is_admin' => 'true'}, 'glance-default' => {'default' => 'rule:admin_or_owner'} } -# Defaults to empty hash. -# -# [*policy_path*] -# (optional) Path to the glance policy.json file -# Defaults to /etc/glance/policy.json -# -class glance::policy ( - $policies = {}, - $policy_path = '/etc/glance/policy.json', -) { - - validate_hash($policies) - - Openstacklib::Policy::Base { - file_path => $policy_path, - } - - create_resources('openstacklib::policy::base', $policies) - -} diff --git a/glance/manifests/registry.pp b/glance/manifests/registry.pp index da242e37f..bac8b7178 100644 --- a/glance/manifests/registry.pp +++ b/glance/manifests/registry.pp @@ -84,13 +84,8 @@ # (optional) Syslog facility to receive log lines. # Defaults to LOG_USER. # -# [*manage_service*] -# (optional) If Puppet should manage service startup / shutdown. -# Defaults to true. -# # [*enabled*] -# (optional) Should the service be enabled. -# Defaults to true. +# (optional) Should the service be enabled. Defaults to true. # # [*purge_config*] # (optional) Whether to create only the specified config values in @@ -133,7 +128,6 @@ $pipeline = 'keystone', $use_syslog = false, $log_facility = 'LOG_USER', - $manage_service = true, $enabled = true, $purge_config = false, $cert_file = false, @@ -323,23 +317,21 @@ '/etc/glance/glance-registry-paste.ini']: } + if $enabled { - if $manage_service { - if $enabled { - Exec['glance-manage db_sync'] ~> Service['glance-registry'] + Exec['glance-manage db_sync'] ~> Service['glance-registry'] - exec { 'glance-manage db_sync': - command => $::glance::params::db_sync_command, - path => '/usr/bin', - user => 'glance', - refreshonly => true, - logoutput => on_failure, - subscribe => [Package[$glance::params::registry_package_name], File['/etc/glance/glance-registry.conf']], - } - $service_ensure = 'running' - } else { - $service_ensure = 'stopped' + exec { 'glance-manage db_sync': + command => $::glance::params::db_sync_command, + path => '/usr/bin', + user => 'glance', + refreshonly => true, + logoutput => on_failure, + subscribe => [Package[$glance::params::registry_package_name], File['/etc/glance/glance-registry.conf']], } + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' } service { 'glance-registry': diff --git a/glance/metadata.json b/glance/metadata.json deleted file mode 100644 index c74a85bd0..000000000 --- a/glance/metadata.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "stackforge-glance", - "version": "5.0.0", - "author": "Puppet Labs and StackForge Contributors", - "summary": "Puppet module for OpenStack Glance", - "license": "Apache License 2.0", - "source": "git://github.com/stackforge/puppet-glance.git", - "project_page": "https://launchpad.net/puppet-glance", - "issues_url": "https://bugs.launchpad.net/puppet-glance", - "requirements": [ - { "name": "pe","version_requirement": "3.x" }, - { "name": "puppet","version_requirement": "3.x" } - ], - "operatingsystem_support": [ - { - "operatingsystem": "Debian", - "operatingsystemrelease": ["7"] - }, - { - "operatingsystem": "Fedora", - "operatingsystemrelease": ["20"] - }, - { - "operatingsystem": "RedHat", - "operatingsystemrelease": ["6.5","7"] - }, - { - "operatingsystem": "Ubuntu", - "operatingsystemrelease": ["12.04","14.04"] - } - ], - "description": "Installs and configures OpenStack Glance (Image Service).", - "dependencies": [ - { "name": "puppetlabs/inifile", "version_requirement": ">=1.0.0 <2.0.0" }, - { "name": "stackforge/keystone", "version_requirement": ">=5.0.0 <6.0.0" }, - { "name": "puppetlabs/stdlib", "version_requirement": ">=4.0.0 <5.0.0" }, - { "name": "stackforge/openstacklib", "version_requirement": ">=5.0.0" } - ] -} diff --git a/glance/spec/classes/glance_api_spec.rb b/glance/spec/classes/glance_api_spec.rb index 7cbc9946a..157c89fb2 100644 --- a/glance/spec/classes/glance_api_spec.rb +++ b/glance/spec/classes/glance_api_spec.rb @@ -22,7 +22,6 @@ :log_dir => '/var/log/glance', :auth_type => 'keystone', :enabled => true, - :manage_service => true, :backlog => '4096', :workers => '7', :auth_host => '127.0.0.1', @@ -38,7 +37,6 @@ :purge_config => false, :known_stores => false, :image_cache_dir => '/var/lib/glance/image-cache', - :os_region_name => 'RegionOne', } end @@ -65,8 +63,7 @@ :database_idle_timeout => '36002', :database_connection => 'mysql:///var:lib@glance/glance', :show_image_direct_url => true, - :image_cache_dir => '/tmp/glance', - :os_region_name => 'RegionOne2', + :image_cache_dir => '/tmp/glance' } ].each do |param_set| @@ -81,10 +78,9 @@ end it { should contain_class 'glance' } - it { should contain_class 'glance::policy' } it { should contain_service('glance-api').with( - 'ensure' => (param_hash[:manage_service] && param_hash[:enabled]) ? 'running': 'stopped', + 'ensure' => param_hash[:enabled] ? 'running': 'stopped', 'enable' => param_hash[:enabled], 'hasstatus' => true, 'hasrestart' => true @@ -99,8 +95,7 @@ 'registry_host', 'registry_port', 'registry_client_protocol', - 'show_image_direct_url', - 'os_region_name', + 'show_image_direct_url' ].each do |config| should contain_glance_api_config("DEFAULT/#{config}").with_value(param_hash[config.intern]) end @@ -111,8 +106,7 @@ 'verbose', 'debug', 'registry_host', - 'registry_port', - 'os_region_name', + 'registry_port' ].each do |config| should contain_glance_cache_config("DEFAULT/#{config}").with_value(param_hash[config.intern]) end @@ -159,23 +153,6 @@ end end - describe 'with disabled service managing' do - let :params do - { - :keystone_password => 'ChangeMe', - :manage_service => false, - :enabled => false, - } - end - - it { should contain_service('glance-api').with( - 'ensure' => nil, - 'enable' => false, - 'hasstatus' => true, - 'hasrestart' => true - ) } - end - describe 'with overridden pipeline' do let :params do { diff --git a/glance/spec/classes/glance_backend_cinder_spec.rb b/glance/spec/classes/glance_backend_cinder_spec.rb index 55c82d526..f3f33ac63 100644 --- a/glance/spec/classes/glance_backend_cinder_spec.rb +++ b/glance/spec/classes/glance_backend_cinder_spec.rb @@ -34,6 +34,7 @@ should contain_glance_api_config('DEFAULT/default_store').with_value('cinder') should contain_glance_api_config('DEFAULT/cinder_api_insecure').with_value(false) should contain_glance_api_config('DEFAULT/cinder_catalog_info').with_value('volume:cinder:publicURL') + should contain_glance_api_config('DEFAULT/os_region_name').with_value('RegionOne') should contain_glance_api_config('DEFAULT/cinder_http_retries').with_value('3') should contain_glance_api_config('DEFAULT/cinder_ca_certificates_file').with(:ensure => 'absent') should contain_glance_api_config('DEFAULT/cinder_endpoint_template').with(:ensure => 'absent') @@ -41,6 +42,7 @@ it 'configures glance-cache.conf' do should contain_glance_cache_config('DEFAULT/cinder_api_insecure').with_value(false) should contain_glance_cache_config('DEFAULT/cinder_catalog_info').with_value('volume:cinder:publicURL') + should contain_glance_cache_config('DEFAULT/os_region_name').with_value('RegionOne') should contain_glance_cache_config('DEFAULT/cinder_http_retries').with_value('3') should contain_glance_cache_config('DEFAULT/cinder_ca_certificates_file').with(:ensure => 'absent') should contain_glance_cache_config('DEFAULT/cinder_endpoint_template').with(:ensure => 'absent') @@ -55,6 +57,7 @@ :cinder_catalog_info => 'volume:cinder:internalURL', :cinder_endpoint_template => 'http://srv-foo:8776/v1/%(project_id)s', :cinder_http_retries => '10', + :os_region_name => 'foo' } end it 'configures glance-api.conf' do @@ -64,6 +67,7 @@ should contain_glance_api_config('DEFAULT/cinder_catalog_info').with_value('volume:cinder:internalURL') should contain_glance_api_config('DEFAULT/cinder_endpoint_template').with_value('http://srv-foo:8776/v1/%(project_id)s') should contain_glance_api_config('DEFAULT/cinder_http_retries').with_value('10') + should contain_glance_api_config('DEFAULT/os_region_name').with_value('foo') end it 'configures glance-cache.conf' do should contain_glance_cache_config('DEFAULT/cinder_api_insecure').with_value(true) @@ -71,6 +75,7 @@ should contain_glance_cache_config('DEFAULT/cinder_catalog_info').with_value('volume:cinder:internalURL') should contain_glance_cache_config('DEFAULT/cinder_endpoint_template').with_value('http://srv-foo:8776/v1/%(project_id)s') should contain_glance_cache_config('DEFAULT/cinder_http_retries').with_value('10') + should contain_glance_cache_config('DEFAULT/os_region_name').with_value('foo') end end diff --git a/glance/spec/classes/glance_backend_swift_spec.rb b/glance/spec/classes/glance_backend_swift_spec.rb index 0c5569a90..3ced3433a 100644 --- a/glance/spec/classes/glance_backend_swift_spec.rb +++ b/glance/spec/classes/glance_backend_swift_spec.rb @@ -25,7 +25,6 @@ should contain_glance_api_config('DEFAULT/swift_store_key').with_value('key') should contain_glance_api_config('DEFAULT/swift_store_user').with_value('user') should contain_glance_api_config('DEFAULT/swift_store_auth_version').with_value('2') - should contain_glance_api_config('DEFAULT/swift_store_large_object_size').with_value('5120') should contain_glance_api_config('DEFAULT/swift_store_auth_address').with_value('127.0.0.1:5000/v2.0/') should contain_glance_api_config('DEFAULT/swift_store_container').with_value('glance') should contain_glance_api_config('DEFAULT/swift_store_create_container_on_put').with_value(false) @@ -35,7 +34,6 @@ should contain_glance_cache_config('DEFAULT/swift_store_key').with_value('key') should contain_glance_cache_config('DEFAULT/swift_store_user').with_value('user') should contain_glance_cache_config('DEFAULT/swift_store_auth_version').with_value('2') - should contain_glance_cache_config('DEFAULT/swift_store_large_object_size').with_value('5120') should contain_glance_cache_config('DEFAULT/swift_store_auth_address').with_value('127.0.0.1:5000/v2.0/') should contain_glance_cache_config('DEFAULT/swift_store_container').with_value('glance') should contain_glance_cache_config('DEFAULT/swift_store_create_container_on_put').with_value(false) @@ -48,7 +46,6 @@ :swift_store_user => 'user', :swift_store_key => 'key', :swift_store_auth_version => '1', - :swift_store_large_object_size => '100', :swift_store_auth_address => '127.0.0.2:8080/v1.0/', :swift_store_container => 'swift', :swift_store_create_container_on_put => true @@ -59,7 +56,6 @@ should contain_glance_api_config('DEFAULT/swift_store_container').with_value('swift') should contain_glance_api_config('DEFAULT/swift_store_create_container_on_put').with_value(true) should contain_glance_api_config('DEFAULT/swift_store_auth_version').with_value('1') - should contain_glance_api_config('DEFAULT/swift_store_large_object_size').with_value('100') should contain_glance_api_config('DEFAULT/swift_store_auth_address').with_value('127.0.0.2:8080/v1.0/') end @@ -67,7 +63,6 @@ should contain_glance_cache_config('DEFAULT/swift_store_container').with_value('swift') should contain_glance_cache_config('DEFAULT/swift_store_create_container_on_put').with_value(true) should contain_glance_cache_config('DEFAULT/swift_store_auth_version').with_value('1') - should contain_glance_cache_config('DEFAULT/swift_store_large_object_size').with_value('100') should contain_glance_cache_config('DEFAULT/swift_store_auth_address').with_value('127.0.0.2:8080/v1.0/') end end diff --git a/glance/spec/classes/glance_backend_vsphere_spec.rb b/glance/spec/classes/glance_backend_vsphere_spec.rb deleted file mode 100644 index 1b1f5465c..000000000 --- a/glance/spec/classes/glance_backend_vsphere_spec.rb +++ /dev/null @@ -1,94 +0,0 @@ -# -# Copyright (C) 2014 Mirantis -# -# Author: Steapn Rogov -# -# 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. -# -# Unit tests for glance::backend::vsphere class -# - -require 'spec_helper' - -describe 'glance::backend::vsphere' do - - let :pre_condition do - 'class { "glance::api": keystone_password => "pass" }' - end - - shared_examples_for 'glance with vsphere backend' do - - context 'when default parameters' do - let :params do - { - :vcenter_host => '10.0.0.1', - :vcenter_user => 'root', - :vcenter_password => '123456', - :vcenter_datacenter => 'Datacenter', - :vcenter_datastore => 'Datastore', - :vcenter_image_dir => '/openstack_glance', - } - end - it 'configures glance-api.conf' do - should contain_glance_api_config('DEFAULT/default_store').with_value('vsphere') - should contain_glance_api_config('DEFAULT/vmware_api_insecure').with_value('False') - should contain_glance_api_config('DEFAULT/vmware_server_host').with_value('10.0.0.1') - should contain_glance_api_config('DEFAULT/vmware_server_username').with_value('root') - should contain_glance_api_config('DEFAULT/vmware_server_password').with_value('123456') - should contain_glance_api_config('DEFAULT/vmware_datastore_name').with_value('Datastore') - should contain_glance_api_config('DEFAULT/vmware_store_image_dir').with_value('/openstack_glance') - should contain_glance_api_config('DEFAULT/vmware_task_poll_interval').with_value('5') - should contain_glance_api_config('DEFAULT/vmware_api_retry_count').with_value('10') - should contain_glance_api_config('DEFAULT/vmware_datacenter_path').with_value('Datacenter') - end - end - - context 'when overriding parameters' do - let :params do - { - :vcenter_host => '10.0.0.1', - :vcenter_user => 'root', - :vcenter_password => '123456', - :vcenter_datacenter => 'Datacenter', - :vcenter_datastore => 'Datastore', - :vcenter_image_dir => '/openstack_glance', - :vcenter_api_insecure => 'True', - :vcenter_task_poll_interval => '6', - :vcenter_api_retry_count => '11', - } - end - it 'configures glance-api.conf' do - should contain_glance_api_config('DEFAULT/vmware_api_insecure').with_value('True') - should contain_glance_api_config('DEFAULT/vmware_task_poll_interval').with_value('6') - should contain_glance_api_config('DEFAULT/vmware_api_retry_count').with_value('11') - end - end - - end - - context 'on Debian platforms' do - let :facts do - { :osfamily => 'Debian' } - end - - it_configures 'glance with vsphere backend' - end - - context 'on RedHat platforms' do - let :facts do - { :osfamily => 'RedHat' } - end - - it_configures 'glance with vsphere backend' - end -end diff --git a/glance/spec/classes/glance_cache_cleaner_spec.rb b/glance/spec/classes/glance_cache_cleaner_spec.rb index a5bb314c9..4a25cb5ad 100644 --- a/glance/spec/classes/glance_cache_cleaner_spec.rb +++ b/glance/spec/classes/glance_cache_cleaner_spec.rb @@ -2,64 +2,21 @@ describe 'glance::cache::cleaner' do - shared_examples_for 'glance cache cleaner' do - - context 'when default parameters' do - - it 'configures a cron' do - should contain_cron('glance-cache-cleaner').with( - :command => 'glance-cache-cleaner ', - :environment => 'PATH=/bin:/usr/bin:/usr/sbin', - :user => 'glance', - :minute => 1, - :hour => 0, - :monthday => '*', - :month => '*', - :weekday => '*' - ) - end - end - - context 'when overriding parameters' do - let :params do - { - :minute => 59, - :hour => 23, - :monthday => '1', - :month => '2', - :weekday => '3', - :command_options => '--config-dir /etc/glance/', - } - end - it 'configures a cron' do - should contain_cron('glance-cache-cleaner').with( - :command => 'glance-cache-cleaner --config-dir /etc/glance/', - :environment => 'PATH=/bin:/usr/bin:/usr/sbin', - :user => 'glance', - :minute => 59, - :hour => 23, - :monthday => '1', - :month => '2', - :weekday => '3' - ) - end - end + let :facts do + { :osfamily => 'Debian' } end - context 'on Debian platforms' do - let :facts do - { :osfamily => 'Debian' } - end - include_examples 'glance cache cleaner' - it { should contain_cron('glance-cache-cleaner').with(:require => 'Package[glance-api]')} + it 'configures a cron' do + should contain_cron('glance-cache-cleaner').with( + :command => 'glance-cache-cleaner', + :environment => 'PATH=/bin:/usr/bin:/usr/sbin', + :user => 'glance', + :minute => 1, + :hour => 0, + :monthday => '*', + :month => '*', + :weekday => '*', + :require => 'Package[glance-api]' + ) end - - context 'on RedHat platforms' do - let :facts do - { :osfamily => 'RedHat' } - end - include_examples 'glance cache cleaner' - it { should contain_cron('glance-cache-cleaner').with(:require => 'Package[openstack-glance]')} - end - end diff --git a/glance/spec/classes/glance_cache_pruner_spec.rb b/glance/spec/classes/glance_cache_pruner_spec.rb index 87bb46ca7..25876a0f3 100644 --- a/glance/spec/classes/glance_cache_pruner_spec.rb +++ b/glance/spec/classes/glance_cache_pruner_spec.rb @@ -2,64 +2,21 @@ describe 'glance::cache::pruner' do - shared_examples_for 'glance cache pruner' do - - context 'when default parameters' do - - it 'configures a cron' do - should contain_cron('glance-cache-pruner').with( - :command => 'glance-cache-pruner ', - :environment => 'PATH=/bin:/usr/bin:/usr/sbin', - :user => 'glance', - :minute => '*/30', - :hour => '*', - :monthday => '*', - :month => '*', - :weekday => '*' - ) - end - end - - context 'when overriding parameters' do - let :params do - { - :minute => 59, - :hour => 23, - :monthday => '1', - :month => '2', - :weekday => '3', - :command_options => '--config-dir /etc/glance/', - } - end - it 'configures a cron' do - should contain_cron('glance-cache-pruner').with( - :command => 'glance-cache-pruner --config-dir /etc/glance/', - :environment => 'PATH=/bin:/usr/bin:/usr/sbin', - :user => 'glance', - :minute => 59, - :hour => 23, - :monthday => '1', - :month => '2', - :weekday => '3' - ) - end - end + let :facts do + { :osfamily => 'Debian' } end - context 'on Debian platforms' do - let :facts do - { :osfamily => 'Debian' } - end - include_examples 'glance cache pruner' - it { should contain_cron('glance-cache-pruner').with(:require => 'Package[glance-api]')} + it 'configures a cron' do + should contain_cron('glance-cache-pruner').with( + :command => 'glance-cache-pruner', + :environment => 'PATH=/bin:/usr/bin:/usr/sbin', + :user => 'glance', + :minute => '*/30', + :hour => '*', + :monthday => '*', + :month => '*', + :weekday => '*', + :require => 'Package[glance-api]' + ) end - - context 'on RedHat platforms' do - let :facts do - { :osfamily => 'RedHat' } - end - include_examples 'glance cache pruner' - it { should contain_cron('glance-cache-pruner').with(:require => 'Package[openstack-glance]')} - end - end diff --git a/glance/spec/classes/glance_keystone_auth_spec.rb b/glance/spec/classes/glance_keystone_auth_spec.rb index 79de6b680..9109973ec 100644 --- a/glance/spec/classes/glance_keystone_auth_spec.rb +++ b/glance/spec/classes/glance_keystone_auth_spec.rb @@ -98,45 +98,6 @@ it { should_not contain_keystone_endpoint('glance') } end - describe 'when disabling user configuration' do - let :params do - { - :configure_user => false, - :password => 'pass', - } - end - - it { should_not contain_keystone_user('glance') } - - it { should contain_keystone_user_role('glance@services') } - - it { should contain_keystone_service('glance').with( - :ensure => 'present', - :type => 'image', - :description => 'Openstack Image Service' - ) } - end - - describe 'when disabling user and user role configuration' do - let :params do - { - :configure_user => false, - :configure_user_role => false, - :password => 'pass', - } - end - - it { should_not contain_keystone_user('glance') } - - it { should_not contain_keystone_user_role('glance@services') } - - it { should contain_keystone_service('glance').with( - :ensure => 'present', - :type => 'image', - :description => 'Openstack Image Service' - ) } - end - describe 'when configuring glance-api and the keystone endpoint' do let :pre_condition do "class { 'glance::api': keystone_password => 'test' }" diff --git a/glance/spec/classes/glance_policy_spec.rb b/glance/spec/classes/glance_policy_spec.rb deleted file mode 100644 index 38ce0faac..000000000 --- a/glance/spec/classes/glance_policy_spec.rb +++ /dev/null @@ -1,41 +0,0 @@ -require 'spec_helper' - -describe 'glance::policy' do - - shared_examples_for 'glance policies' do - let :params do - { - :policy_path => '/etc/glance/policy.json', - :policies => { - 'context_is_admin' => { - 'key' => 'context_is_admin', - 'value' => 'foo:bar' - } - } - } - end - - it 'set up the policies' do - should contain_openstacklib__policy__base('context_is_admin').with({ - :key => 'context_is_admin', - :value => 'foo:bar' - }) - end - end - - context 'on Debian platforms' do - let :facts do - { :osfamily => 'Debian' } - end - - it_configures 'glance policies' - end - - context 'on RedHat platforms' do - let :facts do - { :osfamily => 'RedHat' } - end - - it_configures 'glance policies' - end -end diff --git a/glance/spec/classes/glance_registry_spec.rb b/glance/spec/classes/glance_registry_spec.rb index 28fd59af3..3bcff4654 100644 --- a/glance/spec/classes/glance_registry_spec.rb +++ b/glance/spec/classes/glance_registry_spec.rb @@ -18,7 +18,6 @@ :database_connection => 'sqlite:///var/lib/glance/glance.sqlite', :database_idle_timeout => '3600', :enabled => true, - :manage_service => true, :auth_type => 'keystone', :auth_host => '127.0.0.1', :auth_port => '35357', @@ -64,7 +63,7 @@ it { should contain_class 'glance::registry' } it { should contain_service('glance-registry').with( - 'ensure' => (param_hash[:manage_service] && param_hash[:enabled]) ? 'running' : 'stopped', + 'ensure' => param_hash[:enabled] ? 'running' : 'stopped', 'enable' => param_hash[:enabled], 'hasstatus' => true, 'hasrestart' => true, @@ -118,25 +117,6 @@ end end - describe 'with disabled service managing' do - let :params do - { - :keystone_password => 'ChangeMe', - :manage_service => false, - :enabled => false, - } - end - - it { should contain_service('glance-registry').with( - 'ensure' => nil, - 'enable' => false, - 'hasstatus' => true, - 'hasrestart' => true, - 'subscribe' => 'File[/etc/glance/glance-registry.conf]', - 'require' => 'Class[Glance]' - )} - end - describe 'with overridden pipeline' do # At the time of writing there was only blank and keystone as options # but there is no reason that there can't be more options in the future. diff --git a/glance/spec/unit/provider/glance_spec.rb b/glance/spec/unit/provider/glance_spec.rb index 14d418b97..0834ce01a 100644 --- a/glance/spec/unit/provider/glance_spec.rb +++ b/glance/spec/unit/provider/glance_spec.rb @@ -35,25 +35,19 @@ 'admin_tenant_name' => 'foo', 'admin_user' => 'user', 'admin_password' => 'pass' - }, - 'DEFAULT' => - { - 'os_region_name' => 'SomeRegion', } } Puppet::Util::IniConfig::File.expects(:new).returns(mock) mock.expects(:read).with('/etc/glance/glance-api.conf') klass.expects(:sleep).with(10).returns(nil) klass.expects(:glance).twice.with( - '--os-tenant-name', + '-T', 'foo', - '--os-username', + '-I', 'user', - '--os-password', + '-K', 'pass', - '--os-region-name', - 'SomeRegion', - '--os-auth-url', + '-N', 'http://127.0.0.1:35357/v2.0/', ['test_retries'] ).raises(Exception, valid_message).then.returns('') diff --git a/gluster/.gitignore b/gluster/.gitignore deleted file mode 100644 index 729179bea..000000000 --- a/gluster/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -old/ -tmp/ -pkg/ -docs/ -hacking/ -rpmbuild/ -screencasts/ diff --git a/gluster/.gitmodules b/gluster/.gitmodules deleted file mode 100644 index bc295c271..000000000 --- a/gluster/.gitmodules +++ /dev/null @@ -1,24 +0,0 @@ -[submodule "vagrant/puppet/modules/stdlib"] - path = vagrant/puppet/modules/stdlib - url = https://github.com/purpleidea/puppetlabs-stdlib.git -[submodule "vagrant/puppet/modules/apt"] - path = vagrant/puppet/modules/apt - url = https://github.com/purpleidea/puppetlabs-apt.git -[submodule "vagrant/puppet/modules/common"] - path = vagrant/puppet/modules/common - url = https://github.com/purpleidea/puppet-common.git -[submodule "vagrant/puppet/modules/keepalived"] - path = vagrant/puppet/modules/keepalived - url = https://github.com/purpleidea/puppet-keepalived.git -[submodule "vagrant/puppet/modules/puppet"] - path = vagrant/puppet/modules/puppet - url = https://github.com/purpleidea/puppet-puppet.git -[submodule "vagrant/puppet/modules/shorewall"] - path = vagrant/puppet/modules/shorewall - url = https://github.com/purpleidea/puppet-shorewall.git -[submodule "vagrant/puppet/modules/yum"] - path = vagrant/puppet/modules/yum - url = https://github.com/purpleidea/puppet-yum.git -[submodule "vagrant/puppet/modules/module-data"] - path = vagrant/puppet/modules/module-data - url = https://github.com/purpleidea/puppet-module-data.git diff --git a/gluster/COPYING b/gluster/COPYING deleted file mode 100644 index dba13ed2d..000000000 --- a/gluster/COPYING +++ /dev/null @@ -1,661 +0,0 @@ - GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -our General Public Licenses are intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - Developers that use our General Public Licenses protect your rights -with two steps: (1) assert copyright on the software, and (2) offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - - A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - - The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - - An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing under -this license. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU Affero General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Remote Network Interaction; Use with the GNU General Public License. - - Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your version -supports such interaction) an opportunity to receive the Corresponding -Source of your version by providing access to the Corresponding Source -from a network server at no charge, through some standard or customary -means of facilitating copying of software. This Corresponding Source -shall include the Corresponding Source for any work covered by version 3 -of the GNU General Public License that is incorporated pursuant to the -following paragraph. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the work with which it is combined will remain governed by version -3 of the GNU General Public License. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU Affero General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU Affero General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU Affero General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU Affero General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If your software can interact with users remotely through a computer -network, you should also make sure that it provides a way for users to -get its source. For example, if your program is a web application, its -interface could display a "Source" link that leads users to an archive -of the code. There are many ways you could offer source, and different -solutions will be better for different programs; see section 13 for the -specific requirements. - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU AGPL, see -. diff --git a/gluster/COPYRIGHT b/gluster/COPYRIGHT deleted file mode 100644 index 480149fb7..000000000 --- a/gluster/COPYRIGHT +++ /dev/null @@ -1,16 +0,0 @@ -Copyright (C) 2012-2013+ James Shubin -Written by James Shubin - -This puppet module is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This puppet module is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . - diff --git a/gluster/DOCUMENTATION.md b/gluster/DOCUMENTATION.md deleted file mode 100644 index e1cc82c3c..000000000 --- a/gluster/DOCUMENTATION.md +++ /dev/null @@ -1,964 +0,0 @@ -#Puppet-Gluster - -##A GlusterFS Puppet module by [James](https://ttboj.wordpress.com/) -####Available from: -####[https://github.com/purpleidea/puppet-gluster/](https://github.com/purpleidea/puppet-gluster/) - -####Also available from: -####[https://forge.gluster.org/puppet-gluster/](https://forge.gluster.org/puppet-gluster/) - -####This documentation is available in: [Markdown](https://github.com/purpleidea/puppet-gluster/blob/master/DOCUMENTATION.md) or [PDF](https://github.com/purpleidea/puppet-gluster/raw/master/puppet-gluster-documentation.pdf) format. - -####Table of Contents - -1. [Overview](#overview) -2. [Module description - What the module does](#module-description) -3. [Setup - Getting started with Puppet-Gluster](#setup) - * [What can Puppet-Gluster manage?](#what-can-puppet-gluster-manage) - * [Simple setup](#simple-setup) - * [Elastic setup](#elastic-setup) - * [Advanced setup](#advanced-setup) - * [Client setup](#client-setup) -4. [Usage/FAQ - Notes on management and frequently asked questions](#usage-and-frequently-asked-questions) -5. [Reference - Class and type reference](#reference) - * [gluster::simple](#glustersimple) - * [gluster::elastic](#glusterelastic) - * [gluster::server](#glusterserver) - * [gluster::host](#glusterhost) - * [gluster::brick](#glusterbrick) - * [gluster::volume](#glustervolume) - * [gluster::volume::property](#glustervolumeproperty) - * [gluster::mount](#glustermount) -6. [Examples - Example configurations](#examples) -7. [Limitations - Puppet versions, OS compatibility, etc...](#limitations) -8. [Development - Background on module development and reporting bugs](#development) -9. [Author - Author and contact information](#author) - -##Overview - -The Puppet-Gluster module installs, configures, and manages a GlusterFS cluster. - -##Module Description - -This Puppet-Gluster module handles installation, configuration, and management -of GlusterFS across all of the hosts in the cluster. - -##Setup - -###What can Puppet-Gluster manage? - -Puppet-Gluster is designed to be able to manage as much or as little of your -GlusterFS cluster as you wish. All features are optional. If there is a feature -that doesn't appear to be optional, and you believe it should be, please let me -know. Having said that, it makes good sense to me to have Puppet-Gluster manage -as much of your GlusterFS infrastructure as it can. At the moment, it cannot -rack new servers, but I am accepting funding to explore this feature ;) At the -moment it can manage: - -* GlusterFS packages (rpm) -* GlusterFS configuration files (/var/lib/glusterd/) -* GlusterFS host peering (gluster peer probe) -* GlusterFS storage partitioning (fdisk) -* GlusterFS storage formatting (mkfs) -* GlusterFS brick creation (mkdir) -* GlusterFS services (glusterd) -* GlusterFS firewalling (whitelisting) -* GlusterFS volume creation (gluster volume create) -* GlusterFS volume state (started/stopped) -* GlusterFS volume properties (gluster volume set) -* And much more... - -###Simple setup - -include '::gluster::simple' is enough to get you up and running. When using the -gluster::simple class, or with any other Puppet-Gluster configuration, -identical definitions must be used on all hosts in the cluster. The simplest -way to accomplish this is with a single shared puppet host definition like: - -```puppet -node /^annex\d+$/ { # annex{1,2,..N} - class { '::gluster::simple': - } -} -``` - -If you wish to pass in different parameters, you can specify them in the class -before you provision your hosts: - -```puppet -class { '::gluster::simple': - replica => 2, - volume => ['volume1', 'volume2', 'volumeN'], -} -``` - -###Elastic setup - -The gluster::elastic class is not yet available. Stay tuned! - -###Advanced setup - -Some system administrators may wish to manually itemize each of the required -components for the Puppet-Gluster deployment. This happens automatically with -the higher level modules, but may still be a desirable feature, particularly -for non-elastic storage pools where the configuration isn't expected to change -very often (if ever). - -To put together your cluster piece by piece, you must manually include and -define each class and type that you wish to use. If there are certain aspects -that you wish to manage yourself, you can omit them from your configuration. -See the [reference](#reference) section below for the specifics. Here is one -possible example: - -```puppet -class { '::gluster::server': - shorewall => true, -} - -gluster::host { 'annex1.example.com': - # use uuidgen to make these - uuid => '1f660ca2-2c78-4aa0-8f4d-21608218c69c', -} - -# note that this is using a folder on your existing file system... -# this can be useful for prototyping gluster using virtual machines -# if this isn't a separate partition, remember that your root fs will -# run out of space when your gluster volume does! -gluster::brick { 'annex1.example.com:/data/gluster-storage1': - areyousure => true, -} - -gluster::host { 'annex2.example.com': - # NOTE: specifying a host uuid is now optional! - # if you don't choose one, one will be assigned - #uuid => '2fbe6e2f-f6bc-4c2d-a301-62fa90c459f8', -} - -gluster::brick { 'annex2.example.com:/data/gluster-storage2': - areyousure => true, -} - -$brick_list = [ - 'annex1.example.com:/data/gluster-storage1', - 'annex2.example.com:/data/gluster-storage2', -] - -gluster::volume { 'examplevol': - replica => 2, - bricks => $brick_list, - start => undef, # i'll start this myself -} - -# namevar must be: # -gluster::volume::property { 'examplevol#auth.reject': - value => ['192.0.2.13', '198.51.100.42', '203.0.113.69'], -} -``` - -###Client setup - -Mounting a GlusterFS volume on a client is fairly straightforward. Simply use -the 'gluster::mount' type. - -```puppet - gluster::mount { '/mnt/gluster/puppet/': - server => 'annex.example.com:/puppet', - rw => true, - shorewall => false, - } -``` - -In this example, 'annex.example.com' points to the VIP of the GlusterFS -cluster. Using the VIP for mounting increases the chance that you'll get an -available server when you try to mount. This generally works better than RRDNS -or similar schemes. - -##Usage and frequently asked questions - -All management should be done by manipulating the arguments on the appropriate -Puppet-Gluster classes and types. Since certain manipulations are either not -yet possible with Puppet-Gluster, or are not supported by GlusterFS, attempting -to manipulate the Puppet configuration in an unsupported way will result in -undefined behaviour, and possible even data loss, however this is unlikely. - -###How do I change the replica count? - -You must set this before volume creation. This is a limitation of GlusterFS. -There are certain situations where you can change the replica count by adding -a multiple of the existing brick count to get this desired effect. These cases -are not yet supported by Puppet-Gluster. If you want to use Puppet-Gluster -before and / or after this transition, you can do so, but you'll have to do the -changes manually. - -###Do I need to use a virtual IP? - -Using a virtual IP (VIP) is strongly recommended as a distributed lock manager -(DLM) and also to provide a highly-available (HA) IP address for your clients -to connect to. For a more detailed explanation of the reasoning please see: - -[How to avoid cluster race conditions or: How to implement a distributed lock manager in puppet](https://ttboj.wordpress.com/2012/08/23/how-to-avoid-cluster-race-conditions-or-how-to-implement-a-distributed-lock-manager-in-puppet/) - -Remember that even if you're using a hosted solution (such as AWS) that doesn't -provide an additional IP address, or you want to avoid using an additional IP, -and you're okay not having full HA client mounting, you can use an unused -private RFC1918 IP address as the DLM VIP. Remember that a layer 3 IP can -co-exist on the same layer 2 network with the layer 3 network that is used by -your cluster. - -###Is it possible to have Puppet-Gluster complete in a single run? - -No. This is a limitation of Puppet, and is related to how GlusterFS operates. -For example, it is not reliably possible to predict which ports a particular -GlusterFS volume will run on until after the volume is started. As a result, -this module will initially whitelist connections from GlusterFS host IP -addresses, and then further restrict this to only allow individual ports once -this information is known. This is possible in conjunction with the -[puppet-shorewall](https://github.com/purpleidea/puppet-shorewall) module. -You should notice that each run should complete without error. If you do see an -error, it means that either something is wrong with your system and / or -configuration, or because there is a bug in Puppet-Gluster. - -###Can you integrate this with vagrant? - -Yes, see the -[vagrant/](https://github.com/purpleidea/puppet-gluster/tree/master/vagrant) -directory. This has been tested on Fedora 20, with vagrant-libvirt, as I have -no desire to use VirtualBox for fun. I have written an article about this: - -[Automatically deploying GlusterFS with Puppet-Gluster + Vagrant!](https://ttboj.wordpress.com/2014/01/08/automatically-deploying-glusterfs-with-puppet-gluster-vagrant/) - -You'll probably first need to read my three earlier articles to learn some -vagrant tricks, and to get the needed dependencies installed: - -* [Vagrant on Fedora with libvirt](https://ttboj.wordpress.com/2013/12/09/vagrant-on-fedora-with-libvirt/) -* [Vagrant vsftp and other tricks](https://ttboj.wordpress.com/2013/12/21/vagrant-vsftp-and-other-tricks/) -* [Vagrant clustered SSH and ‘screen’](https://ttboj.wordpress.com/2014/01/02/vagrant-clustered-ssh-and-screen/) - -###Puppet runs fail with "Invalid relationship" errors. - -When running Puppet, you encounter a compilation failure like: - -```bash -Error: Could not retrieve catalog from remote server: -Error 400 on SERVER: Invalid relationship: Exec[gluster-volume-stuck-volname] { -require => Gluster::Brick[annex2.example.com:/var/lib/puppet/tmp/gluster/data/] -}, because Gluster::Brick[annex2.example.com:/var/lib/puppet/tmp/gluster/data/] -doesn't seem to be in the catalog -Warning: Not using cache on failed catalog -Error: Could not retrieve catalog; skipping run -``` - -This can occur if you have changed (usually removed) the available bricks, but -have not cleared the exported resources on the Puppet master, or if there are -stale (incorrect) brick "tags" on the individual host. These tags can usually -be found in the _/var/lib/puppet/tmp/gluster/brick/_ directory. In other words, -when a multi host cluster comes up, each puppet agent tells the master about -which bricks it has available, and each agent also pulls down this list and -stores it in the brick directory. If there is a discrepancy, then the compile -will fail because the individual host is using old data as part of its facts -when it uses the stale brick data as part of its compilation. - -This commonly happens if you're trying to deploy a different Puppet-Gluster -setup without having first erased the host specific exported resources on the -Puppet master or if the machine hasn't been re-provisioned from scratch. - -To solve this problem, do a clean install, and make sure that you've cleaned -the Puppet master with: - -```bash -puppet node deactivate HOSTNAME -``` - -for each host you're using, and that you've removed all of the files from the -brick directories on each host. - -###Puppet runs fail with "Connection refused - connect(2)" errors. - -You may see a "_Connection refused - connect(2)_" message when running puppet. -This typically happens if your puppet vm guest is overloaded. When running high -guest counts on your laptop, or running without hardware virtualization support -this is quite common. Another common causes of this is if your domain type is -set to _qemu_ instead of the accelerated _kvm_. Since the _qemu_ domain type is -much slower, puppet timeouts and failures are common when it doesn't respond. - -###Provisioning fails with: "Can't open /dev/sdb1 exclusively." - -If when provisioning you get an error like: - -_"Can't open /dev/sdb1 exclusively. Mounted filesystem?"_ - -It is possible that dracut might have found an existing logical volume on the -device, and device mapper has made it available. This is common if you are -re-using dirty block devices that haven't run through a _dd_ first. Here is an -example of the diagnosis and treatment of this problem: - -```bash -[root@server mapper]# pwd -/dev/mapper -[root@server mapper]# dmesg | grep dracut -dracut: dracut-004-336.el6_5.2 -dracut: rd_NO_LUKS: removing cryptoluks activation -dracut: Starting plymouth daemon -dracut: rd_NO_DM: removing DM RAID activation -dracut: rd_NO_MD: removing MD RAID activation -dracut: Scanning devices sda3 sdb for LVM logical volumes myvg/rootvol -dracut: inactive '/dev/vg_foo/lv' [4.35 TiB] inherit -dracut: inactive '/dev/myvg/rootvol' [464.00 GiB] inherit -dracut: Mounted root filesystem /dev/mapper/myvg-rootvol -dracut: Loading SELinux policy -dracut: -dracut: Switching root -[root@server mapper]# /sbin/pvcreate --dataalignment 2560K /dev/sdb1 - Can't open /dev/sdb1 exclusively. Mounted filesystem? -[root@server mapper]# ls -control myvg-rootvol vg_foo-lv -[root@server mapper]# ls -lAh -total 0 -crw-rw----. 1 root root 10, 58 Mar 7 16:42 control -lrwxrwxrwx. 1 root root 7 Mar 13 09:56 myvg-rootvol -> ../dm-0 -lrwxrwxrwx. 1 root root 7 Mar 13 09:56 vg_foo-lv -> ../dm-1 -[root@server mapper]# dmsetup remove vg_foo-lv -[root@server mapper]# ls -control myvg-rootvol -[root@server mapper]# pvcreate --dataalignment 2560K /dev/sdb1 - Physical volume "/dev/sdb1" successfully created -[root@server mapper]# HAPPY_ADMIN='yes' -``` - -If you frequently start with "dirty" block devices, you may consider adding a -_dd_ to your hardware provisioning step. The downside is that this can be very -time consuming, and potentially dangerous if you accidentally re-provision the -wrong machine. - -###Provisioning fails with: "cannot open /dev/sdb1: Device or resource busy" - -If when provisioning you get an error like: - -_"mkfs.xfs: cannot open /dev/sdb1: Device or resource busy"_ - -It is possible that dracut might have found an existing logical volume on the -device, and device mapper has made it available. This is common if you are -re-using dirty block devices that haven't run through a _dd_ first. This is -almost identical to the previous frequently asked question, although this -failure message is what is seen when _mkfs.xfs_ is being blocked by dracut, -where in the former problem it was the _pvcreate_ that was being blocked. The -reason that we see this manifest through _mkfs.xfs_ instead of _pvcreate_ is -that this particular cluster is being build with _lvm => false_. Here is an -example of the diagnosis and treatment of this problem: - -```bash -[root@server mapper]# pwd -/dev/mapper -[root@server mapper]# dmesg | grep dracut -dracut: dracut-004-335.el6 -dracut: rd_NO_LUKS: removing cryptoluks activation -dracut: Starting plymouth daemon -dracut: rd_NO_DM: removing DM RAID activation -dracut: rd_NO_MD: removing MD RAID activation -dracut: Scanning devices sda2 sdb for LVM logical volumes vg_server/lv_swap vg_server/lv_root -dracut: inactive '/dev/vg_bricks/b1' [9.00 TiB] inherit -dracut: inactive '/dev/vg_server/lv_root' [50.00 GiB] inherit -dracut: inactive '/dev/vg_server/lv_home' [383.26 GiB] inherit -dracut: inactive '/dev/vg_server/lv_swap' [31.50 GiB] inherit -dracut: Mounted root filesystem /dev/mapper/vg_server-lv_root -dracut: -dracut: Switching root -[root@server mapper]# mkfs.xfs -q -f -i size=512 -n size=8192 /dev/sdb1 -mkfs.xfs: cannot open /dev/sdb1: Device or resource busy -[root@server mapper]# lsof /dev/sdb1 -[root@server mapper]# echo $? -1 -[root@server mapper]# ls -control vg_server-lv_home vg_server-lv_swap -vg_bricks-b1 vg_server-lv_root -[root@server mapper]# ls -lAh -total 0 -crw-rw---- 1 root root 10, 58 May 20 2014 control -lrwxrwxrwx 1 root root 7 May 20 2014 vg_bricks-b1 -> ../dm-2 -lrwxrwxrwx 1 root root 7 May 20 2014 vg_server-lv_home -> ../dm-3 -lrwxrwxrwx 1 root root 7 May 20 2014 vg_server-lv_root -> ../dm-0 -lrwxrwxrwx 1 root root 7 May 20 2014 vg_server-lv_swap -> ../dm-1 -[root@server mapper]# dmsetup remove vg_bricks-b1 -[root@server mapper]# ls -control vg_server-lv_home vg_server-lv_root vg_server-lv_swap -[root@server mapper]# mkfs.xfs -q -f -i size=512 -n size=8192 /dev/sdb1 -[root@server mapper]# echo $? -0 -[root@server mapper]# HAPPY_ADMIN='yes' -``` - -If you frequently start with "dirty" block devices, you may consider adding a -_dd_ to your hardware provisioning step. The downside is that this can be very -time consuming, and potentially dangerous if you accidentally re-provision the -wrong machine. - -###I changed the hardware manually, and now my system won't boot. - -If you're using Puppet-Gluster to manage storage, the filesystem will be -mounted with _UUID_ entries in _/etc/fstab_. This ensures that the correct -filesystem will be mounted, even if the device order changes. If a filesystem -is not available at boot time, startup will abort and offer you the chance to -go into read-only maintenance mode. Either fix the hardware issue, or edit the -_/etc/fstab_ file. - - -###I can't edit /etc/fstab in the maintenance shell because it is read-only. - -In the maintenance shell, your root filesystem will be mounted read-only, to -prevent changes. If you need to edit a file such as _/etc/fstab_, you'll first -need to remount the root filesystem in read-write mode. You can do this with: - -```bash -mount -n -o remount / -``` - -###I get a file dependency error when running Puppet-Gluster. - -In order for Puppet-Gluster to be able to do its magic, it needs to store some -temporary files on each GlusterFS host. These files usually get stored in: -_/var/lib/puppet/tmp/gluster/_. The parent directory (_/var/lib/puppet/tmp/_) -gets created by the _puppet::vardir_ module. The error you'll typically see is: - -```bash -Error: Failed to apply catalog: Could not find dependency -File[/var/lib/puppet/tmp/] for File[/var/lib/puppet/tmp/gluster/] at -/etc/puppet/modules/gluster/manifests/vardir.pp:49 -``` - -This error occurs if you forget to _include_ the _puppet::vardir_ class from -the [puppet-puppet](https://github.com/purpleidea/puppet-puppet/) module. If -you don't want to include the entire module, you can pull in the -_puppet::vardir_ class by itself, or create the contained file type manually in -your puppet manifests. - -###Will this work on my favourite OS? (eg: GNU/Linux F00bar OS v12 ?) -If it's a GNU/Linux based OS, can run GlusterFS, and Puppet, then it will -probably work. Typically, you might need to add a yaml data file to the _data/_ -folder so that Puppet-Gluster knows where certain operating system specific -things are found. The multi-distro support has been designed to make it -particularly easy to add support for additional platforms. If your platform -doesn't work, please submit a yaml data file with the platform specific values. - -###Awesome work, but it's missing support for a feature and/or platform! - -Since this is an Open Source / Free Software project that I also give away for -free (as in beer, free as in gratis, free as in libre), I'm unable to provide -unlimited support. Please consider donating funds, hardware, virtual machines, -and other resources. For specific needs, you could perhaps sponsor a feature! - -###You didn't answer my question, or I have a question! - -Contact me through my [technical blog](https://ttboj.wordpress.com/contact/) -and I'll do my best to help. If you have a good question, please remind me to -add my answer to this documentation! - -##Reference -Please note that there are a number of undocumented options. For more -information on these options, please view the source at: -[https://github.com/purpleidea/puppet-gluster/](https://github.com/purpleidea/puppet-gluster/). -If you feel that a well used option needs documenting here, please contact me. - -###Overview of classes and types - -* [gluster::simple](#glustersimple): Simple Puppet-Gluster deployment. -* [gluster::elastic](#glusterelastic): Under construction. -* [gluster::server](#glusterserver): Base class for server hosts. -* [gluster::host](#glusterhost): Host type for each participating host. -* [gluster::brick](#glusterbrick): Brick type for each defined brick, per host. -* [gluster::volume](#glustervolume): Volume type for each defined volume. -* [gluster::volume::property](#glustervolumeproperty): Manages properties for each volume. -* [gluster::mount](#glustermount): Client volume mount point management. - -###gluster::simple -This is gluster::simple. It should probably take care of 80% of all use cases. -It is particularly useful for deploying quick test clusters. It uses a -finite-state machine (FSM) to decide when the cluster has settled and volume -creation can begin. For more information on the FSM in Puppet-Gluster see: -[https://ttboj.wordpress.com/2013/09/28/finite-state-machines-in-puppet/](https://ttboj.wordpress.com/2013/09/28/finite-state-machines-in-puppet/) - -####`replica` -The replica count. Can't be changed automatically after initial deployment. - -####`volume` -The volume name or list of volume names to create. - -####`path` -The valid brick path for each host. Defaults to local file system. If you need -a different path per host, then Gluster::Simple will not meet your needs. - -####`count` -Number of bricks to build per host. This value is used unless _brick_params_ is -being used. - -####`vip` -The virtual IP address to be used for the cluster distributed lock manager. -This option can be used in conjunction with the _vrrp_ option, but it does not -require it. If you don't want to provide a virtual ip, but you do want to -enforce that certain operations only run on one host, then you can set this -option to be the ip address of an arbitrary host in your cluster. Keep in mind -that if that host is down, certain options won't ever occur. - -####`vrrp` -Whether to automatically deploy and manage _Keepalived_ for use as a _DLM_ and -for use in volume mounting, etc... Using this option requires the _vip_ option. - -####`layout` -Which brick layout to use. The available options are: _chained_, and (default). -To generate a default (symmetrical, balanced) layout, leave this option blank. -If you'd like to include an algorithm that generates a different type of brick -layout, it is easy to drop in an algorithm. Please contact me with the details! - -####`version` -Which version of GlusterFS do you want to install? This is especially handy -when testing new beta releases. You can read more about the technique at: -[Testing GlusterFS during Glusterfest](https://ttboj.wordpress.com/2014/01/16/testing-glusterfs-during-glusterfest/). - -####`repo` -Whether or not to add the necessary software repositories to install the needed -packages. This will typically pull in GlusterFS from _download.gluster.org_ and -should be set to false if you have your own mirrors or repositories managed as -part of your base image. - -####`brick_params` -This parameter lets you specify a hash to use when creating the individual -bricks. This is especially useful because it lets you have the power of -Gluster::Simple when managing a cluster of iron (physical machines) where you'd -like to specify brick specific parameters. This sets the brick count when the -_count_ parameter is 0. The format of this parameter might look like: - -```bash -$brick_params = { - fqdn1 => [ - {dev => '/dev/disk/by-uuid/01234567-89ab-cdef-0123-456789abcdef'}, - {dev => '/dev/sdc', partition => false}, - ], - fqdn2 => [{ - dev => '/dev/disk/by-path/pci-0000:02:00.0-scsi-0:1:0:0', - raid_su => 256, raid_sw => 10, - }], - fqdnN => [...], -} -``` - -####`brick_param_defaults` -This parameter lets you specify a hash of defaults to use when creating each -brick with the _brick_params_ parameter. It is useful because it avoids the -need to repeat the values that are common across all bricks in your cluster. -Since most options work this way, this is an especially nice feature to have. -The format of this parameter might look like: - -```bash -$brick_param_defaults = { - lvm => false, - xfs_inode64 => true, - force => true, -} -``` - -####`brick_params_defaults` -This parameter lets you specify a list of defaults to use when creating each -brick. Each element in the list represents a different brick. The value of each -element is a hash with the actual defaults that you'd like to use for creating -that brick. If you do not specify a brick count by any other method, then the -number of elements in this array will be used as the brick count. This is very -useful if you have consistent device naming across your entire cluster, because -you can very easily specify the devices and brick counts once for all hosts. If -for some reason a particular device requires unique values, then it can be set -manually with the _brick_params_ parameter. Please note the spelling of this -parameter. It is not the same as the _brick_param_defaults_ parameter which is -a global defaults parameter which will apply to all bricks. -The format of this parameter might look like: - -```bash -$brick_params_defaults = [ - {'dev' => '/dev/sdb'}, - {'dev' => '/dev/sdc'}, - {'dev' => '/dev/sdd'}, - {'dev' => '/dev/sde'}, -] -``` - -####`setgroup` -Set a volume property group. The two most common or well-known groups are the -_virt_ group, and the _small-file-perf_ group. This functionality is emulated -whether you're using the RHS version of GlusterFS or if you're using the -upstream GlusterFS project, which doesn't (currently) have the _volume set -group_ command. As package managers update the list of available groups or -their properties, Puppet-Gluster will automatically keep your set group -up-to-date. It is easy to extend Puppet-Gluster to add a custom group without -needing to patch the GlusterFS source. - -####`ping` -Whether to use _fping_ or not to help with ensuring the required hosts are -available before doing certain types of operations. Optional, but recommended. -Boolean value. - -####`again` -Do you want to use _Exec['again']_ ? This helps build your cluster quickly! - -####`baseport` -Specify the base port option as used in the glusterd.vol file. This is useful -if the default port range of GlusterFS conflicts with the ports used for -virtual machine migration, or if you simply like to choose the ports that -you're using. Integer value. - -####`rpcauthallowinsecure` -This is needed in some setups in the glusterd.vol file, particularly (I think) -for some users of _libgfapi_. Boolean value. - -####`shorewall` -Boolean to specify whether puppet-shorewall integration should be used or not. - -###gluster::elastic -Under construction. - -###gluster::server -Main server class for the cluster. Must be included when building the GlusterFS -cluster manually. Wrapper classes such as [gluster::simple](#glustersimple) -include this automatically. - -####`vip` -The virtual IP address to be used for the cluster distributed lock manager. - -####`shorewall` -Boolean to specify whether puppet-shorewall integration should be used or not. - -###gluster::host -Main host type for the cluster. Each host participating in the GlusterFS -cluster must define this type on itself, and on every other host. As a result, -this is not a singleton like the [gluster::server](#glusterserver) class. - -####`ip` -Specify which IP address this host is using. This defaults to the -_$::ipaddress_ variable. Be sure to set this manually if you're declaring this -yourself on each host without using exported resources. If each host thinks the -other hosts should have the same IP address as itself, then Puppet-Gluster and -GlusterFS won't work correctly. - -####`uuid` -Universally unique identifier (UUID) for the host. If empty, Puppet-Gluster -will generate this automatically for the host. You can generate your own -manually with _uuidgen_, and set them yourself. I found this particularly -useful for testing, because I would pick easy to recognize UUID's like: -_aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa_, -_bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb_, and so on. If you set a UUID manually, -and Puppet-Gluster has a chance to run, then it will remember your choice, and -store it locally to be used again if you no longer specify the UUID. This is -particularly useful for upgrading an existing un-managed GlusterFS installation -to a Puppet-Gluster managed one, without changing any UUID's. - -###gluster::brick -Main brick type for the cluster. Each brick is an individual storage segment to -be used on a host. Each host must have at least one brick to participate in the -cluster, but usually a host will have multiple bricks. A brick can be as simple -as a file system folder, or it can be a separate file system. Please read the -official GlusterFS documentation, if you aren't entirely comfortable with the -concept of a brick. - -For most test clusters, and for experimentation, it is easiest to use a -directory on the root file system. You can even use a _/tmp_ sub folder if you -don't care about the persistence of your data. For more serious clusters, you -might want to create separate file systems for your data. On self-hosted iron, -it is not uncommon to create multiple RAID-6 drive pools, and to then create a -separate file system per virtual drive. Each file system can then be used as a -single brick. - -So that each volume in GlusterFS has the maximum ability to grow, without -having to partition storage separately, the bricks in Puppet-Gluster are -actually folders (on whatever backing store you wish) which then contain -sub folders-- one for each volume. As a result, all the volumes on a given -GlusterFS cluster can share the total available storage space. If you wish to -limit the storage used by each volume, you can setup quotas. Alternatively, you -can buy more hardware, and elastically grow your GlusterFS volumes, since the -price per GB will be significantly less than any proprietary storage system. -The one downside to this brick sharing, is that if you have chosen the brick -per host count specifically to match your performance requirements, and -each GlusterFS volume on the same cluster has drastically different brick per -host performance requirements, then this won't suit your needs. I doubt that -anyone actually has such requirements, but if you do insist on needing this -compartmentalization, then you can probably use the Puppet-Gluster grouping -feature to accomplish this goal. Please let me know about your use-case, and -be warned that the grouping feature hasn't been extensively tested. - -To prove to you that I care about automation, this type offers the ability to -automatically partition and format your file systems. This means you can plug -in new iron, boot, provision and configure the entire system automatically. -Regrettably, I don't have a lot of test hardware to routinely use this feature. -If you'd like to donate some, I'd be happy to test this thoroughly. Having said -that, I have used this feature, I consider it to be extremely safe, and it has -never caused me to lose data. If you're uncertain, feel free to look at the -code, or avoid using this feature entirely. If you think there's a way to make -it even safer, then feel free to let me know. - -####`dev` -Block device, such as _/dev/sdc_ or _/dev/disk/by-id/scsi-0123456789abcdef_. By -default, Puppet-Gluster will assume you're using a folder to store the brick -data, if you don't specify this parameter. - -####`raid_su` -Get this information from your RAID device. This is used to do automatic -calculations for alignment, so that the: - -``` - dev -> part -> lvm -> fs -``` - -stack is aligned properly. Future work is possible to manage your RAID devices, -and to read these values automatically. Specify this value as an integer number -of kilobytes (k). - -####`raid_sw` -Get this information from your RAID device. This is used to do automatic -calculations for alignment, so that the: - -``` - dev -> part -> lvm -> fs -``` - -stack is aligned properly. Future work is possible to manage your RAID devices, -and to read these values automatically. Specify this value as an integer. - -####`partition` -Do you want to partition the device and build the next layer on that partition, -or do you want to build on the block device directly? The "next layer" will -typically be lvm if you're using lvm, or your file system (such as xfs) if -you're skipping the lvm layer. - -####`labeltype` -Only _gpt_ is supported. Other options include _msdos_, but this has never been -used because of it's size limitations. - -####`lvm` -Do you want to use lvm on the lower level device (typically a partition, or the -device itself), or not. Using lvm might be required when using a commercially -supported GlusterFS solution. - -####`lvm_thinp` -Set to _true_ to enable LVM thin provisioning. Read 'man 7 lvmthin' to -understand what thin provisioning is all about. This is needed for one form of -GlusterFS snapshots. Obviously this requires that you also enable _LVM_. - -####`lvm_virtsize` -The value that will be passed to _--virtualsize_. By default this will pass in -a command that will return the size of your volume group. This is usually a -sane value, and help you to remember not to overcommit. - -####`lvm_chunksize` -Value of _--chunksize_ for _lvcreate_ when using thin provisioning. - -####`lvm_metadatasize` -Value of _--poolmetadatasize_ for _lvcreate_ when using thin provisioning. - -####`fsuuid` -File system UUID. This ensures we can distinctly identify a file system. You -can set this to be used with automatic file system creation, or you can specify -the file system UUID that you'd like to use. If you leave this blank, then -Puppet-Gluster can automatically pick an fs UUID for you. This is especially -useful if you are automatically deploying a large cluster on physical iron. - -####`fstype` -This should be _xfs_ or _ext4_. Using _xfs_ is recommended, but _ext4_ is also -quite common. This only affects a file system that is getting created by this -module. If you provision a new machine, with a root file system of _ext4_, and -the brick you create is a root file system path, then this option does nothing. -A _btrfs_ option is now available for testing. It is not officially supported -by GlusterFS, but testing it anyways, and reporting any issues is encouraged. - -####`xfs_inode64` -Set _inode64_ mount option when using the _xfs_ fstype. Choose _true_ to set. - -####`xfs_nobarrier` -Set _nobarrier_ mount option when using the _xfs_ fstype. Choose _true_ to set. - -####`ro` -Whether the file system should be mounted read only. For emergencies only. - -####`force` -If _true_, this will overwrite any xfs file system it sees. This is useful for -rebuilding GlusterFS repeatedly and wiping data. There are other safeties in -place to stop this. In general, you probably don't ever want to touch this. - -####`areyousure` -Do you want to allow Puppet-Gluster to do dangerous things? You have to set -this to _true_ to allow Puppet-Gluster to _fdisk_ and _mkfs_ your file system. - -####`again` -Do you want to use _Exec['again']_ ? This helps build your cluster quickly! - -####`comment` -Add any comment you want. This is also occasionally used internally to do magic -things. - -###gluster::volume -Main volume type for the cluster. This is where a lot of the magic happens. -Remember that changing some of these parameters after the volume has been -created won't work, and you'll experience undefined behaviour. There could be -FSM based error checking to verify that no changes occur, but it has been left -out so that this code base can eventually support such changes, and so that the -user can manually change a parameter if they know that it is safe to do so. - -####`bricks` -List of bricks to use for this volume. If this is left at the default value of -_true_, then this list is built automatically. The algorithm that determines -this order does not support all possible situations, and most likely can't -handle certain corner cases. It is possible to examine the FSM to view the -selected brick order before it has a chance to create the volume. The volume -creation script won't run until there is a stable brick list as seen by the FSM -running on the host that has the DLM. If you specify this list of bricks -manually, you must choose the order to match your desired volume layout. If you -aren't sure about how to order the bricks, you should review the GlusterFS -documentation first. - -####`transport` -Only _tcp_ is supported. Possible values can include _rdma_, but this won't get -any testing if I don't have access to infiniband hardware. Donations welcome. - -####`replica` -Replica count. Usually you'll want to set this to _2_. Some users choose _3_. -Other values are seldom seen. A value of _1_ can be used for simply testing a -distributed setup, when you don't care about your data or high availability. A -value greater than _4_ is probably wasteful and unnecessary. It might even -cause performance issues if a synchronous write is waiting on a slow fourth -server. - -####`stripe` -Stripe count. Thoroughly unsupported and untested option. Not recommended for -use by GlusterFS. - -####`layout` -Which brick layout to use. The available options are: _chained_, and (default). -To generate a default (symmetrical, balanced) layout, leave this option blank. -If you'd like to include an algorithm that generates a different type of brick -layout, it is easy to drop in an algorithm. Please contact me with the details! - -####`ping` -Do we want to include ping checks with _fping_? - -####`settle` -Do we want to run settle checks? - -####`again` -Do you want to use _Exec['again']_ ? This helps build your cluster quickly! - -####`start` -Requested state for the volume. Valid values include: _true_ (start), _false_ -(stop), or _undef_ (un-managed start/stop state). - -###gluster::volume::property -Main volume property type for the cluster. This allows you to manage GlusterFS -volume specific properties. There are a wide range of properties that volumes -support. For the full list of properties, you should consult the GlusterFS -documentation, or run the _gluster volume set help_ command. To set a property -you must use the special name pattern of: _volume_#_key_. The value argument is -used to set the associated value. It is smart enough to accept values in the -most logical format for that specific property. Some properties aren't yet -supported, so please report any problems you have with this functionality. -Because this feature is an awesome way to _document as code_ the volume -specific optimizations that you've made, make sure you use this feature even if -you don't use all the others. - -####`value` -The value to be used for this volume property. - -###gluster::mount -Main type to use to mount GlusterFS volumes. This type offers special features, -like shorewall integration, and repo support. - -####`server` -Server specification to use when mounting. Format is _:/volume_. You -may use an _FQDN_ or an _IP address_ to specify the server. - -####`rw` -Mount read-write or read-only. Defaults to read-only. Specify _true_ for -read-write. - -####`mounted` -Mounted argument from standard mount type. Defaults to _true_ (_mounted_). - -####`repo` -Boolean to select if you want automatic repository (package) management or not. - -####`version` -Specify which GlusterFS version you'd like to use. - -####`ip` -IP address of this client. This is usually auto-detected, but you can choose -your own value manually in case there are multiple options available. - -####`shorewall` -Boolean to specify whether puppet-shorewall integration should be used or not. - -##Examples -For example configurations, please consult the [examples/](https://github.com/purpleidea/puppet-gluster/tree/master/examples) directory in the git -source repository. It is available from: - -[https://github.com/purpleidea/puppet-gluster/tree/master/examples](https://github.com/purpleidea/puppet-gluster/tree/master/examples) - -It is also available from: - -[https://forge.gluster.org/puppet-gluster/puppet-gluster/trees/master/examples](https://forge.gluster.org/puppet-gluster/puppet-gluster/trees/master/examples/) - -##Limitations - -This module has been tested against open source Puppet 3.2.4 and higher. - -The module is routinely tested on: - -* CentOS 6.5 - -It will probably work without incident or without major modification on: - -* CentOS 5.x/6.x -* RHEL 5.x/6.x - -It has patches to support: - -* Fedora 20+ -* Ubuntu 12.04+ -* Debian 7+ - -It will most likely work with other Puppet versions and on other platforms, but -testing on those platforms has been minimal due to lack of time and resources. - -Testing is community supported! Please report any issues as there are a lot of -features, and in particular, support for additional distros isn't well tested. -The multi-distro architecture has been chosen to easily support new additions. -Most platforms and versions will only require a change to the yaml based data/ -folder. - -##Development - -This is my personal project that I work on in my free time. -Donations of funding, hardware, virtual machines, and other resources are -appreciated. Please contact me if you'd like to sponsor a feature, invite me to -talk/teach or for consulting. - -You can follow along [on my technical blog](https://ttboj.wordpress.com/). - -To report any bugs, please file a ticket at: [https://bugzilla.redhat.com/enter_bug.cgi?product=GlusterFS&component=puppet-gluster](https://bugzilla.redhat.com/enter_bug.cgi?product=GlusterFS&component=puppet-gluster). - -##Author - -Copyright (C) 2010-2013+ James Shubin - -* [github](https://github.com/purpleidea/) -* [@purpleidea](https://twitter.com/#!/purpleidea) -* [https://ttboj.wordpress.com/](https://ttboj.wordpress.com/) - diff --git a/gluster/INSTALL b/gluster/INSTALL deleted file mode 100644 index f497841f7..000000000 --- a/gluster/INSTALL +++ /dev/null @@ -1,18 +0,0 @@ -To install this puppet module, copy this folder to your puppet modulepath. - -You can usually find out where this is by running: - -$ puppet config print modulepath - -on your puppetmaster. In my case, this contains the directory: - -/etc/puppet/modules/ - -I keep all of my puppet modules in git managed directories named: - -puppet- - -You must remove the 'puppet-' prefix from the directory name for it to work! - -Happy hacking! - diff --git a/gluster/Makefile b/gluster/Makefile deleted file mode 100644 index bb98acf00..000000000 --- a/gluster/Makefile +++ /dev/null @@ -1,146 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -.PHONY: all docs rpm srpm spec tar upload upload-sources upload-srpms upload-rpms -.SILENT: - -# version of the program -VERSION := $(shell cat VERSION) -RELEASE = 1 -SPEC = rpmbuild/SPECS/puppet-gluster.spec -SOURCE = rpmbuild/SOURCES/puppet-gluster-$(VERSION).tar.bz2 -SRPM = rpmbuild/SRPMS/puppet-gluster-$(VERSION)-$(RELEASE).src.rpm -RPM = rpmbuild/RPMS/puppet-gluster-$(VERSION)-$(RELEASE).rpm -SERVER = 'download.gluster.org' -REMOTE_PATH = 'purpleidea/puppet-gluster' - -all: docs rpm - -docs: puppet-gluster-documentation.pdf - -puppet-gluster-documentation.pdf: DOCUMENTATION.md - pandoc DOCUMENTATION.md -o 'puppet-gluster-documentation.pdf' - -# -# aliases -# -# TODO: does making an rpm depend on making a .srpm first ? -rpm: $(SRPM) $(RPM) - # do nothing - -srpm: $(SRPM) - # do nothing - -spec: $(SPEC) - # do nothing - -tar: $(SOURCE) - # do nothing - -upload: upload-sources upload-srpms upload-rpms - # do nothing - -# -# rpmbuild -# -$(RPM): $(SPEC) $(SOURCE) - @echo Running rpmbuild -bb... - rpmbuild --define '_topdir $(shell pwd)/rpmbuild' -bb $(SPEC) && \ - mv rpmbuild/RPMS/noarch/puppet-gluster-$(VERSION)-$(RELEASE).*.rpm $(RPM) - -$(SRPM): $(SPEC) $(SOURCE) - @echo Running rpmbuild -bs... - rpmbuild --define '_topdir $(shell pwd)/rpmbuild' -bs $(SPEC) - # renaming is not needed because we aren't using the dist variable - #mv rpmbuild/SRPMS/puppet-gluster-$(VERSION)-$(RELEASE).*.src.rpm $(SRPM) - -# -# spec -# -$(SPEC): rpmbuild/ puppet-gluster.spec.in - @echo Running templater... - #cat puppet-gluster.spec.in > $(SPEC) - sed -e s/__VERSION__/$(VERSION)/ -e s/__RELEASE__/$(RELEASE)/ < puppet-gluster.spec.in > $(SPEC) - # append a changelog to the .spec file - git log --format="* %cd %aN <%aE>%n- (%h) %s%d%n" --date=local | sed -r 's/[0-9]+:[0-9]+:[0-9]+ //' >> $(SPEC) - -# -# archive -# -$(SOURCE): rpmbuild/ - @echo Running git archive... - # use HEAD if tag doesn't exist yet, so that development is easier... - git archive --prefix=puppet-gluster-$(VERSION)/ -o $(SOURCE) $(VERSION) 2> /dev/null || (echo 'Warning: $(VERSION) does not exist.' && git archive --prefix=puppet-gluster-$(VERSION)/ -o $(SOURCE) HEAD) - -# TODO: ensure that each sub directory exists -rpmbuild/: - mkdir -p rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} - -# -# sha256sum -# -rpmbuild/SOURCES/SHA256SUMS: rpmbuild/SOURCES/ $(SOURCE) - @echo Running SOURCES sha256sum... - cd rpmbuild/SOURCES/ && sha256sum *.tar.bz2 > SHA256SUMS; cd - - -rpmbuild/SRPMS/SHA256SUMS: rpmbuild/SRPMS/ $(SRPM) - @echo Running SRPMS sha256sum... - cd rpmbuild/SRPMS/ && sha256sum *src.rpm > SHA256SUMS; cd - - -rpmbuild/RPMS/SHA256SUMS: rpmbuild/RPMS/ $(RPM) - @echo Running RPMS sha256sum... - cd rpmbuild/RPMS/ && sha256sum *.rpm > SHA256SUMS; cd - - -# -# gpg -# -rpmbuild/SOURCES/SHA256SUMS.asc: rpmbuild/SOURCES/SHA256SUMS - @echo Running SOURCES gpg... - # the --yes forces an overwrite of the SHA256SUMS.asc if necessary - gpg2 --yes --clearsign rpmbuild/SOURCES/SHA256SUMS - -rpmbuild/SRPMS/SHA256SUMS.asc: rpmbuild/SRPMS/SHA256SUMS - @echo Running SRPMS gpg... - gpg2 --yes --clearsign rpmbuild/SRPMS/SHA256SUMS - -rpmbuild/RPMS/SHA256SUMS.asc: rpmbuild/RPMS/SHA256SUMS - @echo Running RPMS gpg... - gpg2 --yes --clearsign rpmbuild/RPMS/SHA256SUMS - -# -# upload -# -# upload to public server -upload-sources: rpmbuild/SOURCES/ rpmbuild/SOURCES/SHA256SUMS rpmbuild/SOURCES/SHA256SUMS.asc - if [ "`cat rpmbuild/SOURCES/SHA256SUMS`" != "`ssh $(SERVER) 'cd $(REMOTE_PATH)/SOURCES/ && cat SHA256SUMS'`" ]; then \ - echo Running SOURCES upload...; \ - rsync -avz rpmbuild/SOURCES/ $(SERVER):$(REMOTE_PATH)/SOURCES/; \ - fi - -upload-srpms: rpmbuild/SRPMS/ rpmbuild/SRPMS/SHA256SUMS rpmbuild/SRPMS/SHA256SUMS.asc - if [ "`cat rpmbuild/SRPMS/SHA256SUMS`" != "`ssh $(SERVER) 'cd $(REMOTE_PATH)/SRPMS/ && cat SHA256SUMS'`" ]; then \ - echo Running SRPMS upload...; \ - rsync -avz rpmbuild/SRPMS/ $(SERVER):$(REMOTE_PATH)/SRPMS/; \ - fi - -upload-rpms: rpmbuild/RPMS/ rpmbuild/RPMS/SHA256SUMS rpmbuild/RPMS/SHA256SUMS.asc - if [ "`cat rpmbuild/RPMS/SHA256SUMS`" != "`ssh $(SERVER) 'cd $(REMOTE_PATH)/RPMS/ && cat SHA256SUMS'`" ]; then \ - echo Running RPMS upload...; \ - rsync -avz --prune-empty-dirs rpmbuild/RPMS/ $(SERVER):$(REMOTE_PATH)/RPMS/; \ - fi - -# vim: ts=8 diff --git a/gluster/Modulefile b/gluster/Modulefile deleted file mode 100644 index 2eaa0ab68..000000000 --- a/gluster/Modulefile +++ /dev/null @@ -1,12 +0,0 @@ -name 'purpleidea-gluster' -author 'James Shubin' -license 'GNU Affero General Public License, Version 3.0+' -summary 'GlusterFS module by James' -description 'A Puppet module for GlusterFS' -project_page 'https://github.com/purpleidea/puppet-gluster/' -source 'https://ttboj.wordpress.com/' -version '0.0.4' - -## Add dependencies, if any: -# dependency 'username/name', '>= 1.2.0' - diff --git a/gluster/README b/gluster/README deleted file mode 100644 index c9482ddbf..000000000 --- a/gluster/README +++ /dev/null @@ -1,34 +0,0 @@ -This is puppet-gluster a puppet module for gluster. - -Please read the INSTALL file for instructions on getting this installed. -Look in the examples/ folder for usage. If none exist, please contribute one! -This code may be a work in progress. The interfaces may change without notice. -Patches are welcome, but please be patient. They are best received by email. -Please ping me if you have big changes in mind, before you write a giant patch. - -Module specific notes: -* This is _the_ puppet module for gluster. Accept no imitations! -* All the participating nodes, need to have an identical puppet-gluster config. -* Using gluster::simple is probably the best way to try this out. -* This is easily deployed with vagrant. See the vagrant/ directory! -* You can use less of the available resources, if you only want to manage some. -* You can get CentOS and RHEL rpms from: - * http://download.gluster.org/pub/gluster/glusterfs/LATEST/CentOS/ or: - * http://repos.fedorapeople.org/repos/kkeithle/glusterfs/epel-6/x86_64/ -* Documentation is now available! Please report grammar and spelling bugs. - -Dependencies: -* puppetlabs-stdlib (required) -* puppet-module-data (optional, puppet >= 3.0.0) -* my puppet-common module (optional) -* my puppet-shorewall module (optional) -* my puppet-keepalived module (optional) -* my puppet-puppet module (optional) -* my puppet-yum module (optional) -* gluster packages (see above notes) -* pandoc (for building a pdf of the documentation) - - -Happy hacking, -James Shubin , https://ttboj.wordpress.com/ - diff --git a/gluster/README.md b/gluster/README.md deleted file mode 120000 index fc84f4f88..000000000 --- a/gluster/README.md +++ /dev/null @@ -1 +0,0 @@ -DOCUMENTATION.md \ No newline at end of file diff --git a/gluster/VERSION b/gluster/VERSION deleted file mode 100644 index 81340c7e7..000000000 --- a/gluster/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.0.4 diff --git a/gluster/builder/README b/gluster/builder/README deleted file mode 100644 index 2bffb7126..000000000 --- a/gluster/builder/README +++ /dev/null @@ -1,10 +0,0 @@ -This folder (including history) has been split off into a separate repository. - -The new git repository is now located at: - - https://github.com/purpleidea/vagrant-builder - -Happy hacking! - -James / @purpleidea - diff --git a/gluster/data/common.yaml b/gluster/data/common.yaml deleted file mode 100644 index ea6f232c8..000000000 --- a/gluster/data/common.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# gluster/data/common.yaml ---- -gluster::params::comment: 'Hello from @purpleidea!' # do not erase! - -# vim: ts=8 diff --git a/gluster/data/hiera.yaml b/gluster/data/hiera.yaml deleted file mode 100644 index 52b83b57a..000000000 --- a/gluster/data/hiera.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# gluster/data/hiera.yaml ---- -:hierarchy: -- tree/%{::osfamily}/%{::operatingsystem}/%{::operatingsystemrelease} -- tree/%{::osfamily}/%{::operatingsystem} -- tree/%{::osfamily} -- common - -# vim: ts=8 diff --git a/gluster/data/tree/Debian.yaml b/gluster/data/tree/Debian.yaml deleted file mode 100644 index 098123cb9..000000000 --- a/gluster/data/tree/Debian.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# gluster/data/tree/Debian.yaml ---- -gluster::params::package_glusterfs: 'glusterfs-client' -gluster::params::package_glusterfs_api: '' # doesn't exist -gluster::params::package_glusterfs_fuse: '' # doesn't exist -gluster::params::service_glusterd: 'glusterfs-server' -# TODO: the debian family of glusterd needs a reload command in the init file ! -gluster::params::misc_gluster_reload: '/usr/sbin/service glusterfs-server restart' -# vim: ts=8 diff --git a/gluster/data/tree/Debian/Debian.yaml b/gluster/data/tree/Debian/Debian.yaml deleted file mode 100644 index b6d2ec32a..000000000 --- a/gluster/data/tree/Debian/Debian.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# gluster/data/tree/Debian/Debian.yaml ---- - -# vim: ts=8 diff --git a/gluster/data/tree/Debian/Debian/7.4.yaml b/gluster/data/tree/Debian/Debian/7.4.yaml deleted file mode 100644 index 94cbd639e..000000000 --- a/gluster/data/tree/Debian/Debian/7.4.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# gluster/data/tree/Debian/Debian/7.4.yaml ---- - -# vim: ts=8 diff --git a/gluster/data/tree/Debian/Ubuntu.yaml b/gluster/data/tree/Debian/Ubuntu.yaml deleted file mode 100644 index 4bfe9d769..000000000 --- a/gluster/data/tree/Debian/Ubuntu.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# gluster/data/tree/Debian/Ubuntu.yaml ---- - -# vim: ts=8 diff --git a/gluster/data/tree/Debian/Ubuntu/12.04.yaml b/gluster/data/tree/Debian/Ubuntu/12.04.yaml deleted file mode 100644 index e6c234a9c..000000000 --- a/gluster/data/tree/Debian/Ubuntu/12.04.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# gluster/data/tree/Debian/Ubuntu/12.04.yaml ---- - -# vim: ts=8 diff --git a/gluster/data/tree/Gentoo.yaml b/gluster/data/tree/Gentoo.yaml deleted file mode 100644 index b1d63e788..000000000 --- a/gluster/data/tree/Gentoo.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# gluster/data/tree/Gentoo.yaml ---- - -# vim: ts=8 diff --git a/gluster/data/tree/RedHat.yaml b/gluster/data/tree/RedHat.yaml deleted file mode 100644 index f320de481..000000000 --- a/gluster/data/tree/RedHat.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# gluster/data/tree/RedHat.yaml ---- -gluster::params::misc_gluster_repo: 'https://download.gluster.org/pub/gluster/glusterfs/' - -# vim: ts=8 diff --git a/gluster/data/tree/RedHat/CentOS.yaml b/gluster/data/tree/RedHat/CentOS.yaml deleted file mode 100644 index d9ef302e0..000000000 --- a/gluster/data/tree/RedHat/CentOS.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# gluster/data/tree/RedHat/CentOS.yaml ---- - -# vim: ts=8 diff --git a/gluster/data/tree/RedHat/CentOS/6.5.yaml b/gluster/data/tree/RedHat/CentOS/6.5.yaml deleted file mode 100644 index 06d5f62ce..000000000 --- a/gluster/data/tree/RedHat/CentOS/6.5.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# gluster/data/tree/RedHat/CentOS/6.5.yaml ---- - -# vim: ts=8 diff --git a/gluster/data/tree/RedHat/Fedora.yaml b/gluster/data/tree/RedHat/Fedora.yaml deleted file mode 100644 index 09a878955..000000000 --- a/gluster/data/tree/RedHat/Fedora.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# gluster/data/tree/RedHat/Fedora.yaml ---- -gluster::params::package_python_argparse: '' # doesn't exist -gluster::params::program_modprobe: '/usr/sbin/modprobe' -gluster::params::program_lsmod: '/usr/sbin/lsmod' -gluster::params::program_parted: '/usr/sbin/parted' -gluster::params::program_pvcreate: '/usr/sbin/pvcreate' -gluster::params::program_vgcreate: '/usr/sbin/vgcreate' -gluster::params::program_lvcreate: '/usr/sbin/lvcreate' -gluster::params::program_vgs: '/usr/sbin/vgs' -gluster::params::program_lvs: '/usr/sbin/lvs' -gluster::params::program_pvdisplay: '/usr/sbin/pvdisplay' -gluster::params::program_vgdisplay: '/usr/sbin/vgdisplay' -#gluster::params::program_lvdisplay: '/usr/sbin/lvdisplay' -gluster::params::program_mkfs_xfs: '/usr/sbin/mkfs.xfs' -gluster::params::program_mkfs_ext4: '/usr/sbin/mkfs.ext4' -gluster::params::program_mkfs_btrfs: '/usr/sbin/mkfs.btrfs' -gluster::params::program_findmnt: '/usr/bin/findmnt' -gluster::params::misc_gluster_reload: '/usr/bin/systemctl reload glusterd' - -# vim: ts=8 diff --git a/gluster/data/tree/RedHat/Fedora/20.yaml b/gluster/data/tree/RedHat/Fedora/20.yaml deleted file mode 100644 index e1320920d..000000000 --- a/gluster/data/tree/RedHat/Fedora/20.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# gluster/data/tree/RedHat/Fedora/20.yaml ---- - -# vim: ts=8 diff --git a/gluster/data/tree/RedHat/RedHat.yaml b/gluster/data/tree/RedHat/RedHat.yaml deleted file mode 100644 index 459369b40..000000000 --- a/gluster/data/tree/RedHat/RedHat.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# gluster/data/tree/RedHat/RedHat.yaml ---- - -# vim: ts=8 diff --git a/gluster/data/tree/RedHat/RedHat/6.5.yaml b/gluster/data/tree/RedHat/RedHat/6.5.yaml deleted file mode 100644 index a65e8f519..000000000 --- a/gluster/data/tree/RedHat/RedHat/6.5.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# gluster/data/tree/RedHat/RedHat/6.5.yaml ---- - -# vim: ts=8 diff --git a/gluster/data/tree/RedHat/RedHat/7.0.yaml b/gluster/data/tree/RedHat/RedHat/7.0.yaml deleted file mode 100644 index e7f0ae796..000000000 --- a/gluster/data/tree/RedHat/RedHat/7.0.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# gluster/data/tree/RedHat/RedHat/7.0.yaml ---- -gluster::params::package_python_argparse: '' # doesn't exist -gluster::params::misc_gluster_reload: '/usr/bin/systemctl reload glusterd' - -# vim: ts=8 diff --git a/gluster/examples/distributed-replicate-example.pp b/gluster/examples/distributed-replicate-example.pp deleted file mode 100644 index 900d9caa7..000000000 --- a/gluster/examples/distributed-replicate-example.pp +++ /dev/null @@ -1,154 +0,0 @@ -# -# example of a distributed-replicate with four hosts, and two bricks each -# NOTE: this should be put on *every* gluster host -# -#$annex_loc_vip_1 = '172.16.1.80' # vip -$annex_loc_ip_1 = '172.16.1.81' -$annex_loc_ip_2 = '172.16.1.82' -$annex_loc_ip_3 = '172.16.1.83' -$annex_loc_ip_4 = '172.16.1.84' - -$some_client_ip = '' - -class gluster_base { - - class { 'gluster::server': - ips => ["${annex_loc_ip_1}", "${annex_loc_ip_2}", "${annex_loc_ip_3}", "${annex_loc_ip_4}"], - #vip => "${annex_loc_vip_1}", - clients => [$some_client_ip], - zone => 'loc', - shorewall => true, - } - - gluster::host { 'annex1.example.com': - # use uuidgen to make these - # NOTE: specifying a host uuid is optional and can be automatic - #uuid => '1f660ca2-2c78-4aa0-8f4d-21608218c69c', - } - - gluster::brick { 'annex1.example.com:/mnt/storage1a': - dev => '/dev/disk/by-id/scsi-36003048007e26c00173ad3b633a2ef67', # /dev/sda - labeltype => 'gpt', - fstype => 'xfs', - fsuuid => '1ae49642-7f34-4886-8d23-685d13867fb1', - xfs_inode64 => true, - xfs_nobarrier => true, - areyousure => true, - } - - gluster::brick { 'annex1.example.com:/mnt/storage1c': - dev => '/dev/disk/by-id/scsi-36003048007e26c00173ad3b633a36700', # /dev/sdb - labeltype => 'gpt', - fstype => 'xfs', - fsuuid => '1c9ee010-9cd1-4d81-9a73-f0788d5b3e33', - xfs_inode64 => true, - xfs_nobarrier => true, - areyousure => true, - } - - gluster::host { 'annex2.example.com': - uuid => '2fbe6e2f-f6bc-4c2d-a301-62fa90c459f8', - } - - gluster::brick { 'annex2.example.com:/mnt/storage2a': - dev => '/dev/disk/by-id/scsi-36003048007df450014ca27ee19eaec55', # /dev/sdc - labeltype => 'gpt', - fstype => 'xfs', - fsuuid => '2affe5e3-c10d-4d42-a887-cf8993a6c7b5', - xfs_inode64 => true, - xfs_nobarrier => true, - areyousure => true, - } - - gluster::brick { 'annex2.example.com:/mnt/storage2c': - dev => '/dev/disk/by-id/scsi-36003048007df450014ca280e1bda8e70', # /dev/sdd - labeltype => 'gpt', - fstype => 'xfs', - fsuuid => '2c434d6c-7800-4eec-9121-483bee2336f6', - xfs_inode64 => true, - xfs_nobarrier => true, - areyousure => true, - } - - gluster::host { 'annex3.example.com': - uuid => '3f5a86fd-6956-46ca-bb80-65278dc5b945', - } - - gluster::brick { 'annex3.example.com:/mnt/storage3b': - dev => '/dev/disk/by-id/scsi-36003048007e14f0014ca2722130bc87c', # /dev/sdc - labeltype => 'gpt', - fstype => 'xfs', - fsuuid => '3b79d76b-a519-493c-9f21-ca35524187ef', - xfs_inode64 => true, - xfs_nobarrier => true, - areyousure => true, - } - - gluster::brick { 'annex3.example.com:/mnt/storage3d': - dev => '/dev/disk/by-id/scsi-36003048007e14f0014ca2743150a5471', # /dev/sdd - labeltype => 'gpt', - fstype => 'xfs', - fsuuid => '3d125f8a-c3c3-490d-a606-453401e9bc21', - xfs_inode64 => true, - xfs_nobarrier => true, - areyousure => true, - } - - gluster::host { 'annex4.example.com': - uuid => '4f8e3157-e1e3-4f13-b9a8-51e933d53915', - } - - gluster::brick { 'annex4.example.com:/mnt/storage4b': - dev => '/dev/disk/by-id/scsi-36003048007e36700174029270d81faa1', # /dev/sdc - labeltype => 'gpt', - fstype => 'xfs', - fsuuid => '4bf21ae6-06a0-44ad-ab48-ea23417e4e44', - xfs_inode64 => true, - xfs_nobarrier => true, - areyousure => true, - } - - gluster::brick { 'annex4.example.com:/mnt/storage4d': - dev => '/dev/disk/by-id/scsi-36003048007e36700174029270d82724d', # /dev/sdd - labeltype => 'gpt', - fstype => 'xfs', - fsuuid => '4dfa7e50-2315-44d3-909b-8e9423def6e5', - xfs_inode64 => true, - xfs_nobarrier => true, - areyousure => true, - } - - $brick_list = [ - 'annex1.example.com:/mnt/storage1a', - 'annex2.example.com:/mnt/storage2a', - 'annex3.example.com:/mnt/storage3b', - 'annex4.example.com:/mnt/storage4b', - 'annex1.example.com:/mnt/storage1c', - 'annex2.example.com:/mnt/storage2c', - 'annex3.example.com:/mnt/storage3d', - 'annex4.example.com:/mnt/storage4d', - ] - - # TODO: have this run transactionally on *one* gluster host. - gluster::volume { 'examplevol': - replica => 2, - bricks => $brick_list, - start => undef, # i'll start this myself - } - - # namevar must be: # - gluster::volume::property { 'examplevol#auth.reject': - value => ['192.0.2.13', '198.51.100.42', '203.0.113.69'], - } - - #gluster::volume::property { 'examplevol#cluster.data-self-heal-algorithm': - # value => 'full', - #} - - #gluster::volume { 'someothervol': - # replica => 2, - # bricks => $brick_list, - # start => undef, - #} - -} diff --git a/gluster/examples/filesystem-backed-bricks-example.pp b/gluster/examples/filesystem-backed-bricks-example.pp deleted file mode 100644 index 89b77719d..000000000 --- a/gluster/examples/filesystem-backed-bricks-example.pp +++ /dev/null @@ -1,52 +0,0 @@ -# -# example of a simple replicate with 2 hosts, and filesystem path bricks -# NOTE: this should be put on *every* gluster host -# - -class gluster_base { - - class { '::gluster::server': - ips => ['192.168.123.101', '192.168.123.102'], - shorewall => true, - } - - gluster::host { 'annex1.example.com': - # use uuidgen to make these - uuid => '1f660ca2-2c78-4aa0-8f4d-21608218c69c', - } - - # note that this is using a folder on your existing filesystem... - # this can be useful for prototyping gluster using virtual machines - # if this isn't a separate partition, remember that your root fs will - # run out of space when your gluster volume does! - gluster::brick { 'annex1.example.com:/data/gluster-storage1': - areyousure => true, - } - - gluster::host { 'annex2.example.com': - # NOTE: specifying a host uuid is now optional! - # if you don't choose one, one will be assigned - #uuid => '2fbe6e2f-f6bc-4c2d-a301-62fa90c459f8', - } - - gluster::brick { 'annex2.example.com:/data/gluster-storage2': - areyousure => true, - } - - $brick_list = [ - 'annex1.example.com:/data/gluster-storage1', - 'annex2.example.com:/data/gluster-storage2', - ] - - gluster::volume { 'examplevol': - replica => 2, - bricks => $brick_list, - start => undef, # i'll start this myself - } - - # namevar must be: # - gluster::volume::property { 'examplevol#auth.reject': - value => ['192.0.2.13', '198.51.100.42', '203.0.113.69'], - } -} - diff --git a/gluster/examples/gluster-nfs-ipa-example.pp b/gluster/examples/gluster-nfs-ipa-example.pp deleted file mode 100644 index b08c7f2c4..000000000 --- a/gluster/examples/gluster-nfs-ipa-example.pp +++ /dev/null @@ -1,47 +0,0 @@ -# gluster::mount example using puppet-nfs and puppet-ipa to serve up your data! -# NOTE: you'll need to consult puppet-ipa/examples/ to setup the freeipa server - -# mount a share on your nfs server, at the moment that nfs server is a SPOF :-( -$gvip = '203.0.113.42' -gluster::mount { '/export/homes': - server => "${gvip}:/homes", - rw => true, - mounted => true, - require => Gluster::Volume['homes'], # TODO: too bad this can't ensure it's started -} - -class { '::nfs::server': - domain => "${::domain}", - ipa => 'nfs', # the ipa::client::service name - kerberos => 'ipa', # optional when we're using ipa - shorewall => true, -} - -# the $name here is the client mountpoint when we use: safety => false! -nfs::server::export { '/homes/': # name is the client mountpoint - export => '/export/homes/', - rw => true, - async => false, - wdelay => true, # if false then async must be false too - rootsquash => true, - sec => true, # set true for automatic kerberos magic - options => [], # add any other options you might want! - hosts => ["ws*.${domain}"], # export to these hosts only... - exported => true, # create exported resources for clients - tagas => 'homes', - safety => false, # be super clever (see the module docs) - comment => 'Export home directories for ws*', - require => Gluster::Mount['/export/homes/'], -} - -# and here is how you can collect / mount ~automatically on the client: -class { '::nfs::client': - kerberos => true, -} - -nfs::client::mount::collect { 'homes': # match the $tagas from export! - server => "${::hostname}.${::domain}", - #suid => false, - #clientaddr => "${::ipaddress}", # use this if you want! -} - diff --git a/gluster/examples/gluster-simple-example.pp b/gluster/examples/gluster-simple-example.pp deleted file mode 100644 index 02e59f3ab..000000000 --- a/gluster/examples/gluster-simple-example.pp +++ /dev/null @@ -1,17 +0,0 @@ -# -# simple gluster setup. yeah, that's it. -# this should run on *every* gluster host -# NOTE: this should use a VIP. -# - -node /^annex\d+$/ { # annex{1,2,..N} - - # NOTE: this class offers some configuration, see the source for info. - # NOTE: this is mostly intended for fast gluster testing. for more - # complex setups, you might want to look at the other examples. - class { '::gluster::simple': - setgroup => 'virt', # or: 'small-file-perf', or others too! - } - -} - diff --git a/gluster/examples/gluster-simple-physical-example-best.pp b/gluster/examples/gluster-simple-physical-example-best.pp deleted file mode 100644 index 06be1eda1..000000000 --- a/gluster/examples/gluster-simple-physical-example-best.pp +++ /dev/null @@ -1,28 +0,0 @@ -# -# really simple gluster setup for physical provisioning. -# (yeah, that's it-- for iron!) -# - -node /^annex\d+$/ { # annex{1,2,..N} - - class { '::gluster::simple': - replica => 2, - vip = '192.168.1.42', - vrrp = true, - # NOTE: _each_ host will have four bricks with these devices... - brick_params_defaults = [ # note the spelling and type... - {'dev' => '/dev/sdb'}, - {'dev' => '/dev/sdc'}, - {'dev' => '/dev/sdd'}, - {'dev' => '/dev/sde'}, - ], - brick_param_defaults => { # every brick will use these... - lvm => false, - xfs_inode64 => true, - force => true, - }, - #brick_params => {}, # NOTE: you can also use this option to - # override a particular fqdn with the options that you need to! - } -} - diff --git a/gluster/examples/gluster-simple-physical-example.pp b/gluster/examples/gluster-simple-physical-example.pp deleted file mode 100644 index 377958bb4..000000000 --- a/gluster/examples/gluster-simple-physical-example.pp +++ /dev/null @@ -1,40 +0,0 @@ -# -# simple gluster setup for physical provisioning. -# (yeah, that's it-- for iron!) -# - -node /^annex\d+$/ { # annex{1,2,..N} - - class { '::gluster::simple': - # by allowing you to enumerate these things here in this class, - # you're able to specify all of these from a provisioning tool. - # this is useful in a tool like foreman which only lets you set - # class variables, and doesn't let you define individual types! - replica => 2, - vip = '192.168.1.42', - vrrp = true, - # NOTE: this example will show you different possibilities, but - # it is probably not sane to define your devices in a mixed way - brick_params => { - 'fqdn1.example.com' => [ - {dev => '/dev/disk/by-uuid/01234567-89ab-cdef-0123-456789abcdef'}, - {dev => '/dev/sde', partition => false}, - ], - 'fqdn2.example.com' => [ - {dev => '/dev/disk/by-path/pci-0000:02:00.0-scsi-0:1:0:0', raid_su => 256, raid_sw => 10} - {dev => '/dev/disk/by-id/wwn-0x600508e0000000002b012b744715743a', lvm => true}, - ], - #'fqdnN.example.com' => [...], - }, - - # these will get used by every brick, even if only specified by - # the count variable... keep in mind that without the $dev var, - # some of these parameters aren't used by the filesystem brick. - brick_param_defaults => { - lvm => false, - xfs_inode64 => true, - force => true, - }, - } -} - diff --git a/gluster/examples/mount-example.pp b/gluster/examples/mount-example.pp deleted file mode 100644 index 90930e32a..000000000 --- a/gluster/examples/mount-example.pp +++ /dev/null @@ -1,21 +0,0 @@ -# gluster::mount example -# This is the recommended way of mounting puppet-gluster. -# NOTE: It makes sense to use the VIP as the server to mount from, since it -# stays HA if one of the other nodes goes down. - -# mount a share on one of the gluster hosts (note the added require) -$annex_loc_vip_1 = '172.16.1.80' -gluster::mount { '/mnt/gshared': - server => "${annex_loc_vip_1}:/gshared", - rw => true, - mounted => true, - require => Gluster::Volume['gshared'], # TODO: too bad this can't ensure it's started -} - -# mount a share on a client somewhere -gluster::mount { '/mnt/some_mount_point': - server => "${annex_loc_vip_1}:/some_volume_name", - rw => true, - mounted => true, -} - diff --git a/gluster/examples/wrapper-example.pp b/gluster/examples/wrapper-example.pp deleted file mode 100644 index bae6614a9..000000000 --- a/gluster/examples/wrapper-example.pp +++ /dev/null @@ -1,152 +0,0 @@ -# gluster::wrapper example -# This is the recommended way of using puppet-gluster. -# NOTE: I have broken down the trees into pieces to make them easier to read. -# You can do it exactly like this, use giant trees, or even generate the tree -# using your favourite puppet tool. -# NOTE: These tree objects are actually just nested ruby hashes. - -class { 'gluster::wrapper': - nodetree => $nodetree, - volumetree => $volumetree, - # NOTE: this is the virtual ip as managed by keepalived. At this time, - # you must set up this part on your own. Using the VIP is recommended. - # NOTE: you can set this to any of the node ip's to manage puppet from - # a single master, or you can leave it blank to get the nodes to race. - vip => '172.16.1.80', -} - -$brick1a = { - dev => '/dev/disk/by-id/scsi-36003048007e26c00173ad3b633a2ef67', # /dev/sda - labeltype => 'gpt', - fstype => 'xfs', - fsuuid => '1ae49642-7f34-4886-8d23-685d13867fb1', - xfs_inode64 => true, - xfs_nobarrier => true, - areyousure => true, -} - -$brick1c = { - dev => '/dev/disk/by-id/scsi-36003048007e26c00173ad3b633a36700', # /dev/sdb - labeltype => 'gpt', - fstype => 'xfs', - fsuuid => '1c9ee010-9cd1-4d81-9a73-f0788d5b3e33', - xfs_inode64 => true, - xfs_nobarrier => true, - areyousure => true, -} - -$brick2a = { - dev => '/dev/disk/by-id/scsi-36003048007df450014ca27ee19eaec55', # /dev/sdc - labeltype => 'gpt', - fstype => 'xfs', - fsuuid => '2affe5e3-c10d-4d42-a887-cf8993a6c7b5', - xfs_inode64 => true, - xfs_nobarrier => true, - areyousure => true, -} - -$brick2c = { - dev => '/dev/disk/by-id/scsi-36003048007df450014ca280e1bda8e70', # /dev/sdd - labeltype => 'gpt', - fstype => 'xfs', - fsuuid => '2c434d6c-7800-4eec-9121-483bee2336f6', - xfs_inode64 => true, - xfs_nobarrier => true, - areyousure => true, -} - -$brick3b = { - dev => '/dev/disk/by-id/scsi-36003048007e14f0014ca2722130bc87c', # /dev/sdc - labeltype => 'gpt', - fstype => 'xfs', - fsuuid => '3b79d76b-a519-493c-9f21-ca35524187ef', - xfs_inode64 => true, - xfs_nobarrier => true, - areyousure => true, -} - -$brick3d = { - dev => '/dev/disk/by-id/scsi-36003048007e14f0014ca2743150a5471', # /dev/sdd - labeltype => 'gpt', - fstype => 'xfs', - fsuuid => '3d125f8a-c3c3-490d-a606-453401e9bc21', - xfs_inode64 => true, - xfs_nobarrier => true, - areyousure => true, -} - -$brick4b = { - dev => '/dev/disk/by-id/scsi-36003048007e36700174029270d81faa1', # /dev/sdc - labeltype => 'gpt', - fstype => 'xfs', - fsuuid => '4bf21ae6-06a0-44ad-ab48-ea23417e4e44', - xfs_inode64 => true, - xfs_nobarrier => true, - areyousure => true, -} - -$brick4d = { - dev => '/dev/disk/by-id/scsi-36003048007e36700174029270d82724d', # /dev/sdd - labeltype => 'gpt', - fstype => 'xfs', - fsuuid => '4dfa7e50-2315-44d3-909b-8e9423def6e5', - xfs_inode64 => true, - xfs_nobarrier => true, - areyousure => true, -} - -$nodetree = { - 'annex1.example.com' => { - 'ip' => '172.16.1.81', - 'uuid' => '1f660ca2-2c78-4aa0-8f4d-21608218c69c', - 'bricks' => { - '/mnt/storage1a' => $brick1a, - '/mnt/storage1c' => $brick1c, - }, - }, - 'annex2.example.com' => { - 'ip' => '172.16.1.82', - 'uuid' => '2fbe6e2f-f6bc-4c2d-a301-62fa90c459f8', - 'bricks' => { - '/mnt/storage2a' => $brick2a, - '/mnt/storage2c' => $brick2c, - }, - }, - 'annex3.example.com' => { - 'ip' => '172.16.1.83', - 'uuid' => '3f5a86fd-6956-46ca-bb80-65278dc5b945', - 'bricks' => { - '/mnt/storage3b' => $brick3b, - '/mnt/storage3d' => $brick3d, - }, - }, - 'annex4.example.com' => { - 'ip' => '172.16.1.84', - 'uuid' => '4f8e3157-e1e3-4f13-b9a8-51e933d53915', - 'bricks' => { - '/mnt/storage4b' => $brick4b, - '/mnt/storage4d' => $brick4d, - }, - } -} - -$volumetree = { - 'examplevol1' => { - 'transport' => 'tcp', - 'replica' => 2, - 'clients' => ['172.16.1.143'], # for the auth.allow and $FW - # NOTE: if you *don't* specify a bricks argument, the full list - # of bricks above will be used for your new volume. This is the - # usual thing that you want to do. Alternatively you can choose - # your own bricks[] if you're doing something special or weird! - #'bricks' => [], - }, - - 'examplevol2' => { - 'transport' => 'tcp', - 'replica' => 2, - 'clients' => ['172.16.1.143', '172.16.1.253'], - #'bricks' => [], - } -} - diff --git a/gluster/files/groups/small-file-perf b/gluster/files/groups/small-file-perf deleted file mode 100644 index a718522cf..000000000 --- a/gluster/files/groups/small-file-perf +++ /dev/null @@ -1,2 +0,0 @@ -quick-read=on -open-behind=on diff --git a/gluster/files/groups/virt b/gluster/files/groups/virt deleted file mode 100644 index 0abe9f400..000000000 --- a/gluster/files/groups/virt +++ /dev/null @@ -1,8 +0,0 @@ -quick-read=off -read-ahead=off -io-cache=off -stat-prefetch=off -eager-lock=enable -remote-dio=enable -quorum-type=auto -server-quorum-type=server diff --git a/gluster/files/sponge.py b/gluster/files/sponge.py deleted file mode 100755 index 3ed44db5d..000000000 --- a/gluster/files/sponge.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# This is meant to be a replacement for the excellent 'sponge' utility from the -# 'moreutils' package by Joey Hess. It doesn't do exactly what sponge does, but -# it does to the extent of what is used here for this Puppet-Gluster module. It -# is useful for environments that do not have the sponge package in their repo. - -import sys - -x = sys.stdin.readlines() -# TODO: can we be certain to sync here, and that the compiler doesn't optimize? - -if len(sys.argv) == 1: - # stdout - # TODO: untested - for l in x: sys.stdout.write(l) - sys.stdout.flush() - -elif len(sys.argv) == 2: - # file - f = open(sys.argv[1], 'w') - for l in x: f.write(l) - f.close() - -else: - sys.exit(1) - -# vim: ts=8 diff --git a/gluster/files/xml.py b/gluster/files/xml.py deleted file mode 100755 index 0650127f0..000000000 --- a/gluster/files/xml.py +++ /dev/null @@ -1,261 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# EXAMPLE: -# $ gluster peer status --xml | ./xml.py connected -# - -# EXAMPLE: -# $ gluster peer status --xml | ./xml.py stuck -# - -# EXAMPLE: -# $ gluster volume info --xml | ./xml.py property --key -# - -# EXAMPLE: -# $ gluster volume status --xml [] | ./xml.py port --volume --host --path -# - -# EXAMPLE: -# $ gluster volume status --xml [] | ./xml.py ports [--volume ] [--host ] -# [,[,]] - -import sys -import argparse -import lxml.etree as etree - -# List of state codes: -# -# static char *glusterd_friend_sm_state_names[] = { # glusterd-sm.c -# "Establishing Connection", # 0 -# "Probe Sent to Peer", # 1 -# "Probe Received from Peer", # 2 -# "Peer in Cluster", # 3 (verified) -# "Accepted peer request", # 4 -# "Sent and Received peer request", # 5 -# "Peer Rejected", # 6 (verified) -# "Peer detach in progress", # 7 -# "Probe Received from peer", # 8 -# "Connected to Peer", # 9 -# "Peer is connected and Accepted", # 10 -# "Invalid State" # 11 -# }; -VALID_PEERED = ['3'] -VALID_STUCK = ['4'] - -parser = argparse.ArgumentParser(description='gluster xml parsing tools') -#parser.add_argument('--debug', dest='debug', action='store_true', default=False) -subparsers = parser.add_subparsers(dest='mode') - -# -# 'connected' parser -# -parser_connected = subparsers.add_parser('connected') -parser_connected.add_argument('peers', type=str, nargs='*', action='store') - -# -# 'stuck' parser -# -parser_stuck = subparsers.add_parser('stuck') -parser_stuck.add_argument('peers', type=str, nargs='*', action='store') - -# -# 'property' parser -# -parser_property = subparsers.add_parser('property') -parser_property.add_argument('--key', dest='key', action='store') - -# -# 'port' parser -# -parser_port = subparsers.add_parser('port') -parser_port.add_argument('--volume', dest='volume', action='store', required=True) -parser_port.add_argument('--host', dest='host', action='store', required=True) -parser_port.add_argument('--path', dest='path', action='store', required=True) - -# -# 'ports' parser -# -parser_ports = subparsers.add_parser('ports') -parser_ports.add_argument('--volume', dest='volume', action='store', required=False) -parser_ports.add_argument('--host', dest='host', action='store', required=False) - -# -# final setup... -# -args = parser.parse_args() -tree = etree.parse(sys.stdin) -root = tree.getroot() - -# are all the hostnames in argv connected ? -if args.mode == 'connected': - store = {} - peers = args.peers - - l = root.findall('.//peerStatus') - if len(l) != 1: - sys.exit(3) - - for p in l[0].findall('.//peer'): - h = p.find('hostname').text - c = (str(p.find('connected').text) == '1') # connected...? - s = (str(p.find('state').text) in VALID_PEERED) # valid peering - store[h] = c and s # save for later... - - # if no peers specified, assume we should check all... - if len(peers) == 0: - peers = store.keys() - - for i in peers: - if i in store.keys(): - if not store[i]: - # someone is unconnected - sys.exit(1) - else: - # we're looking for a peer that isn't peered yet - sys.exit(2) - - # must be good! - sys.exit(0) - -# are any hosts 'stuck' ? -elif args.mode == 'stuck': - store = {} - peers = args.peers - - l = root.findall('.//peerStatus') - if len(l) != 1: - sys.exit(3) - - for p in l[0].findall('.//peer'): - h = p.find('hostname').text - c = (str(p.find('connected').text) == '1') # connected...? - s = (str(p.find('state').text) in VALID_STUCK) # is it stuck ? - store[h] = c and s # save for later... - - # if no peers specified, assume we should check all... - if len(peers) == 0: - peers = store.keys() - - for i in peers: - if i in store.keys(): - if store[i]: - # someone is stuck - sys.exit(0) - else: - # we're looking for a peer that isn't peered yet - sys.exit(2) - - # nobody is stuck - sys.exit(1) - -elif args.mode == 'property': - store = [] - for i in root.findall('.//option'): - - key = str(i.find('name').text) - # if the key requested has no '.' in the name, we match loosely - if args.key.find('.') == -1: # no '.' have been found - # try and match key without a '.' in the name... - key = key[key.find('.')+1:] # remove prefix! - - if key == args.key: - store.append(i.find('value').text) - - if len(store) == 1: - print(store[0]) - sys.exit(0) - else: # more than one value found - sys.exit(1) - -elif args.mode == 'port': - port = 0 - found = False - #print args.volume # volume - #print args.host # hostname - #print args.path # path - for i in root.findall('.//volumes'): - for j in i.findall('.//volume'): - v = str(j.find('volName').text) - #print v - for k in j.findall('.//node'): - if k.find('node') is not None: - # this is a node in a node - # the NFS Server entry is doing - # doing this and might be a bug - continue - - h = str(k.find('hostname').text) - p = str(k.find('path').text) - #print h, p - #if v == args.volume and h == args.host and p == args.path: - if (v, h, p) == (args.volume, args.host, args.path): - if found: - # we have already found a match. - # there's a bug somewhere... - sys.exit(2) - found = True - port = int(k.find('port').text) - - if found and port > 0: - print(port) - sys.exit(0) - else: # no value found - sys.exit(1) - -# list all the ports used by one volume -elif args.mode == 'ports': - ports = [] - found = False - #print args.volume # volume (optional) - for i in root.findall('.//volumes'): - for j in i.findall('.//volume'): - v = str(j.find('volName').text) - #print v - # if no volume is specified, we use all of them... - if args.volume is None or args.volume == v: - for k in j.findall('.//node'): - if k.find('node') is not None: - # this is a node in a node - # the NFS Server entry is doing - # doing this and might be a bug - continue - - h = str(k.find('hostname').text) - p = str(k.find('path').text) - #print h, p - if args.host is None or args.host == h: - try: - ports.append(int(k.find('port').text)) - found = True - except ValueError, e: - pass - - if found and len(ports) > 0: - # NOTE: you may get duplicates if you lookup multiple hosts... - # here we remove any duplicates and convert each int to strings - print(','.join([str(x) for x in list(set(ports))])) - sys.exit(0) - else: # no value found - sys.exit(1) - -# else: -sys.exit(3) - -# vim: ts=8 diff --git a/gluster/lib/facter/gluster_bricks.rb b/gluster/lib/facter/gluster_bricks.rb deleted file mode 100644 index e258fdd79..000000000 --- a/gluster/lib/facter/gluster_bricks.rb +++ /dev/null @@ -1,104 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -require 'facter' - -# regexp to match a brick pattern, eg: annex1.example.com:/storage1a -regexp = /^[a-zA-Z]{1}[a-zA-Z0-9\.\-]{0,}:\/[a-zA-Z0-9]{1}[a-zA-Z0-9\/\.\-]{0,}$/ # TODO: is this right ? - -# find the module_vardir -dir = Facter.value('puppet_vardirtmp') # nil if missing -if dir.nil? # let puppet decide if present! - dir = Facter.value('puppet_vardir') - if dir.nil? - var = nil - else - var = dir.gsub(/\/$/, '')+'/'+'tmp/' # ensure trailing slash - end -else - var = dir.gsub(/\/$/, '')+'/' -end - -if var.nil? - # if we can't get a valid vardirtmp, then we can't continue - valid_brickdir = nil -else - module_vardir = var+'gluster/' - valid_brickdir = module_vardir.gsub(/\/$/, '')+'/brick/' -end - -found = {} -result = {} - -if not(valid_brickdir.nil?) and File.directory?(valid_brickdir) - Dir.glob(valid_brickdir+'*.*').each do |f| - b = File.basename(f) - g = b.split('.') # $name.group - - group = g.pop() # pop off suffix (the group name) - if not found.key?(group) - found[group] = [] # initialize - end - - if g.length >= 1 - x = g.join('.') # in case value had dots in it. - - brick = File.open(f, 'r').read.strip # read into str - # eg: annex1.example.com:/storage1a - split = brick.split(':') # do some $name parsing - host = split[0] # host fqdn - # NOTE: technically $path should be everything BUT split[0]. This - # lets our $path include colons if for some reason they're needed. - #path = split[1] # brick mount or storage path - path = brick.slice(host.length+1, brick.length-host.length-1) - - if brick.length > 0 and regexp.match(brick) - found[group].push({'host' => host, 'path' => path}) - # TODO: print warning on else... - end - end - end -end - -# transform to strings -found.keys.each do |group| - # build final result - result[group] = found[group].collect {|x| x['host']+':'+x['path'] } -end - -# build the correctly sorted brick list... -result.keys.each do |x| - Facter.add('gluster_brick_group_'+x) do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - # don't reuse single variable to avoid bug #: - # http://projects.puppetlabs.com/issues/22455 - # TODO: facter should support native list types :) - result[x].join(',') - } - end -end - -# list of generated gluster_ports_volume's -Facter.add('gluster_brick_group_facts') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - result.keys.collect {|x| 'gluster_brick_group_'+x }.join(',') - } -end - -# vim: ts=8 diff --git a/gluster/lib/facter/gluster_fsm.rb b/gluster/lib/facter/gluster_fsm.rb deleted file mode 100644 index fae84cca9..000000000 --- a/gluster/lib/facter/gluster_fsm.rb +++ /dev/null @@ -1,162 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -require 'facter' -require 'base64' # TODO: i wish facter worked with types instead of hacks - -# regexp to match gluster volume name, eg: testvolume -volume_regexp = /^[a-z]{1}[a-z0-9]{0,}$/ # TODO: is this perfect ? - -# returns true if each brick in the list matches -def brick_match(l) - # regexp to match a brick pattern, eg: annex1.example.com:/storage1a - brick_regexp = /^[a-zA-Z]{1}[a-zA-Z0-9\.\-]{0,}:\/[a-zA-Z0-9]{1}[a-zA-Z0-9\/\.\-]{0,}$/ # TODO: is this perfect ? - l.each do |x| - if not brick_regexp.match(x) - return false - end - end - return true -end - -# find the module_vardir -dir = Facter.value('puppet_vardirtmp') # nil if missing -if dir.nil? # let puppet decide if present! - dir = Facter.value('puppet_vardir') - if dir.nil? - var = nil - else - var = dir.gsub(/\/$/, '')+'/'+'tmp/' # ensure trailing slash - end -else - var = dir.gsub(/\/$/, '')+'/' -end - -if var.nil? - # if we can't get a valid vardirtmp, then we can't collect... - fsm_dir = nil -else - module_vardir = var+'gluster/' - valid_dir = module_vardir.gsub(/\/$/, '')+'/' - fsm_dir = valid_dir+'volume/fsm/' -end - -state = {} -stack = {} -watch = {} - -if not(fsm_dir.nil?) and File.directory?(fsm_dir) - # loop through each sub directory in the gluster::volume fsm - Dir.glob(fsm_dir+'*').each do |d| - n = File.basename(d) # should be the gluster::volume name - if n.length > 0 and volume_regexp.match(n) - - f = d.gsub(/\/$/, '')+'/state' # full file path - if File.exists?(f) - # TODO: future versions should unpickle (but with yaml) - v = File.open(f, 'r').read.strip # read into str - if v.length > 0 and brick_match(v.split(',')) - state[n] = v - end - end - - f = d.gsub(/\/$/, '')+'/stack' # full file path - if File.exists?(f) - stack[n] = [] # initialize empty array - File.readlines(f).each do |l| - l = l.strip # clean off /n's - # TODO: future versions should unpickle (but with yaml) - if l.length > 0 and brick_match(l.split(',')) - #stack[n].push(l) - stack[n].push(Base64.encode64(l).delete("\n")) - end - end - end - - f = d.gsub(/\/$/, '')+'/watch' # full file path - if File.exists?(f) - watch[n] = [] # initialize empty array - File.readlines(f).each do |l| - l = l.strip # clean off /n's - # TODO: future versions should unpickle (but with yaml) - if l.length > 0 and brick_match(l.split(',')) - #watch[n].push(l) - watch[n].push(Base64.encode64(l).delete("\n")) - end - end - end - - end - end -end - -state.keys.each do |x| - Facter.add('gluster_volume_fsm_state_'+x) do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - state[x] - } - end - - if stack.key?(x) - Facter.add('gluster_volume_fsm_stack_'+x) do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - stack[x].join(',') - } - end - end - - if watch.key?(x) - Facter.add('gluster_volume_fsm_watch_'+x) do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - watch[x].join(',') - } - end - end -end - -# list of gluster volume fsm state fact names -Facter.add('gluster_volume_fsm_states') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - state.keys.sort.collect {|x| 'gluster_volume_fsm_state_'+x }.join(',') - } -end - -Facter.add('gluster_volume_fsm_stacks') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - (state.keys & stack.keys).sort.collect {|x| 'gluster_volume_fsm_stack_'+x }.join(',') - } -end - -Facter.add('gluster_volume_fsm_watchs') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - (state.keys & watch.keys).sort.collect {|x| 'gluster_volume_fsm_watch_'+x }.join(',') - } -end - -Facter.add('gluster_fsm_debug') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - 'Oh cool, james added fsm support to puppet-gluster. Sweet!' - } -end - diff --git a/gluster/lib/facter/gluster_fsuuid.rb b/gluster/lib/facter/gluster_fsuuid.rb deleted file mode 100644 index 1f340750c..000000000 --- a/gluster/lib/facter/gluster_fsuuid.rb +++ /dev/null @@ -1,148 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -require 'facter' -require 'digest/sha1' - -# TODO: the ruby uuid method can be used when newer ruby versions are used here -# require 'securerandom' -# SecureRandom.uuid - -# uuid regexp -regexp = /^[a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12}$/ -fqdn = Facter.value('fqdn') # this could be nil ! - -# find the module_vardir -dir = Facter.value('puppet_vardirtmp') # nil if missing -if dir.nil? # let puppet decide if present! - dir = Facter.value('puppet_vardir') - if dir.nil? - var = nil - else - var = dir.gsub(/\/$/, '')+'/'+'tmp/' # ensure trailing slash - end -else - var = dir.gsub(/\/$/, '')+'/' -end - -if var.nil? - # if we can't get a valid vardirtmp, then we can't continue - module_vardir = nil - valid_brickdir = nil - uuiddir = nil -else - module_vardir = var+'gluster/' - valid_brickdir = module_vardir.gsub(/\/$/, '')+'/brick/' - uuiddir = valid_brickdir+'fsuuid/' # safe dir that won't get purged... -end - -# NOTE: module specific mkdirs, needed to ensure there is no blocking/deadlock! -if not(var.nil?) and not File.directory?(var) - Dir::mkdir(var) -end - -if not(module_vardir.nil?) and not File.directory?(module_vardir) - Dir::mkdir(module_vardir) -end - -if not(valid_brickdir.nil?) and not File.directory?(valid_brickdir) - Dir::mkdir(valid_brickdir) -end - -found = {} - -# generate uuid and parent directory if they don't already exist... -if not(valid_brickdir.nil?) and File.directory?(valid_brickdir) - if not File.directory?(uuiddir) - Dir::mkdir(uuiddir) - end - - # loop through brick dir, looking for brick names to make fsuuid's for! - if not(valid_brickdir.nil?) and File.directory?(valid_brickdir) and File.directory?(uuiddir) - Dir.glob(valid_brickdir+'*.*').each do |f| - b = File.basename(f) - g = b.split('.') # $name.group - - group = g.pop() # pop off suffix (the group name) - - if g.length >= 1 - # NOTE: some of this code is unnecessary, but i - # kept it because it matches the brick parsing. - - x = g.join('.') # in case value had dots in it. - - brick = File.open(f, 'r').read.strip # read into str - # eg: annex1.example.com:/storage1a - split = brick.split(':') # do some $name parsing - host = split[0] # host fqdn - # NOTE: technically $path should be everything BUT split[0]. This - # lets our $path include colons if for some reason they're needed. - #path = split[1] # brick mount or storage path - path = brick.slice(host.length+1, brick.length-host.length-1) - - # if fqdn is nil, generate for everyone... - # (other hosts data will just be unused...) - if not(fqdn.nil?) - # otherwise, skip hosts that aren't us! - if host != fqdn - next - end - end - - uuidfile = uuiddir + b - # we sha1 to prevent weird characters in facter - key = Digest::SHA1.hexdigest(host + ':' + path + '.' + group) - - # create an fsuuid for each brick and store it - # in our vardir if it doesn't already exist... - if not File.exist?(uuidfile) - result = system("/usr/bin/uuidgen > '" + uuidfile + "'") - if not(result) - # TODO: print warning - end - end - - # create facts from all the uuid files found... - uuid = File.open(uuidfile, 'r').read.strip.downcase # read into str - if uuid.length == 36 and regexp.match(uuid) - # avoid: http://projects.puppetlabs.com/issues/22455 - found[key] = uuid - # TODO: print warning on else... - end - end - end - end -end - -found.keys.each do |x| - Facter.add('gluster_brick_fsuuid_'+x) do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - found[x] - } - end -end - -# list of generated gluster_brick_fsuuid's -Facter.add('gluster_brick_fsuuid_facts') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - found.keys.collect {|x| 'gluster_brick_fsuuid_'+x }.join(',') - } -end - -# vim: ts=8 diff --git a/gluster/lib/facter/gluster_host.rb b/gluster/lib/facter/gluster_host.rb deleted file mode 100644 index 181abb833..000000000 --- a/gluster/lib/facter/gluster_host.rb +++ /dev/null @@ -1,35 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -require 'facter' -require 'resolv' - -# try and pick the _right_ ip that gluster should use by default... -fqdn = Facter.value('fqdn') -if not fqdn.nil? - ip = Resolv.getaddress "#{fqdn}" - if not ip.nil? - Facter.add('gluster_host_ip') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - ip - } - end - end -end - -# vim: ts=8 diff --git a/gluster/lib/facter/gluster_ports.rb b/gluster/lib/facter/gluster_ports.rb deleted file mode 100644 index 209538a57..000000000 --- a/gluster/lib/facter/gluster_ports.rb +++ /dev/null @@ -1,89 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -require 'facter' - -# get the gluster path. this fact comes from an external fact set in: params.pp -gluster = Facter.value('gluster_program_gluster').to_s.chomp -if gluster == '' - gluster = `which gluster 2> /dev/null`.chomp - if gluster == '' - gluster = '/usr/sbin/gluster' - end -end - -# find the module_vardir -dir = Facter.value('puppet_vardirtmp') # nil if missing -if dir.nil? # let puppet decide if present! - dir = Facter.value('puppet_vardir') - if dir.nil? - var = nil - else - var = dir.gsub(/\/$/, '')+'/'+'tmp/' # ensure trailing slash - end -else - var = dir.gsub(/\/$/, '')+'/' -end - -if var.nil? - # if we can't get a valid vardirtmp, then we can't continue - xmlfile = nil -else - module_vardir = var+'gluster/' - xmlfile = module_vardir+'xml.py' -end - -host = Facter.value('fqdn') -found = {} - -# we need the script installed first to be able to generate the port facts... -if not(xmlfile.nil?) and File.exist?(xmlfile) - volumes = `#{gluster} volume list` - if $?.exitstatus == 0 - volumes.split.each do |x| - # values come out as comma separated strings for direct usage - cmd = gluster+' volume status --xml | '+xmlfile+" ports --volume '"+x+"' --host '"+host+"'" - result = `#{cmd}` - if $?.exitstatus == 0 - found[x] = result - # TODO: else, print warning - end - end - # TODO: else, print warning - end -end - -found.keys.each do |x| - Facter.add('gluster_ports_volume_'+x) do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - # don't reuse single variable to avoid bug #: - # http://projects.puppetlabs.com/issues/22455 - found[x] - } - end -end - -# list of generated gluster_ports_volume's -Facter.add('gluster_ports_volumes_facts') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - found.keys.collect {|x| 'gluster_ports_volume_'+x }.join(',') - } -end - -# vim: ts=8 diff --git a/gluster/lib/facter/gluster_property.rb b/gluster/lib/facter/gluster_property.rb deleted file mode 100644 index d38614e1d..000000000 --- a/gluster/lib/facter/gluster_property.rb +++ /dev/null @@ -1,103 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -require 'facter' - -groupdir = '/var/lib/glusterd/groups/' # dir from the upstream package - -# find the module_vardir -dir = Facter.value('puppet_vardirtmp') # nil if missing -if dir.nil? # let puppet decide if present! - dir = Facter.value('puppet_vardir') - if dir.nil? - var = nil - else - var = dir.gsub(/\/$/, '')+'/'+'tmp/' # ensure trailing slash - end -else - var = dir.gsub(/\/$/, '')+'/' -end - -if var.nil? - # if we can't get a valid vardirtmp, then we can't continue - valid_setgroupdir = nil -else - module_vardir = var+'gluster/' - valid_setgroupdir = module_vardir.gsub(/\/$/, '')+'/groups/' -end - -found = {} - -# loop through each directory to avoid code duplication... later dirs override! -[valid_setgroupdir, groupdir].each do |g| - - if not(g.nil?) and File.directory?(g) - Dir.glob(g+'*').each do |f| - b = File.basename(f) - - # a later entry overrides an earlier one... - #if not found.key?(b) - found[b] = {} # initialize (or erase) - #end - - groups = File.open(f, 'r').read # read into str - groups.each_line do |line| - split = line.strip.split('=') # split key=value pairs - if split.length == 2 - key = split[0] - value = split[1] - if found[b].key?(key) - # NOTE: error found in file... - print "There is a duplicate key in the '#{b}' group." - end - found[b][key] = value - end - end - end - end -end - -# list of available property groups -Facter.add('gluster_property_groups') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - found.keys.sort.join(',') - } -end - -# each group's list of key value pairs -found.keys.each do |x| - Facter.add('gluster_property_group_'+x) do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - # don't reuse single variable to avoid bug #: - # http://projects.puppetlabs.com/issues/22455 - # TODO: facter should support native hash types :) - found[x].collect{|k,v| k+'='+v}.join(',') - } - end -end - -# has the custom group directory been created yet? -Facter.add('gluster_property_groups_ready') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - (File.directory?(valid_setgroupdir) ? 'true':'false') - } -end - -# vim: ts=8 diff --git a/gluster/lib/facter/gluster_uuid.rb b/gluster/lib/facter/gluster_uuid.rb deleted file mode 100644 index fa99a4026..000000000 --- a/gluster/lib/facter/gluster_uuid.rb +++ /dev/null @@ -1,153 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -require 'facter' - -# TODO: the ruby uuid method can be used when newer ruby versions are used here -# require 'securerandom' -# SecureRandom.uuid - -# uuid regexp -regexp = /^[a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12}$/ - -# find the module_vardir -dir = Facter.value('puppet_vardirtmp') # nil if missing -if dir.nil? # let puppet decide if present! - dir = Facter.value('puppet_vardir') - if dir.nil? - var = nil - else - var = dir.gsub(/\/$/, '')+'/'+'tmp/' # ensure trailing slash - end -else - var = dir.gsub(/\/$/, '')+'/' -end - -if var.nil? - # if we can't get a valid vardirtmp, then we can't continue - module_vardir = nil - uuiddir = nil - uuidfile = nil -else - module_vardir = var+'gluster/' - uuiddir = module_vardir+'uuid/' # safe dir that won't get purged... - uuidfile = uuiddir+'uuid' -end - -# NOTE: module specific mkdirs, needed to ensure there is no blocking/deadlock! -if not(var.nil?) and not File.directory?(var) - Dir::mkdir(var) -end - -if not(module_vardir.nil?) and not File.directory?(module_vardir) - Dir::mkdir(module_vardir) -end - -if not(uuiddir.nil?) and not File.directory?(uuiddir) - Dir::mkdir(uuiddir) -end - -# generate uuid and parent directory if they don't already exist... -if not(module_vardir.nil?) and File.directory?(module_vardir) - - create = false - if File.directory?(uuiddir) - - if File.exist?(uuidfile) - test = File.open(uuidfile, 'r').read.strip.downcase # read into str - # skip over uuid's of the wrong length or that don't match (security!!) - if test.length == 36 and regexp.match(test) - create = false - else - create = true - end - else - create = true - end - end - - # create a uuid and store it in our vardir if it doesn't already exist! - if create - result = system("/usr/bin/uuidgen > '" + uuidfile + "'") - if not(result) - # TODO: print warning - end - end -end - -# create the fact if the uuid file contains a valid uuid -if not(uuidfile.nil?) and File.exist?(uuidfile) - uuid = File.open(uuidfile, 'r').read.strip.downcase # read into str - # skip over uuid's of the wrong length or that don't match (security!!) - if uuid.length == 36 and regexp.match(uuid) - Facter.add('gluster_uuid') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - # don't reuse uuid variable to avoid bug #: - # http://projects.puppetlabs.com/issues/22455 - uuid - } - end - # TODO: print warning on else... - end -end - -# create facts from externally collected uuid files -_uuid = '' -found = {} -prefix = 'uuid_' -if not(uuiddir.nil?) and File.directory?(uuiddir) - Dir.glob(uuiddir+prefix+'*').each do |f| - - b = File.basename(f) - # strip off leading prefix - fqdn = b[prefix.length, b.length-prefix.length] - - _uuid = File.open(f, 'r').read.strip.downcase # read into str - if _uuid.length == 36 and regexp.match(_uuid) - # avoid: http://projects.puppetlabs.com/issues/22455 - found[fqdn] = _uuid - # TODO: print warning on else... - end - end -end - -found.keys.each do |x| - Facter.add('gluster_uuid_'+x) do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - found[x] - } - end -end - -# list of generated gluster_uuid's -Facter.add('gluster_uuid_facts') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - found.keys.collect {|x| 'gluster_uuid_'+x }.join(',') - } -end - -Facter.add('gluster_fqdns') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - found.keys.sort.join(',') - } -end - -# vim: ts=8 diff --git a/gluster/lib/facter/gluster_version.rb b/gluster/lib/facter/gluster_version.rb deleted file mode 100644 index a3df7bc79..000000000 --- a/gluster/lib/facter/gluster_version.rb +++ /dev/null @@ -1,40 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -require 'facter' - -# get the gluster path. this fact comes from an external fact set in: params.pp -gluster = Facter.value('gluster_program_gluster').to_s.chomp -if gluster == '' - gluster = `which gluster 2> /dev/null`.chomp - if gluster == '' - gluster = '/usr/sbin/gluster' - end -end -cut = `which cut 2> /dev/null`.chomp - -# create the fact if the gluster executable exists -if File.exist?(gluster) - Facter.add('gluster_version') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - Facter::Util::Resolution.exec(gluster+' --version | /usr/bin/head -1 | '+cut+' -d " " -f 2').chomp - } - end -end - -# vim: ts=8 diff --git a/gluster/lib/facter/gluster_vrrp.rb b/gluster/lib/facter/gluster_vrrp.rb deleted file mode 100644 index 79676e671..000000000 --- a/gluster/lib/facter/gluster_vrrp.rb +++ /dev/null @@ -1,201 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -require 'facter' -require 'digest/sha1' -require 'ipaddr' - -length = 16 -# pass regexp -regexp = /^[a-zA-Z0-9]{#{length}}$/ -ipregexp = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/ -netmaskregexp = /^(((128|192|224|240|248|252|254)\.0\.0\.0)|(255\.(0|128|192|224|240|248|252|254)\.0\.0)|(255\.255\.(0|128|192|224|240|248|252|254)\.0)|(255\.255\.255\.(0|128|192|224|240|248|252|254)))$/ -chars = [('a'..'z'), ('A'..'Z'), (0..9)].map { |i| i.to_a }.flatten - - -# find the module_vardir -dir = Facter.value('puppet_vardirtmp') # nil if missing -if dir.nil? # let puppet decide if present! - dir = Facter.value('puppet_vardir') - if dir.nil? - var = nil - else - var = dir.gsub(/\/$/, '')+'/'+'tmp/' # ensure trailing slash - end -else - var = dir.gsub(/\/$/, '')+'/' -end - -if var.nil? - # if we can't get a valid vardirtmp, then we can't continue - module_vardir = nil - vrrpdir = nil - vrrpfile = nil - ipfile = nil -else - module_vardir = var+'gluster/' - vrrpdir = module_vardir+'vrrp/' - vrrpfile = vrrpdir+'vrrp' - ipfile = vrrpdir+'ip' -end - -# NOTE: module specific mkdirs, needed to ensure there is no blocking/deadlock! -if not(var.nil?) and not File.directory?(var) - Dir::mkdir(var) -end - -if not(module_vardir.nil?) and not File.directory?(module_vardir) - Dir::mkdir(module_vardir) -end - -if not(vrrpdir.nil?) and not File.directory?(vrrpdir) - Dir::mkdir(vrrpdir) -end - -# generate pass and parent directory if they don't already exist... -if not(module_vardir.nil?) and File.directory?(module_vardir) - if not File.directory?(vrrpdir) - Dir::mkdir(vrrpdir) - end - - # create a pass and store it in our vardir if it doesn't already exist! - if File.directory?(vrrpdir) and ((not File.exist?(vrrpfile)) or (File.size(vrrpfile) == 0)) - # include a built-in pwgen-like backup - string = (0..length-1).map { chars[rand(chars.length)] }.join - result = system("(/usr/bin/test -z /usr/bin/pwgen && /usr/bin/pwgen -N 1 #{length} || /bin/echo '#{string}') > '" + vrrpfile + "'") - if not(result) - # TODO: print warning - end - end -end - -# create the fact if the vrrp file contains a valid pass -if not(vrrpfile.nil?) and File.exist?(vrrpfile) - pass = File.open(vrrpfile, 'r').read.strip # read into str - # skip over pass's of the wrong length or that don't match (security!!) - if pass.length == length and regexp.match(pass) - Facter.add('gluster_vrrp') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - # don't reuse pass variable to avoid bug #: - # http://projects.puppetlabs.com/issues/22455 - pass - } - end - # TODO: print warning on else... - end -end - -# create facts from externally collected vrrp files -_pass = '' -found = {} -prefix = 'vrrp_' -if not(vrrpdir.nil?) and File.directory?(vrrpdir) - Dir.glob(vrrpdir+prefix+'*').each do |f| - - b = File.basename(f) - # strip off leading prefix - fqdn = b[prefix.length, b.length-prefix.length] - - _pass = File.open(f, 'r').read.strip.downcase # read into str - if _pass.length == length and regexp.match(_pass) - # avoid: http://projects.puppetlabs.com/issues/22455 - found[fqdn] = _pass - # TODO: print warning on else... - end - end -end - -#found.keys.each do |x| -# Facter.add('gluster_vrrp_'+x) do -# #confine :operatingsystem => %w{CentOS, RedHat, Fedora} -# setcode { -# found[x] -# } -# end -#end - -#Facter.add('gluster_vrrp_facts') do -# #confine :operatingsystem => %w{CentOS, RedHat, Fedora} -# setcode { -# found.keys.collect {|x| 'gluster_vrrp_'+x }.join(',') -# } -#end - -# distributed password (uses a piece from each host) -collected = found.keys.sort.collect {|x| found[x] }.join('#') # combine pieces -Facter.add('gluster_vrrp_password') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - Digest::SHA1.hexdigest(collected) - } -end - -Facter.add('gluster_vrrp_fqdns') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - # sorting is very important - found.keys.sort.join(',') - } -end - -# create these facts if the ip file contains a valid ip address -if not(ipfile.nil?) and File.exist?(ipfile) - ip = File.open(ipfile, 'r').read.strip.downcase # read into str - # skip over ip that doesn't match (security!!) - if ipregexp.match(ip) - - # TODO: replace with system-getifaddrs if i can get it working! - cmd = "/sbin/ip -o a show to #{ip} | /bin/awk '{print $2}'" - interface = `#{cmd}`.strip - if $?.exitstatus == 0 and interface.length > 0 - - Facter.add('gluster_vrrp_interface') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - interface - } - end - - # lookup from fact - netmask = Facter.value('netmask_'+interface) - if netmaskregexp.match(netmask) - - Facter.add('gluster_vrrp_netmask') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - netmask - } - end - - cidr = IPAddr.new("#{netmask}").to_i.to_s(2).count('1') - Facter.add('gluster_vrrp_cidr') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - cidr - } - end - end - - # TODO: print warning on else... - end - - # TODO: print warning on else... - end -end - -# vim: ts=8 diff --git a/gluster/lib/puppet/parser/functions/brick_layout_chained.rb b/gluster/lib/puppet/parser/functions/brick_layout_chained.rb deleted file mode 100644 index 0dff9aa7c..000000000 --- a/gluster/lib/puppet/parser/functions/brick_layout_chained.rb +++ /dev/null @@ -1,137 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -module Puppet::Parser::Functions - newfunction(:brick_layout_chained, :type => :rvalue, :doc => <<-'ENDHEREDOC') do |args| - Return the complex chained brick list - - Example: - - $layout = brick_layout_chained($replica, $bricks) - notice("layout is: ${layout}") - - This function is used internally for automatic brick layouts. - - ENDHEREDOC - - Puppet::Parser::Functions.function('warning') # load function - # signature: replica, bricks -> bricks - unless args.length == 2 - raise Puppet::ParseError, "brick_layout_chained(): wrong number of arguments (#{args.length}; must be 2)" - end - if not(args[0].is_a?(Integer)) and not(args[0].is_a?(String)) - # NOTE: strings that convert to int's with .to_i are ok - raise Puppet::ParseError, "brick_layout_chained(): expects the first argument to be an integer, got #{args[0].inspect} which is of type #{args[0].class}" - end - unless args[1].is_a?(Array) - raise Puppet::ParseError, "brick_layout_chained(): expects the first argument to be an array, got #{args[1].inspect} which is of type #{args[1].class}" - end - - replica = args[0].to_i # convert from string if needed - bricks = args[1] - - # TODO: these functions could be in separate puppet files - # eg: Puppet::Parser::Functions.function('myfunc') - # function_myfunc(...) - def brick_str_to_hash(bricks) - # this loop converts brick strings to brick dict's... - result = [] - bricks.each do |x| - a = x.split(':') - #assert a.length == 2 # TODO - p = a[1] - p = ((p[-1, 1] == '/') ? p : (p+'/')) # endswith - - result.push({'host'=> a[0], 'path'=> p}) - end - return result - end - - def get_hostlist(bricks) - hosts = [] - bricks.each do |x| - key = x['host'] - val = x['path'] - - if not hosts.include?(key) - hosts.push(key) - end - end - return hosts - end - - def get_brickstacks(bricks, sort=false) - stacks = {} - hosts = get_hostlist(bricks) - bricks.each do |x| - key = x['host'] - val = x['path'] - if not stacks.include?(key); stacks[key] = []; end # initialize - stacks[key].push(val) - end - - # optionally sort the paths in each individual host stack... - if sort - sorted_stacks = {} - stacks.each do |k, v| - # TODO: there should probably be a proper 'sorted' function for - # paths, in case they aren't numbered sanely _WITH_ padding. - sorted_stacks[k] = v.sort - end - return sorted_stacks - end - return stacks - end - - final = [] - pointer = 0 - parsed = brick_str_to_hash(bricks) - # TODO: there should probably be a proper 'sorted' function for - # hostnames, in case they aren't numbered sanely _WITH_ padding. - hosts = get_hostlist(parsed).sort - brickstack = get_brickstacks(parsed, sort=true) - - if bricks.length == 0; return []; end - - # FIXME: this works with homogeneous volumes only! - while pointer < (hosts.length * brickstack[hosts[0]].length) do - start = hosts[pointer % hosts.length] - #puts "host is #{host}, pointer is: #{pointer}" - - index = 0 - while index < replica do - host = hosts[(pointer+index) % hosts.length] - #puts "host is #{host}, index is: #{index}" - index+= 1 - - path = brickstack[host].shift # yoink - if path.nil? - function_warning(["brick_layout_chained(): brick list is not valid"]) - next - end - final.push({'host' => host, 'path' => path}) # save - end - pointer+=1 - end - - # build final result - result = final.collect {|x| x['host']+':'+x['path'] } - result # return - end -end - -# vim: ts=8 diff --git a/gluster/lib/puppet/parser/functions/brick_layout_simple.rb b/gluster/lib/puppet/parser/functions/brick_layout_simple.rb deleted file mode 100644 index aefc1e29e..000000000 --- a/gluster/lib/puppet/parser/functions/brick_layout_simple.rb +++ /dev/null @@ -1,111 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: -# sort the bricks in a logical manner... i think this is the optimal algorithm, -# but i'd be happy if someone thinks they can do better! this assumes that the -# bricks and hosts are named in a logical manner. alphanumeric sorting is used -# to determine the default ordering... - -module Puppet::Parser::Functions - newfunction(:brick_layout_simple, :type => :rvalue, :doc => <<-'ENDHEREDOC') do |args| - Return the simple symmetrical brick list - - Example: - - $layout = brick_layout_simple($replica, $bricks) - notice("layout is: ${layout}") - - This function is used internally for automatic brick layouts. - - ENDHEREDOC - - # signature: replica, bricks -> bricks - unless args.length == 2 - raise Puppet::ParseError, "brick_layout_simple(): wrong number of arguments (#{args.length}; must be 2)" - end - if not(args[0].is_a?(Integer)) and not(args[0].is_a?(String)) - # NOTE: strings that convert to int's with .to_i are ok - raise Puppet::ParseError, "brick_layout_simple(): expects the first argument to be an integer, got #{args[0].inspect} which is of type #{args[0].class}" - end - unless args[1].is_a?(Array) - raise Puppet::ParseError, "brick_layout_simple(): expects the first argument to be an array, got #{args[1].inspect} which is of type #{args[1].class}" - end - - replica = args[0].to_i # convert from string if needed - bricks = args[1] - - # TODO: these functions could be in separate puppet files - # eg: Puppet::Parser::Functions.function('myfunc') - # function_myfunc(...) - def brick_str_to_hash(bricks) - # this loop converts brick strings to brick dict's... - result = [] - bricks.each do |x| - a = x.split(':') - #assert a.length == 2 # TODO - p = a[1] - p = ((p[-1, 1] == '/') ? p : (p+'/')) # endswith - - result.push({'host'=> a[0], 'path'=> p}) - end - return result - end - - collect = {} - parsed = brick_str_to_hash(bricks) - parsed.each do |x| - key = x['host'] - val = x['path'] - - if not collect.has_key?(key) - collect[key] = [] # initialize - end - - collect[key].push(val) # save in array - # TODO: ensure this array is always sorted (we could also do this after - # or always insert elements in the correct sorted order too :P) - collect[key] = collect[key].sort - end - - # we also could do this sort here... - collect.keys.each do |x| - collect[x] = collect[x].sort - end - - final = [] # final order... - # TODO: here we can probably detect if this is an asymmetrical configurations, or maybe bad naming... - while collect.size > 0 - collect.keys.sort.each do |x| - - # NOTE: this array should already be sorted! - p = collect[x].shift # assume an array of at least 1 element - final.push( { 'host' => x, 'path' => p } ) # save - - if collect[x].size == 0 # maybe the array is empty now - collect.delete(x) # remove that empty list's key - end - end - end - - # build final result - result = final.collect {|x| x['host']+':'+x['path'] } - result # return - end -end - -# vim: ts=8 diff --git a/gluster/manifests/again.pp b/gluster/manifests/again.pp deleted file mode 100644 index 63ee8d1be..000000000 --- a/gluster/manifests/again.pp +++ /dev/null @@ -1,39 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: use with: -# notify => Common::Again::Delta['gluster-exec-again'], - -# NOTE: this should be attached to the logical (often last) thing that, when it -# runs, means or is a signal that something more is going to happen in the next -# puppet run, and that stuff that is going to happen is useful to what just did -# run, which is why we don't want to wait for it to happen naturally in 30 min. - -class gluster::again { - - # TODO: we could include an option to disable this exec again and - # replace it with a "dummy" noop if someone doesn't want to use it. - include common::again - - # when notified, this will run puppet again, delta sec after it ends! - common::again::delta { 'gluster-exec-again': - delta => 120, # 2 minutes - } - -} - -# vim: ts=8 diff --git a/gluster/manifests/api.pp b/gluster/manifests/api.pp deleted file mode 100644 index 3f73baa2d..000000000 --- a/gluster/manifests/api.pp +++ /dev/null @@ -1,44 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class gluster::api( - $repo = true, # are we using the automatic repo ? - $version = '' # pick a specific version (defaults to latest) -) { - include gluster::params - - $rname = "${version}" ? { - '' => 'gluster', - default => "gluster-${version}", - } - - # certain packages don't exist on certain operating systems - if "${::gluster::params::package_glusterfs_api}" != '' { - package { "${::gluster::params::package_glusterfs_api}": - ensure => "${version}" ? { - '' => present, - default => "${version}", - }, - require => $repo ? { - false => undef, - default => Gluster::Repo["${rname}"], - }, - } - } -} - -# vim: ts=8 diff --git a/gluster/manifests/brick.pp b/gluster/manifests/brick.pp deleted file mode 100644 index 8358ad216..000000000 --- a/gluster/manifests/brick.pp +++ /dev/null @@ -1,536 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -define gluster::brick( - $group = 'default', # grouping for multiple puppet-glusters - # if dev is false, path in $name is used directly after a mkdir -p - $dev = false, # /dev/sdc, /dev/disk/by-id/scsi-36003048007e14f0014ca2743150a5471 - - $raid_su = '', # used by mkfs.xfs and lvm, eg: 256 (K) - $raid_sw = '', # used by mkfs.xfs and lvm, eg: 10 - - $partition = true, # partition, or build on the block dev? - $labeltype = '', # gpt - - $lvm = true, # use lvm or not ? - $lvm_thinp = false, # use lvm thin-p or not ? - $lvm_virtsize = '', # defaults to 100% available. - $lvm_chunksize = '', # chunk size for thin-p - $lvm_metadatasize = '', # meta data size for thin-p - - $fsuuid = '', # set a uuid for this fs (uuidgen) - $fstype = '', # xfs - $ro = false, # use for emergencies only- you want your fs rw - - $xfs_inode64 = false, - $xfs_nobarrier = false, - $force = false, # if true, this will overwrite any xfs fs it sees, useful for rebuilding gluster and wiping data. NOTE: there are other safeties in place to stop this. - $areyousure = false, # do you allow puppet to do dangerous things ? - $again = true, # do we want to use Exec['again'] ? - $comment = '' -) { - include gluster::brick::base - if $again { - include gluster::again - } - include gluster::vardir - include gluster::params - - #$vardir = $::gluster::vardir::module_vardir # with trailing slash - $vardir = regsubst($::gluster::vardir::module_vardir, '\/$', '') - - # eg: annex1.example.com:/storage1a - $split = split($name, ':') # do some $name parsing - $host = $split[0] # host fqdn - # NOTE: technically $path should be everything BUT split[0]. This - # lets our $path include colons if for some reason they're needed. - #$path = $split[1] # brick mount or storage path - # TODO: create substring function - $path = inline_template("<%= '${name}'.slice('${host}'.length+1, '${name}'.length-'${host}'.length-1) %>") - $short_path = sprintf("%s", regsubst($path, '\/$', '')) # no trailing - $valid_path = sprintf("%s/", regsubst($path, '\/$', '')) - - if ! ( "${host}:${path}" == "${name}" ) { - fail('The brick $name must match a $host-$path pattern.') - } - - Gluster::Host[$host] -> Gluster::Brick[$name] # brick requires host - - # create a brick tag to be collected by the gluster_brick_group_* fact! - $safename = regsubst("${name}", '/', '_', 'G') # make /'s safe - file { "${vardir}/brick/${safename}.${group}": - content => "${name}\n", - owner => root, - group => root, - mode => 644, - ensure => present, - require => File["${vardir}/brick/"], - } - - # - # fsuuid... - # - if ("${fsuuid}" != '') and "${fsuuid}" =~ /^[a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12}$/ { - fail("The chosen fs uuid: '${fsuuid}' is not valid.") - } - - # if we manually *pick* a uuid, then store it too, so that it - # sticks if we ever go back to using automatic uuids. this is - # useful if a user wants to initially import uuids by picking - # them manually, and then letting puppet take over afterwards - if "${fsuuid}" != '' { - # $group is unnecessary, but i left it in for consistency... - file { "${vardir}/brick/fsuuid/${safename}.${group}": - content => "${fsuuid}\n", - owner => root, - group => root, - mode => 600, # might as well... - ensure => present, - require => File["${vardir}/brick/fsuuid/"], - } - } - - # we sha1 to prevent weird characters in facter - $fsuuid_safename = sha1("${name}.${group}") - $valid_fsuuid = "${fsuuid}" ? { - # fact from the data generated in: ${vardir}/brick/fsuuid/* - '' => getvar("gluster_brick_fsuuid_${fsuuid_safename}"), # fact! - default => "${fsuuid}", - } - - # you might see this on first run if the fsuuid isn't generated yet - if (type($dev) != 'boolean') and ("${valid_fsuuid}" == '') { - warning('An $fsuuid must be specified or generated.') - } - - # - # raid... - # - # TODO: check inputs for sanity and auto-detect if one is empty - # TODO: maybe we can detect these altogether from the raid set! - if "${raid_su}" == '' and "${raid_sw}" == '' { - # if we are not using a real device, we should ignore warnings! - if type($dev) != 'boolean' { # real devices! - if $lvm or "${fstype}" == 'xfs' { - warning('Setting $raid_su and $raid_sw is recommended.') - } - } - } elsif "${raid_su}" != '' and "${raid_sw}" != '' { - # ensure both are positive int's ! - validate_re("${raid_su}", '^\d+$') - validate_re("${raid_sw}", '^\d+$') - - } else { - fail('You must set both $raid_su and $raid_sw or neither.') - } - - # - # partitioning... - # - $valid_labeltype = $labeltype ? { - #'msdos' => 'msdos', # TODO - default => 'gpt', - } - - # get the raw /dev/vdx device, and append the partition number - $dev0 = "`/bin/readlink -e ${dev}`" # resolve to /dev/ - - $part_mklabel = "${::gluster::params::program_parted} -s -m -a optimal ${dev0} mklabel ${valid_labeltype}" - $part_mkpart = "${::gluster::params::program_parted} -s -m -a optimal ${dev0} mkpart primary 0% 100%" - - # - $dev1 = $partition ? { - false => "${dev0}", # block device without partition - default => "${dev0}1", # partition one (eg: /dev/sda1) - } - - # - # lvm... - # - if $lvm_thinp and ( ! $lvm ) { - warning('You must enable $lvm if you want to use LVM thin-p.') - } - - if $lvm { - # NOTE: this is used for thin-provisioning, and RHS compliance! - - # NOTE: as a consequence of this type of automation, we generate - # really ugly vg names like: "vg_annex1.example.com+_gluster_" ! - # TODO: in the future, it might be nice to provide an option to - # use simplified naming based on hostname and a brick number... - $lvm_safename = regsubst("${safename}", ':', '+', 'G') # safe! - $lvm_vgname = "vg_${lvm_safename}" - $lvm_lvname = "lv_${lvm_safename}" - $lvm_tpname = "tp_${lvm_safename}" # thin pool (tp) - - $lvm_dataalignment = inline_template('<%= @raid_su.to_i*@raid_sw.to_i %>') - - $lvm_pvcreate = "${raid_su}${raid_sw}" ? { # both empty ? - '' => "${::gluster::params::program_pvcreate} ${dev1}", - default => "${::gluster::params::program_pvcreate} --dataalignment ${lvm_dataalignment}K ${dev1}", - } - - $lvm_vgcreate = "${::gluster::params::program_vgcreate} ${lvm_vgname} ${dev1}" - - # match --virtualsize with 100% of available vg by default - $lvm_thinp_virtsize = "${lvm_virtsize}" ? { # --virtualsize - '' => "`${::gluster::params::program_vgs} -o size --units b --noheadings ${lvm_vgname}`", - default => "${lvm_virtsize}", - } - - # TODO: is 64k a good/sane default ? - $lvm_thinp_chunksize = "${lvm_chunksize}" ? { - '' => '', - default => "--chunksize ${lvm_chunksize}", - } - - # TODO: is 16384 a good/sane default ? - $lvm_thinp_metadatasize = "${lvm_metadatasize}" ? { - '' => '', - default => "--poolmetadatasize ${lvm_metadatasize}", - } - - # README: 'man 7 lvmthin' to understand lvm thin provisioning - # MIRROR: http://man7.org/linux/man-pages/man7/lvmthin.7.html - # TODO: is this the optimal setup for thin-p ? - $lvm_thinp_lvcreate_cmdlist = [ - "${::gluster::params::program_lvcreate}", - "--thinpool ${lvm_vgname}/${lvm_tpname}", # thinp - '--extents 100%FREE', # let lvm figure out the --size - "--virtualsize ${lvm_thinp_virtsize}", - "${lvm_thinp_chunksize}", - "${lvm_thinp_metadatasize}", - " -n ${lvm_lvname}", # name it - ] - $lvm_thinp_lvcreate = join(delete($lvm_thinp_lvcreate_cmdlist, ''), ' ') - - # creates dev /dev/vgname/lvname - $lvm_lvcreate = $lvm_thinp ? { - true => "${lvm_thinp_lvcreate}", - default => "${::gluster::params::program_lvcreate} --extents 100%PVS -n ${lvm_lvname} ${lvm_vgname}", - } - } - - $dev2 = $lvm ? { - false => "${dev1}", # pass through, because not using lvm - default => "/dev/${lvm_vgname}/${lvm_lvname}", # thin-p too :) - } - - # - # mkfs... - # - $ro_bool = $ro ? { # this has been added as a convenience - true => 'ro', - default => 'rw', - } - - # if $dev is false, we assume we're using a path backing store on brick - $valid_fstype = type($dev) ? { - 'boolean' => $dev ? { - false => 'path', # no dev, just a path spec - default => '', # invalid type - }, - default => $fstype ? { - 'ext4' => 'ext4', # TODO - 'btrfs' => 'btrfs', - default => 'xfs', - }, - } - - if ( $valid_fstype == 'path' ) { - - # do a mkdir -p in the execution section below... - $options_list = [] # n/a - - # XFS mount options: - # http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=Documentation/filesystems/xfs.txt;hb=HEAD - } elsif ( $valid_fstype == 'xfs' ) { - # exec requires - include gluster::brick::xfs - $exec_requires = [Package["${::gluster::params::package_xfsprogs}"]] - - $xfs_arg00 = "${::gluster::params::program_mkfs_xfs}" - - $xfs_arg01 = '-q' # shh! - - # NOTE: the -f forces creation when it sees an old xfs part - $xfs_arg02 = $force ? { - true => '-f', - default => '', - } - - # Due to extensive use of extended attributes, RHS recommends - # XFS inode size set to 512 bytes from the defaults 256 Bytes. - $xfs_arg03 = '-i size=512' - - # An XFS file system allows you to select a logical block size - # for the file-system directory that is greater than the - # logical block size of the file-system. Increasing the logical - # block size for the directories from the default of 4K, - # decreases the directory IO, which improves the performance of - # directory operations. See: - # http://xfs.org/index.php/XFS_FAQ#Q:_Performance:_mkfs.xfs_-n_size.3D64k_option - $xfs_arg04 = '-n size=8192' - - # To align the IO at the file system layer it is important that - # we set the correct stripe unit (stripe element size) and - # stripe width (number of data disks) while formatting the file - # system. These options are sometimes auto-detected but manual - # configuration is needed with many of the hardware RAID - # volumes. - $xfs_arg05 = "${raid_su}${raid_sw}" ? { # both empty ? - '' => '', - default => "-d su=${raid_su}k,sw=${raid_sw}", - } - - $xfs_cmdlist = [ - "${xfs_arg00}", - "${xfs_arg01}", - "${xfs_arg02}", - "${xfs_arg03}", - "${xfs_arg04}", - "${xfs_arg05}", - "${dev2}" - ] - $xfs_cmd = join(delete($xfs_cmdlist, ''), ' ') - - # TODO: xfs_admin doesn't have a --quiet flag. silence it... - $xfs_admin = "${::gluster::params::program_xfsadmin} -U '${valid_fsuuid}' ${dev2}" - - # mkfs w/ uuid command - $mkfs_exec = "${xfs_cmd} && ${xfs_admin}" - - # By default, XFS allocates inodes to reflect their on-disk - # location. However, because some 32-bit userspace applications - # are not compatible with inode numbers greater than 232, XFS - # will allocate all inodes in disk locations which result in - # 32-bit inode numbers. This can lead to decreased performance - # on very large filesystems (i.e. larger than 2 terabytes), - # because inodes are skewed to the beginning of the block - # device, while data is skewed towards the end. - # To address this, use the inode64 mount option. This option - # configures XFS to allocate inodes and data across the entire - # file system, which can improve performance. - $option01 = $xfs_inode64 ? { - true => 'inode64', - default => '', - } - - # By default, XFS uses write barriers to ensure file system - # integrity even when power is lost to a device with write - # caches enabled. For devices without write caches, or with - # battery-backed write caches, disable barriers using the - # nobarrier option. - $option02 = $xfs_nobarrier ? { - true => 'nobarrier', - default => '', - } - - $options_list = ["${option01}", "${option02}"] - - } elsif ( $valid_fstype == 'ext4' ) { - # exec requires - include gluster::brick::ext4 - $exec_requires = [Package["${::gluster::params::package_e2fsprogs}"]] - - # mkfs w/ uuid command - $mkfs_exec = "${::gluster::params::program_mkfs_ext4} -U '${valid_fsuuid}' ${dev2}" - - # mount options - $options_list = [] # TODO - - } elsif ( $valid_fstype == 'btrfs' ) { - # exec requires - include gluster::brick::btrfs - $exec_requires = [Package["${::gluster::params::package_btrfsprogs}"]] - - # FIXME: this filesystem has not yet been optimized for performance - - # mkfs w/ uuid command - $mkfs_exec = "${::gluster::params::program_mkfs_btrfs} -U '${valid_fsuuid}' ${dev2}" - - # mount options - $options_list = [] # TODO - - } else { - fail('The $fstype is invalid.') - } - - # put all the options in an array, remove the empty ones, and join with - # commas (this removes ',,' double comma uglyness) - # adding 'defaults' here ensures no ',' (leading comma) in mount command - $mount_options = inline_template('<%= (["defaults"]+@options_list).delete_if {|x| x.empty? }.join(",") %>') - - $exec_noop = $areyousure ? { - true => false, - default => true, - } - - # if we're on itself, and we have a real device to work with - if (type($dev) != 'boolean') and ("${fqdn}" == "${host}") { - - # partitioning... - if $partition { - if $exec_noop { - notify { "noop for partitioning: ${name}": - message => "${part_mklabel} && ${part_mkpart}", - } - } - - exec { "${part_mklabel} && ${part_mkpart}": - logoutput => on_failure, - unless => [ # if one element is true, this *doesn't* run - "/usr/bin/test -e ${dev1}", # does the partition 1 exist ? - '/bin/false', # TODO: add more criteria - ], - require => $exec_requires, - timeout => 3600, # TODO - noop => $exec_noop, - before => $lvm ? { # if no lvm, skip to mkfs - false => Exec["gluster-brick-mkfs-${name}"], - default => Exec["gluster-brick-lvm-pvcreate-${name}"], - }, - alias => "gluster-brick-partition-${name}", - } - } - - # lvm... - if $lvm { - if $exec_noop { - notify { "noop for lvm: ${name}": - message => "${lvm_pvcreate} && ${lvm_vgcreate} && ${lvm_lvcreate}", - } - } - - exec { "${lvm_pvcreate}": - logoutput => on_failure, - unless => [ # if one element is true, this *doesn't* run - "${::gluster::params::program_pvdisplay} ${dev1}", - '/bin/false', # TODO: add more criteria - ], - require => $exec_requires, - timeout => 3600, # set to something very long - noop => $exec_noop, - before => Exec["gluster-brick-lvm-vgcreate-${name}"], - alias => "gluster-brick-lvm-pvcreate-${name}", - } - - exec { "${lvm_vgcreate}": - logoutput => on_failure, - unless => [ # if one element is true, this *doesn't* run - "${::gluster::params::program_vgdisplay} ${lvm_vgname}", - '/bin/false', # TODO: add more criteria - ], - require => $exec_requires, - timeout => 3600, # set to something very long - noop => $exec_noop, - before => Exec["gluster-brick-lvm-lvcreate-${name}"], - alias => "gluster-brick-lvm-vgcreate-${name}", - } - - exec { "${lvm_lvcreate}": - logoutput => on_failure, - unless => [ # if one element is true, this *doesn't* run - #"${::gluster::params::program_lvdisplay} ${lvm_lvname}", # nope! - "${::gluster::params::program_lvs} --separator ':' | /usr/bin/tr -d ' ' | /bin/awk -F ':' '{print \$1}' | /bin/grep -q '${lvm_lvname}'", - '/bin/false', # TODO: add more criteria - ], - require => $exec_requires, - timeout => 3600, # set to something very long - noop => $exec_noop, - before => Exec["gluster-brick-mkfs-${name}"], - alias => "gluster-brick-lvm-lvcreate-${name}", - } - } - - if $exec_noop { - notify { "noop for mkfs: ${name}": - message => "${mkfs_exec}", - } - } else { - # if valid_fsuuid isn't ready, trigger an exec again... - exec { "gluster-brick-fsuuid-execagain-${name}": - command => '/bin/true', # do nothing but notify - logoutput => on_failure, - onlyif => "/usr/bin/test -z '${valid_fsuuid}'", - notify => $again ? { - false => undef, - default => Common::Again::Delta['gluster-exec-again'], - }, - # this (optional) require makes it more logical - require => File["${vardir}/brick/fsuuid/"], - } - } - - # mkfs... - exec { "${mkfs_exec}": - logoutput => on_failure, - onlyif => "/usr/bin/test -n '${valid_fsuuid}'", - unless => [ # if one element is true, this *doesn't* run - "/usr/bin/test -e /dev/disk/by-uuid/${valid_fsuuid}", - "${::gluster::params::program_findmnt} --output 'TARGET,SOURCE' -t ${valid_fstype} --target '${valid_path}' -n", - '/bin/false', # TODO: add more criteria - ], - require => $exec_requires, - timeout => 3600, # set to something very long - noop => $exec_noop, - alias => "gluster-brick-mkfs-${name}", - } - - # make an empty directory for the mount point - file { "${valid_path}": - ensure => directory, # make sure this is a directory - recurse => false, # don't recurse into directory - purge => false, # don't purge unmanaged files - force => false, # don't purge subdirs and links - require => Exec["gluster-brick-mkfs-${name}"], - } - - # mount points don't seem to like trailing slashes... - if "${valid_fsuuid}" != '' { # in case fsuuid isn't ready yet - mount { "${short_path}": - atboot => true, - ensure => mounted, - device => "UUID=${valid_fsuuid}", - fstype => "${valid_fstype}", - # noatime,nodiratime to save gluster from silly updates - options => "${mount_options},${ro_bool},noatime,nodiratime,noexec", # TODO: is nodev? nosuid? noexec? a good idea? - dump => '0', # fs_freq: 0 to skip file system dumps - # NOTE: technically this should be '2', to `fsck.xfs` - # after the rootfs ('1'), but fsck.xfs actually does - # 'nothing, successfully', so it's irrelevant, because - # xfs uses xfs_check and friends only when suspect. - pass => '2', # fs_passno: 0 to skip fsck on boot - require => [ - File["${valid_path}"], - ], - } - } - - } elsif ((type($dev) == 'boolean') and (! $dev)) and ("${fqdn}" == "${host}") { - - # ensure the full path exists! - exec { "/bin/mkdir -p '${valid_path}'": - creates => "${valid_path}", - logoutput => on_failure, - noop => $exec_noop, - alias => "gluster-brick-mkdir ${name}", - } - } -} - -# vim: ts=8 diff --git a/gluster/manifests/brick/base.pp b/gluster/manifests/brick/base.pp deleted file mode 100644 index c39cb3f45..000000000 --- a/gluster/manifests/brick/base.pp +++ /dev/null @@ -1,41 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class gluster::brick::base { - - include gluster::vardir - #$vardir = $::gluster::vardir::module_vardir # with trailing slash - $vardir = regsubst($::gluster::vardir::module_vardir, '\/$', '') - - file { "${vardir}/brick/": - ensure => directory, # make sure this is a directory - recurse => true, # don't recurse into directory - purge => true, # don't purge unmanaged files - force => true, # don't purge subdirs and links - require => File["${vardir}/"], - } - - # don't purge the fsuuid file's generated within - file { "${vardir}/brick/fsuuid/": - ensure => directory, # make sure this is a directory - recurse => false, # don't recurse into directory - purge => false, # don't purge unmanaged files - force => false, # don't purge subdirs and links - require => File["${vardir}/brick/"], - } -} -# vim: ts=8 diff --git a/gluster/manifests/brick/btrfs.pp b/gluster/manifests/brick/btrfs.pp deleted file mode 100644 index 94232b380..000000000 --- a/gluster/manifests/brick/btrfs.pp +++ /dev/null @@ -1,27 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class gluster::brick::btrfs { - - include gluster::params - - package { "${::gluster::params::package_btrfsprogs}": - ensure => present, - } -} - -# vim: ts=8 diff --git a/gluster/manifests/brick/ext4.pp b/gluster/manifests/brick/ext4.pp deleted file mode 100644 index 9cbef7407..000000000 --- a/gluster/manifests/brick/ext4.pp +++ /dev/null @@ -1,27 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class gluster::brick::ext4 { - - include gluster::params - - package { "${::gluster::params::package_e2fsprogs}": - ensure => present, - } -} - -# vim: ts=8 diff --git a/gluster/manifests/brick/xfs.pp b/gluster/manifests/brick/xfs.pp deleted file mode 100644 index 8c1440cbe..000000000 --- a/gluster/manifests/brick/xfs.pp +++ /dev/null @@ -1,27 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class gluster::brick::xfs { - - include gluster::params - - package { "${::gluster::params::package_xfsprogs}": - ensure => present, - } -} - -# vim: ts=8 diff --git a/gluster/manifests/host.pp b/gluster/manifests/host.pp deleted file mode 100644 index 7f6efede0..000000000 --- a/gluster/manifests/host.pp +++ /dev/null @@ -1,386 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# TODO: instead of peering manually this way (which makes the most sense, but -# might be unsupported by gluster) we could peer using the cli, and ensure that -# only the host holding the vip is allowed to execute cluster peer operations. - -define gluster::host( - $ip = '', # you can specify which ip address to use (if multiple) - $uuid = '', # if empty, puppet will attempt to use the gluster fact - $password = '' # if empty, puppet will attempt to choose one magically -) { - include gluster::vardir - include gluster::params - - #$vardir = $::gluster::vardir::module_vardir # with trailing slash - $vardir = regsubst($::gluster::vardir::module_vardir, '\/$', '') - - if ("${uuid}" != '') and (! ("${uuid}" =~ /^[a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12}$/)) { - fail("The chosen UUID: '${uuid}' is not valid.") - } - - Gluster::Host[$name] -> Service["${::gluster::params::service_glusterd}"] # glusterd requires host - - # if we're on itself - if "${fqdn}" == "${name}" { - - $valid_ip = "${ip}" ? { - '' => "${::gluster_host_ip}" ? { # smart fact... - '' => "${::ipaddress}", # puppet picks! - default => "${::gluster_host_ip}", # smart - }, - default => "${ip}", # user selected - } - if "${valid_ip}" == '' { - fail('No valid IP exists!') - } - - # store the ip here so that it can be accessed by bricks... - class { '::gluster::host::data': - #name => $name, - ip => "${valid_ip}", - fqdn => "${fqdn}", - } - - # don't purge the uuid file generated within - file { "${vardir}/uuid/": - ensure => directory, # make sure this is a directory - recurse => true, # recurse into directory - purge => true, # purge unmanaged files - force => true, # purge subdirs and links - require => File["${vardir}/"], - } - - # if we manually *pick* a uuid, then store it too, so that it - # sticks if we ever go back to using automatic uuids. this is - # useful if a user wants to initially import uuids by picking - # them manually, and then letting puppet take over afterwards - file { "${vardir}/uuid/uuid": - content => "${uuid}" ? { - '' => undef, - default => "${uuid}\n", - }, - owner => root, - group => root, - mode => 600, # might as well... - ensure => present, - require => File["${vardir}/uuid/"], - } - - $valid_uuid = "${uuid}" ? { - # fact from the data generated in: ${vardir}/uuid/uuid - '' => "${::gluster_uuid}", - default => "${uuid}", - } - if "${valid_uuid}" == '' { - fail('No valid UUID exists yet!') - } else { - # get shorter version string for loose matching... - $gluster_main_version = regsubst( - "${gluster_version}", # eg: 3.4.0 - '^(\d+)\.(\d+)\.(\d+)$', # int.int.int - '\1.\2' # print int.int - ) - - # TODO: add additional values to this table... - $operating_version = "${gluster_version}" ? { - '' => '', # gluster not yet installed... - # specific version matches go here... - '3.4.0' => '2', - default => "${gluster_main_version}" ? { - # loose version matches go here... - #'3.3' => '1', # blank... - '3.4' => '2', - #'3.5' => '3', # guessing... - default => '-1', # unknown... - }, - } - - # this catches unknown gluster versions to add to table - if "${operating_version}" == '-1' { - warning("Gluster version '${gluster_version}' is unknown.") - } - - # set a unique uuid per host, and operating version... - file { '/var/lib/glusterd/glusterd.info': - content => template('gluster/glusterd.info.erb'), - owner => root, - group => root, - mode => 600, # u=rw,go=r - seltype => 'glusterd_var_lib_t', - seluser => 'system_u', - ensure => present, - notify => Service["${::gluster::params::service_glusterd}"], - require => File['/var/lib/glusterd/'], - } - - # NOTE: $name here should probably be the fqdn... - @@file { "${vardir}/uuid/uuid_${name}": - content => "${valid_uuid}\n", - tag => 'gluster_uuid', - owner => root, - group => root, - mode => 600, - ensure => present, - } - } - - File <<| tag == 'gluster_uuid' |>> { # collect to make facts - } - - } else { - $valid_uuid = "${uuid}" ? { - # fact from the data generated in: ${vardir}/uuid/uuid - '' => getvar("gluster_uuid_${name}"), # fact ! - default => "${uuid}", - } - if "${valid_uuid}" == '' { - notice('No valid UUID exists yet.') # different msg - } else { - # set uuid= - exec { "/bin/echo 'uuid=${valid_uuid}' >> '/var/lib/glusterd/peers/${valid_uuid}'": - logoutput => on_failure, - unless => "/bin/grep -qF 'uuid=' '/var/lib/glusterd/peers/${valid_uuid}'", - notify => [ - # propagate the notify up - File['/var/lib/glusterd/peers/'], - Service["${::gluster::params::service_glusterd}"], # ensure reload - ], - before => File["/var/lib/glusterd/peers/${valid_uuid}"], - alias => "gluster-host-uuid-${name}", - # FIXME: doing this causes a dependency cycle! adding - # the Package[] require doesn't. It would be most - # correct to require the peers/ folder, but since it's - # not working, requiring the Package[] will still give - # us the same result. (Package creates peers/ folder). - # NOTE: it's possible the cycle is a bug in puppet or a - # bug in the dependencies somewhere else in this module. - #require => File['/var/lib/glusterd/peers/'], - require => Package["${::gluster::params::package_glusterfs_server}"], - } - - # set state= - exec { "/bin/echo 'state=3' >> '/var/lib/glusterd/peers/${valid_uuid}'": - logoutput => on_failure, - unless => "/bin/grep -qF 'state=' '/var/lib/glusterd/peers/${valid_uuid}'", - notify => [ - # propagate the notify up - File['/var/lib/glusterd/peers/'], - Service["${::gluster::params::service_glusterd}"], # ensure reload - ], - before => File["/var/lib/glusterd/peers/${valid_uuid}"], - require => Exec["gluster-host-uuid-${name}"], - alias => "gluster-host-state-${name}", - } - - # set hostname1=... - exec { "/bin/echo 'hostname1=${name}' >> '/var/lib/glusterd/peers/${valid_uuid}'": - logoutput => on_failure, - unless => "/bin/grep -qF 'hostname1=' '/var/lib/glusterd/peers/${valid_uuid}'", - notify => [ - # propagate the notify up - File['/var/lib/glusterd/peers/'], - Service["${::gluster::params::service_glusterd}"], # ensure reload - ], - before => File["/var/lib/glusterd/peers/${valid_uuid}"], - require => Exec["gluster-host-state-${name}"], - } - - # tag the file so it doesn't get removed by purge - file { "/var/lib/glusterd/peers/${valid_uuid}": - ensure => present, - owner => root, - group => root, - # NOTE: this mode was found by inspecting the process - mode => 600, # u=rw,go=r - seltype => 'glusterd_var_lib_t', - seluser => 'system_u', - notify => [ - # propagate the notify up - File['/var/lib/glusterd/peers/'], - Service["${::gluster::params::service_glusterd}"], # ensure reload - ], - } - } - } - - # vrrp... - $vrrp = $::gluster::server::vrrp - if ( "${fqdn}" == "${name}" ) and $vrrp { - - $vip = $::gluster::server::vip - if ! ($vip =~ /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/) { - fail('You must specify a valid VIP to use with VRRP.') - } - - file { "${vardir}/vrrp/": - ensure => directory, # make sure this is a directory - recurse => true, # recurse into directory - purge => true, # purge unmanaged files - force => true, # purge subdirs and links - require => File["${vardir}/"], - } - - # store so that a fact can figure out the interface and cidr... - file { "${vardir}/vrrp/ip": - content => "${valid_ip}\n", - owner => root, - group => root, - mode => 600, # might as well... - ensure => present, - require => File["${vardir}/vrrp/"], - } - - # NOTE: this is a tag to protect the pass file... - file { "${vardir}/vrrp/vrrp": - content => "${password}" ? { - '' => undef, - default => "${password}", - }, - owner => root, - group => root, - mode => 600, # might as well... - ensure => present, - require => File["${vardir}/vrrp/"], - } - - # NOTE: $name here should probably be the fqdn... - @@file { "${vardir}/vrrp/vrrp_${name}": - content => "${::gluster_vrrp}\n", - tag => 'gluster_vrrp', - owner => root, - group => root, - mode => 600, - ensure => present, - } - - File <<| tag == 'gluster_vrrp' |>> { # collect to make facts - } - - # this figures out the interface from the $valid_ip value - $if = "${::gluster_vrrp_interface}" # a smart fact! - $cidr = "${::gluster_vrrp_cidr}" # even smarter! - $p = "${::gluster::server::password}" ? { # shh secret... - '' => "${::gluster_vrrp_password}", # combined fact - default => "${::gluster::server::password}", - } - # this fact is sorted, which is very, very important...! - $fqdns_fact = "${::gluster_vrrp_fqdns}" # fact ! - $fqdns = split($fqdns_fact, ',') # list ! - - if "${if}" != '' and "${cidr}" != '' and "${p}" != '' { - - keepalived::vrrp { 'VI_GLUSTER': # TODO: groups! - state => "${fqdns[0]}" ? { # first in list - '' => 'MASTER', # list is empty - "${fqdn}" => 'MASTER', # we are first! - default => 'BACKUP', # other in list - }, - interface => "${if}", - mcastsrc => "${valid_ip}", - # TODO: support configuring the label index! - # label ethX:1 for first VIP ethX:2 for second... - ipaddress => "${vip}/${cidr} dev ${if} label ${if}:1", - # FIXME: this limits puppet-gluster to 256 hosts maximum - priority => inline_template("<%= 255 - (@fqdns.index('${fqdn}') or 0) %>"), - routerid => 42, # TODO: support configuring it! - advertint => 3, # TODO: support configuring it! - password => "${p}", - #group => 'gluster', # TODO: groups! - watchip => "${vip}", - shorewall_zone => "${::gluster::server::zone}", - shorewall_ipaddress => "${valid_ip}", - } - } - } - - # firewalling... - $shorewall = $::gluster::server::shorewall - if ( "${fqdn}" == "${name}" ) and $shorewall { - $zone = $::gluster::server::zone # firewall zone - $ips = $::gluster::server::ips # override host ip list - - #$other_host_ips = inline_template("<%= ips.delete_if {|x| x == '${ipaddress}' }.join(',') %>") # list of ips except myself - #$all_ips = inline_template("<%= (ips+[vip]+clients).uniq.delete_if {|x| x.empty? }.join(',') %>") - $source_ips = type($ips) ? { - 'array' => inline_template("<%= (ips+[]).uniq.delete_if {|x| x.empty? }.join(',') %>"), - default => ["${valid_ip}"], - } - - @@shorewall::rule { "glusterd-management-${name}": - action => 'ACCEPT', - source => "${zone}", # override this on collect... - source_ips => $source_ips, - dest => '$FW', - proto => 'tcp', - port => '24007', - comment => 'Allow incoming tcp:24007 from each glusterd.', - tag => 'gluster_firewall_management', - ensure => present, - } - - # NOTE: used by rdma - @@shorewall::rule { "glusterd-rdma-${name}": - action => 'ACCEPT', - source => "${zone}", # override this on collect... - source_ips => $source_ips, - dest => '$FW', - proto => 'tcp', - port => '24008', - comment => 'Allow incoming tcp:24008 for rdma.', - tag => 'gluster_firewall_management', - ensure => present, - } - - # TODO: is this only used for nfs? - @@shorewall::rule { "gluster-tcp111-${name}": - action => 'ACCEPT', - source => "${zone}", # override this on collect... - source_ips => $source_ips, - dest => '$FW', - proto => 'tcp', - port => '111', - comment => 'Allow tcp 111.', - tag => 'gluster_firewall_management', - ensure => present, - } - - # TODO: is this only used for nfs? - # TODO: johnmark says gluster nfs udp doesn't work :P - @@shorewall::rule { "gluster-udp111-${name}": - action => 'ACCEPT', - source => "${zone}", # override this on collect... - source_ips => $source_ips, - dest => '$FW', - proto => 'udp', - port => '111', - comment => 'Allow udp 111.', - tag => 'gluster_firewall_management', - ensure => present, - } - - # TODO: this collects our own entries too... we could exclude - # them but this isn't a huge issue at the moment... - Shorewall::Rule <<| tag == 'gluster_firewall_management' |>> { - source => "${zone}", # use our source zone - before => Service["${::gluster::params::service_glusterd}"], - } - } -} - -# vim: ts=8 diff --git a/gluster/manifests/host/data.pp b/gluster/manifests/host/data.pp deleted file mode 100644 index dad0b3733..000000000 --- a/gluster/manifests/host/data.pp +++ /dev/null @@ -1,26 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class gluster::host::data( - #$name, - $ip, - $fqdn -) { - # so far, this does nothing but 'store' variables -} - -# vim: ts=8 diff --git a/gluster/manifests/init.pp b/gluster/manifests/init.pp deleted file mode 100644 index 952843fbb..000000000 --- a/gluster/manifests/init.pp +++ /dev/null @@ -1,63 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# -# NOTES -# - -# * To rebuild gluster (erasing all data), rm -rf the storage dirs to -# clear metadata. To do this without erasing data, read this article: -# http://joejulian.name/blog/glusterfs-path-or-a-prefix-of-it-is-already-part-of-a-volume/ -# -# * List of state codes: -# -# static char *glusterd_friend_sm_state_names[] = { # glusterd-sm.c -# "Establishing Connection", # 0 -# "Probe Sent to Peer", # 1 -# "Probe Received from Peer", # 2 -# "Peer in Cluster", # 3 (verified) -# "Accepted peer request", # 4 -# "Sent and Received peer request", # 5 -# "Peer Rejected", # 6 (verified) -# "Peer detach in progress", # 7 -# "Probe Received from peer", # 8 -# "Connected to Peer", # 9 -# "Peer is connected and Accepted", # 10 -# "Invalid State" # 11 -# }; -# -# * To use this gluster module, it's recommended that all nodes receive -# the same puppet configuration. Puppet is smart enough to know what to -# run on each participating node. Watchout for the mild race condition. -# -# * TODO: add more notes... - -# -# XXX: FIXME: TODO -# -# XXX: does parted align disks properly ? -# XXX: mkfs.xfs -ssize=4k /dev/sdc1 ? # should "-s sector_size" be used ? http://kb.lsi.com/KnowledgebaseArticle16187.aspx ? -# XXX: setup auth somehow... ip address based for now # XXX: use volume::property... - -# FIXME: test this: https://bugzilla.redhat.com/show_bug.cgi?id=GLUSTER-3769 -# FIXME: peering: maybe we can just specify a guid somewhere so that everyone peers together ? -# FIXME: can we setup gluster by using templated volume files instead ? - -# TODO: package { 'xfsdump': ensure => present } is this useful for something ? -# TODO: find out when ports are actually necessary for version 3.3 - -# vim: ts=8 diff --git a/gluster/manifests/mount.pp b/gluster/manifests/mount.pp deleted file mode 100644 index 2e842b1cd..000000000 --- a/gluster/manifests/mount.pp +++ /dev/null @@ -1,180 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# XXX: try mounting with: glusterfs --volfile-server= --volfile-id= --xlator-option='*dht*.assert-no-child-down=yes' # TODO: quotes or not? -define gluster::mount( - $server, # NOTE: use a vip as server hostname - $rw = false, # mount read only (true) or rw (false) -# $suid = false, # mount with suid (true) or nosuid (false) # TODO: will this work with gluster ? - $mounted = true, # useful if we want to pull in the group - # defs, but not actually mount (testing) - $repo = true, # add a repo automatically? true or false - $version = '', # pick a specific version (defaults to latest) - $ip = '', # you can specify which ip address to use (if multiple) - $shorewall = false -) { - include gluster::params - - #mount -t glusterfs brick1.example.com:/test /test - #include gluster::mount::base - #class { '::gluster::mount::base': - # repo => $repo, - # version => $version, - #} - $params = { - 'repo' => $repo, - 'version' => $version, - } - # because multiple gluster::mount types are allowed on the same server, - # we include with the ensure_resource function to avoid identical calls - ensure_resource('class', 'gluster::mount::base', $params) - - # eg: vip:/volume - $split = split($server, ':') # do some $server parsing - $host = $split[0] # host fqdn or ip (eg: vip) - # NOTE: technically $path should be everything BUT split[0]. This - # lets our $path include colons if for some reason they're needed. - #$path = $split[1] # volume - # TODO: create substring function - $path = inline_template("<%= '${server}'.slice('${host}'.length+1, '${server}'.length-'${host}'.length-1) %>") - $short_path = sprintf("%s", regsubst($path, '\/$', '')) # no trailing - #$valid_path = sprintf("%s/", regsubst($path, '\/$', '')) - $volume = sprintf("%s", regsubst($short_path, '^\/', '')) # no leading - - if ! ( "${host}:${path}" == "${server}" ) { - fail('The $server must match a $host:$path pattern.') - } - - if ! ( "${host}:/${volume}" == "${server}" ) { - fail('The $server must match a $host:/$volume pattern.') - } - - $short_name = sprintf("%s", regsubst("${name}", '\/$', '')) # no trailing - $long_name = sprintf("%s/", regsubst("${name}", '\/$', '')) # trailing... - - $valid_ip = "${ip}" ? { - '' => "${::gluster_host_ip}" ? { # smart fact... - '' => "${::ipaddress}", # puppet picks! - default => "${::gluster_host_ip}", # smart - }, - default => "${ip}", # user selected - } - if "${valid_ip}" == '' { - fail('No valid IP exists!') - } - - if $shorewall { - $safename = regsubst("${name}", '/', '_', 'G') # make /'s safe - @@shorewall::rule { "glusterd-management-${fqdn}-${safename}": - #@@shorewall::rule { "glusterd-management-${volume}-${fqdn}": - action => 'ACCEPT', - source => '', # override this on collect... - source_ips => ["${valid_ip}"], - dest => '$FW', - proto => 'tcp', - port => '24007', - comment => 'Allow incoming tcp:24007 from each glusterd.', - tag => 'gluster_firewall_management', - ensure => present, - } - - # wrap shorewall::rule in a fake type so that we can add $match - #@@shorewall::rule { "gluster-volume-${fqdn}-${safename}": - @@gluster::rulewrapper { "gluster-volume-${fqdn}-${safename}": - action => 'ACCEPT', - source => '', # override this on collect... - source_ips => ["${valid_ip}"], - dest => '$FW', - proto => 'tcp', - port => '', # override this on collect... - #comment => "${fqdn}", - comment => 'Allow incoming tcp port from glusterfsds.', - tag => 'gluster_firewall_volume', - match => "${volume}", # used for collection - ensure => present, - } - } - - $rw_bool = $rw ? { - true => 'rw', - default => 'ro', - } - - # TODO: will this work with gluster ? - #$suid_bool = $suid ? { - # true => 'suid', - # default => 'nosuid', - #} - - $mounted_bool = $mounted ? { - false => unmounted, - default => mounted, - } - - # ensure parent directories exist - exec { "gluster-mount-mkdir-${name}": - command => "/bin/mkdir -p '${long_name}'", - creates => "${long_name}", - logoutput => on_failure, - before => File["${long_name}"], - } - - # make an empty directory for the mount point - file { "${long_name}": # ensure a trailing slash - ensure => directory, # make sure this is a directory - recurse => false, # don't recurse into directory - purge => false, # don't purge unmanaged files - force => false, # don't purge subdirs and links - alias => "${short_name}", # don't allow duplicates name's - } - - $packages = "${::gluster::params::package_glusterfs_fuse}" ? { - '' => ["${::gluster::params::package_glusterfs}"], - default => [ - "${::gluster::params::package_glusterfs}", - "${::gluster::params::package_glusterfs_fuse}", - ], - } - # Mount Options: - # * backupvolfile-server=server-name - # * fetch-attempts=N (where N is number of attempts) - # * log-level=loglevel - # * log-file=logfile - # * direct-io-mode=[enable|disable] - # * ro (for readonly mounts) - # * acl (for enabling posix-ACLs) - # * worm (making the mount WORM - Write Once, Read Many type) - # * selinux (enable selinux on GlusterFS mount) - # XXX: consider mounting only if some exported resource, collected and turned into a fact shows that the volume is available... - # XXX: or something... consider adding the notify => Poke[] functionality - mount { "${short_name}": - atboot => true, - ensure => $mounted_bool, - device => "${server}", - fstype => 'glusterfs', - options => "defaults,_netdev,${rw_bool}", # TODO: will $suid_bool work with gluster ? - dump => '0', # fs_freq: 0 to skip file system dumps - pass => '0', # fs_passno: 0 to skip fsck on boot - require => [ - Package[$packages], - File["${long_name}"], # the mountpoint - Exec['gluster-fuse'], # ensure fuse is loaded! - ], - } -} - -# vim: ts=8 diff --git a/gluster/manifests/mount/base.pp b/gluster/manifests/mount/base.pp deleted file mode 100644 index 38bdd8552..000000000 --- a/gluster/manifests/mount/base.pp +++ /dev/null @@ -1,97 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class gluster::mount::base( - $repo = true, # add a repo automatically? true or false - $version = '' # pick a specific version (defaults to latest) -) { - include gluster::vardir - include gluster::params - #$vardir = $::gluster::vardir::module_vardir # with trailing slash - $vardir = regsubst($::gluster::vardir::module_vardir, '\/$', '') - - # if we use ::mount and ::server on the same machine, this could clash, - # so we use the ensure_resource function to allow identical duplicates! - $rname = "${version}" ? { - '' => 'gluster', - default => "gluster-${version}", - } - if $repo { - $params = { - 'version' => "${version}", - } - ensure_resource('gluster::repo', "${rname}", $params) - } - - $packages = "${::gluster::params::package_glusterfs_fuse}" ? { - '' => ["${::gluster::params::package_glusterfs}"], - default => [ - "${::gluster::params::package_glusterfs}", - "${::gluster::params::package_glusterfs_fuse}", - ], - } - package { $packages: - ensure => "${version}" ? { - '' => present, - default => "${version}", - }, - before => "${::gluster::params::package_glusterfs_api}" ? { - '' => undef, - default => Package["${::gluster::params::package_glusterfs_api}"], - }, - require => $repo ? { - false => undef, - default => Gluster::Repo["${rname}"], - }, - } - - $api_params = { - 'repo' => $repo, - 'version' => "${version}", - } - ensure_resource('class', 'gluster::api', $api_params) - - # FIXME: choose a reliable and correct way to ensure fuse is loaded - # dmesg | grep -i fuse - # modprobe fuse - # dmesg | grep -i fuse - #fuse init (API version 7.13) - # - - # modprobe fuse if it's missing - exec { "${::gluster::params::program_modprobe} fuse": - logoutput => on_failure, - onlyif => '/usr/bin/test -z "`/bin/dmesg | /bin/grep -i fuse`"', - alias => 'gluster-fuse', - } - #exec { "${::gluster::params::program_modprobe} fuse": - # logoutput => on_failure, - # unless => "${::gluster::params::program_lsmod} | /bin/grep -q '^fuse'", - # alias => 'gluster-modprobe-fuse', - #} - - # TODO: will this autoload the fuse module? - #file { '/etc/modprobe.d/fuse.conf': - # content => "fuse\n", # TODO: "install fuse ${::gluster::params::program_modprobe} --ignore-install fuse ; /bin/true\n" ? - # owner => root, - # group => root, - # mode => 644, # u=rw,go=r - # ensure => present, - #} -} - -# vim: ts=8 diff --git a/gluster/manifests/params.pp b/gluster/manifests/params.pp deleted file mode 100644 index 8ed265e3c..000000000 --- a/gluster/manifests/params.pp +++ /dev/null @@ -1,98 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class gluster::params( - # packages... - $package_glusterfs = 'glusterfs', - $package_glusterfs_fuse = 'glusterfs-fuse', - $package_glusterfs_server = 'glusterfs-server', - $package_glusterfs_api = 'glusterfs-api', - - $package_e2fsprogs = 'e2fsprogs', - $package_xfsprogs = 'xfsprogs', - $package_btrfsprogs = 'btrfs-progs', - - $package_python_argparse = 'python-argparse', - $package_python_lxml = 'python-lxml', - $package_fping = 'fping', - - # programs... - $program_gluster = '/usr/sbin/gluster', - - $program_modprobe = '/sbin/modprobe', - $program_lsmod = '/sbin/lsmod', - - $program_parted = '/sbin/parted', - $program_pvcreate = '/sbin/pvcreate', - $program_vgcreate = '/sbin/vgcreate', - $program_lvcreate = '/sbin/lvcreate', - $program_vgs = '/sbin/vgs', - $program_lvs = '/sbin/lvs', - $program_pvdisplay = '/sbin/pvdisplay', - $program_vgdisplay = '/sbin/vgdisplay', - #$program_lvdisplay = '/sbin/lvdisplay', - $program_xfsadmin = '/usr/sbin/xfs_admin', - $program_mkfs_xfs = '/sbin/mkfs.xfs', - $program_mkfs_ext4 = '/sbin/mkfs.ext4', - $program_mkfs_btrfs = '/sbin/mkfs.btrfs', - - $program_fping = '/usr/sbin/fping', - $program_findmnt = '/bin/findmnt', - - # services... - $service_glusterd = 'glusterd', - - # external modules... - $include_puppet_facter = true, - - # misc... - $misc_gluster_reload = '/sbin/service glusterd reload', - $misc_gluster_repo = 'https://download.gluster.org/pub/gluster/glusterfs/', - - # comment... - $comment = '' -) { - if "${comment}" == '' { - warning('Unable to load yaml data/ directory!') - } - - $valid_include_puppet_facter = $include_puppet_facter ? { - true => true, - false => false, - 'true' => true, - 'false' => false, - default => true, - } - - if $valid_include_puppet_facter { - include puppet::facter - $factbase = "${::puppet::facter::base}" - $hash = { - 'gluster_program_gluster' => $program_gluster, - } - # create a custom external fact! - file { "${factbase}gluster_program.yaml": - content => inline_template('<%= @hash.to_yaml %>'), - owner => root, - group => root, - mode => 644, # u=rw,go=r - ensure => present, - } - } -} - -# vim: ts=8 diff --git a/gluster/manifests/repo.pp b/gluster/manifests/repo.pp deleted file mode 100644 index 57b8db66f..000000000 --- a/gluster/manifests/repo.pp +++ /dev/null @@ -1,121 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -define gluster::repo( - # if you specify 'x.y', it will find the latest x.y.* - # if you specify 'x.y.z', it will stick to that version - # anything omitted is taken to mean "latest" - # if you leave this blank, we assume you want the latest version... - $version = '' -) { - include gluster::params - - $base = "${::gluster::params::misc_gluster_repo}" - - if "${version}" == '' { - # latest - $base_v = "${base}LATEST/" - } else { - notice("GlusterFS version: '${version}' was chosen.") - - # parse out the -release if it exists. example: 3.4.2-13.el6 - # \1 is the major/minor version, eg: 3.4.2 - # \2 is the release with a leading dash, eg: -13.el6 - # \3 is the first part of the release, eg: 13 - # \4 is the second part of the release, eg: el6 - $real_v = regsubst("${version}", '^([\d\.]*)(\-([\d]{1,})\.([a-zA-Z\d]{1,}))?$', '\1') - - # search for qa style releases - $qa_pattern = '^([\d\.]*)(\-(0\.[\d]{1,}\.((alpha|beta|qa|rc)[\d]{1,}))\.([a-zA-Z\d]{1,}))?$' - $qa_type = regsubst("${version}", "${qa_pattern}", '\5') - $qa = "${qa_type}" ? { - /(alpha|beta|qa|rc)/ => true, - default => false, - } - - if $qa { - $qa_folder = regsubst("${version}", "${qa_pattern}", '\1\4') - # look inside the qa-releases/ subfolder... - $base_v = "${base}qa-releases/${qa_folder}/" - - } elsif "${real_v}" =~ /^(\d+)\.(\d+)$/ { # x.y - #$base_v = "${base}${1}.${2}/LATEST/" # same! - $base_v = "${base}${real_v}/LATEST/" - - } elsif "${real_v}" =~ /^(\d+)\.(\d+)\.(\d+)$/ { # x.y.z - #$base_v = "${base}${1}.${2}/${1}.${2}.${3}/" # same! - $base_v = "${base}${1}.${2}/${real_v}/" - - } else { - fail('The version string is invalid.') - } - } - - case $operatingsystem { - 'CentOS': { - $base_os = "${base_v}CentOS/" - } - 'RedHat': { - $base_os = "${base_v}RHEL/" - } - #'Debian', 'Ubuntu': { - #} - default: { - fail("Operating system: '${operatingsystem}' not yet supported.") - } - } - - $arch = "${architecture}" ? { - 'x86_64' => 'x86_64', - 'i386' => 'i386', - 'i486' => 'i386', - 'i586' => 'i386', - 'i686' => 'i386', - default => '', - } - if "${arch}" == '' { - fail("Architecture: '${architecture}' not yet supported.") - } - - $base_arch = "${base_os}epel-${operatingsystemrelease}/" - - $gpgkey = "${base_os}pub.key" - - include ::yum - - #yum::repos::repo { "gluster-${arch}": - yum::repos::repo { "${name}": - baseurl => "${base_arch}${arch}/", - enabled => true, - gpgcheck => true, - # XXX: this should not be an https:// link, it should be a file - gpgkeys => ["${gpgkey}"], - ensure => present, - } - - # TODO: technically, i don't think this is needed yet... - #yum::repos::repo { 'gluster-noarch': - # baseurl => "${base_arch}noarch/", - # enabled => true, - # gpgcheck => true, - # # XXX: this should not be an https:// link, it should be a file - # gpgkeys => ["${gpgkey}"], - # ensure => present, - #} -} - -# vim: ts=8 diff --git a/gluster/manifests/rulewrapper.pp b/gluster/manifests/rulewrapper.pp deleted file mode 100644 index 4ac641989..000000000 --- a/gluster/manifests/rulewrapper.pp +++ /dev/null @@ -1,47 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: this wraps shorewall::rule so that we can add on additional fake 'tags' -define gluster::rulewrapper( - $action = '', - $source = '', - $source_ips = [], - $dest = '', - $dest_ips = [], - $proto = '', - $port = [], - $sport = [], - $original = [], - $comment = '', - $ensure = present, - $match = '' # additional tag parameter -) { - shorewall::rule { "${name}": - action => "${action}", - source => "${source}", - source_ips => $source_ips, - dest => "${dest}", - dest_ips => $dest_ips, - proto => "${proto}", - port => $port, - sport => $sport, - comment => "${comment}", - ensure => $ensure, - } -} - -# vim: ts=8 diff --git a/gluster/manifests/server.pp b/gluster/manifests/server.pp deleted file mode 100644 index d0650919a..000000000 --- a/gluster/manifests/server.pp +++ /dev/null @@ -1,186 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class gluster::server( - $nfs = false, # TODO - $vip = '', # vip of the cluster (optional but recommended) - $vrrp = false, - $password = '', # global vrrp password to use - $version = '', # pick a specific version (defaults to latest) - $repo = true, # add a repo automatically? true or false - $baseport = '', # specify base port option as used in glusterd.vol file - $rpcauthallowinsecure = false, # needed in some setups in glusterd.vol - $shorewall = false, - $zone = 'net', # TODO: allow a list of zones - $ips = false, # an optional list of ip's for each in hosts[] - $clients = [] # list of allowed client ip's # TODO: get from exported resources -) { - $FW = '$FW' # make using $FW in shorewall easier - - include gluster::vardir - include gluster::params - - #$vardir = $::gluster::vardir::module_vardir # with trailing slash - $vardir = regsubst($::gluster::vardir::module_vardir, '\/$', '') - - # if we use ::mount and ::server on the same machine, this could clash, - # so we use the ensure_resource function to allow identical duplicates! - $rname = "${version}" ? { - '' => 'gluster', - default => "gluster-${version}", - } - if $repo { - $params = { - 'version' => "${version}", - } - ensure_resource('gluster::repo', "${rname}", $params) - } - - # this is meant to be replace the excellent sponge utility by sponge.py - file { "${vardir}/sponge.py": # for scripts needing: 'sponge' - source => 'puppet:///modules/gluster/sponge.py', - owner => root, - group => nobody, - mode => 700, # u=rwx - backup => false, # don't backup to filebucket - ensure => present, - before => Package["${::gluster::params::package_glusterfs_server}"], - require => File["${vardir}/"], - } - - package { "${::gluster::params::package_glusterfs_server}": - ensure => "${version}" ? { - '' => present, - default => "${version}", - }, - before => "${::gluster::params::package_glusterfs_api}" ? { - '' => undef, - default => Package["${::gluster::params::package_glusterfs_api}"], - }, - require => $repo ? { - false => undef, - default => Gluster::Repo["${rname}"], - }, - } - - $api_params = { - 'repo' => $repo, - 'version' => "${version}", - } - ensure_resource('class', 'gluster::api', $api_params) - - # NOTE: not that we necessarily manage anything in here at the moment... - file { '/etc/glusterfs/': - ensure => directory, # make sure this is a directory - recurse => false, # TODO: eventually... - purge => false, # TODO: eventually... - force => false, # TODO: eventually... - owner => root, - group => root, - mode => 644, - #notify => Service["${::gluster::params::service_glusterd}"], # TODO: ??? - require => Package["${::gluster::params::package_glusterfs_server}"], - } - - # NOTE: this option can be useful for users of libvirt migration as in: - # https://bugzilla.redhat.com/show_bug.cgi?id=987555 - $valid_baseport = inline_template('<%= [Fixnum, String].include?(@baseport.class) ? @baseport.to_i : 0 %>') - - $valid_rpcauthallowinsecure = $rpcauthallowinsecure ? { - true => true, - default => false, - } - - file { '/etc/glusterfs/glusterd.vol': - content => template('gluster/glusterd.vol.erb'), - owner => root, - group => root, - mode => 644, # u=rw,go=r - ensure => present, - require => File['/etc/glusterfs/'], - } - - file { '/var/lib/glusterd/': - ensure => directory, # make sure this is a directory - recurse => false, # TODO: eventually... - purge => false, # TODO: eventually... - force => false, # TODO: eventually... - owner => root, - group => root, - mode => 644, - #notify => Service["${::gluster::params::service_glusterd}"], # TODO: eventually... - require => File['/etc/glusterfs/glusterd.vol'], - } - - file { '/var/lib/glusterd/peers/': - ensure => directory, # make sure this is a directory - recurse => true, # recursively manage directory - purge => true, - force => true, - owner => root, - group => root, - mode => 644, - notify => Service["${::gluster::params::service_glusterd}"], - require => File['/var/lib/glusterd/'], - } - - if $vrrp { - class { '::keepalived': - start => true, - shorewall => $shorewall, - } - } - - if $shorewall { - # XXX: WIP - #if type($ips) == 'array' { - # #$other_host_ips = inline_template("<%= ips.delete_if {|x| x == '${ipaddress}' }.join(',') %>") # list of ips except myself - # $source_ips = inline_template("<%= (ips+clients).uniq.delete_if {|x| x.empty? }.join(',') %>") - # #$all_ips = inline_template("<%= (ips+[vip]+clients).uniq.delete_if {|x| x.empty? }.join(',') %>") - - # $src = "${source_ips}" ? { - # '' => "${zone}", - # default => "${zone}:${source_ips}", - # } - - #$endport = inline_template('<%= 24009+hosts.count %>') - #$nfs_endport = inline_template('<%= 38465+hosts.count %>') - #shorewall::rule { 'gluster-24000': - # rule => " - # ACCEPT ${src} $FW tcp 24009:${endport} - # ", - # comment => 'Allow 24000s for gluster', - # before => Service["${::gluster::params::service_glusterd}"], - #} - - #if $nfs { # FIXME: TODO - # shorewall::rule { 'gluster-nfs': rule => " - # ACCEPT $(src} $FW tcp 38465:${nfs_endport} - # ", comment => 'Allow nfs for gluster'} - #} - } - - # start service only after the firewall is opened and hosts are defined - service { "${::gluster::params::service_glusterd}": - enable => true, # start on boot - ensure => running, # ensure it stays running - hasstatus => false, # FIXME: BUG: https://bugzilla.redhat.com/show_bug.cgi?id=836007 - hasrestart => true, # use restart, not start; stop - } -} - -# vim: ts=8 diff --git a/gluster/manifests/simple.pp b/gluster/manifests/simple.pp deleted file mode 100644 index 1b72df3ba..000000000 --- a/gluster/manifests/simple.pp +++ /dev/null @@ -1,189 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class gluster::simple( - $path = '', - $volume = 'puppet', # NOTE: this can be a list... - $replica = 1, - $stripe = 1, # TODO: not fully implemented in puppet-gluster - $layout = '', # brick layout to use (default, chained, etc...) - $vip = '', # strongly recommended - $vrrp = false, - $password = '', # global vrrp password to use - $version = '', - $repo = true, - $count = 0, # 0 means build 1 brick, unless $brick_params exists... - $brick_params = {}, # this sets the brick count when $count is 0... - $brick_param_defaults = {}, # these always get used to build bricks - $brick_params_defaults = [], # array of hashes to use as brick count - $setgroup = '', # pick a volume property group to set, eg: virt - $ping = true, # use fping or not? - $again = true, # do we want to use Exec['again'] ? - $baseport = '', # specify base port option as used in glusterd.vol file - $rpcauthallowinsecure = false, # needed in some setups in glusterd.vol - $shorewall = true -) { - include gluster::vardir - include gluster::volume::property::group::data # make the groups early - - #$vardir = $::gluster::vardir::module_vardir # with trailing slash - $vardir = regsubst($::gluster::vardir::module_vardir, '\/$', '') - - if "${path}" == '' { - file { "${vardir}/data/": - ensure => directory, # make sure this is a directory - recurse => false, # don't recurse into directory - purge => false, # don't purge unmanaged files - force => false, # don't purge subdirs and links - require => File["${vardir}/"], - } - } - - $chosen_path = "${path}" ? { - '' => "${vardir}/data/", - default => "${path}", - } - - $valid_path = sprintf("%s/", regsubst($chosen_path, '\/$', '')) - - $valid_volumes = type($volume) ? { # always an array of volumes... - 'array' => $volume, - default => ["${volume}"], - } - - # if this is a hash, then it's used as the defaults for all the bricks! - validate_hash($brick_param_defaults) - # if someone explicitly added this value, then don't overwrite it... - $areyousure = {'areyousure' => true} - $againhash = {'again' => $again} # pass through the $again value - $valid_brick_param_defaults = merge($areyousure, $againhash, $brick_param_defaults) - - # if this is an array, then each element is the default for each brick! - # if this is an array, then the number of elements is the brick count!! - validate_array($brick_params_defaults) - # TODO: check that each element of array is a valid hash! - $valid_brick_params_defaults = $brick_params_defaults - - notify { 'gluster::simple': - message => 'You are using gluster::simple !', - } - - if "${vip}" == '' { - # If you don't use a VIP, things will be racy, but could mostly - # work. If you run puppet manually, then a vip isn't necessary. - # see: http://ttboj.wordpress.com/2012/08/23/how-to-avoid-cluster-race-conditions-or-how-to-implement-a-distributed-lock-manager-in-puppet/ - warning('It is highly recommended to use a VIP.') - } - - class { '::gluster::server': - vip => "${vip}", - vrrp => $vrrp, - password => "${password}", - version => "${version}", - repo => $repo, - baseport => $baseport, - rpcauthallowinsecure => $rpcauthallowinsecure, - #zone => 'net', # defaults to net - shorewall => $shorewall, - } - - if "${::fqdn}" == '' { - fail('Your $fqdn is empty. Please check your DNS settings.') - } - - @@gluster::host { "${::fqdn}": - } - Gluster::Host <<||>> - - # the idea here is to build a list of bricks from a list of parameters, - # with each element in the list, corresponding to a hash of key=>values - # each element in the list is a different brick. the key for the master - # hash is the fqdn of the host that those bricks correspond to. you can - # also specify a list of defaults for when you have common brick values - # such as $xfs_inode64=>true, or raid_* if your cluster is symmetrical! - # if you set the $count variable, then that brick count will be forced. - validate_re("${count}", '^\d+$') # ensure this is a positive int - - # here some wizardry happens... - if has_key($brick_params, "${::fqdn}") { - $brick_params_list = $brick_params["${::fqdn}"] - } else { - $brick_params_list = [] - } - - $brick_params_list_length = inline_template('<%= @brick_params_list.length %>') - $brick_params_defaults_length = inline_template('<%= @valid_brick_params_defaults.length %>') - $valid_count = "${count}" ? { - '0' => "${brick_params_list_length}" ? { - '0' => "${brick_params_defaults_length}" ? { - '0' => 1, # 0 means undefined, so use the default - default => "${brick_params_defaults_length}", - }, - default => "${brick_params_list_length}", - }, - default => $count, - } - - $brick_params_names = "${valid_count}" ? { - # TODO: should we use the same pattern for 1 or many ? - '1' => ["${::fqdn}:${valid_path}"], - default => split(inline_template("<%= (1..@valid_count.to_i).collect{|i| '${::fqdn}:${valid_path}brick' + i.to_s.rjust(7, '0') + '/' }.join(',') %>"), ','), - } - - validate_array($brick_params_list) - - # NOTE: I've kept this template split as two comment chunks for - # readability. Puppet needs to fix this issue somehow! Creating - # a separate template removes the logic from the code, but as a - # large inline template it's very hard to read/write the logic! - #DEFAULTS = (((i < @valid_brick_params_defaults.length) and @valid_brick_params_defaults[i].is_a?(Hash)) ? @valid_brick_params_defaults[i] : {}) - #$yaml = inline_template("<%= (0..@valid_count.to_i-1).inject(Hash.new) { |h,i| {@brick_params_names[i] => DEFAULTS.merge((i < @brick_params_list.length) ? @brick_params_list[i] : {})}.merge(h) }.to_yaml %>") - $yaml = inline_template("<%= (0..@valid_count.to_i-1).inject(Hash.new) { |h,i| {@brick_params_names[i] => (((i < @valid_brick_params_defaults.length) and @valid_brick_params_defaults[i].is_a?(Hash)) ? @valid_brick_params_defaults[i] : {}).merge((i < @brick_params_list.length) ? @brick_params_list[i] : {})}.merge(h) }.to_yaml %>") - $hash = parseyaml($yaml) - create_resources('@@gluster::brick', $hash, $valid_brick_param_defaults) - #@@gluster::brick { "${::fqdn}:${valid_path}": - # areyousure => true, - #} - Gluster::Brick <<||>> - - gluster::volume { $valid_volumes: - replica => $replica, - stripe => $stripe, - layout => "${layout}", - # NOTE: with this method you do not choose the order of course! - # the gluster_fqdns fact is alphabetical, but not complete till - # at least a puppet run of each node has occured. watch out for - # partial clusters missing some of the nodes with bad ordering! - #bricks => split(inline_template("<%= @gluster_fqdns.split(',').collect {|x| x+':${valid_path}' }.join(',') %>"), ','), - # the only semi-safe way is the new built in automatic collect: - bricks => true, # automatic brick collection... - ping => $ping, - start => true, - again => $again, - } - Gluster::Volume <<||>> - - # set a group of volume properties - if "${setgroup}" != '' { - $setgroup_yaml = inline_template("<%= @valid_volumes.inject(Hash.new) { |h,i| {i+'#'+@setgroup => {}}.merge(h) }.to_yaml %>") - $setgroup_hash = parseyaml($setgroup_yaml) - $setgroup_defaults = {'vip' => "${vip}"} - create_resources('gluster::volume::property::group', $setgroup_hash, $setgroup_defaults) - } -} - -# vim: ts=8 diff --git a/gluster/manifests/vardir.pp b/gluster/manifests/vardir.pp deleted file mode 100644 index 2dd40d500..000000000 --- a/gluster/manifests/vardir.pp +++ /dev/null @@ -1,52 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class gluster::vardir { # module vardir snippet - if "${::puppet_vardirtmp}" == '' { - if "${::puppet_vardir}" == '' { - # here, we require that the puppetlabs fact exist! - fail('Fact: $puppet_vardir is missing!') - } - $tmp = sprintf("%s/tmp/", regsubst($::puppet_vardir, '\/$', '')) - # base directory where puppet modules can work and namespace in - file { "${tmp}": - ensure => directory, # make sure this is a directory - recurse => false, # don't recurse into directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, - group => nobody, - mode => 600, - backup => false, # don't backup to filebucket - #before => File["${module_vardir}"], # redundant - #require => Package['puppet'], # no puppet module seen - } - } else { - $tmp = sprintf("%s/", regsubst($::puppet_vardirtmp, '\/$', '')) - } - $module_vardir = sprintf("%s/gluster/", regsubst($tmp, '\/$', '')) - file { "${module_vardir}": # /var/lib/puppet/tmp/gluster/ - ensure => directory, # make sure this is a directory - recurse => true, # recursively manage directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, group => nobody, mode => 600, backup => false, - require => File["${tmp}"], # File['/var/lib/puppet/tmp/'] - } -} - -# vim: ts=8 diff --git a/gluster/manifests/volume.pp b/gluster/manifests/volume.pp deleted file mode 100644 index b203482b0..000000000 --- a/gluster/manifests/volume.pp +++ /dev/null @@ -1,442 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -define gluster::volume( - $bricks = true, # specify a list of bricks, or true for auto... - $group = 'default', # use this bricks group name if we auto collect - $transport = 'tcp', - $replica = 1, - $stripe = 1, - # TODO: maybe this should be called 'chained' => true/false, and maybe, - # we can also specify an offset count for chaining, or other parameters - $layout = '', # brick layout to use (default, chained, etc...) - $vip = '', # vip of the cluster (optional but recommended) - $ping = true, # do we want to include fping checks ? - $settle = true, # do we want to run settle checks ? - $again = true, # do we want to use Exec['again'] ? - $start = undef # start volume ? true, false (stop it) or undef -) { - include gluster::xml - if $again { - include gluster::again - } - include gluster::vardir - include gluster::params - include gluster::volume::base - if $ping { - include gluster::volume::ping - } - - #$vardir = $::gluster::vardir::module_vardir # with trailing slash - $vardir = regsubst($::gluster::vardir::module_vardir, '\/$', '') - - $shorewall = $::gluster::server::shorewall - - $settle_count = 3 # three is a reasonable default! - $maxlength = 3 - if $maxlength < $settle_count { - fail('The $maxlength needs to be greater than or equal to the $settle_count.') - } - $are_bricks_collected = (type($bricks) == 'boolean' and ($bricks == true)) - # NOTE: settle checks are still useful even if we are using ping checks - # the reason why they are still useful, is that they can detect changes - # in the bricks, which might propagate slowly because of exported types - # the fping checks can only verify that the individual hosts are alive! - $settle_count_check = $are_bricks_collected ? { - false => false, - default => $settle ? { - false => false, - default => true, - } - } - # TODO: implement settle_time_check - $settle_time_check = $settle ? { - false => false, - default => true, - } - - # clean up old fsm data when not in use, because parent $vardir purges! - if $are_bricks_collected { - include gluster::volume::fsm - file { "${vardir}/volume/fsm/${name}/": - ensure => directory, # make sure this is a directory - recurse => true, # recurse into directory - purge => false, # don't purge unmanaged files - force => false, # don't purge subdirs and links - require => File["${vardir}/volume/fsm/"], - } - } - - $gluster_brick_group_fact = getvar("gluster_brick_group_${group}") - $collected_bricks = split($gluster_brick_group_fact, ',') - # run the appropriate layout function here - $ordered_brick_layout = $layout ? { - 'chained' => brick_layout_chained($replica, $collected_bricks), - default => brick_layout_simple($replica, $collected_bricks), - } - - $valid_bricks = type($bricks) ? { - 'boolean' => $bricks ? { - true => $ordered_brick_layout, # an array... - default => [], # invalid type - }, - 'array' => $bricks, - default => [], # invalid type - } - - # helpful debugging! - notice(inline_template('collected_bricks: <%= @collected_bricks.inspect %>')) - notice(inline_template('valid_bricks: <%= @valid_bricks.inspect %>')) - - # NOTE: we're using the valid_bricks value here, and not the collected - # value. while we only need the $collected value for settle detection, - # the actual brick value is needed for future add/remove brick code... - $valid_input = join($valid_bricks, ',') # TODO: this should be pickled - if $are_bricks_collected and "${valid_input}" == '' { - notice('The gluster::brick collection is not ready yet.') - } - $last = getvar("gluster_volume_fsm_state_${name}") # fact ! - $valid_last = "${last}" ? { - # initialize the $last var to match the $input if it's empty... - '' => "${valid_input}", - default => "${last}", - } - if $are_bricks_collected and ("${valid_input}" != '') and ("${valid_last}" == '') { - fail('Previous state is invalid.') # fact was tampered with - } - - # NOTE: the stack lists are left base64 encoded since we only care if they change! :P - # NOTE: each element in stack is base64 encoded because they contain: , - $stack_fact = getvar("gluster_volume_fsm_stack_${name}") # fact ! - $stack_full = split("${stack_fact}", ',') - $stack_trim = "${maxlength}" ? { - '-1' => $stack_full, # unlimited - #default => split(inline_template('<%= @stack_full[0,@maxlength.to_i.abs].join(",") %>'), ','), - default => split(inline_template('<%= @stack_full[[@stack_full.size-@maxlength.to_i.abs,0].max,@maxlength.to_i.abs].join(",") %>'), ','), - } - - $watch_fact = getvar("gluster_volume_fsm_watch_${name}") # fact ! - $watch_full = split("${watch_fact}", ',') - $watch_trim = "${maxlength}" ? { - '-1' => $watch_full, # unlimited - #default => split(inline_template('<%= @watch_full[0,@maxlength.to_i.abs].join(",") %>'), ','), - default => split(inline_template('<%= @watch_full[[@watch_full.size-@maxlength.to_i.abs,0].max,@maxlength.to_i.abs].join(",") %>'), ','), - } - - # if the last $settle_count elements are the same, the template - # should reduce down to the value '1'. check this and the size. - $one = inline_template('<%= @watch_trim[[@watch_trim.size-@settle_count.to_i,0].max,@settle_count.to_i].uniq.size %>') - $watch_trim_size = size($watch_trim) - $settled = ((! $settle_count_check) or ((size($watch_trim) >= $settle_count) and "${one}" == '1')) - - # TODO: if using rdma, maybe we should pull in the rdma package... ? - $valid_transport = $transport ? { - 'rdma' => 'rdma', - 'tcp,rdma' => 'tcp,rdma', - default => 'tcp', - } - - $valid_replica = $replica ? { - '1' => '', - default => "replica ${replica} ", - } - - $valid_stripe = $stripe ? { - '1' => '', - default => "stripe ${stripe} ", - } - - $valid_vip = "${vip}" ? { - '' => $::gluster::server::vip, - default => "${vip}", - } - - # returns interface name that has vip, or '' if none are found. - $vipif = inline_template("<%= @interfaces.split(',').find_all {|x| '${valid_vip}' == scope.lookupvar('ipaddress_'+x) }[0,1].join('') %>") - - #Gluster::Brick[$valid_bricks] -> Gluster::Volume[$name] # volume requires bricks - - # get the bricks that match our fqdn, and append /$name to their path. - # return only these paths, which can be used to build the volume dirs. - # NOTE: gluster v3.4 won't create a volume if this dir already exists. - # TODO: is this needed when bricks are devices and not on filesystem ? - #$volume_dirs = split(inline_template("<%= @valid_bricks.find_all{|x| x.split(':')[0] == '${fqdn}' }.collect {|y| y.split(':')[1].chomp('/')+'/${name}' }.join(' ') %>"), ' ') - #file { $volume_dirs: - # ensure => directory, # make sure this is a directory - # recurse => false, # don't recurse into directory - # purge => false, # don't purge unmanaged files - # force => false, # don't purge subdirs and links - # before => Exec["gluster-volume-create-${name}"], - # require => Gluster::Brick[$valid_bricks], - #} - - # add /${name} to the end of each: brick:/path entry - $brick_spec = inline_template("<%= @valid_bricks.collect {|x| ''+x.chomp('/')+'/${name}' }.join(' ') %>") - - # if volume creation fails for a stupid reason, in many cases, glusterd - # already did some of the work and left us with volume name directories - # on all bricks. the problem is that the future volume create commands, - # will error if they see that volume directory already present, so when - # we error we should rmdir any empty volume dirs to keep it pristine... - # TODO: this should be a gluster bug... we must hope it doesn't happen! - # maybe related to: https://bugzilla.redhat.com/show_bug.cgi?id=835494 - $rmdir_volume_dirs = sprintf("/bin/rmdir '%s'", inline_template("<%= @valid_bricks.find_all{|x| x.split(':')[0] == '${fqdn}' }.collect {|y| y.split(':')[1].chomp('/')+'/${name}/' }.join('\' \'') %>")) - - # get the list of bricks fqdn's that don't have our fqdn - $others = inline_template("<%= @valid_bricks.find_all{|x| x.split(':')[0] != '${fqdn}' }.collect {|y| y.split(':')[0] }.join(' ') %>") - - $fping = sprintf("${::gluster::params::program_fping} -q %s", $others) - $status = sprintf("${::gluster::params::program_gluster} peer status --xml | ${vardir}/xml.py connected %s", $others) - - $onlyif = $ping ? { - false => "${status}", - default => [ - "${fping}", - "${status}", - ], - } - - $require = $ping ? { - false => [ - Service["${::gluster::params::service_glusterd}"], - File["${vardir}/volume/create-${name}.sh"], - File["${vardir}/xml.py"], # status check - Gluster::Brick[$valid_bricks], - Exec["gluster-volume-stuck-${name}"], - ], - default => [ - Service["${::gluster::params::service_glusterd}"], - File["${vardir}/volume/create-${name}.sh"], - Package["${::gluster::params::package_fping}"], - File["${vardir}/xml.py"], # status check - Gluster::Brick[$valid_bricks], - Exec["gluster-volume-stuck-${name}"], - ], - } - - # work around stuck connection state (4) of: 'Accepted peer request'... - exec { "gluster-volume-stuck-${name}": - command => "${::gluster::params::misc_gluster_reload}", - logoutput => on_failure, - unless => "${::gluster::params::program_gluster} volume list | /bin/grep -qxF '${name}' -", # reconnect if it doesn't exist - onlyif => sprintf("${::gluster::params::program_gluster} peer status --xml | ${vardir}/xml.py stuck %s", $others), - notify => $again ? { - false => undef, - default => Common::Again::Delta['gluster-exec-again'], - }, - require => [ - Service["${::gluster::params::service_glusterd}"], - File["${vardir}/xml.py"], # stuck check - Gluster::Brick[$valid_bricks], - ], - } - - # store command in a separate file to run as bash... - # NOTE: we sleep for 5 seconds to give glusterd a chance to - # settle down first if we're doing a hot (clean) puppet run - # NOTE: force is needed for now because of the following error: - # volume create: puppet: failed: The brick annex1.example.com:/var/lib/puppet/tmp/gluster/data/puppet is is being created in the root partition. It is recommended that you don't use the system's root partition for storage backend. Or use 'force' at the end of the command if you want to override this behavior. - # FIXME: it would be create to have an --allow-root-storage type option - # instead, so that we don't inadvertently force some other bad thing... - file { "${vardir}/volume/create-${name}.sh": - content => inline_template("#!/bin/bash\n/bin/sleep 5s && ${::gluster::params::program_gluster} volume create ${name} ${valid_replica}${valid_stripe}transport ${valid_transport} ${brick_spec} force > >(/usr/bin/tee '/tmp/gluster-volume-create-${name}.stdout') 2> >(/usr/bin/tee '/tmp/gluster-volume-create-${name}.stderr' >&2) || (${rmdir_volume_dirs} && /bin/false)\nexit \$?\n"), - owner => root, - group => root, - mode => 755, - ensure => present, - # this notify is the first to kick off the 2nd step! it - # was put here after a process of elimination, and this - # location makes a lot of sense: on change exec[again]! - notify => $again ? { - false => undef, - default => Common::Again::Delta['gluster-exec-again'], - }, - require => File["${vardir}/volume/"], - } - - # run if vip not defined (bypass mode) or if vip exists on this machine - if ("${valid_vip}" == '' or "${vipif}" != '') { - - # NOTE: This should only happen on one host! - # NOTE: There's maybe a theoretical race condition if this runs - # at exactly the same time on more than one host. That's why it - # is advisable to use a vip. - # NOTE: This could probably fail on at least N-1 nodes (without - # vip) or one (the vip node, when using vip) before it succeeds - # because it shouldn't work until all the bricks are available, - # which per node will happen right before this runs. - # fping all the other nodes to ensure they're up for creation - # TODO: consider piping in a /usr/bin/yes to avoid warnings... - # NOTE: in this command, we save the std{out,err} and pass them - # on too for puppet to consume. we save in /tmp for fast access - # EXAMPLE: gluster volume create test replica 2 transport tcp annex1.example.com:/storage1a/test annex2.example.com:/storage2a/test annex3.example.com:/storage3b/test annex4.example.com:/storage4b/test annex1.example.com:/storage1c/test annex2.example.com:/storage2c/test annex3.example.com:/storage3d/test annex4.example.com:/storage4d/test - # TODO: add a timer similar to my puppet-runonce timer code... to wait X minutes after we've settled... useful if we Exec['again'] to speed things up... - if $settled { - exec { "gluster-volume-create-${name}": - command => "${vardir}/volume/create-${name}.sh", - logoutput => on_failure, - unless => "${::gluster::params::program_gluster} volume list | /bin/grep -qxF '${name}' -", # add volume if it doesn't exist - onlyif => $onlyif, - #before => TODO?, - require => $require, - alias => "gluster-volume-create-${name}", - } - } - - if $start == true { - # try to start volume if stopped - exec { "${::gluster::params::program_gluster} volume start ${name}": - logoutput => on_failure, - onlyif => "${::gluster::params::program_gluster} volume list | /bin/grep -qxF '${name}' -", - unless => "${::gluster::params::program_gluster} volume status ${name}", # returns false if stopped - notify => $shorewall ? { - false => undef, - default => $again ? { - false => undef, - default => Common::Again::Delta['gluster-exec-again'], - }, - }, - require => $settled ? { # require if type exists - false => undef, - default => Exec["gluster-volume-create-${name}"], - }, - alias => "gluster-volume-start-${name}", - } - } elsif ( $start == false ) { - # try to stop volume if running - # NOTE: this will still succeed even if a client is mounted - # NOTE: This uses `yes` to workaround the: Stopping volume will - # make its data inaccessible. Do you want to continue? (y/n) - # TODO: http://community.gluster.org/q/how-can-i-make-automatic-scripts/ - # TODO: gluster --mode=script volume stop ... - exec { "/usr/bin/yes | ${::gluster::params::program_gluster} volume stop ${name}": - logoutput => on_failure, - onlyif => "${::gluster::params::program_gluster} volume status ${name}", # returns true if started - require => $settled ? { # require if type exists - false => undef, - default => Exec["gluster-volume-create-${name}"], - }, - alias => "gluster-volume-stop-${name}", - } - } else { # 'undef'-ined - # don't manage volume run state - } - } - - if $shorewall { - $zone = $::gluster::server::zone # firewall zone - - $ips = $::gluster::server::ips # override host ip list - $ip = $::gluster::host::data::ip # ip of brick's host... - $source_ips = type($ips) ? { - 'array' => inline_template("<%= (@ips+[]).uniq.delete_if {|x| x.empty? }.join(',') %>"), - default => ["${ip}"], - } - - $port = getvar("gluster_ports_volume_${name}") # fact ! - - # NOTE: we need to add the $fqdn so that exported resources - # don't conflict... I'm not sure they should anyways though - @@shorewall::rule { "gluster-volume-${name}-${fqdn}": - action => 'ACCEPT', - source => "${zone}", # override this on collect... - source_ips => $source_ips, - dest => '$FW', - proto => 'tcp', - port => "${port}", # comma separated string or list - #comment => "${fqdn}", - comment => 'Allow incoming tcp port from glusterfsds.', - tag => 'gluster_firewall_volume', - ensure => present, - } - # we probably shouldn't collect the above rule from our self... - #Shorewall::Rule <<| tag == 'gluster_firewall_volume' and comment != "${fqdn}" |>> { - Shorewall::Rule <<| tag == 'gluster_firewall_volume' |>> { - source => "${zone}", # use our source zone - before => Service["${::gluster::params::service_glusterd}"], - } - - Gluster::Rulewrapper <<| tag == 'gluster_firewall_volume' and match == "${name}" |>> { - #Shorewall::Rule <<| tag == 'gluster_firewall_volume' and match == "${name}" |>> { - source => "${zone}", # use our source zone - port => "${port}", # comma separated string or list - before => Service["${::gluster::params::service_glusterd}"], - } - } - - # fsm variables and boilerplate - $statefile = "${vardir}/volume/fsm/${name}/state" - $stackfile = "${vardir}/volume/fsm/${name}/stack" - $watchfile = "${vardir}/volume/fsm/${name}/watch" - $diff = "/usr/bin/test '${valid_input}' != '${valid_last}'" - $stack_truncate = "${maxlength}" ? { - '-1' => '', # unlimited - #default => sprintf("&& /bin/sed -i '%d,$ d' ${stackfile}", inline_template('<%= @maxlength.to_i.abs+1 %>')), - default => sprintf(" && (/bin/grep -v '^$' ${stackfile} | /usr/bin/tail -n %d | ${vardir}/sponge.py ${stackfile})", inline_template('<%= @maxlength.to_i.abs %>')), - } - $watch_truncate = "${maxlength}" ? { - '-1' => '', # unlimited - #default => sprintf("&& /bin/sed -i '%d,$ d' ${watchfile}", inline_template('<%= @maxlength.to_i.abs+1 %>')), - default => sprintf(" && (/bin/grep -v '^$' ${watchfile} | /usr/bin/tail -n %d | ${vardir}/sponge.py ${watchfile})", inline_template('<%= @maxlength.to_i.abs %>')), - } - - if $are_bricks_collected and ("${valid_input}" != '') { # ready or not? - - # TODO: future versions should pickle (but with yaml) - exec { "/bin/echo '${valid_input}' > '${statefile}'": - logoutput => on_failure, - onlyif => "/usr/bin/test ! -e '${statefile}' || ${diff}", - require => File["${vardir}/volume/fsm/${name}/"], - alias => "gluster-volume-fsm-state-${name}", - } - - # NOTE: keep a stack of past transitions, and load them in as a list... - exec { "/bin/echo '${valid_input}' >> '${stackfile}'${stack_truncate}": - logoutput => on_failure, - onlyif => "/usr/bin/test ! -e '${stackfile}' || ${diff}", - require => [ - File["${vardir}/volume/fsm/${name}/"], - # easy way to ensure the transition types don't need to - # add a before to both exec's since this one follows it - Exec["gluster-volume-fsm-state-${name}"], - ], - alias => "gluster-volume-fsm-stack-${name}", - } - - # NOTE: watch *all* transitions, and load them in as a list... - exec { "/bin/echo '${valid_input}' >> '${watchfile}'${watch_truncate}": - logoutput => on_failure, - # we run this if the file doesn't exist, or there is a - # difference to record, or the sequence hasn't settled - # we also check that we have our minimum settle count! - onlyif => "/usr/bin/test ! -e '${watchfile}' || ${diff} || /usr/bin/test '1' != '${one}' || /usr/bin/test ${watch_trim_size} -lt ${settle_count}", - notify => $again ? { - false => undef, - default => Common::Again::Delta['gluster-exec-again'], - }, - require => [ - File["${vardir}/volume/fsm/${name}/"], - # easy way to ensure the transition types don't need to - # add a before to both exec's since this one follows it - Exec["gluster-volume-fsm-state-${name}"], - ], - alias => "gluster-volume-fsm-watch-${name}", - } - } -} - -# vim: ts=8 diff --git a/gluster/manifests/volume/base.pp b/gluster/manifests/volume/base.pp deleted file mode 100644 index 1991ce738..000000000 --- a/gluster/manifests/volume/base.pp +++ /dev/null @@ -1,32 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class gluster::volume::base { - - include gluster::vardir - #$vardir = $::gluster::vardir::module_vardir # with trailing slash - $vardir = regsubst($::gluster::vardir::module_vardir, '\/$', '') - - file { "${vardir}/volume/": - ensure => directory, # make sure this is a directory - recurse => true, # don't recurse into directory - purge => true, # don't purge unmanaged files - force => true, # don't purge subdirs and links - require => File["${vardir}/"], - } -} -# vim: ts=8 diff --git a/gluster/manifests/volume/fsm.pp b/gluster/manifests/volume/fsm.pp deleted file mode 100644 index c780ede6d..000000000 --- a/gluster/manifests/volume/fsm.pp +++ /dev/null @@ -1,32 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class gluster::volume::fsm { - - include gluster::vardir - #$vardir = $::gluster::vardir::module_vardir # with trailing slash - $vardir = regsubst($::gluster::vardir::module_vardir, '\/$', '') - - file { "${vardir}/volume/fsm/": - ensure => directory, # make sure this is a directory - recurse => true, # don't recurse into directory - purge => true, # don't purge unmanaged files - force => true, # don't purge subdirs and links - require => File["${vardir}/volume/"], - } -} -# vim: ts=8 diff --git a/gluster/manifests/volume/ping.pp b/gluster/manifests/volume/ping.pp deleted file mode 100644 index d2ec4a2f9..000000000 --- a/gluster/manifests/volume/ping.pp +++ /dev/null @@ -1,28 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class gluster::volume::ping { - - include gluster::params - - # for checking other bricks are up - package { "${::gluster::params::package_fping}": - ensure => present, - } -} - -# vim: ts=8 diff --git a/gluster/manifests/volume/property.pp b/gluster/manifests/volume/property.pp deleted file mode 100644 index 923e39495..000000000 --- a/gluster/manifests/volume/property.pp +++ /dev/null @@ -1,222 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: thanks to Joe Julian for: http://community.gluster.org/q/what-is-the-command-that-someone-can-run-to-get-the-value-of-a-given-property/ - -define gluster::volume::property( - $value, - $vip = '', # vip of the cluster (optional but recommended) - $autotype = true # set to false to disable autotyping -) { - include gluster::xml - include gluster::vardir - include gluster::params - include gluster::volume::property::data - - #$vardir = $::gluster::vardir::module_vardir # with trailing slash - $vardir = regsubst($::gluster::vardir::module_vardir, '\/$', '') - - $split = split($name, '#') # do some $name parsing - $volume = $split[0] # volume name - $key = $split[1] # key name - - if ! ( "${volume}#${key}" == "${name}" ) { - fail('The property $name must match a $volume#$key pattern.') - } - - # split out $etype and $jchar lookup into a separate file - $etypes = $::gluster::volume::property::data::etypes - $jchars = $::gluster::volume::property::data::jchars - - # transform our etypes into short etypes (missing the prefix) - # TODO: we should see if there are duplicates (collisions) - # if there are collisions, and a gluster volume set group type contains - # one of these keys, then it's ambiguous and it's clearly a gluster bug - $short_etypes_yaml = inline_template('<%= @etypes.inject({}) {|h, (x,y)| h[ (x.index(".").nil?? x : x[x.index(".")+1..-1]) ] = y; h }.to_yaml %>') - $short_jchars_yaml = inline_template('<%= @jchars.inject({}) {|h, (x,y)| h[ (x.index(".").nil?? x : x[x.index(".")+1..-1]) ] = y; h }.to_yaml %>') - $short_etypes = parseyaml($short_etypes_yaml) - $short_jchars = parseyaml($short_jchars_yaml) - - # FIXME: a short key should lookup the equivalent in the normal table, - # and vice-versa, and set an alias so that you can't define a short - # key and a long key at the same time which refer to the same variable! - - # expected type - if has_key($etypes, "${key}") { - $etype = $etypes["${key}"] ? { - '' => 'undefined', - default => $etypes["${key}"], - } - # the keys of these etypes are missing their prefix up to the first '.' - } elsif has_key($short_etypes, "${key}") { - $etype = $short_etypes["${key}"] ? { - '' => 'undefined', - default => $short_etypes["${key}"], - } - } else { - $etype = 'undefined' - } - - if (! $autotype) { - if type($value) != 'string' { - fail('Expecting type(string) if autotype is disabled.') - } - $safe_value = shellquote($value) # TODO: is this the safe thing? - - # if it's a special offon type and of an acceptable value - } elsif ($etype == 'offon') { # default is off - if type($value) == 'boolean' { - $safe_value = $value ? { - true => 'on', - default => 'off', - } - - } elsif type($value) == 'string' { - $downcase_value = inline_template('<%= @value.downcase %>') - $safe_value = $downcase_value ? { - 'on' => 'on', - #'off' => 'off', - default => 'off', - } - - } else { - fail("Gluster::Volume::Property[${key}] must be type: ${etype}.") - } - - # if it's a special onoff type and of an acceptable value - } elsif ($etype == 'onoff') { # default is on - if type($value) == 'boolean' { - $safe_value = $value ? { - false => 'off', - default => 'on', - } - - } elsif type($value) == 'string' { - $downcase_value = inline_template('<%= @value.downcase %>') - $safe_value = $downcase_value ? { - 'off' => 'off', - #'on' => 'on', - default => 'on', - } - - } else { - fail("Gluster::Volume::Property[${key}] must be type: ${etype}.") - } - - # if it's a special truefalse type and of an acceptable value - } elsif ($etype == 'truefalse') { # default is true - if type($value) == 'boolean' { - $safe_value = $value ? { - false => 'false', - default => 'true', - } - - } elsif type($value) == 'string' { - $downcase_value = inline_template('<%= @value.downcase %>') - $safe_value = $downcase_value ? { - 'false' => 'false', - default => 'true', - } - - } else { - fail("Gluster::Volume::Property[${key}] must be type: ${etype}.") - } - - # if it's a special falsetrue type and of an acceptable value - } elsif ($etype == 'falsetrue') { # default is false - if type($value) == 'boolean' { - $safe_value = $value ? { - true => 'true', - default => 'false', - } - - } elsif type($value) == 'string' { - $downcase_value = inline_template('<%= @value.downcase %>') - $safe_value = $downcase_value ? { - 'true' => 'true', - default => 'false', - } - - } else { - fail("Gluster::Volume::Property[${key}] must be type: ${etype}.") - } - - } elsif $etype == 'integer' { - # TODO: we could also add range and/or set validation - $safe_value = inline_template('<%= [Fixnum, String].include?(@value.class) ? @value.to_i : "null" %>') - if "${safe_value}" == 'null' { # value was of an invalid type! - fail("Gluster::Volume::Property[${key}] must be type: ${etype}.") - } - - } elsif $etype == 'string' { - $safe_value = shellquote($value) # TODO: is this the safe thing? - - # if it's not a string and it's not the expected type, fail - } elsif ( type($value) != $etype ) { # type() from puppetlabs-stdlib - fail("Gluster::Volume::Property[${key}] must be type: ${etype}.") - - # convert to correct type - } else { - - if $etype == 'string' { - $safe_value = shellquote($value) # TODO: is this the safe thing? - } elsif $etype == 'array' { - - # join char - if has_key($jchars, "${key}") { - $jchar = $jchars["${key}"] - } elsif has_key($short_jchars, "${key}") { - $jchar = $short_jchars["${key}"] - } else { - $jchar = '' - } - - $safe_value = inline_template('<%= @value.join(jchar) %>') - #} elsif ... { # TODO: add more conversions here if needed - - } else { - fail("Unknown type: ${etype}.") - } - } - - $valid_vip = "${vip}" ? { - '' => $::gluster::server::vip, - default => "${vip}", - } - - # returns interface name that has vip, or '' if none are found. - $vipif = inline_template("<%= @interfaces.split(',').find_all {|x| '${valid_vip}' == scope.lookupvar('ipaddress_'+x) }[0,1].join('') %>") - - # run if vip not defined (bypass mode) or if vip exists on this machine - if ("${valid_vip}" == '' or "${vipif}" != '') { - # volume set - # set a volume property only if value doesn't match what is available - # FIXME: check that the value we're setting isn't the default - # FIXME: you can check defaults with... gluster volume set help | ... - exec { "${::gluster::params::program_gluster} volume set ${volume} ${key} ${safe_value}": - unless => "/usr/bin/test \"`${::gluster::params::program_gluster} volume --xml info ${volume} | ${vardir}/xml.py property --key '${key}'`\" = '${safe_value}'", - onlyif => "${::gluster::params::program_gluster} volume list | /bin/grep -qxF '${volume}' -", - logoutput => on_failure, - require => [ - Gluster::Volume[$volume], - File["${vardir}/xml.py"], - ], - } - } -} - -# vim: ts=8 diff --git a/gluster/manifests/volume/property/data.pp b/gluster/manifests/volume/property/data.pp deleted file mode 100644 index 99c29c78d..000000000 --- a/gluster/manifests/volume/property/data.pp +++ /dev/null @@ -1,336 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class gluster::volume::property::data() { - - # expected type - $etypes = { - - # Allow a comma separated list of addresses and/or hostnames to connect to the server. By default, all connections are allowed. - 'auth.allow' => 'array', # default: (null) - - # Reject a comma separated list of addresses and/or hostnames to connect to the server. By default, all connections are allowed. - 'auth.reject' => 'array', # default: (null) - - # This specifies the number of self-heals that can be performed in background without blocking the fop - 'cluster.background-self-heal-count' => 'integer', # default: 16 - - # Choose a local subvolume (i.e. Brick) to read from if read-subvolume is not explicitly set. - 'cluster.choose-local' => 'truefalse', # default: true - - # Data fops like write/truncate will not perform pre/post fop changelog operations in afr transaction if this option is disabled - 'cluster.data-change-log' => 'onoff', # default: on - - # Using this option we can enable/disable data self-heal on the file. "open" means data self-heal action will only be triggered by file open operations. - 'cluster.data-self-heal' => 'onoff', # default: on - - # Select between "full", "diff". The "full" algorithm copies the entire file from source to sink. The "diff" algorithm copies to sink only those blocks whose checksums don't match with those of source. If no option is configured the option is chosen dynamically as follows: If the file does not exist on one of the sinks or empty file exists or if the source file size is about the same as page size the entire file will be read and written i.e "full" algo, otherwise "diff" algo is chosen. - 'cluster.data-self-heal-algorithm' => 'string', # default: (reset) - - # Lock phase of a transaction has two sub-phases. First is an attempt to acquire locks in parallel by broadcasting non-blocking lock requests. If lock acquisition fails on any server, then the held locks are unlocked and revert to a blocking locked mode sequentially on one server after another. If this option is enabled the initial broadcasting lock request attempt to acquire lock on the entire file. If this fails, we revert back to the sequential "regional" blocking lock as before. In the case where such an "eager" lock is granted in the non-blocking phase, it gives rise to an opportunity for optimization. i.e, if the next write transaction on the same FD arrives before the unlock phase of the first transaction, it "takes over" the full file lock. Similarly if yet another data transaction arrives before the unlock phase of the "optimized" transaction, that in turn "takes over" the lock as well. The actual unlock now happens at the end of the last "optimized" transaction. - 'cluster.eager-lock' => 'onoff', # default: on - - # Entry fops like create/unlink will not perform pre/post fop changelog operations in afr transaction if this option is disabled - 'cluster.entry-change-log' => 'onoff', # default: on - - # Using this option we can enable/disable entry self-heal on the directory. - 'cluster.entry-self-heal' => 'onoff', # default: on - - # time interval for checking the need to self-heal in self-heal-daemon - 'cluster.heal-timeout' => 'integer', # default: 600 - - # This option if set to ON, does a lookup through all the sub-volumes, in case a lookup didn't return any result from the hash subvolume. If set to OFF, it does not do a lookup on the remaining subvolumes. - 'cluster.lookup-unhashed' => 'onoff', # default: on - - # Metadata fops like setattr/setxattr will not perform pre/post fop changelog operations in afr transaction if this option is disabled - 'cluster.metadata-change-log' => 'onoff', # default: on - - # Using this option we can enable/disable metadata i.e. Permissions, ownerships, xattrs self-heal on the file/directory. - 'cluster.metadata-self-heal' => 'onoff', # default: on - - # Percentage/Size of disk space, after which the process starts balancing out the cluster, and logs will appear in log files - 'cluster.min-free-disk' => 'string', # default: 10% - - # after system has only N% of inodes, warnings starts to appear in log files - 'cluster.min-free-inodes' => 'string', # default: 5% - - # If quorum-type is "fixed" only allow writes if this many bricks or present. Other quorum types will OVERWRITE this value. - 'cluster.quorum-count' => 'integer', # default: (null) - - # If value is "fixed" only allow writes if quorum-count bricks are present. If value is "auto" only allow writes if more than half of bricks, or exactly half including the first, are present. - 'cluster.quorum-type' => 'string', # default: none - - # readdir(p) will not failover if this option is off - 'cluster.readdir-failover' => 'onoff', # default: on - - # This option if set to ON enables the optimization that allows DHT to requests non-first subvolumes to filter out directory entries. - 'cluster.readdir-optimize' => 'offon', # default: off - - # inode-read fops happen only on one of the bricks in replicate. AFR will prefer the one computed using the method specified using this option0 = first responder, 1 = hash by GFID of file (all clients use same subvolume), 2 = hash by GFID of file and client PID - 'cluster.read-hash-mode' => 'integer', # default: 0 - - # inode-read fops happen only on one of the bricks in replicate. Afr will prefer the one specified using this option if it is not stale. Option value must be one of the xlator names of the children. Ex: -client-0 till -client- - 'cluster.read-subvolume' => 'string', # default: (null) - - # inode-read fops happen only on one of the bricks in replicate. AFR will prefer the one specified using this option if it is not stale. allowed options include -1 till replica-count - 1 - 'cluster.read-subvolume-index' => 'integer', # default: -1 - - # This option if set to ON displays and logs the time taken for migration of each file, during the rebalance process. If set to OFF, the rebalance logs will only display the time spent in each directory. - 'cluster.rebalance-stats' => 'offon', # default: off - - # This option applies to only self-heal-daemon. Index directory crawl and automatic healing of files will not be performed if this option is turned off. - 'cluster.self-heal-daemon' => 'offon', # default: off - - # readdirp size for performing entry self-heal - 'cluster.self-heal-readdir-size' => 'integer', # default: 1024 - Min 1024 Max 131072 - - # Maximum number blocks per file for which self-heal process would be applied simultaneously. - 'cluster.self-heal-window-size' => 'integer', # default: 1 - - # Sets the quorum percentage for the trusted storage pool. - 'cluster.server-quorum-ratio' => 'integer', # in % default: (null) - - # If set to server, enables the specified volume to participate in quorum. - 'cluster.server-quorum-type' => 'string', # default: (null) - - # Size of the stripe unit that would be read from or written to the striped servers. - 'cluster.stripe-block-size' => 'string', # default: 128KB - - # Enable coalesce mode to flatten striped files as stored on the server (i.e., eliminate holes caused by the traditional format). - 'cluster.stripe-coalesce' => 'falsetrue', # default: false - - # Specifies the directory layout spread. - 'cluster.subvols-per-directory' => 'integer', # default: (null) - - # Changes the log-level of the bricks - 'diagnostics.brick-log-level' => 'string', # default: INFO - - # Gluster's syslog log-level - 'diagnostics.brick-sys-log-level' => 'string', # default: CRITICAL - - # Changes the log-level of the clients - 'diagnostics.client-log-level' => 'string', # default: INFO - - # Gluster's syslog log-level - 'diagnostics.client-sys-log-level' => 'string', # default: CRITICAL - - # If on stats related to file-operations would be tracked inside GlusterFS data-structures. - 'diagnostics.dump-fd-stats' => 'offon', # default: off - - # If on stats related to the latency of each operation would be tracked inside GlusterFS data-structures. - 'diagnostics.latency-measurement' => 'offon', # default: off - - # Sets the grace-timeout value. Valid range 10-1800. - 'features.grace-timeout' => 'integer', # default: (null) - - # Enables or disables the lock heal. - 'features.lock-heal' => 'offon', # default: off - - # quota caches the directory sizes on client. Timeout indicates the timeout for the cache to be revalidated. - 'features.quota-timeout' => 'integer', # default: 0 - - # Time frame after which the (file) operation would be declared as dead, if the server does not respond for a particular (file) operation. - 'network.frame-timeout' => 'integer', # default: 1800 - - # Specifies the maximum megabytes of memory to be used in the inode cache. - 'network.inode-lru-limit' => 'integer', # default: 16384 - - # Time duration for which the client waits to check if the server is responsive. - 'network.ping-timeout' => 'integer', # default: 42 - - # If enabled, in open() and creat() calls, O_DIRECT flag will be filtered at the client protocol level so server will still continue to cache the file. This works similar to NFS's behavior of O_DIRECT - 'network.remote-dio' => 'string', # default: disable - - # XXX: this appears twice - # Specifies the window size for tcp socket. - 'network.tcp-window-size' => 'integer', # default: (null) - - # Users have the option of turning on name lookup for incoming client connections using this option. Use this option to turn on name lookups during address-based authentication. Turning this on will enable you to use hostnames in rpc-auth.addr.* filters. In some setups, the name server can take too long to reply to DNS queries resulting in timeouts of mount requests. By default, name lookup is off - 'nfs.addr-namelookup' => 'offon', # default: (off) - - # This option is used to start or stop NFS server for individual volume. - 'nfs.disable' => 'offon', # default: (off) - - # Internal option set to tell gnfs to use a different scheme for encoding file handles when DVM is being used. - 'nfs.dynamic-volumes' => 'offon', # default: (off) - - # For nfs clients or apps that do not support 64-bit inode numbers, use this option to make NFS return 32-bit inode numbers instead. Disabled by default, so NFS returns 64-bit inode numbers. - 'nfs.enable-ino32' => 'offon', # default: (off) - - # By default, all subvolumes of nfs are exported as individual exports. There are cases where a subdirectory or subdirectories in the volume need to be exported separately. This option can also be used in conjunction with nfs3.export-volumes option to restrict exports only to the subdirectories specified through this option. Must be an absolute path. - 'nfs.export-dir' => 'string', # default: (null) - - # By default, all subvolumes of nfs are exported as individual exports. There are cases where a subdirectory or subdirectories in the volume need to be exported separately. Enabling this option allows any directory on a volumes to be exported separately. Directory exports are enabled by default. - 'nfs.export-dirs' => 'onoff', # default: (on) - - # Enable or disable exporting whole volumes, instead if used in conjunction with nfs3.export-dir, can allow setting up only subdirectories as exports. On by default. - 'nfs.export-volumes' => 'onoff', # default: (on) - - # Use this option to make NFS be faster on systems by using more memory. This option specifies a multiple that determines the total amount of memory used. Default value is 15. Increase to use more memory in order to improve performance for certain use cases. Please consult gluster-users list before using this option. - 'nfs.mem-factor' => 'integer', # default: (null) - - # set the option to 'on' to enable mountd on UDP. Required for some Solaris and AIX NFS clients. The need for enabling this option often depends on the usage of NLM. - 'nfs.mount-udp' => 'offon', # default: (off) - - # This option, if set to 'off', disables NLM server by not registering the service with the portmapper. Set it to 'on' to re-enable it. Default value: 'on' - 'nfs.nlm' => 'onoff', # default: (on) - - # Use this option on systems that need Gluster NFS to be associated with a non-default port number. - 'nfs.port' => 'integer', # default: (null) - - # Allow client connections from unprivileged ports. By default only privileged ports are allowed. Use this option to enable or disable insecure ports for a specific subvolume and to override the global setting set by the previous option. - 'nfs.ports-insecure' => 'offon', # default: (off) - - # For systems that need to run multiple nfs servers, only one registration is possible with portmap service. Use this option to turn off portmap registration for Gluster NFS. On by default - 'nfs.register-with-portmap' => 'onoff', # default: (on) - - # Allow a comma separated list of addresses and/or hostnames to connect to the server. By default, all connections are allowed. This allows users to define a rule for a specific exported volume. - 'nfs.rpc-auth-allow' => 'array', # default: (null) - - # Disable or enable the AUTH_NULL authentication type for a particular exported volume overriding defaults and general setting for AUTH_NULL. Must always be enabled. This option is here only to avoid unrecognized option warnings. - 'nfs.rpc-auth-null' => 'onoff', # default: (on) - - # Reject a comma separated list of addresses and/or hostnames from connecting to the server. By default, all connections are allowed. This allows users to define a rule for a specific exported volume. - 'nfs.rpc-auth-reject' => 'array', # default: (null) - - # Disable or enable the AUTH_UNIX authentication type for a particular exported volume overriding defaults and general setting for AUTH_UNIX scheme. Must always be enabled for better interoperability.However, can be disabled if needed. Enabled by default. - 'nfs.rpc-auth-unix' => 'onoff', # default: (on) - - # Specifies the nfs transport type. Valid transport types are 'tcp' and 'rdma'. - 'nfs.transport-type' => 'string', # default: tcp - - # All writes and COMMIT requests are treated as async. This implies that no write requests are guaranteed to be on server disks when the write reply is received at the NFS client. Trusted sync includes trusted-write behaviour. Off by default. - 'nfs.trusted-sync' => 'offon', # default: (off) - - # On an UNSTABLE write from client, return STABLE flag to force client to not send a COMMIT request. In some environments, combined with a replicated GlusterFS setup, this option can improve write performance. This flag allows user to trust Gluster replication logic to sync data to the disks and recover when required. COMMIT requests if received will be handled in a default manner by fsyncing. STABLE writes are still handled in a sync manner. Off by default. - 'nfs.trusted-write' => 'offon', # default: (off) - - # Type of access desired for this subvolume: read-only, read-write(default) - 'nfs.volume-access' => 'string', # default: (read-write) - - # Maximum file size which would be cached by the io-cache translator. - 'performance.cache-max-file-size' => 'integer', # default: 0 - - # Minimum file size which would be cached by the io-cache translator. - 'performance.cache-min-file-size' => 'integer', # default: 0 - - # Assigns priority to filenames with specific patterns so that when a page needs to be ejected out of the cache, the page of a file whose priority is the lowest will be ejected earlier - 'performance.cache-priority' => 'string', # default: - - # The cached data for a file will be retained till 'cache-refresh-timeout' seconds, after which data re-validation is performed. - 'performance.cache-refresh-timeout' => 'integer', # default: 1 - - # XXX: this appears twice, with different defaults ! - # Size of the read cache. - 'performance.cache-size' => 'string', # default: 32MB - - # Size of the read cache. - 'performance.cache-size' => 'string', # default: 128MB - - # enable/disable io-threads translator in the client graph of volume. - 'performance.client-io-threads' => 'offon', # default: off - - # Enable/Disable least priority - 'performance.enable-least-priority' => 'onoff', # default: on - - # If this option is set ON, instructs write-behind translator to perform flush in background, by returning success (or any errors, if any of previous writes were failed) to application even before flush FOP is sent to backend filesystem. - 'performance.flush-behind' => 'onoff', # default: on - - # Convert all readdir requests to readdirplus to collect stat info on each entry. - 'performance.force-readdirp' => 'onoff', # default: on - - # Max number of threads in IO threads translator which perform high priority IO operations at a given time - 'performance.high-prio-threads' => 'integer', # default: 16 - - # enable/disable io-cache translator in the volume. - 'performance.io-cache' => 'onoff', # default: on - - # Number of threads in IO threads translator which perform concurrent IO operations - 'performance.io-thread-count' => 'integer', # default: 16 - - # Max number of threads in IO threads translator which perform least priority IO operations at a given time - 'performance.least-prio-threads' => 'integer', # default: 1 - - # Max number of least priority operations to handle per-second - 'performance.least-rate-limit' => 'integer', # default: 0 - - # Max number of threads in IO threads translator which perform low priority IO operations at a given time - 'performance.low-prio-threads' => 'integer', # default: 16 - - # Time period after which cache has to be refreshed - 'performance.md-cache-timeout' => 'integer', # default: 1 - - # Max number of threads in IO threads translator which perform normal priority IO operations at a given time - 'performance.normal-prio-threads' => 'integer', # default: 16 - - # enable/disable open-behind translator in the volume. - 'performance.open-behind' => 'onoff', # default: on - - # enable/disable quick-read translator in the volume. - 'performance.quick-read' => 'onoff', # default: on - - # enable/disable read-ahead translator in the volume. - 'performance.read-ahead' => 'onoff', # default: on - - # Number of pages that will be pre-fetched - 'performance.read-ahead-page-count' => 'integer', # default: 4 - - # enable/disable meta-data caching translator in the volume. - 'performance.stat-prefetch' => 'onoff', # default: on - - # This option when set to off, ignores the O_DIRECT flag. - 'performance.strict-o-direct' => 'offon', # default: off - - # Do not let later writes overtake earlier writes even if they do not overlap - 'performance.strict-write-ordering' => 'offon', # default: off - - # enable/disable write-behind translator in the volume. - 'performance.write-behind' => 'onoff', # default: on - - # Size of the write-behind buffer for a single file (inode). - 'performance.write-behind-window-size' => 'string', # default: 1MB - - # NOTE: this option taken from gluster documentation - # Allow client connections from unprivileged ports. By default only privileged ports are allowed. This is a global setting in case insecure ports are to be enabled for all exports using a single option. - 'server.allow-insecure' => 'offon', # XXX: description and manual differ in their mention of what the correct default is, so which is it? - - # Map requests from uid/gid 0 to the anonymous uid/gid. Note that this does not apply to any other uids or gids that might be equally sensitive, such as user bin or group staff. - 'server.root-squash' => 'offon', # default: off - - # Specifies directory in which gluster should save its statedumps. By default it is the /tmp directory - 'server.statedump-path' => 'string', # default: /var/run/gluster - - # Support for native Linux AIO - 'storage.linux-aio' => 'offon', # default: off - - # Support for setting gid of brick's owner - 'storage.owner-gid' => 'integer', # default: (null) - - # Support for setting uid of brick's owner - 'storage.owner-uid' => 'integer', # default: (null) - } - - # join char - $jchars = { - 'auth.allow' => ',', - 'auth.reject' => ',', - 'nfs.rpc-auth-allow' => ',', - 'nfs.rpc-auth-reject' => ',', - } -} - -# vim: ts=8 diff --git a/gluster/manifests/volume/property/group.pp b/gluster/manifests/volume/property/group.pp deleted file mode 100644 index d0b7a60fe..000000000 --- a/gluster/manifests/volume/property/group.pp +++ /dev/null @@ -1,73 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: The most well known group is 'virt', and is a collection of properties. -# NOTE: This type is particularly useful, because if you've set a certain group -# for your volume, and your package updates the group properties, then this can -# notice those changes and keep your volume in sync with the latest properties! -# NOTE: This intentionally conflicts with properties that are defined manually. -# NOTE: this does the equivalent of: gluster volume set group - -define gluster::volume::property::group( - $vip = '', # vip of the cluster (optional but recommended) -) { - include gluster::xml - include gluster::vardir - include gluster::volume::property::group::data - - #$vardir = $::gluster::vardir::module_vardir # with trailing slash - $vardir = regsubst($::gluster::vardir::module_vardir, '\/$', '') - - $split = split($name, '#') # do some $name parsing - $volume = $split[0] # volume name - $group = $split[1] # group name - - if ! ( "${volume}#${group}" == "${name}" ) { - fail('The property $name must match a $volume#$group pattern.') - } - - $groups = split($gluster_property_groups, ',') # fact - - if ! ("${group}" in $groups) { - - # check a fact to see if the directory is built yet... this - # prevents weird corner cases where this module is added to - # a new machine which is already built, except doesn't have - # the custom group data installed yet. if we fail, we won't - # be able to install it, so we don't fail, we warn instead! - if "${gluster_property_groups_ready}" == 'true' { - warning("The group named '${group}' is not available.") - } else { - notice("The group '${group}' might not be built yet.") - } - } else { - # read the fact that comes from the data in: /var/lib/glusterd/groups/* - $group_data_string = getvar("gluster_property_group_${group}") # fact! - # each element in this list is a key=value string - $group_data_list = split("${group_data_string}", ',') - # split into the correct hash to create all the properties - $group_data_yaml = inline_template("<%= @group_data_list.inject(Hash.new) { |h,i| { '${volume}#'+((i.split('=').length == 2) ? i.split('=')[0] : '') => {'value' => ((i.split('=').length == 2) ? i.split('=')[1] : '')} }.merge(h) }.to_yaml %>") - # build into a hash - $group_data_hash = parseyaml($group_data_yaml) - # pass through the vip - $group_data_defaults = {'vip' => "${vip}"} - # create the properties - create_resources('gluster::volume::property', $group_data_hash, $group_data_defaults) - } -} - -# vim: ts=8 diff --git a/gluster/manifests/volume/property/group/data.pp b/gluster/manifests/volume/property/group/data.pp deleted file mode 100644 index b79c4dd0a..000000000 --- a/gluster/manifests/volume/property/group/data.pp +++ /dev/null @@ -1,43 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: this provides an internal repository of volume parameter set groups and -# can be useful if the version of glusterfs does not have set group support, or -# if this module wants to distribute some custom groups which are not upstream. - -class gluster::volume::property::group::data() { - - include gluster::vardir - - #$vardir = $::gluster::vardir::module_vardir # with trailing slash - $vardir = regsubst($::gluster::vardir::module_vardir, '\/$', '') - - file { "${vardir}/groups/": - source => 'puppet:///modules/gluster/groups/', - ensure => directory, - recurse => true, - purge => true, - force => true, - owner => root, - group => nobody, - mode => 644, # u=rwx - backup => false, # don't backup to filebucket - require => File["${vardir}/"], - } -} - -# vim: ts=8 diff --git a/gluster/manifests/wrapper.pp b/gluster/manifests/wrapper.pp deleted file mode 100644 index 54156de33..000000000 --- a/gluster/manifests/wrapper.pp +++ /dev/null @@ -1,135 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class gluster::wrapper( - $nodetree, - $volumetree, - $vip = '', # vip of the cluster (optional but recommended) - - $nfs = false, # TODO in server.pp - $shorewall = false, - $zone = 'net', # TODO: allow a list of zones - $allow = 'all' -) { - # - # build gluster::server - # - - $hosts = split(inline_template("<%= @nodetree.keys.join(',') %>"), ',') - $ips = split(inline_template('<%= @nodetree.map{ |host,value| \'#{value["ip"]}\' }.join(",") %>'), ',') - - class { 'gluster::server': - hosts => $hosts, - ips => $ips, -#XXX: TODO? clients => XXX, - nfs => $nfs, - shorewall => $shorewall, - zone => $zone, - allow => $allow, - } - - # - # build gluster::host - # - - # EXAMPLE: - #gluster::host { 'annex1.example.com': - # # use uuidgen to make these - # uuid => '1f660ca2-2c78-4aa0-8f4d-21608218c69c', - #} - - # filter the nodetree so that only host elements with uuid's are left - # XXX: each_with_object doesn't exist in rhel6 ruby, so use inject - #$hosttree = inline_template('<%= @nodetree.each_with_object({}) {|(x,y), h| h[x] = y.select{ |key,value| ["uuid"].include?(key) } }.to_yaml %>') - $hosttree = inline_template('<%= @nodetree.inject({}) {|h, (x,y)| h[x] = y.select{ |key,value| ["uuid"].include?(key) }; h }.to_yaml %>') - # newhash = oldhash.inject({}) { |h,(k,v)| h[k] = some_operation(v); h } # XXX: does this form work ? - $yaml_host = parseyaml($hosttree) - create_resources('gluster::host', $yaml_host) - - # - # build gluster::brick - # - - # EXAMPLE: - #gluster::brick { 'annex1.example.com:/mnt/storage1a': - # dev => '/dev/disk/by-id/scsi-36003048007e26c00173ad3b633a2ef67', # /dev/sda - # labeltype => 'gpt', - # fstype => 'xfs', - # fsuuid => '1ae49642-7f34-4886-8d23-685d13867fb1', - # xfs_inode64 => true, - # xfs_nobarrier => true, - # areyousure => true, - #} - - # filter the nodetree and build out each brick element from the hosts - $bricktree = inline_template('<%= r = {}; @nodetree.each {|x,y| y["bricks"].each {|k,v| r[x+":"+k] = v} }; r.to_yaml %>') - # this version removes any invalid keys from the brick specifications - #$bricktree = inline_template('<%= r = {}; @nodetree.each {|x,y| y["bricks"].each {|k,v| r[x+":"+k] = v.select{ |key,value| ["dev", "labeltype", "fstype", "fsuuid", "..."].include?(key) } } }; r.to_yaml %>') - $yaml_brick = parseyaml($bricktree) - create_resources('gluster::brick', $yaml_brick) - - # - # build gluster::volume - # - - # EXAMPLE: - #gluster::volume { 'examplevol': - # replica => 2, - # bricks => $brick_list, - # start => undef, # i'll start this myself - #} - - # to be used as default gluster::volume brick list - $bricklist = split(inline_template("<%= @bricktree.keys.join(',') %>"), ',') - - # semi ok method: - #$volumetree_defaults_all = { - # "bricks" => $bricklist, - # "transport" => 'tcp', - # "replica" => 1, - # "stripe" => 1, - # "vip" => $vip, - # "start" => undef, # ? - #} - #$semi_ok = inline_template('<%= @volumetree.each_with_object({}) {|(x,y), h| h[x] = @volumetree_defaults_all.each_with_object({}) {|(xx,yy), hh| hh[xx] = y.fetch(xx, @volumetree_defaults_all[xx])} }.to_yaml %>') - - # good method - $volumetree_defaults = { - 'bricks' => $bricklist, - 'vip' => $vip, - } - # loop through volumetree... if special defaults are missing, then add! - $volumetree_updated = inline_template('<%= @volumetree.each_with_object({}) {|(x,y), h| h[x] = y; @volumetree_defaults.each {|k,v| h[k] = h.fetch(k, v)} }.to_yaml %>') - $yaml_volume = parseyaml($volumetree_updated) - create_resources('gluster::volume', $yaml_volume) - - # - # build gluster::volume::property (auth.allow) - # - - # EXAMPLE: - #gluster::volume::property { 'examplevol#auth.allow': - # value => '192.0.2.13,198.51.100.42,203.0.113.69', - #} - - #$simplewrongname = inline_template('<%= @volumetree.each_with_object({}) {|(x,y), h| h[x+"#auth.allow"] = y.select{ |key,value| ["clients"].include?(key) } }.to_yaml %>') - $propertytree = inline_template('<%= @volumetree.each_with_object({}) {|(x,y), h| h[x+"#auth.allow"] = { "value" => y.fetch("clients", []) } }.to_yaml %>') - $yaml_volume_property = parseyaml($propertytree) - create_resources('gluster::volume::property', $yaml_volume_property) -} - -# vim: ts=8 diff --git a/gluster/manifests/xml.pp b/gluster/manifests/xml.pp deleted file mode 100644 index 489a7c5d4..000000000 --- a/gluster/manifests/xml.pp +++ /dev/null @@ -1,50 +0,0 @@ -# GlusterFS module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class gluster::xml { - include gluster::vardir - include gluster::params - - #$vardir = $::gluster::vardir::module_vardir # with trailing slash - $vardir = regsubst($::gluster::vardir::module_vardir, '\/$', '') - - # argparse is built into python on new platforms and isn't needed here! - if "${::gluster::params::package_python_argparse}" != '' { - package { "${::gluster::params::package_python_argparse}": - ensure => present, - before => File["${vardir}/xml.py"], - } - } - - # for parsing gluster xml output - package { "${::gluster::params::package_python_lxml}": - ensure => present, - before => File["${vardir}/xml.py"], - } - - file { "${vardir}/xml.py": - source => 'puppet:///modules/gluster/xml.py', - owner => root, - group => nobody, - mode => 700, # u=rwx - backup => false, # don't backup to filebucket - ensure => present, - require => File["${vardir}/"], - } -} - -# vim: ts=8 diff --git a/gluster/puppet-gluster-documentation.pdf b/gluster/puppet-gluster-documentation.pdf deleted file mode 100644 index 69ba9de03..000000000 Binary files a/gluster/puppet-gluster-documentation.pdf and /dev/null differ diff --git a/gluster/puppet-gluster.spec.in b/gluster/puppet-gluster.spec.in deleted file mode 100644 index 0fde079fd..000000000 --- a/gluster/puppet-gluster.spec.in +++ /dev/null @@ -1,49 +0,0 @@ -# special thanks to kkeithley for using his wizard rpm skills to get this going -%global puppet_module_version __VERSION__ - -Name: puppet-gluster -Version: __VERSION__ -#Release: __RELEASE__%{?dist} # use this to make dist specific builds -Release: __RELEASE__ -Summary: A puppet module for GlusterFS -License: AGPLv3+ -URL: https://github.com/purpleidea/puppet-gluster -Source0: https://download.gluster.org/pub/gluster/purpleidea/puppet-gluster/SOURCES/puppet-gluster-%{puppet_module_version}.tar.bz2 -BuildArch: noarch - -Requires: puppet >= 3.0.0 -Requires: puppetlabs-stdlib >= 4.1.0 - -# these should be 'Suggests:' or similar, since they aren't absolutely required -#Requires: puppetlabs-apt >= 1.4.0 -Requires: puppet-common >= 0.0.1 -Requires: puppet-keepalived >= 0.0.1 -Requires: puppet-puppet >= 0.0.1 -Requires: puppet-shorewall >= 0.0.1 -Requires: puppet-yum >= 0.0.1 - -%description -A puppet module used for installing, configuring and managing GlusterFS. - -%prep -%setup -c -q -T -D -a 0 - -find %{_builddir} -type f -name ".*" -exec rm {} + -find %{_builddir} -size 0 -exec rm {} + -find %{_builddir} \( -name "*.pl" -o -name "*.sh" \) -exec chmod +x {} + -find %{_builddir} \( -name "*.pp" -o -name "*.py" \) -exec chmod -x {} + -find %{_builddir} \( -name "*.rb" -o -name "*.erb" \) -exec chmod -x {} + -exec sed -i "/^#!/{d;q}" {} + - -%build - -%install -rm -rf %{buildroot} -# _datadir is typically /usr/share/ -install -d -m 0755 %{buildroot}/%{_datadir}/puppet/modules/ -cp -r puppet-gluster-%{puppet_module_version} %{buildroot}/%{_datadir}/puppet/modules/gluster - -%files -%{_datadir}/puppet/modules/* - -# this changelog is auto-generated by git log -%changelog diff --git a/gluster/templates/glusterd.info.erb b/gluster/templates/glusterd.info.erb deleted file mode 100644 index 94acebc9e..000000000 --- a/gluster/templates/glusterd.info.erb +++ /dev/null @@ -1,4 +0,0 @@ -UUID=<%= @valid_uuid %> -<% if @operating_version != '' and @operating_version != '-1' -%> -operating-version=<%= @operating_version %> -<% end -%> diff --git a/gluster/templates/glusterd.vol.erb b/gluster/templates/glusterd.vol.erb deleted file mode 100644 index a258b9a4a..000000000 --- a/gluster/templates/glusterd.vol.erb +++ /dev/null @@ -1,16 +0,0 @@ -volume management - type mgmt/glusterd - option working-directory /var/lib/glusterd - option transport-type socket,rdma - option transport.socket.keepalive-time 10 - option transport.socket.keepalive-interval 2 - option transport.socket.read-fail-log off -<% if @valid_rpcauthallowinsecure -%> - option rpc-auth-allow-insecure on -<% end -%> -<% if @valid_baseport.to_i > 0 -%> - option base-port <%= @valid_baseport %> -<% else -%> -# option base-port 49152 -<% end -%> -end-volume diff --git a/gluster/vagrant/.gitignore b/gluster/vagrant/.gitignore deleted file mode 100644 index 3883822f5..000000000 --- a/gluster/vagrant/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -puppet-gluster.yaml -.vagrant/ -.ssh/ diff --git a/gluster/vagrant/README b/gluster/vagrant/README deleted file mode 100644 index 7918d1160..000000000 --- a/gluster/vagrant/README +++ /dev/null @@ -1,31 +0,0 @@ -This is Puppet-Gluster+Vagrant! (https://ttboj.wordpress.com/) - -You'll first need to get vagrant working. Here are some background articles: - -* https://ttboj.wordpress.com/2013/12/09/vagrant-on-fedora-with-libvirt/ -* https://ttboj.wordpress.com/2013/12/21/vagrant-vsftp-and-other-tricks/ -* https://ttboj.wordpress.com/2014/01/02/vagrant-clustered-ssh-and-screen/ - -I've written a detailed article about all of this, which is available here: - -* https://ttboj.wordpress.com/2014/01/08/automatically-deploying-glusterfs-with-puppet-gluster-vagrant - -This will not work perfectly on Fedora 19. You must use Fedora 20 or greater. - -Once you're comfortable that vagrant is working properly, run this command: - - vagrant up puppet && sudo -v && vagrant up annex{1..4} --no-parallel - -Sit back and watch, or go have a beverage... -The first run can take a while because it has to download/install a base image. - -When the above command completes, puppet will still be provisioning your hosts. -Once the cluster state settles, puppet will create and start a gluster volume. -Once your volume is started, you can build a few clients: - - vagrant up client{1..2} --gluster-clients=2 - -Happy hacking, - -James - diff --git a/gluster/vagrant/Vagrantfile b/gluster/vagrant/Vagrantfile deleted file mode 100644 index aef25d1bb..000000000 --- a/gluster/vagrant/Vagrantfile +++ /dev/null @@ -1,599 +0,0 @@ -# -*- mode: ruby -*- -# vi: set ft=ruby : - -# Vagrantfile for GlusterFS using Puppet-Gluster -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: vagrant-libvirt needs to run in series (not in parallel) to avoid trying -# to create the network twice, eg: 'vagrant up --no-parallel'. alternatively you -# can build the first host (puppet server) manually, and then the hosts next eg: -# 'vagrant up puppet && vagrant up' which ensures the puppet server is up first! -# NOTE: https://github.com/pradels/vagrant-libvirt/issues/104 is the open bug... - -# README: https://ttboj.wordpress.com/2013/12/09/vagrant-on-fedora-with-libvirt/ -# README: https://ttboj.wordpress.com/2013/12/21/vagrant-vsftp-and-other-tricks/ -# ALSO: https://ttboj.wordpress.com/2014/01/02/vagrant-clustered-ssh-and-screen/ -# README: https://ttboj.wordpress.com/2014/01/08/automatically-deploying-glusterfs-with-puppet-gluster-vagrant - -# NOTE: this will not work properly on Fedora 19 or anything that does not have -# the libvirt broadcast patch included. You can check which libvirt version you -# have and see if that version tag is in the libvirt git or in your distro. eg: -# git tag --contains 51e184e9821c3740ac9b52055860d683f27b0ab6 | grep -# this is because old libvirt broke the vrrp broadcast packets from keepalived! - -# TODO: the /etc/hosts DNS setup is less than ideal, but I didn't implement -# anything better yet. Please feel free to suggest something else! - -# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! -VAGRANTFILE_API_VERSION = '2' - -require 'ipaddr' -require 'yaml' - -# -# globals -# -domain = 'example.com' -network = IPAddr.new '192.168.142.0/24' -range = network.to_range.to_a -cidr = (32-(Math.log(range.length)/Math.log(2))).to_i -offset = 100 # start gluster hosts after here -#puts range[0].to_s # network -#puts range[1].to_s # router (reserved) -#puts range[2].to_s # puppetmaster -#puts range[3].to_s # vip - -network2 = IPAddr.new '192.168.143.0/24' -range2 = network2.to_range.to_a -cidr2 = (32-(Math.log(range2.length)/Math.log(2))).to_i -offset2 = 2 -netmask2 = IPAddr.new('255.255.255.255').mask(cidr2).to_s - -# mutable by ARGV and settings file -count = 4 # default number of gluster hosts to build -disks = 0 # default number of disks to attach (after the host os) -bricks = 0 # default number of bricks to build (0 defaults to 1) -fstype = '' # default filesystem for bricks (requires bricks > 0) -version = '' # default gluster version (empty string means latest!) -firewall = false # default firewall enabled (FIXME: default to true when keepalived bug is fixed) -replica = 1 # default replica count -clients = 1 # default number of gluster clients to build -layout = '' # default brick layout -setgroup = '' # default setgroup value -sync = 'rsync' # default sync type -cachier = false # default cachier usage - -# -# ARGV parsing -# -projectdir = File.expand_path File.dirname(__FILE__) # vagrant project dir!! -f = File.join(projectdir, 'puppet-gluster.yaml') - -# load settings -if File.exist?(f) - settings = YAML::load_file f - count = settings[:count] - disks = settings[:disks] - bricks = settings[:bricks] - fstype = settings[:fstype] - version = settings[:version] - firewall = settings[:firewall] - replica = settings[:replica] - clients = settings[:clients] - layout = settings[:layout] - setgroup = settings[:setgroup] - sync = settings[:sync] - cachier = settings[:cachier] -end - -# ARGV parser -skip = 0 -while skip < ARGV.length - #puts "#{skip}, #{ARGV[skip]}" # debug - if ARGV[skip].start_with?(arg='--gluster-count=') - v = ARGV.delete_at(skip).dup - v.slice! arg - #puts "#{arg}, #{v}" # debug - - count = v.to_i # set gluster host count - - elsif ARGV[skip].start_with?(arg='--gluster-disks=') - v = ARGV.delete_at(skip).dup - v.slice! arg - - disks = v.to_i # set gluster disk count - - elsif ARGV[skip].start_with?(arg='--gluster-bricks=') - v = ARGV.delete_at(skip).dup - v.slice! arg - - bricks = v.to_i # set gluster brick count - - elsif ARGV[skip].start_with?(arg='--gluster-fstype=') - v = ARGV.delete_at(skip).dup - v.slice! arg - - fstype = v.to_s # set gluster fstype - - elsif ARGV[skip].start_with?(arg='--gluster-version=') - v = ARGV.delete_at(skip).dup - v.slice! arg - - version = v.to_s # set gluster version - - elsif ARGV[skip].start_with?(arg='--gluster-firewall=') - v = ARGV.delete_at(skip).dup - v.slice! arg - - firewall = v.to_s # set firewall flag - if ['false', 'no'].include?(firewall.downcase) - firewall = false - else - firewall = true - end - - elsif ARGV[skip].start_with?(arg='--gluster-replica=') - v = ARGV.delete_at(skip).dup - v.slice! arg - - replica = v.to_i # set gluster replica - - elsif ARGV[skip].start_with?(arg='--gluster-clients=') - v = ARGV.delete_at(skip).dup - v.slice! arg - - clients = v.to_i # set gluster client count - - elsif ARGV[skip].start_with?(arg='--gluster-layout=') - v = ARGV.delete_at(skip).dup - v.slice! arg - - layout = v.to_s # set gluster brick layout - - elsif ARGV[skip].start_with?(arg='--gluster-setgroup=') - v = ARGV.delete_at(skip).dup - v.slice! arg - - setgroup = v.to_s # set gluster setgroup - - elsif ARGV[skip].start_with?(arg='--gluster-sync=') - v = ARGV.delete_at(skip).dup - v.slice! arg - - sync = v.to_s # set sync type - - elsif ARGV[skip].start_with?(arg='--gluster-cachier=') - v = ARGV.delete_at(skip).dup - v.slice! arg - - cachier = v.to_s # set cachier flag - if ['true', 'yes'].include?(cachier.downcase) - cachier = true - else - cachier = false - end - - else # skip over "official" vagrant args - skip = skip + 1 - end -end - -# save settings (ARGV overrides) -settings = { - :count => count, - :disks => disks, - :bricks => bricks, - :fstype => fstype, - :version => version, - :firewall => firewall, - :replica => replica, - :clients => clients, - :layout => layout, - :setgroup => setgroup, - :sync => sync, - :cachier => cachier -} -File.open(f, 'w') do |file| - file.write settings.to_yaml -end - -#puts "ARGV: #{ARGV}" # debug - -# erase host information from puppet so that the user can do partial rebuilds -snoop = ARGV.select { |x| !x.start_with?('-') } -if snoop.length > 1 and snoop[0] == 'destroy' - snoop.shift # left over array snoop should be list of hosts - if snoop.include?('puppet') # doesn't matter then... - snoop = [] - end -else - # important! clear snoop because we're not using 'destroy' - snoop = [] -end - -# figure out which hosts are getting destroyed -destroy = ARGV.select { |x| !x.start_with?('-') } -if destroy.length > 0 and destroy[0] == 'destroy' - destroy.shift # left over array destroy should be list of hosts or [] - if destroy.length == 0 - destroy = true # destroy everything - end -else - destroy = false # destroy nothing -end - -# figure out which hosts are getting provisioned -provision = ARGV.select { |x| !x.start_with?('-') } -if provision.length > 0 and ['up', 'provision'].include?(provision[0]) - provision.shift # left over array provision should be list of hosts or [] - if provision.length == 0 - provision = true # provision everything - end -else - provision = false # provision nothing -end - -# XXX: workaround for: https://github.com/mitchellh/vagrant/issues/2447 -# only run on 'vagrant init' or if it's the first time running vagrant -if sync == 'nfs' and ((ARGV.length > 0 and ARGV[0] == 'init') or not(File.exist?(f))) - `sudo systemctl restart nfs-server` - `firewall-cmd --permanent --zone public --add-service mountd` - `firewall-cmd --permanent --zone public --add-service rpc-bind` - `firewall-cmd --permanent --zone public --add-service nfs` - `firewall-cmd --reload` -end - -Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| - - #config.landrush.enable # TODO ? - - # - # box (pre-built base image) - # - config.vm.box = 'centos-6' # i built it - - # box source url - # TODO: this box should be GPG signed - config.vm.box_url = 'https://download.gluster.org/pub/gluster/purpleidea/vagrant/centos-6/centos-6.box' - - # - # sync type - # - config.vm.synced_folder './', '/vagrant', type: sync # nfs, rsync - - # - # cache - # - # NOTE: you should probably erase the cache between rebuilds if you are - # installing older package versions. This is because the newer packages - # will get cached, and then subsequently might get silently installed!! - if cachier - # TODO: this doesn't cache metadata, full offline operation not possible - config.cache.auto_detect = true - config.cache.enable :yum - #config.cache.enable :apt - if not ARGV.include?('--no-parallel') # when running in parallel, - config.cache.scope = :machine # use the per machine cache - end - if sync == 'nfs' # TODO: support other sync types here... - config.cache.enable_nfs = true # sets nfs => true on the synced_folder - # the nolock option is required, otherwise the NFSv3 client will try to - # access the NLM sideband protocol to lock files needed for /var/cache/ - # all of this can be avoided by using NFSv4 everywhere. die NFSv3, die! - config.cache.mount_options = ['rw', 'vers=3', 'tcp', 'nolock'] - end - end - - # - # vip - # - vip_ip = range[3].to_s - vip_hostname = 'annex' - - # - # puppetmaster - # - puppet_ip = range[2].to_s - puppet_hostname = 'puppet' - fv = File.join(projectdir, '.vagrant', "#{puppet_hostname}-hosts.done") - if destroy.is_a?(TrueClass) or (destroy.is_a?(Array) and destroy.include?(puppet_hostname)) - if File.exists?(fv) # safety - puts "Unlocking shell provisioning for: #{puppet_hostname}..." - File.delete(fv) # delete hosts token - end - end - - #puppet_fqdn = "#{puppet_hostname}.#{domain}" - config.vm.define :puppet, :primary => true do |vm| - vm.vm.hostname = puppet_hostname - # red herring network so that management happens here... - vm.vm.network :private_network, - :ip => range2[2].to_s, - :libvirt__netmask => netmask2, - #:libvirt__dhcp_enabled => false, # XXX: not allowed here - :libvirt__network_name => 'default' - - # this is the real network that we'll use... - vm.vm.network :private_network, - :ip => puppet_ip, - :libvirt__dhcp_enabled => false, - :libvirt__network_name => 'gluster' - - #vm.landrush.host puppet_hostname, puppet_ip # TODO ? - - # ensure the gluster module is present for provisioning... - if provision.is_a?(TrueClass) or (provision.is_a?(Array) and provision.include?(puppet_hostname)) - cwd = `pwd` - mod = File.join(projectdir, 'puppet', 'modules') - `cd #{mod} && make gluster &> /dev/null; cd #{cwd}` - end - - # - # shell - # - if not File.exists?(fv) # only modify /etc/hosts once - if provision.is_a?(TrueClass) or (provision.is_a?(Array) and provision.include?(puppet_hostname)) - File.open(fv, 'w') {} # touch - end - vm.vm.provision 'shell', inline: 'puppet resource host localhost.localdomain ip=127.0.0.1 host_aliases=localhost' - vm.vm.provision 'shell', inline: "puppet resource host #{puppet_hostname} ensure=absent" # so that fqdn works - - vm.vm.provision 'shell', inline: "puppet resource host #{vip_hostname}.#{domain} ip=#{vip_ip} host_aliases=#{vip_hostname} ensure=present" - vm.vm.provision 'shell', inline: "puppet resource host #{puppet_hostname}.#{domain} ip=#{puppet_ip} host_aliases=#{puppet_hostname} ensure=present" - (1..count).each do |i| - h = "annex#{i}" - ip = range[offset+i].to_s - vm.vm.provision 'shell', inline: "puppet resource host #{h}.#{domain} ip=#{ip} host_aliases=#{h} ensure=present" - end - - # hosts entries for all clients - (1..clients).each do |i| - h = "client#{i}" - ip = range[offset+count+i].to_s - vm.vm.provision 'shell', inline: "puppet resource host #{h}.#{domain} ip=#{ip} host_aliases=#{h} ensure=present" - end - end - # - # puppet (apply) - # - vm.vm.provision :puppet do |puppet| - puppet.module_path = 'puppet/modules' - puppet.manifests_path = 'puppet/manifests' - puppet.manifest_file = 'site.pp' - # custom fact - puppet.facter = { - 'vagrant' => '1', - 'vagrant_gluster_firewall' => firewall ? 'true' : 'false', - 'vagrant_gluster_allow' => (1..count).map{|z| range[offset+z].to_s}.join(','), - } - puppet.synced_folder_type = sync - end - - vm.vm.provider :libvirt do |libvirt| - libvirt.cpus = 2 - libvirt.memory = 1024 - end - end - - # - # annex - # - (1..count).each do |i| - h = "annex#{i}" - ip = range[offset+i].to_s # eg: "192.168.142.#{100+i}" - #fqdn = "annex#{i}.#{domain}" - fvx = File.join(projectdir, '.vagrant', "#{h}-hosts.done") - if destroy.is_a?(TrueClass) or (destroy.is_a?(Array) and destroy.include?(h)) - if File.exists?(fvx) # safety - puts "Unlocking shell provisioning for: #{h}..." - File.delete(fvx) # delete hosts token - end - end - - if snoop.include?(h) # should we clean this machine? - cmd = "puppet cert clean #{h}.#{domain}" - puts "Running 'puppet cert clean' for: #{h}..." - `vagrant ssh #{puppet_hostname} -c 'sudo #{cmd}'` - cmd = "puppet node deactivate #{h}.#{domain}" - puts "Running 'puppet node deactivate' for: #{h}..." - `vagrant ssh #{puppet_hostname} -c 'sudo #{cmd}'` - end - - config.vm.define h.to_sym do |vm| - vm.vm.hostname = h - # red herring network so that management happens here... - vm.vm.network :private_network, - :ip => range2[offset2+i].to_s, - :libvirt__netmask => netmask2, - :libvirt__network_name => 'default' - - # this is the real network that we'll use... - vm.vm.network :private_network, - :ip => ip, - :libvirt__dhcp_enabled => false, - :libvirt__network_name => 'gluster' - - #vm.landrush.host h, ip # TODO ? - - # - # shell - # - if not File.exists?(fvx) # only modify /etc/hosts once - if provision.is_a?(TrueClass) or (provision.is_a?(Array) and provision.include?(h)) - File.open(fvx, 'w') {} # touch - end - vm.vm.provision 'shell', inline: 'puppet resource host localhost.localdomain ip=127.0.0.1 host_aliases=localhost' - vm.vm.provision 'shell', inline: "puppet resource host #{h} ensure=absent" # so that fqdn works - - vm.vm.provision 'shell', inline: "puppet resource host #{vip_hostname}.#{domain} ip=#{vip_ip} host_aliases=#{vip_hostname} ensure=present" - vm.vm.provision 'shell', inline: "puppet resource host #{puppet_hostname}.#{domain} ip=#{puppet_ip} host_aliases=#{puppet_hostname} ensure=present" - #vm.vm.provision 'shell', inline: "[ ! -e /root/puppet-cert-is-clean ] && ssh -o 'StrictHostKeyChecking=no' #{puppet_hostname} puppet cert clean #{h}.#{domain} ; touch /root/puppet-cert-is-clean" - # hosts entries for all hosts - (1..count).each do |j| - oh = "annex#{j}" - oip = range[offset+j].to_s # eg: "192.168.142.#{100+i}" - vm.vm.provision 'shell', inline: "puppet resource host #{oh}.#{domain} ip=#{oip} host_aliases=#{oh} ensure=present" - end - - # hosts entries for all clients - (1..clients).each do |j| - oh = "client#{j}" - oip = range[offset+count+j].to_s - vm.vm.provision 'shell', inline: "puppet resource host #{oh}.#{domain} ip=#{oip} host_aliases=#{oh} ensure=present" - end - end - # - # puppet (agent) - # - vm.vm.provision :puppet_server do |puppet| - #puppet.puppet_node = "#{h}" # redundant - #puppet.puppet_server = "#{puppet_hostname}.#{domain}" - puppet.puppet_server = puppet_hostname - #puppet.options = '--verbose --debug' - puppet.options = '--test' # see the output - puppet.facter = { - 'vagrant' => '1', - 'vagrant_gluster_disks' => disks.to_s, - 'vagrant_gluster_bricks' => bricks.to_s, - 'vagrant_gluster_fstype' => fstype.to_s, - 'vagrant_gluster_replica' => replica.to_s, - 'vagrant_gluster_layout' => layout, - 'vagrant_gluster_setgroup' => setgroup, - 'vagrant_gluster_vip' => vip_ip, - 'vagrant_gluster_vip_fqdn' => "#{vip_hostname}.#{domain}", - 'vagrant_gluster_firewall' => firewall ? 'true' : 'false', - 'vagrant_gluster_version' => version, - } - end - - vm.vm.provider :libvirt do |libvirt| - # add additional disks to the os - (1..disks).each do |j| # if disks is 0, this passes :) - #print "disk: #{j}" - libvirt.storage :file, - #:path => '', # auto! - #:device => 'vdb', # auto! - #:size => '10G', # auto! - :type => 'qcow2' - - end - end - end - end - - # - # client - # - (1..clients).each do |i| - h = "client#{i}" - ip = range[offset+count+i].to_s - #fqdn = "annex#{i}.#{domain}" - fvy = File.join(projectdir, '.vagrant', "#{h}-hosts.done") - if destroy.is_a?(TrueClass) or (destroy.is_a?(Array) and destroy.include?(h)) - if File.exists?(fvy) # safety - puts "Unlocking shell provisioning for: #{h}..." - File.delete(fvy) # delete hosts token - end - end - - if snoop.include?(h) # should we clean this machine? - cmd = "puppet cert clean #{h}.#{domain}" - puts "Running 'puppet cert clean' for: #{h}..." - `vagrant ssh #{puppet_hostname} -c 'sudo #{cmd}'` - cmd = "puppet node deactivate #{h}.#{domain}" - puts "Running 'puppet node deactivate' for: #{h}..." - `vagrant ssh #{puppet_hostname} -c 'sudo #{cmd}'` - end - - config.vm.define h.to_sym do |vm| - vm.vm.hostname = h - # red herring network so that management happens here... - vm.vm.network :private_network, - :ip => range2[offset2+count+i].to_s, - :libvirt__netmask => netmask2, - :libvirt__network_name => 'default' - - # this is the real network that we'll use... - vm.vm.network :private_network, - :ip => ip, - :libvirt__dhcp_enabled => false, - :libvirt__network_name => 'gluster' - - #vm.landrush.host h, ip # TODO ? - - # - # shell - # - if not File.exists?(fvy) # only modify /etc/hosts once - if provision.is_a?(TrueClass) or (provision.is_a?(Array) and provision.include?(h)) - File.open(fvy, 'w') {} # touch - end - vm.vm.provision 'shell', inline: 'puppet resource host localhost.localdomain ip=127.0.0.1 host_aliases=localhost' - vm.vm.provision 'shell', inline: "puppet resource host #{h} ensure=absent" # so that fqdn works - - vm.vm.provision 'shell', inline: "puppet resource host #{vip_hostname}.#{domain} ip=#{vip_ip} host_aliases=#{vip_hostname} ensure=present" - vm.vm.provision 'shell', inline: "puppet resource host #{puppet_hostname}.#{domain} ip=#{puppet_ip} host_aliases=#{puppet_hostname} ensure=present" - # hosts entries for all hosts - (1..count).each do |j| - oh = "annex#{j}" - oip = range[offset+j].to_s # eg: "192.168.142.#{100+i}" - vm.vm.provision 'shell', inline: "puppet resource host #{oh}.#{domain} ip=#{oip} host_aliases=#{oh} ensure=present" - end - - # hosts entries for all clients - (1..clients).each do |j| - oh = "client#{j}" - oip = range[offset+count+j].to_s - vm.vm.provision 'shell', inline: "puppet resource host #{oh}.#{domain} ip=#{oip} host_aliases=#{oh} ensure=present" - end - end - # - # puppet (agent) - # - vm.vm.provision :puppet_server do |puppet| - #puppet.puppet_node = "#{h}" # redundant - #puppet.puppet_server = "#{puppet_hostname}.#{domain}" - puppet.puppet_server = puppet_hostname - #puppet.options = '--verbose --debug' - puppet.options = '--test' # see the output - puppet.facter = { - 'vagrant' => '1', - 'vagrant_gluster_vip' => vip_ip, - 'vagrant_gluster_vip_fqdn' => "#{vip_hostname}.#{domain}", - 'vagrant_gluster_firewall' => firewall ? 'true' : 'false', - 'vagrant_gluster_version' => version, - } - end - end - end - - # - # libvirt - # - config.vm.provider :libvirt do |libvirt| - libvirt.driver = 'kvm' # needed for kvm performance benefits ! - # leave out to connect directly with qemu:///system - #libvirt.host = 'localhost' - libvirt.connect_via_ssh = false - libvirt.username = 'root' - libvirt.storage_pool_name = 'default' - #libvirt.default_network = 'default' # XXX: this does nothing - libvirt.default_prefix = 'gluster' # prefix for your vm's! - end - -end - diff --git a/gluster/vagrant/puppet/files/README b/gluster/vagrant/puppet/files/README deleted file mode 100644 index 3f5a63aa7..000000000 --- a/gluster/vagrant/puppet/files/README +++ /dev/null @@ -1,2 +0,0 @@ -This is Puppet-Gluster+Vagrant! (https://ttboj.wordpress.com/) - diff --git a/gluster/vagrant/puppet/hiera.yaml b/gluster/vagrant/puppet/hiera.yaml deleted file mode 100644 index 5aaf25d18..000000000 --- a/gluster/vagrant/puppet/hiera.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -:backends: - - yaml -:yaml: - :datadir: /etc/puppet/hieradata/ -:hierarchy: - - common diff --git a/gluster/vagrant/puppet/hieradata/common.yaml b/gluster/vagrant/puppet/hieradata/common.yaml deleted file mode 100644 index d0fa00580..000000000 --- a/gluster/vagrant/puppet/hieradata/common.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -welcome: 'This is Puppet-Gluster+Vagrant! (https://ttboj.wordpress.com/)' -# vim:expandtab ts=8 sw=8 sta diff --git a/gluster/vagrant/puppet/manifests/site.pp b/gluster/vagrant/puppet/manifests/site.pp deleted file mode 100644 index e0decc3ad..000000000 --- a/gluster/vagrant/puppet/manifests/site.pp +++ /dev/null @@ -1,197 +0,0 @@ -node default { - # this will get put on every host... - $url = 'https://ttboj.wordpress.com/' - file { '/etc/motd': - content => "This is Puppet-Gluster+Vagrant! (${url})\n", - } -} - -# puppetmaster -node puppet inherits default { - - if "${::vagrant_gluster_firewall}" != 'false' { - include firewall - } - - $allow = split("${::vagrant_gluster_allow}", ',') # ip list fact - - class { '::puppet::server': - pluginsync => true, # do we want to enable pluginsync? - storeconfigs => true, # do we want to enable storeconfigs? - autosign => [ - '*', # FIXME: this is a temporary solution - #"*.${domain}", # FIXME: this is a temporary solution - ], - #allow_duplicate_certs => true, # redeploy without cert clean - allow => $allow, # also used in fileserver.conf - repo => true, # automatic repos - shorewall => "${::vagrant_gluster_firewall}" ? { - 'false' => false, - default => true, - }, - start => true, - } - - class { '::puppet::deploy': - path => '/vagrant/puppet/', # puppet folder is put here... - backup => false, # don't use puppet to backup... - } -} - -node /^annex\d+$/ inherits default { # annex{1,2,..N} - - if "${::vagrant_gluster_firewall}" != 'false' { - include firewall - } - - class { '::puppet::client': - #start => true, - start => false, # useful for testing manually... - } - - # build a list of hashes with ordered vdX devices - # (s='';q=i;(q, r = (q - 1).divmod(26)) && s.prepend(('a'..'z').to_a[r]) until q.zero?;'/dev/vd'+s) - $skip = 1 # skip over 1 disk (eg: /dev/vda from the host) - $disks = "${::vagrant_gluster_disks}" - $disks_yaml = inline_template("<%= (1+@skip.to_i..@disks.to_i+@skip.to_i).collect { |i| { 'dev' => (s='';q=i;(q, r = (q - 1).divmod(26)) && s.insert(0, ('a'..'z').to_a[r]) until q.zero?;'/dev/vd'+s) } }.to_yaml %>") - #$brick_params_defaults = [ # this is one possible example data set - # {'dev' => '/dev/vdb'}, - # {'dev' => '/dev/vdc'}, - # {'dev' => '/dev/vdd'}, - # {'dev' => '/dev/vde'}, - #] - $brick_params_defaults = parseyaml($disks_yaml) - notice(inline_template('disks: <%= YAML::load(@disks_yaml).inspect %>')) - #notify { 'disks': - # message => inline_template('disks: <%= YAML::load(@disks_yaml).inspect %>'), - #} - - $brick_param_defaults = { - # TODO: set these from vagrant variables... - 'lvm' => false, - 'fstype' => "${::vagrant_gluster_fstype}" ? { - '' => undef, - default => "${::vagrant_gluster_fstype}", - }, - 'xfs_inode64' => true, - 'force' => true, - } - - # this is a simple way to setup gluster - class { '::gluster::simple': - volume => 'puppet', - replica => "${::vagrant_gluster_replica}", - count => "${::vagrant_gluster_bricks}", # brick count - layout => "${::vagrant_gluster_layout}", - vip => "${::vagrant_gluster_vip}", # from vagrant - version => "${::vagrant_gluster_version}", - vrrp => true, - setgroup => "${::vagrant_gluster_setgroup}", - shorewall => "${::vagrant_gluster_firewall}" ? { - 'false' => false, - default => true, - }, - # NOTE: this is brick_params_defaults NOT param! param is below - brick_params_defaults => "${::vagrant_gluster_disks}" ? { - '0' => undef, - # NOTE: _each_ host will have N bricks with these devs! - default => $brick_params_defaults, - }, - brick_param_defaults => "${::vagrant_gluster_disks}" ? { - '0' => undef, - # NOTE: _each_ brick will use these... - default => $brick_param_defaults, - }, - } -} - -node /^client\d+$/ inherits default { # client{1,2,..N} - - if "${::vagrant_gluster_firewall}" != 'false' { - include firewall - } - - class { '::puppet::client': - #start => true, - start => false, # useful for testing manually... - } - - $host = "${::vagrant_gluster_vip_fqdn}" ? { - '' => "${::vagrant_gluster_vip}", - default => "${::vagrant_gluster_vip_fqdn}", - } - - gluster::mount { '/mnt/gluster/puppet/': - server => "${host}:/puppet", - rw => true, - version => "${::vagrant_gluster_version}", - shorewall => "${::vagrant_gluster_firewall}" ? { - 'false' => false, - default => true, - }, - } -} - -class firewall { - - $FW = '$FW' # make using $FW in shorewall easier - - class { '::shorewall::configuration': - # NOTE: no configuration specifics are needed at the moment - } - - shorewall::zone { ['net', 'man']: - type => 'ipv4', - options => [], # these aren't really needed right now - } - - # management zone interface used by vagrant-libvirt - shorewall::interface { 'man': - interface => 'MAN_IF', - broadcast => 'detect', - physical => 'eth0', # XXX: set manually! - options => ['dhcp', 'tcpflags', 'routefilter', 'nosmurfs', 'logmartians'], - comment => 'Management zone.', # FIXME: verify options - } - - # XXX: eth1 'dummy' zone to trick vagrant-libvirt into leaving me alone - # - - # net zone that gluster uses to communicate - shorewall::interface { 'net': - interface => 'NET_IF', - broadcast => 'detect', - physical => 'eth2', # XXX: set manually! - options => ['tcpflags', 'routefilter', 'nosmurfs', 'logmartians'], - comment => 'Public internet zone.', # FIXME: verify options - } - - # TODO: is this policy really what we want ? can we try to limit this ? - shorewall::policy { '$FW-net': - policy => 'ACCEPT', # TODO: shouldn't we whitelist? - } - - shorewall::policy { '$FW-man': - policy => 'ACCEPT', # TODO: shouldn't we whitelist? - } - - #################################################################### - #ACTION SOURCE DEST PROTO DEST SOURCE ORIGINAL - # PORT PORT(S) DEST - shorewall::rule { 'ssh': rule => " - SSH/ACCEPT net $FW - SSH/ACCEPT man $FW - ", comment => 'Allow SSH'} - - shorewall::rule { 'ping': rule => " - #Ping/DROP net $FW - Ping/ACCEPT net $FW - Ping/ACCEPT man $FW - ", comment => 'Allow ping from the `bad` net zone'} - - shorewall::rule { 'icmp': rule => " - ACCEPT $FW net icmp - ACCEPT $FW man icmp - ", comment => 'Allow icmp from the firewall zone'} -} - diff --git a/gluster/vagrant/puppet/modules/.gitignore b/gluster/vagrant/puppet/modules/.gitignore deleted file mode 100644 index 8a495bf49..000000000 --- a/gluster/vagrant/puppet/modules/.gitignore +++ /dev/null @@ -1 +0,0 @@ -gluster/ diff --git a/gluster/vagrant/puppet/modules/Makefile b/gluster/vagrant/puppet/modules/Makefile deleted file mode 100644 index c6507733a..000000000 --- a/gluster/vagrant/puppet/modules/Makefile +++ /dev/null @@ -1,67 +0,0 @@ -# Makefile for pulling in git modules for Vagrant deployment for Puppet-Gluster -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: if we remove a module, it won't get purged from the destination! - -# NOTE: this script can sync puppet-gluster to a specific sha1sum commit, or it -# can sync all of the repos to git master. This option can be useful for devel. - -BASE = 'https://github.com/purpleidea' -MODULES := \ - puppet-common \ - puppet-gluster \ - puppet-keepalived \ - puppet-module-data \ - puppet-puppet \ - puppet-shorewall \ - puppet-yum \ - puppetlabs-apt \ - puppetlabs-stdlib -# NOTE: set to a git commit id if we need an specific commit for vagrant builds -# NOTE: remember that new commits to master should change this to a specific id -# if they will break the vagrant build process. hopefully we don't forget this! -#SHA1SUM := master -SHA1SUM := $(shell git rev-parse --verify HEAD) # goto whatever the main tree is at - -.PHONY: all modules gluster -.SILENT: all modules gluster - -all: - -# -# modules -# -# clone, and then pull -modules: - basename `pwd` | grep -q '^modules' || exit 1 # run in a modules dir! - for i in $(MODULES); do \ - j=`echo $$i | awk -F '-' '{print $$2}'`; \ - [ -d "$$j" ] || git clone --depth 1 $(BASE)/$$i.git $$j; \ - [ -d "$$j" ] && cd $$j && git pull; cd ..; \ - done - -# -# gluster -# -# just clone and pull this one -gluster: - basename `pwd` | grep -q '^modules' || exit 1 # run in a modules dir! - i='puppet-gluster'; \ - j=`echo $$i | awk -F '-' '{print $$2}'`; \ - [ -d "$$j" ] || git clone ../../../. $$j; \ - [ -d "$$j" ] && cd $$j && git checkout master && git pull && git checkout $(SHA1SUM); cd .. - diff --git a/gluster/vagrant/puppet/modules/README b/gluster/vagrant/puppet/modules/README deleted file mode 100644 index c1837a538..000000000 --- a/gluster/vagrant/puppet/modules/README +++ /dev/null @@ -1,22 +0,0 @@ -This directory contains the puppet (git) module dependencies for Puppet-Gluster. They are included as git submodules for convenience and version compatibility. The Puppet-Gluster module itself is cloned in by a Makefile on provisioning. - -The one problem is Puppet-Gluster itself, since it is the parent repository to this sub-directory. There were a few options: - -1) Maintain the vagrant/ directory as a separate git project. -This would make a lot of sense, but I wanted to keep the vagrant portions bundled with Puppet-Gluster since they are so closely connected and for ease of distribution. In this situation, the vagrant/puppet/modules/ directory would include the Puppet-Gluster submodule along with all the other puppet (git) modules. - -2) Fill the vagrant/puppet/modules/ directory with git submodules. -This would make a lot of sense because you can reference specific commits, and it's easy to recursively clone all of the necessary code for a vagrant run. The problem is that recursively referencing the Puppet-Gluster might be a little awkward for some hackers to understand. One inconvenience, is that to update the modules/ directory, you'd have to first push your code changes to the server, get the sha1 commit hash, and then in a secondary commit change the submodules pointer. This would apparently cause a cascase of extra cloning each new commit. - -3) Fill the vagrant/puppet/modules/ directory with git submodules & 1 symlink. -This option seems to be the best solution. As in #2, we use git submodules. For the tricky Puppet-Gluster recursion scenario, we symlink the correct parent directory so that the relevant puppet code is present for the puppet::deploy. This only works if the provisioner follows the symlinks. For vagrant-libvirt, rsync needs the --copy-dirlinks option added. - -4) Maintain a Makefile and sync in Puppet-Gluster as needed. -This is what I've adopted for now. It works, and is mostly straightforward. If you can find a better solution, please let me know! - -Hope this gives you some helpful background, and thanks to #git for consulting. - -Happy hacking, - -James - diff --git a/gluster/vagrant/puppet/modules/apt b/gluster/vagrant/puppet/modules/apt deleted file mode 160000 index 5b54eda36..000000000 --- a/gluster/vagrant/puppet/modules/apt +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5b54eda3668a42b12e21696e500b40a27005bf2b diff --git a/gluster/vagrant/puppet/modules/common b/gluster/vagrant/puppet/modules/common deleted file mode 160000 index 2c0ed2844..000000000 --- a/gluster/vagrant/puppet/modules/common +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2c0ed2844c606fd806bde0c02e47e79c88fab4a9 diff --git a/gluster/vagrant/puppet/modules/keepalived b/gluster/vagrant/puppet/modules/keepalived deleted file mode 160000 index f0585c45e..000000000 --- a/gluster/vagrant/puppet/modules/keepalived +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f0585c45e7c5d5c2d86d0a84690a75dc522b2cd4 diff --git a/gluster/vagrant/puppet/modules/module-data b/gluster/vagrant/puppet/modules/module-data deleted file mode 160000 index 4b3ad1cc2..000000000 --- a/gluster/vagrant/puppet/modules/module-data +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4b3ad1cc239d7831616e69796184e400de0f5fe4 diff --git a/gluster/vagrant/puppet/modules/puppet b/gluster/vagrant/puppet/modules/puppet deleted file mode 160000 index f139d0b7c..000000000 --- a/gluster/vagrant/puppet/modules/puppet +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f139d0b7cfe6d55c0848d0d338e19fe640a961f2 diff --git a/gluster/vagrant/puppet/modules/shorewall b/gluster/vagrant/puppet/modules/shorewall deleted file mode 160000 index fbc7c6576..000000000 --- a/gluster/vagrant/puppet/modules/shorewall +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fbc7c6576092ceaf81b837989e086cb7fcc071d8 diff --git a/gluster/vagrant/puppet/modules/stdlib b/gluster/vagrant/puppet/modules/stdlib deleted file mode 160000 index 44c181ec0..000000000 --- a/gluster/vagrant/puppet/modules/stdlib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 44c181ec0e230768b8dce10de57f9b32638e66e1 diff --git a/gluster/vagrant/puppet/modules/yum b/gluster/vagrant/puppet/modules/yum deleted file mode 160000 index d098f6de9..000000000 --- a/gluster/vagrant/puppet/modules/yum +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d098f6de9a38055b3482325d371722835b576976 diff --git a/haproxy/.fixtures.yml b/haproxy/.fixtures.yml index 8d6f22d6f..c682e73b6 100644 --- a/haproxy/.fixtures.yml +++ b/haproxy/.fixtures.yml @@ -1,5 +1,10 @@ fixtures: repositories: - concat: "git://github.com/ripienaar/puppet-concat.git" + concat: + repo: "git://github.com/puppetlabs/puppetlabs-concat.git" + ref: '1.0.0' + stdlib: + repo: "git://github.com/puppetlabs/puppetlabs-stdlib.git" + ref: '2.4.0' symlinks: haproxy: "#{source_dir}" diff --git a/haproxy/.gemfile b/haproxy/.gemfile deleted file mode 100644 index 9aad840c0..000000000 --- a/haproxy/.gemfile +++ /dev/null @@ -1,5 +0,0 @@ -source :rubygems - -puppetversion = ENV.key?('PUPPET_VERSION') ? "= #{ENV['PUPPET_VERSION']}" : ['>= 2.7'] -gem 'puppet', puppetversion -gem 'puppetlabs_spec_helper', '>= 0.1.0' diff --git a/haproxy/.gitignore b/haproxy/.gitignore new file mode 100644 index 000000000..b844b143d --- /dev/null +++ b/haproxy/.gitignore @@ -0,0 +1 @@ +Gemfile.lock diff --git a/haproxy/.nodeset.yml b/haproxy/.nodeset.yml new file mode 100644 index 000000000..481ba4c0d --- /dev/null +++ b/haproxy/.nodeset.yml @@ -0,0 +1,20 @@ +--- +default_set: 'centos-64-x64' +sets: + 'centos-64-x64': + nodes: + "main.foo.vm": + prefab: 'centos-64-x64' + 'multi-centos-64-x64': + default_node: 'lb' + nodes: + "lb": + prefab: 'centos-64-x64' + "slave1": + prefab: 'centos-64-x64' + "slave2": + prefab: 'centos-64-x64' + 'ubuntu-server-12042-x64': + nodes: + "main.foo.vm": + prefab: 'ubuntu-server-12042-x64' diff --git a/haproxy/.travis.yml b/haproxy/.travis.yml index fdbc95dc6..bed0c4b1c 100644 --- a/haproxy/.travis.yml +++ b/haproxy/.travis.yml @@ -1,23 +1,31 @@ +--- +branches: + only: + - master language: ruby +bundler_args: --without development +script: bundle exec rake spec SPEC_OPTS='--format documentation' rvm: - - 1.8.7 - - 1.9.3 +- 1.8.7 +- 1.9.3 +- 2.0.0 script: "rake spec" -branches: - only: - - master env: - - PUPPET_VERSION=2.6.17 - - PUPPET_VERSION=2.7.19 - #- PUPPET_VERSION=3.0.1 # Breaks due to rodjek/rspec-puppet#58 -notifications: - email: false -gemfile: .gemfile + matrix: + - PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.6.0" + - PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.7.0" + - PUPPET_GEM_VERSION="~> 3.0" matrix: exclude: - rvm: 1.9.3 - gemfile: .gemfile - env: PUPPET_VERSION=2.6.17 + env: PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.6.0" + - rvm: 1.9.3 + env: PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.7.0" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.6.0" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.7.0" - rvm: 1.8.7 - gemfile: .gemfile - env: PUPPET_VERSION=3.0.1 + env: PUPPET_GEM_VERSION="~> 3.2.0" +notifications: + email: false diff --git a/haproxy/CHANGELOG b/haproxy/CHANGELOG index 99a992b2f..b0b97240f 100644 --- a/haproxy/CHANGELOG +++ b/haproxy/CHANGELOG @@ -1,7 +1,53 @@ +2014-05-28 - Version 0.5.0 + +Summary: + +The primary feature of this release is a reorganization of the +module to match best practices. There are several new parameters +and some bug fixes. + +Features: +- Reorganized the module to follow install/config/service pattern +- Added bind_options parameter to haproxy::listen +- Updated tests + +Fixes: +- Add license file +- Whitespace cleanup +- Use correct port in README +- Fix order of concat fragments + +2013-10-08 - Version 0.4.1 + +Summary: + +Fix the dependency for concat. + +Fixes: +- Changed the dependency to be the puppetlabs/concat version. + +2013-10-03 - Version 0.4.0 + +Summary: + +The largest feature in this release is the new haproxy::frontend +and haproxy::backend defines. The other changes are mostly to +increase flexibility. + +Features: +- Added parameters to haproxy: + - `package_name`: Allows alternate package name. +- Add haproxy::frontend and haproxy::backend defines. +- Add an ensure parameter to balancermember so they can be removed. +- Made chroot optional + +Fixes: +- Remove deprecation warnings from templates. + 2013-05-25 - Version 0.3.0 Features: - Add travis testing -- Add `haproxy::basancermember` `define_cookies` parameter +- Add `haproxy::balancermember` `define_cookies` parameter - Add array support to `haproxy::listen` `ipaddress` parameter Bugfixes: diff --git a/haproxy/Gemfile b/haproxy/Gemfile new file mode 100644 index 000000000..bf676242e --- /dev/null +++ b/haproxy/Gemfile @@ -0,0 +1,27 @@ +source 'https://rubygems.org' + +group :development, :test do + gem 'rake', '~> 10.1.0', :require => false + gem 'rspec-puppet', :require => false + gem 'puppetlabs_spec_helper', :require => false + gem 'serverspec', :require => false + gem 'puppet-lint', :require => false + gem 'pry', :require => false + gem 'simplecov', :require => false + gem 'beaker', :require => false + gem 'beaker-rspec', :require => false +end + +if facterversion = ENV['FACTER_GEM_VERSION'] + gem 'facter', facterversion, :require => false +else + gem 'facter', :require => false +end + +if puppetversion = ENV['PUPPET_GEM_VERSION'] + gem 'puppet', puppetversion, :require => false +else + gem 'puppet', :require => false +end + +# vim:ft=ruby diff --git a/haproxy/LICENSE b/haproxy/LICENSE new file mode 100644 index 000000000..c4a243518 --- /dev/null +++ b/haproxy/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2012 Puppet Labs + + 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. diff --git a/haproxy/Modulefile b/haproxy/Modulefile index 7f70ac0c2..d08718ae3 100644 --- a/haproxy/Modulefile +++ b/haproxy/Modulefile @@ -1,5 +1,5 @@ name 'puppetlabs-haproxy' -version '0.3.0' +version '0.5.0' source 'git://github.com/puppetlabs/puppetlabs-haproxy' author 'Puppet Labs' license 'Apache License, Version 2.0' @@ -7,6 +7,5 @@ summary 'Haproxy Module' description 'An Haproxy module for Redhat family OSes using Storeconfigs' project_page 'http://github.com/puppetlabs/puppetlabs-haproxy' -## Add dependencies, if any: -# dependency 'username/name', '>= 1.2.0' -dependency 'ripienaar/concat', '>= 0.1.0' +dependency 'puppetlabs/concat', '>= 1.0.0' +dependency 'puppetlabs/stdlib', '>= 2.4.0' diff --git a/haproxy/README.md b/haproxy/README.md index 8e51edc5c..ac2216051 100644 --- a/haproxy/README.md +++ b/haproxy/README.md @@ -1,6 +1,8 @@ PuppetLabs Module for haproxy ============================= +[![Build Status](https://travis-ci.org/puppetlabs/puppetlabs-haproxy.svg?branch=master)](https://travis-ci.org/puppetlabs/puppetlabs-haproxy) + HAProxy is an HA proxying daemon for load-balancing to clustered services. It can proxy TCP directly, or other kinds of traffic such as HTTP. @@ -10,7 +12,7 @@ Basic Usage This haproxy uses storeconfigs to collect and realize balancer member servers on a load balancer server. Currently Redhat family OSes are supported. -*To install and configure HAProxy server listening on port 80* +*To install and configure HAProxy server listening on port 8140* ```puppet node 'haproxy-server' { @@ -68,6 +70,12 @@ which is serving loadbalanced traffic. the `listening_service` attribute will associate it with `haproxy::listen` directives on the haproxy node. `ipaddresses` and `ports` will be assigned to the member to be contacted on. If an array of `ipaddresses` and `server_names` are provided then they will be added to the config in lock-step. +Configuring haproxy daemon userlist +----------------------------------- + +One `haproxy::userlist` defined resource should be defined for each HAProxy userlist. +The users and groups for the userlist are supplied to the resource via an array. + Dependencies ------------ diff --git a/haproxy/manifests/backend.pp b/haproxy/manifests/backend.pp index 3ae6781bd..aa9b8b2bd 100644 --- a/haproxy/manifests/backend.pp +++ b/haproxy/manifests/backend.pp @@ -16,20 +16,19 @@ # === Parameters # # [*name*] -# The namevar of the defined resource type is the backend service's name. -# This name goes right after the 'backend' statement in haproxy.cfg +# The namevar of the defined resource type is the backend service's name. +# This name goes right after the 'backend' statement in haproxy.cfg # # [*options*] -# A hash of options that are inserted into the backend configuration block. +# A hash of options that are inserted into the backend configuration block. # # [*collect_exported*] -# Boolean, default 'true'. True means 'collect exported @@balancermember -# resources' (for the case when every balancermember node exports itself), -# false means 'rely on the existing declared balancermember resources' (for -# the case when you know the full set of balancermember in advance and use -# haproxy::balancermember with array arguments, which allows you to deploy -# everything in 1 run) -# +# Boolean, default 'true'. True means 'collect exported @@balancermember +# resources' (for the case when every balancermember node exports itself), +# false means 'rely on the existing declared balancermember resources' (for +# the case when you know the full set of balancermember in advance and use +# haproxy::balancermember with array arguments, which allows you to deploy +# everything in 1 run) # # === Examples # @@ -69,7 +68,7 @@ } if $collect_exported { - Haproxy::Balancermember <<| listening_service == $name |>> + haproxy::balancermember::collect_exported { $name: } } # else: the resources have been created and they introduced their # concat fragments. We don't have to do anything about them. diff --git a/haproxy/manifests/balancermember.pp b/haproxy/manifests/balancermember.pp index 4fe949ae8..35e1cf65d 100644 --- a/haproxy/manifests/balancermember.pp +++ b/haproxy/manifests/balancermember.pp @@ -21,39 +21,39 @@ # fragment name. # # [*listening_service*] -# The haproxy service's instance name (or, the title of the -# haproxy::listen resource). This must match up with a declared -# haproxy::listen resource. +# The haproxy service's instance name (or, the title of the +# haproxy::listen resource). This must match up with a declared +# haproxy::listen resource. # # [*ports*] -# An array or commas-separated list of ports for which the balancer member -# will accept connections from the load balancer. Note that cookie values -# aren't yet supported, but shouldn't be difficult to add to the -# configuration. If you use an array in server_names and ipaddresses, the -# same port is used for all balancermembers. +# An array or commas-separated list of ports for which the balancer member +# will accept connections from the load balancer. Note that cookie values +# aren't yet supported, but shouldn't be difficult to add to the +# configuration. If you use an array in server_names and ipaddresses, the +# same port is used for all balancermembers. # # [*server_names*] -# The name of the balancer member server as known to haproxy in the -# listening service's configuration block. This defaults to the -# hostname. Can be an array of the same length as ipaddresses, -# in which case a balancermember is created for each pair of -# server_names and ipaddresses (in lockstep). +# The name of the balancer member server as known to haproxy in the +# listening service's configuration block. This defaults to the +# hostname. Can be an array of the same length as ipaddresses, +# in which case a balancermember is created for each pair of +# server_names and ipaddresses (in lockstep). # # [*ipaddresses*] -# The ip address used to contact the balancer member server. -# Can be an array, see documentation to server_names. +# The ip address used to contact the balancer member server. +# Can be an array, see documentation to server_names. # # [*ensure*] -# If the balancermember should be present or absent. -# Defaults to present. +# If the balancermember should be present or absent. +# Defaults to present. # # [*options*] -# An array of options to be specified after the server declaration -# in the listening service's configuration block. +# An array of options to be specified after the server declaration +# in the listening service's configuration block. # # [*define_cookies*] -# If true, then add "cookie SERVERID" stickiness options. -# Default false. +# If true, then add "cookie SERVERID" stickiness options. +# Default false. # # === Examples # @@ -95,9 +95,10 @@ $options = '', $define_cookies = false ) { + # Template uses $ipaddresses, $server_name, $ports, $option concat::fragment { "${listening_service}_balancermember_${name}": - order => "20-${listening_service}-${name}", + order => "20-${listening_service}-01-${name}", ensure => $ensure, target => '/etc/haproxy/haproxy.cfg', content => template('haproxy/haproxy_balancermember.erb'), diff --git a/haproxy/manifests/balancermember/collect_exported.pp b/haproxy/manifests/balancermember/collect_exported.pp new file mode 100644 index 000000000..87e897194 --- /dev/null +++ b/haproxy/manifests/balancermember/collect_exported.pp @@ -0,0 +1,15 @@ +# == Define: haproxy::balancermember::collect_exported +# +# Defined type used to collect haproxy::balancermember exported resource +# +# === Parameters +# +# None +# +# === Examples +# +# haproxy::balancermember::collect_exported { 'SomeService': } +# +define haproxy::balancermember::collect_exported { + Haproxy::Balancermember <<| listening_service == $name |>> +} diff --git a/haproxy/manifests/config.pp b/haproxy/manifests/config.pp new file mode 100644 index 000000000..796d1daf7 --- /dev/null +++ b/haproxy/manifests/config.pp @@ -0,0 +1,34 @@ +# Private class +class haproxy::config inherits haproxy { + if $caller_module_name != $module_name { + fail("Use of private class ${name} by ${caller_module_name}") + } + + concat { '/etc/haproxy/haproxy.cfg': + owner => '0', + group => '0', + mode => '0644', + } + + # Simple Header + concat::fragment { '00-header': + target => '/etc/haproxy/haproxy.cfg', + order => '01', + content => "# This file managed by Puppet\n", + } + + # Template uses $global_options, $defaults_options + concat::fragment { 'haproxy-base': + target => '/etc/haproxy/haproxy.cfg', + order => '10', + content => template('haproxy/haproxy-base.cfg.erb'), + } + + if $global_options['chroot'] { + file { $global_options['chroot']: + ensure => directory, + owner => $global_options['user'], + group => $global_options['group'], + } + } +} diff --git a/haproxy/manifests/frontend.pp b/haproxy/manifests/frontend.pp index 488acaf0f..0f5128ce0 100644 --- a/haproxy/manifests/frontend.pp +++ b/haproxy/manifests/frontend.pp @@ -12,37 +12,42 @@ # === Parameters # # [*name*] -# The namevar of the defined resource type is the frontend service's name. -# This name goes right after the 'frontend' statement in haproxy.cfg +# The namevar of the defined resource type is the frontend service's name. +# This name goes right after the 'frontend' statement in haproxy.cfg # # [*ports*] -# Ports on which the proxy will listen for connections on the ip address +# Ports on which the proxy will listen for connections on the ip address # specified in the ipaddress parameter. Accepts either a single # comma-separated string or an array of strings which may be ports or # hyphenated port ranges. # # [*ipaddress*] -# The ip address the proxy binds to. Empty addresses, '*', and '0.0.0.0' -# mean that the proxy listens to all valid addresses on the system. +# The ip address the proxy binds to. Empty addresses, '*', and '0.0.0.0' +# mean that the proxy listens to all valid addresses on the system. # # [*mode*] -# The mode of operation for the frontend service. Valid values are undef, +# The mode of operation for the frontend service. Valid values are undef, # 'tcp', 'http', and 'health'. # +# [*bind_options*] +# An array of options to be specified after the bind declaration in the +# bind's configuration block. +# # [*options*] -# A hash of options that are inserted into the frontend service -# configuration block. +# A hash of options that are inserted into the frontend service +# configuration block. # # === Examples # # Exporting the resource for a balancer member: # # haproxy::frontend { 'puppet00': -# ipaddress => $::ipaddress, -# ports => '18140', -# mode => 'tcp', -# options => { -# 'option' => [ +# ipaddress => $::ipaddress, +# ports => '18140', +# mode => 'tcp', +# bind_options => 'accept-proxy', +# options => { +# 'option' => [ # 'tcplog', # 'accept-invalid-http-request', # ], @@ -59,6 +64,7 @@ $ports, $ipaddress = [$::ipaddress], $mode = undef, + $bind_options = undef, $collect_exported = true, $options = { 'option' => [ @@ -66,11 +72,11 @@ ], } ) { + # Template uses: $name, $ipaddress, $ports, $options concat::fragment { "${name}_frontend_block": order => "15-${name}-00", target => '/etc/haproxy/haproxy.cfg', content => template('haproxy/haproxy_frontend_block.erb'), } - } diff --git a/haproxy/manifests/init.pp b/haproxy/manifests/init.pp index 8fb60d7e9..995117ab8 100644 --- a/haproxy/manifests/init.pp +++ b/haproxy/manifests/init.pp @@ -5,15 +5,24 @@ # # === Requirement/Dependencies: # -# Currently requires the ripienaar/concat module on the Puppet Forge and +# Currently requires the puppetlabs/concat module on the Puppet Forge and # uses storeconfigs on the Puppet Master to export/collect resources # from all balancer members. # # === Parameters # -# [*enable*] -# Chooses whether haproxy should be installed or ensured absent. -# Currently ONLY accepts valid boolean true/false values. +# [*package_ensure*] +# Chooses whether the haproxy package should be installed or uninstalled. Defaults to 'present' +# +# [*package_name*] +# The package name of haproxy. Defaults to 'haproxy' +# +# [*service_ensure*] +# Chooses whether the haproxy service should be running & enabled at boot, or +# stopped and disabled at boot. Defaults to 'running' +# +# [*service_manage*] +# Chooses whether the haproxy service state should be managed by puppet at all. Defaults to true # # [*global_options*] # A hash of all the haproxy global options. If you want to specify more @@ -27,11 +36,21 @@ # options as an array and you will get a line for each of them in the # resultant haproxy.cfg file. # +#[*restart_command*] +# Command to use when restarting the on config changes. +# Passed directly as the 'restart' parameter to the service resource. +# Defaults to undef i.e. whatever the service default is. +# +#[*custom_fragment*] +# Allows arbitrary HAProxy configuration to be passed through to support +# additional configuration not available via parameters, or to short-circute +# the defined resources such as haproxy::listen when an operater would rather +# just write plain configuration. Accepts a string (ie, output from the +# template() function). Defaults to undef # # === Examples # # class { 'haproxy': -# enable => true, # global_options => { # 'log' => "${::ipaddress} local0", # 'chroot' => '/var/lib/haproxy', @@ -60,91 +79,55 @@ # } # class haproxy ( - $manage_service = true, - $enable = true, + $package_ensure = 'present', + $package_name = $haproxy::params::package_name, + $service_ensure = 'running', + $service_manage = true, $global_options = $haproxy::params::global_options, - $defaults_options = $haproxy::params::defaults_options + $defaults_options = $haproxy::params::defaults_options, + $restart_command = undef, + $custom_fragment = undef, + + # Deprecated + $manage_service = undef, + $enable = undef, ) inherits haproxy::params { include concat::setup - package { 'haproxy': - ensure => $enable ? { - true => present, - false => absent, - }, - name => 'haproxy', - } - - if $enable { - concat { '/etc/haproxy/haproxy.cfg': - owner => '0', - group => '0', - mode => '0644', - require => Package['haproxy'], - notify => $manage_service ? { - true => Service['haproxy'], - false => undef, - }, + if $service_ensure != true and $service_ensure != false { + if ! ($service_ensure in [ 'running','stopped']) { + fail('service_ensure parameter must be running, stopped, true, or false') } - - # Simple Header - concat::fragment { '00-header': - target => '/etc/haproxy/haproxy.cfg', - order => '01', - content => "# This file managed by Puppet\n", - } - - # Template uses $global_options, $defaults_options - concat::fragment { 'haproxy-base': - target => '/etc/haproxy/haproxy.cfg', - order => '10', - content => template('haproxy/haproxy-base.cfg.erb'), - } - - if ($::osfamily == 'Debian') { - file { '/etc/default/haproxy': - content => 'ENABLED=1', - require => Package['haproxy'], - before => $manage_service ? { - true => Service['haproxy'], - false => undef, - }, - } - } - - if $global_options['chroot'] { - file { $global_options['chroot']: - ensure => directory, - } - } - } + validate_string($package_name,$package_ensure) + validate_bool($service_manage) - if $manage_service { - if $global_options['chroot'] { - $deps = [ - Concat['/etc/haproxy/haproxy.cfg'], - File[$global_options['chroot']], - ] + # To support deprecating $enable + if $enable != undef { + warning('The $enable parameter is deprecated; please use service_ensure and/or package_ensure instead') + if $enable { + $_package_ensure = 'present' + $_service_ensure = 'running' } else { - $deps = [ - Concat['/etc/haproxy/haproxy.cfg'], - ] + $_package_ensure = 'absent' + $_service_ensure = 'stopped' } + } else { + $_package_ensure = $package_ensure + $_service_ensure = $service_ensure + } - service { 'haproxy': - ensure => $enable ? { - true => running, - false => stopped, - }, - enable => $enable ? { - true => true, - false => false, - }, - name => 'haproxy', - hasrestart => true, - hasstatus => true, - require => $deps, - } + # To support deprecating $manage_service + if $manage_service != undef { + warning('The $manage_service parameter is deprecated; please use $service_manage instead') + $_service_manage = $manage_service + } else { + $_service_manage = $service_manage } + + anchor { 'haproxy::begin': } + -> class { 'haproxy::install': } + -> class { 'haproxy::config': } + ~> class { 'haproxy::service': } + -> anchor { 'haproxy::end': } } diff --git a/haproxy/manifests/install.pp b/haproxy/manifests/install.pp new file mode 100644 index 000000000..5b55ab32a --- /dev/null +++ b/haproxy/manifests/install.pp @@ -0,0 +1,11 @@ +# Private class +class haproxy::install inherits haproxy { + if $caller_module_name != $module_name { + fail("Use of private class ${name} by ${caller_module_name}") + } + + package { $package_name: + ensure => $_package_ensure, + alias => 'haproxy', + } +} diff --git a/haproxy/manifests/listen.pp b/haproxy/manifests/listen.pp index 5e2fc762f..913c77a63 100644 --- a/haproxy/manifests/listen.pp +++ b/haproxy/manifests/listen.pp @@ -17,35 +17,38 @@ # === Parameters # # [*name*] -# The namevar of the defined resource type is the listening service's name. -# This name goes right after the 'listen' statement in haproxy.cfg +# The namevar of the defined resource type is the listening service's name. +# This name goes right after the 'listen' statement in haproxy.cfg # # [*ports*] -# Ports on which the proxy will listen for connections on the ip address +# Ports on which the proxy will listen for connections on the ip address # specified in the ipaddress parameter. Accepts either a single # comma-separated string or an array of strings which may be ports or # hyphenated port ranges. # # [*ipaddress*] -# The ip address the proxy binds to. Empty addresses, '*', and '0.0.0.0' -# mean that the proxy listens to all valid addresses on the system. +# The ip address the proxy binds to. Empty addresses, '*', and '0.0.0.0' +# mean that the proxy listens to all valid addresses on the system. # # [*mode*] -# The mode of operation for the listening service. Valid values are undef, +# The mode of operation for the listening service. Valid values are undef, # 'tcp', 'http', and 'health'. # # [*options*] -# A hash of options that are inserted into the listening service -# configuration block. +# A hash of options that are inserted into the listening service +# configuration block. +# +# [*bind_options*] +# An array of options to be specified after the bind declaration in the +# listening serivce's configuration block. # # [*collect_exported*] -# Boolean, default 'true'. True means 'collect exported @@balancermember resources' +# Boolean, default 'true'. True means 'collect exported @@balancermember resources' # (for the case when every balancermember node exports itself), false means # 'rely on the existing declared balancermember resources' (for the case when you # know the full set of balancermembers in advance and use haproxy::balancermember # with array arguments, which allows you to deploy everything in 1 run) # -# # === Examples # # Exporting the resource for a balancer member: @@ -78,8 +81,10 @@ 'ssl-hello-chk' ], 'balance' => 'roundrobin' - } + }, + $bind_options = '' ) { + # Template uses: $name, $ipaddress, $ports, $options concat::fragment { "${name}_listen_block": order => "20-${name}-00", @@ -88,7 +93,7 @@ } if $collect_exported { - Haproxy::Balancermember <<| listening_service == $name |>> + haproxy::balancermember::collect_exported { $name: } } # else: the resources have been created and they introduced their # concat fragments. We don't have to do anything about them. diff --git a/haproxy/manifests/params.pp b/haproxy/manifests/params.pp index 53442ddcc..a1f46ed49 100644 --- a/haproxy/manifests/params.pp +++ b/haproxy/manifests/params.pp @@ -6,34 +6,8 @@ # class haproxy::params { case $osfamily { - Redhat: { - $global_options = { - 'log' => "${::ipaddress} local0", - 'chroot' => '/var/lib/haproxy', - 'pidfile' => '/var/run/haproxy.pid', - 'maxconn' => '4000', - 'user' => 'haproxy', - 'group' => 'haproxy', - 'daemon' => '', - 'stats' => 'socket /var/lib/haproxy/stats' - } - $defaults_options = { - 'log' => 'global', - 'stats' => 'enable', - 'option' => 'redispatch', - 'retries' => '3', - 'timeout' => [ - 'http-request 10s', - 'queue 1m', - 'connect 10s', - 'client 1m', - 'server 1m', - 'check 10s', - ], - 'maxconn' => '8000' - } - } - Debian: { + 'Archlinux', 'Debian', 'Redhat': { + $package_name = 'haproxy' $global_options = { 'log' => "${::ipaddress} local0", 'chroot' => '/var/lib/haproxy', diff --git a/haproxy/manifests/service.pp b/haproxy/manifests/service.pp new file mode 100644 index 000000000..12b6c5a16 --- /dev/null +++ b/haproxy/manifests/service.pp @@ -0,0 +1,28 @@ +# Private class +class haproxy::service inherits haproxy { + if $caller_module_name != $module_name { + fail("Use of private class ${name} by ${caller_module_name}") + } + + if $_service_manage { + if ($::osfamily == 'Debian') { + file { '/etc/default/haproxy': + content => 'ENABLED=1', + before => Service['haproxy'], + } + } + + service { 'haproxy': + ensure => $_service_ensure, + enable => $_service_ensure ? { + 'running' => true, + 'stopped' => false, + default => $_service_ensure, + }, + name => 'haproxy', + hasrestart => true, + hasstatus => true, + restart => $restart_command, + } + } +} diff --git a/haproxy/manifests/userlist.pp b/haproxy/manifests/userlist.pp new file mode 100644 index 000000000..e02d9e92a --- /dev/null +++ b/haproxy/manifests/userlist.pp @@ -0,0 +1,41 @@ +# == Define Resource Type: haproxy::userlist +# +# This type will set up a userlist configuration block inside the haproxy.cfg +# file on an haproxy load balancer. +# +# See http://cbonte.github.io/haproxy-dconv/configuration-1.4.html#3.4 for more info +# +# === Requirement/Dependencies: +# +# Currently requires the ripienaar/concat module on the Puppet Forge +# +# === Parameters +# +# [*name*] +# The namevar of the define resource type is the userlist name. +# This name goes right after the 'userlist' statement in haproxy.cfg +# +# [*users*] +# An array of users in the userlist. +# See http://cbonte.github.io/haproxy-dconv/configuration-1.4.html#3.4-user +# +# [*groups*] +# An array of groups in the userlist. +# See http://cbonte.github.io/haproxy-dconv/configuration-1.4.html#3.4-group +# +# === Authors +# +# Jeremy Kitchen +# +define haproxy::userlist ( + $users = undef, + $groups = undef, +) { + + # Template usse $name, $users, $groups + concat::fragment { "${name}_userlist_block": + order => "12-${name}-00", + target => '/etc/haproxy/haproxy.cfg', + content => template('haproxy/haproxy_userlist_block.erb'), + } +} diff --git a/haproxy/metadata.json b/haproxy/metadata.json new file mode 100644 index 000000000..f8f81ebf7 --- /dev/null +++ b/haproxy/metadata.json @@ -0,0 +1,58 @@ +{ + "name": "puppetlabs-haproxy", + "version": "0.5.0", + "source": "https://github.com/puppetlabs/puppetlabs-haproxy", + "author": "Puppet Labs", + "license": "Apache-2.0", + "project_page": "https://github.com/puppetlabs/puppetlabs-haproxy", + "summary": "Puppet module for HAProxy", + "operatingsystem_support": [ + { + "operatingsystem": "RedHat", + "operatingsystemrelease": [ + "5", + "6" + ] + }, + { + "operatingsystem": "CentOS", + "operatingsystemrelease": [ + "5", + "6" + ] + }, + { + "operatingsystem": "OracleLinux", + "operatingsystemrelease": [ + "5", + "6" + ] + }, + { + "operatingsystem": "Scientific", + "operatingsystemrelease": [ + "5", + "6" + ] + }, + { + "operatingsystem": "Debian", + "operatingsystemrelease": [ + "6", + "7" + ] + }, + { + "operatingsystem": "Ubuntu", + "operatingsystemrelease": [ + "10.04", + "12.04" + ] + } + ], + "requirements": [ + { "name": "pe", "version_requirement": ">= 3.2.0 < 3.4.0" }, + { "name": "puppet", "version_requirement": "3.x" } + ], + "dependencies": [] +} diff --git a/haproxy/spec/acceptance/multi_node/multi_spec.rb b/haproxy/spec/acceptance/multi_node/multi_spec.rb new file mode 100644 index 000000000..dcd55a3ee --- /dev/null +++ b/haproxy/spec/acceptance/multi_node/multi_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper_acceptance' + +if hosts.length >= 3 + describe "configuring multi-node loadbalancer" do + let(:ipaddresses) do + hosts_as('slave').inject({}) do |memo,host| + memo[host] = fact_on host, "ipaddress_eth1" + memo + end + end + + hosts_as('slave').each do |host| + it "should be able to configure the daemon on #{host}" do + pp = <<-EOS + package { 'nc': ensure => present, } + package { 'screen': ensure => present, } + service { 'iptables': ensure => stopped, } + EOS + apply_manifest_on(host, pp, :catch_failures => true) + on host, %{echo 'while :; do echo "#{host}" | nc -l 5555 ; done' > /root/script.sh} + on host, %{/usr/bin/screen -dmS slave sh /root/script.sh} #, { :pty => true } + sleep 1 + on host, %{netstat -tnl|grep ':5555\\s'} + end + end + + it 'should be able to configure the loadbalancer with puppet' do + pp = <<-EOS + class { 'haproxy': } + haproxy::listen { 'app00': + ipaddress => $::ipaddress_lo, + ports => '5555', + } + #{ipaddresses.collect do |host,ipaddress| + %{ + haproxy::balancermember { '#{host}': + listening_service => 'app00', + ports => '5555', + ipaddresses => '#{ipaddress}', + }} + end.join + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + # This is not great since it depends on the ordering served by the load + # balancer. Something with retries would be better. + hosts_as('slave').each do |slave| + it "should do a curl against the LB to make sure it gets a response from #{slave}" do + shell('curl localhost:5555').stdout.chomp.should eq(slave.to_s) + end + end + end +end diff --git a/haproxy/spec/acceptance/nodesets/centos-64-x64.yml b/haproxy/spec/acceptance/nodesets/centos-64-x64.yml new file mode 100644 index 000000000..0385e951d --- /dev/null +++ b/haproxy/spec/acceptance/nodesets/centos-64-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + centos-64-x64: + roles: + - default + platform: el-6-x86_64 + box : centos-64-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: foss diff --git a/haproxy/spec/acceptance/nodesets/multi-centos-64-x64.yml b/haproxy/spec/acceptance/nodesets/multi-centos-64-x64.yml new file mode 100644 index 000000000..84003e2f4 --- /dev/null +++ b/haproxy/spec/acceptance/nodesets/multi-centos-64-x64.yml @@ -0,0 +1,25 @@ +HOSTS: + lb: + roles: + - loadbalancer + - default + platform: el-6-x86_64 + box : centos-64-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box + hypervisor : vagrant + slave1: + roles: + - slave + platform: el-6-x86_64 + box : centos-64-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box + hypervisor : vagrant + slave2: + roles: + - slave + platform: el-6-x86_64 + box : centos-64-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: foss diff --git a/haproxy/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml b/haproxy/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml new file mode 100644 index 000000000..711352211 --- /dev/null +++ b/haproxy/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + ubuntu-server-12042-x64: + roles: + - default + platform: ubuntu-12.04-amd64 + box : ubuntu-server-12042-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-12042-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: foss diff --git a/haproxy/spec/acceptance/single_node/basic_spec.rb b/haproxy/spec/acceptance/single_node/basic_spec.rb new file mode 100644 index 000000000..dedb8c006 --- /dev/null +++ b/haproxy/spec/acceptance/single_node/basic_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper_acceptance' + +if hosts.length == 1 + describe "running puppet" do + it 'should be able to apply class haproxy' do + pp = <<-EOS + class { 'haproxy': + custom_fragment => "listen stats :9090 + mode http + stats uri / + stats auth puppet:puppet", + } + haproxy::listen { 'test00': ports => '80',} + EOS + apply_manifest(pp, :catch_failures => true) + end + + it 'should have stats listening' do + shell("/usr/bin/curl -u puppet:puppet localhost:9090") do |r| + r.stdout.should =~ /HAProxy/ + r.exit_code.should == 0 + end + end + end +end diff --git a/haproxy/spec/classes/haproxy_spec.rb b/haproxy/spec/classes/haproxy_spec.rb index 4b5902ce5..022941aed 100644 --- a/haproxy/spec/classes/haproxy_spec.rb +++ b/haproxy/spec/classes/haproxy_spec.rb @@ -9,15 +9,19 @@ end context 'on supported platforms' do describe 'for OS-agnostic configuration' do - ['Debian', 'RedHat'].each do |osfamily| + ['Debian', 'RedHat', 'Archlinux'].each do |osfamily| context "on #{osfamily} family operatingsystems" do let(:facts) do { :osfamily => osfamily }.merge default_facts end let(:params) do - {'enable' => true} + { + 'service_ensure' => 'running', + 'package_ensure' => 'present', + 'service_manage' => true + } end - it { should include_class('concat::setup') } + it { should contain_class('concat::setup') } it 'should install the haproxy package' do subject.should contain_package('haproxy').with( 'ensure' => 'present' @@ -28,11 +32,7 @@ 'ensure' => 'running', 'enable' => 'true', 'hasrestart' => 'true', - 'hasstatus' => 'true', - 'require' => [ - 'Concat[/etc/haproxy/haproxy.cfg]', - 'File[/var/lib/haproxy]' - ] + 'hasstatus' => 'true' ) end it 'should set up /etc/haproxy/haproxy.cfg as a concat resource' do @@ -44,7 +44,9 @@ end it 'should manage the chroot directory' do subject.should contain_file('/var/lib/haproxy').with( - 'ensure' => 'directory' + 'ensure' => 'directory', + 'owner' => 'haproxy', + 'group' => 'haproxy' ) end it 'should contain a header concat fragment' do @@ -89,19 +91,96 @@ end let(:params) do { - 'enable' => true, - 'manage_service' => false, + 'service_ensure' => true, + 'package_ensure' => 'present', + 'service_manage' => false } end - it { should include_class('concat::setup') } + it { should contain_class('concat::setup') } it 'should install the haproxy package' do subject.should contain_package('haproxy').with( 'ensure' => 'present' ) end - it 'should install the haproxy service' do + it 'should not manage the haproxy service' do subject.should_not contain_service('haproxy') end + it 'should set up /etc/haproxy/haproxy.cfg as a concat resource' do + subject.should contain_concat('/etc/haproxy/haproxy.cfg').with( + 'owner' => '0', + 'group' => '0', + 'mode' => '0644' + ) + end + it 'should manage the chroot directory' do + subject.should contain_file('/var/lib/haproxy').with( + 'ensure' => 'directory' + ) + end + it 'should contain a header concat fragment' do + subject.should contain_concat__fragment('00-header').with( + 'target' => '/etc/haproxy/haproxy.cfg', + 'order' => '01', + 'content' => "# This file managed by Puppet\n" + ) + end + it 'should contain a haproxy-base concat fragment' do + subject.should contain_concat__fragment('haproxy-base').with( + 'target' => '/etc/haproxy/haproxy.cfg', + 'order' => '10' + ) + end + describe 'Base concat fragment contents' do + let(:contents) { param_value(subject, 'concat::fragment', 'haproxy-base', 'content').split("\n") } + it 'should contain global and defaults sections' do + contents.should include('global') + contents.should include('defaults') + end + it 'should log to an ip address for local0' do + contents.should be_any { |match| match =~ / log \d+(\.\d+){3} local0/ } + end + it 'should specify the default chroot' do + contents.should include(' chroot /var/lib/haproxy') + end + it 'should specify the correct user' do + contents.should include(' user haproxy') + end + it 'should specify the correct group' do + contents.should include(' group haproxy') + end + it 'should specify the correct pidfile' do + contents.should include(' pidfile /var/run/haproxy.pid') + end + end + end + context "on #{osfamily} when specifying a restart_command" do + let(:facts) do + { :osfamily => osfamily }.merge default_facts + end + let(:params) do + { + 'restart_command' => '/etc/init.d/haproxy reload', + 'service_manage' => true + } + end + it 'should set the haproxy package' do + subject.should contain_service('haproxy').with( + 'restart' => '/etc/init.d/haproxy reload' + ) + end + end + context "on #{osfamily} when specifying custom content" do + let(:facts) do + { :osfamily => osfamily }.merge default_facts + end + let(:params) do + { 'custom_fragment' => "listen stats :9090\n mode http\n stats uri /\n stats auth puppet:puppet\n" } + end + it 'should set the haproxy package' do + subject.should contain_concat__fragment('haproxy-base').with_content( + /listen stats :9090\n mode http\n stats uri \/\n stats auth puppet:puppet\n/ + ) + end end end end @@ -111,10 +190,7 @@ { :osfamily => 'Debian' }.merge default_facts end it 'should manage haproxy service defaults' do - subject.should contain_file('/etc/default/haproxy').with( - 'before' => 'Service[haproxy]', - 'require' => 'Package[haproxy]' - ) + subject.should contain_file('/etc/default/haproxy') verify_contents(subject, '/etc/default/haproxy', ['ENABLED=1']) end end diff --git a/haproxy/spec/defines/balancermember_spec.rb b/haproxy/spec/defines/balancermember_spec.rb index 4da2815e2..f0739e8e2 100644 --- a/haproxy/spec/defines/balancermember_spec.rb +++ b/haproxy/spec/defines/balancermember_spec.rb @@ -20,7 +20,7 @@ end it { should contain_concat__fragment('croy_balancermember_tyler').with( - 'order' => '20-croy-tyler', + 'order' => '20-croy-01-tyler', 'target' => '/etc/haproxy/haproxy.cfg', 'content' => " server dero 1.1.1.1:18140 check\n" ) } @@ -37,7 +37,7 @@ end it { should contain_concat__fragment('croy_balancermember_tyler').with( - 'order' => '20-croy-tyler', + 'order' => '20-croy-01-tyler', 'target' => '/etc/haproxy/haproxy.cfg', 'ensure' => 'absent', 'content' => " server dero 1.1.1.1:18140 \n" @@ -55,7 +55,7 @@ end it { should contain_concat__fragment('croy_balancermember_tyler').with( - 'order' => '20-croy-tyler', + 'order' => '20-croy-01-tyler', 'target' => '/etc/haproxy/haproxy.cfg', 'content' => " server dero 1.1.1.1:18140 check close\n" ) } @@ -73,7 +73,7 @@ end it { should contain_concat__fragment('croy_balancermember_tyler').with( - 'order' => '20-croy-tyler', + 'order' => '20-croy-01-tyler', 'target' => '/etc/haproxy/haproxy.cfg', 'content' => " server dero 1.1.1.1:18140 cookie dero check close\n" ) } @@ -91,7 +91,7 @@ end it { should contain_concat__fragment('croy_balancermember_tyler').with( - 'order' => '20-croy-tyler', + 'order' => '20-croy-01-tyler', 'target' => '/etc/haproxy/haproxy.cfg', 'content' => " server server01 192.168.56.200:18140 check\n server server02 192.168.56.201:18140 check\n" ) } @@ -109,7 +109,7 @@ end it { should contain_concat__fragment('croy_balancermember_tyler').with( - 'order' => '20-croy-tyler', + 'order' => '20-croy-01-tyler', 'target' => '/etc/haproxy/haproxy.cfg', 'content' => " server server01 192.168.56.200:18140,192.168.56.200:18150 check\n server server02 192.168.56.201:18140,192.168.56.201:18150 check\n" ) } diff --git a/haproxy/spec/defines/frontend_spec.rb b/haproxy/spec/defines/frontend_spec.rb index 04acce86e..f1a6241af 100644 --- a/haproxy/spec/defines/frontend_spec.rb +++ b/haproxy/spec/defines/frontend_spec.rb @@ -14,7 +14,7 @@ it { should contain_concat__fragment('croy_frontend_block').with( 'order' => '15-croy-00', 'target' => '/etc/haproxy/haproxy.cfg', - 'content' => "\nfrontend croy\n bind 1.1.1.1:18140\n option tcplog\n" + 'content' => "\nfrontend croy\n bind 1.1.1.1:18140 \n option tcplog\n" ) } end context "when an array of ports is provided" do @@ -32,7 +32,22 @@ it { should contain_concat__fragment('apache_frontend_block').with( 'order' => '15-apache-00', 'target' => '/etc/haproxy/haproxy.cfg', - 'content' => "\nfrontend apache\n bind 23.23.23.23:80\n bind 23.23.23.23:443\n option tcplog\n" + 'content' => "\nfrontend apache\n bind 23.23.23.23:80 \n bind 23.23.23.23:443 \n option tcplog\n" + ) } + end + context "when bind options are provided" do + let(:params) do + { + :name => 'apache', + :ports => ['80','8080'], + :bind_options => [ 'the options', 'go here' ] + } + end + + it { should contain_concat__fragment('apache_frontend_block').with( + 'order' => '15-apache-00', + 'target' => '/etc/haproxy/haproxy.cfg', + 'content' => "\nfrontend apache\n bind 1.1.1.1:80 the options go here\n bind 1.1.1.1:8080 the options go here\n option tcplog\n" ) } end context "when a comma-separated list of ports is provided" do @@ -47,7 +62,7 @@ it { should contain_concat__fragment('apache_frontend_block').with( 'order' => '15-apache-00', 'target' => '/etc/haproxy/haproxy.cfg', - 'content' => "\nfrontend apache\n bind 23.23.23.23:80\n bind 23.23.23.23:443\n option tcplog\n" + 'content' => "\nfrontend apache\n bind 23.23.23.23:80 \n bind 23.23.23.23:443 \n option tcplog\n" ) } end end diff --git a/haproxy/spec/defines/listen_spec.rb b/haproxy/spec/defines/listen_spec.rb index ef46dcacd..4c313e549 100644 --- a/haproxy/spec/defines/listen_spec.rb +++ b/haproxy/spec/defines/listen_spec.rb @@ -14,7 +14,7 @@ it { should contain_concat__fragment('croy_listen_block').with( 'order' => '20-croy-00', 'target' => '/etc/haproxy/haproxy.cfg', - 'content' => "\nlisten croy\n bind 1.1.1.1:18140\n balance roundrobin\n option tcplog\n option ssl-hello-chk\n" + 'content' => "\nlisten croy\n bind 1.1.1.1:18140 \n balance roundrobin\n option tcplog\n option ssl-hello-chk\n" ) } end context "when an array of ports is provided" do @@ -32,7 +32,7 @@ it { should contain_concat__fragment('apache_listen_block').with( 'order' => '20-apache-00', 'target' => '/etc/haproxy/haproxy.cfg', - 'content' => "\nlisten apache\n bind 23.23.23.23:80\n bind 23.23.23.23:443\n balance roundrobin\n option tcplog\n option ssl-hello-chk\n" + 'content' => "\nlisten apache\n bind 23.23.23.23:80 \n bind 23.23.23.23:443 \n balance roundrobin\n option tcplog\n option ssl-hello-chk\n" ) } end context "when a comma-separated list of ports is provided" do @@ -47,7 +47,22 @@ it { should contain_concat__fragment('apache_listen_block').with( 'order' => '20-apache-00', 'target' => '/etc/haproxy/haproxy.cfg', - 'content' => "\nlisten apache\n bind 23.23.23.23:80\n bind 23.23.23.23:443\n balance roundrobin\n option tcplog\n option ssl-hello-chk\n" + 'content' => "\nlisten apache\n bind 23.23.23.23:80 \n bind 23.23.23.23:443 \n balance roundrobin\n option tcplog\n option ssl-hello-chk\n" + ) } + end + context "when bind options are provided" do + let(:params) do + { + :name => 'apache', + :ports => '80', + :bind_options => [ 'the options', 'go here' ] + } + end + + it { should contain_concat__fragment('apache_listen_block').with( + 'order' => '20-apache-00', + 'target' => '/etc/haproxy/haproxy.cfg', + 'content' => "\nlisten apache\n bind 1.1.1.1:80 the options go here\n balance roundrobin\n option tcplog\n option ssl-hello-chk\n" ) } end end diff --git a/haproxy/spec/defines/userlist_spec.rb b/haproxy/spec/defines/userlist_spec.rb new file mode 100644 index 000000000..1e1a56423 --- /dev/null +++ b/haproxy/spec/defines/userlist_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' + +describe 'haproxy::userlist' do + let(:title) { 'admins' } + let(:facts) {{ :ipaddress => '1.1.1.1' }} + + context "when users and groups are passed" do + let (:params) do + { + :name => "admins", + :users => [ + 'scott insecure-password elgato', + 'kitchen insecure-password foobar' + ], + :groups => [ + 'superadmins users kitchen scott', + 'megaadmins users kitchen' + ] + } + end + + it { should contain_concat__fragment('admins_userlist_block').with( + 'order' => '12-admins-00', + 'target' => '/etc/haproxy/haproxy.cfg', + 'content' => "\nuserlist admins\n group superadmins users kitchen scott\n group megaadmins users kitchen\n user scott insecure-password elgato\n user kitchen insecure-password foobar\n" + ) } + + end + +end diff --git a/haproxy/spec/spec_helper_acceptance.rb b/haproxy/spec/spec_helper_acceptance.rb new file mode 100644 index 000000000..e304f82c6 --- /dev/null +++ b/haproxy/spec/spec_helper_acceptance.rb @@ -0,0 +1,33 @@ +require 'beaker-rspec' + +UNSUPPORTED_PLATFORMS = [] + +unless ENV['RS_PROVISION'] == 'no' or ENV['BEAKER_provision'] == 'no' + if hosts.first.is_pe? + install_pe + else + install_puppet + end + hosts.each do |host| + on host, "mkdir -p #{host['distmoduledir']}" + end +end + +RSpec.configure do |c| + # Project root + proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) + + # Readable test descriptions + c.formatter = :documentation + + # Configure all nodes in nodeset + c.before :suite do + # Install module and dependencies + puppet_module_install(:source => proj_root, :module_name => 'haproxy') + hosts.each do |host| + on host, puppet('module','install','puppetlabs-stdlib'), { :acceptable_exit_codes => [0,1] } + on host, puppet('module','install','puppetlabs-concat'), { :acceptable_exit_codes => [0,1] } + on host, '/bin/touch /etc/puppet/hiera.yaml' + end + end +end diff --git a/haproxy/templates/fragments/_bind.erb b/haproxy/templates/fragments/_bind.erb new file mode 100644 index 000000000..2fff31136 --- /dev/null +++ b/haproxy/templates/fragments/_bind.erb @@ -0,0 +1,3 @@ +<% Array(@ipaddress).uniq.each do |virtual_ip| (@ports.is_a?(Array) ? @ports : Array(@ports.split(","))).each do |port| -%> + bind <%= virtual_ip %>:<%= port %> <%= Array(@bind_options).join(" ") %> +<% end end -%> diff --git a/haproxy/templates/fragments/_mode.erb b/haproxy/templates/fragments/_mode.erb new file mode 100644 index 000000000..944785771 --- /dev/null +++ b/haproxy/templates/fragments/_mode.erb @@ -0,0 +1,3 @@ +<% if @mode -%> + mode <%= @mode %> +<% end -%> diff --git a/haproxy/templates/fragments/_options.erb b/haproxy/templates/fragments/_options.erb new file mode 100644 index 000000000..4a9b14919 --- /dev/null +++ b/haproxy/templates/fragments/_options.erb @@ -0,0 +1,5 @@ +<% @options.sort.each do |key, val| -%> +<% Array(val).each do |item| -%> + <%= key %> <%= item %> +<% end -%> +<% end -%> diff --git a/haproxy/templates/haproxy-base.cfg.erb b/haproxy/templates/haproxy-base.cfg.erb index f25d5c344..228d6be36 100644 --- a/haproxy/templates/haproxy-base.cfg.erb +++ b/haproxy/templates/haproxy-base.cfg.erb @@ -19,3 +19,7 @@ defaults <%= key %> <%= val %> <% end -%> <% end -%> +<% if @custom_fragment -%> + +<%= @custom_fragment %> +<% end -%> diff --git a/haproxy/templates/haproxy_backend_block.erb b/haproxy/templates/haproxy_backend_block.erb index 95114cf46..c5df88842 100644 --- a/haproxy/templates/haproxy_backend_block.erb +++ b/haproxy/templates/haproxy_backend_block.erb @@ -1,7 +1,3 @@ backend <%= @name %> -<% @options.sort.each do |key, val| -%> -<% Array(val).each do |item| -%> - <%= key %> <%= item %> -<% end -%> -<% end -%> +<%= scope.function_template(['haproxy/fragments/_options.erb']) -%> diff --git a/haproxy/templates/haproxy_balancermember.erb b/haproxy/templates/haproxy_balancermember.erb index 47c409c0b..e67d997c8 100644 --- a/haproxy/templates/haproxy_balancermember.erb +++ b/haproxy/templates/haproxy_balancermember.erb @@ -1,3 +1,3 @@ <% Array(@ipaddresses).zip(Array(@server_names)).each do |ipaddress,host| -%> - server <%= host %> <%= ipaddress %>:<%= Array(@ports).collect {|x|x.split(',')}.flatten.join(",#{ipaddress}:") %> <%= if @define_cookies then "cookie " + host end %> <%= Array(@options).join(" ") %> + server <%= host %> <%= ipaddress %>:<%= Array(@ports).collect {|x|x.split(',')}.flatten.join(",#{ipaddress}:") %> <%= if @define_cookies then "cookie " + host end %> <%= Array(@options).sort.join(" ") %> <% end -%> diff --git a/haproxy/templates/haproxy_frontend_block.erb b/haproxy/templates/haproxy_frontend_block.erb index a1803ce4b..9e9333d0d 100644 --- a/haproxy/templates/haproxy_frontend_block.erb +++ b/haproxy/templates/haproxy_frontend_block.erb @@ -1,13 +1,5 @@ frontend <%= @name %> -<% Array(@ipaddress).uniq.each do |virtual_ip| (@ports.is_a?(Array) ? @ports : Array(@ports.split(","))).each do |port| -%> - bind <%= virtual_ip %>:<%= port %> -<% end end -%> -<% if @mode -%> - mode <%= @mode %> -<% end -%> -<% @options.sort.each do |key, val| -%> -<% Array(val).each do |item| -%> - <%= key %> <%= item %> -<% end -%> -<% end -%> +<%= scope.function_template(['haproxy/fragments/_bind.erb']) -%> +<%= scope.function_template(['haproxy/fragments/_mode.erb']) -%> +<%= scope.function_template(['haproxy/fragments/_options.erb']) -%> diff --git a/haproxy/templates/haproxy_listen_block.erb b/haproxy/templates/haproxy_listen_block.erb index 62f295469..421942cd9 100644 --- a/haproxy/templates/haproxy_listen_block.erb +++ b/haproxy/templates/haproxy_listen_block.erb @@ -1,13 +1,5 @@ listen <%= @name %> -<% Array(@ipaddress).uniq.each do |virtual_ip| (@ports.is_a?(Array) ? @ports : Array(@ports.split(","))).each do |port| -%> - bind <%= virtual_ip %>:<%= port %> -<% end end -%> -<% if @mode -%> - mode <%= @mode %> -<% end -%> -<% @options.sort.each do |key, val| -%> -<% Array(val).each do |item| -%> - <%= key %> <%= item %> -<% end -%> -<% end -%> +<%= scope.function_template(['haproxy/fragments/_bind.erb']) -%> +<%= scope.function_template(['haproxy/fragments/_mode.erb']) -%> +<%= scope.function_template(['haproxy/fragments/_options.erb']) -%> diff --git a/haproxy/templates/haproxy_userlist_block.erb b/haproxy/templates/haproxy_userlist_block.erb new file mode 100644 index 000000000..85398e5bd --- /dev/null +++ b/haproxy/templates/haproxy_userlist_block.erb @@ -0,0 +1,8 @@ + +userlist <%= @name %> +<%- Array(@groups).each do |group | -%> + group <%= group %> +<%- end -%> +<%- Array(@users).each do |user | -%> + user <%= user %> +<%- end -%> diff --git a/heat/Gemfile b/heat/Gemfile index c79324f1a..0d35201b4 100644 --- a/heat/Gemfile +++ b/heat/Gemfile @@ -2,8 +2,7 @@ source 'https://rubygems.org' group :development, :test do gem 'puppetlabs_spec_helper', :require => false - gem 'puppet-lint', '~> 1.1' - gem 'puppet-lint-param-docs', '1.1.0' + gem 'puppet-lint', '~> 0.3.2' gem 'rake', '10.1.1' end diff --git a/heat/Modulefile b/heat/Modulefile new file mode 100644 index 000000000..f3f6087c3 --- /dev/null +++ b/heat/Modulefile @@ -0,0 +1,13 @@ +name 'puppetlabs-heat' +version '4.0.0' +author 'eNovance and StackForge Contributors' +license 'Apache License 2.0' +summary 'Puppet module for OpenStack Heat' +description 'Installs and configures OpenStack Heat (Orchestration).' +project_page 'https://launchpad.net/puppet-heat' +source 'https://github.com/stackforge/puppet-heat' + +dependency 'puppetlabs/inifile', '>= 1.0.0 <2.0.0' +dependency 'puppetlabs/keystone', '>=4.0.0 <5.0.0' +dependency 'puppetlabs/stdlib', '>= 4.0.0 < 5.0.0' +dependency 'stackforge/openstacklib', '>=5.0.0' diff --git a/heat/manifests/api-cfn.pp b/heat/manifests/api-cfn.pp index 9ba359aac..6e30387b8 100644 --- a/heat/manifests/api-cfn.pp +++ b/heat/manifests/api-cfn.pp @@ -1,38 +1,5 @@ -# == Class: heat::api-cfn -# -# WARNING: Deprecated class. Use heat::api_cfn instead ! # Installs & configure the heat CloudFormation API service # -# === Parameters -# [*enabled*] -# (Optional) Should the service be enabled. -# Defaults to 'true'. -# -# [*keystone_host*] -# -# [*keystone_port*] -# -# [*keystone_protocol*] -# -# [*keystone_user*] -# -# [*keystone_tenant*] -# -# [*keystone_password*] -# -# [*keystone_ec2_uri*] -# -# [*auth_uri*] -# -# [*bind_host*] -# -# [*bind_port*] -# -# [*verbose*] -# -# [*debug*] -# -# class heat::api-cfn ( $enabled = true, $keystone_host = '127.0.0.1', diff --git a/heat/manifests/api-cloudwatch.pp b/heat/manifests/api-cloudwatch.pp index 224481133..67657b16b 100644 --- a/heat/manifests/api-cloudwatch.pp +++ b/heat/manifests/api-cloudwatch.pp @@ -1,38 +1,5 @@ -# == Class: heat::api-cloudwatch -# -# WARNING: Deprecated class. Use heat::api_cloudwatch instead ! # Installs & configure the heat CloudWatch API service # -# === Parameters -# [*enabled*] -# (Optional) Should the service be enabled. -# Defaults to 'true'. -# -# [*keystone_host*] -# -# [*keystone_port*] -# -# [*keystone_protocol*] -# -# [*keystone_user*] -# -# [*keystone_tenant*] -# -# [*keystone_password*] -# -# [*keystone_ec2_uri*] -# -# [*auth_uri*] -# -# [*bind_host*] -# -# [*bind_port*] -# -# [*verbose*] -# -# [*debug*] -# -# class heat::api-cloudwatch ( $enabled = true, $keystone_host = '127.0.0.1', diff --git a/heat/manifests/api.pp b/heat/manifests/api.pp index 2c4fdf98f..4a3dcfac2 100644 --- a/heat/manifests/api.pp +++ b/heat/manifests/api.pp @@ -1,46 +1,13 @@ -# == Class: heat::api -# # Installs & configure the heat API service # -# === Parameters -# [*enabled*] -# (optional) Should the service be enabled. -# Defaults to 'true'. -# -# [*manage_service*] -# (optional) Whether the service should be managed by Puppet. -# Defaults to 'true'. -# -# [*bind_host*] -# (Optional) Address to bind the server. Useful when -# selecting a particular network interface. -# Defaults to '0.0.0.0'. -# -# [*bind_port*] -# (Optional) The port on which the server will listen. -# Defaults to '8004'. -# -# [*workers*] -# (Optional) The port on which the server will listen. -# Defaults to '0'. -# -# [*use_ssl*] -# (Optional) Whether to use ssl or not. -# Defaults to 'false'. -# -# [*cert_file*] -# (Optional) Location of the SSL certificate file to use for SSL mode. -# Required when $use_ssl is set to 'true'. -# Defaults to 'false'. -# -# [*key_file*] -# (Optional) Location of the SSL key file to use for enabling SSL mode. -# Required when $use_ssl is set to 'true'. -# Defaults to 'false'. -# -# === Deprecated Parameters +# == Parameters +# [*enabled*] +# (optional) Should the service be enabled. +# Defaults to true # -# No Deprecated Parameters. +# [*manage_service*] +# (optional) Whether the service should be managed by Puppet. +# Defaults to true. # class heat::api ( $manage_service = true, @@ -55,13 +22,10 @@ include heat include heat::params - include heat::policy Heat_config<||> ~> Service['heat-api'] - Class['heat::policy'] -> Service['heat-api'] Package['heat-api'] -> Heat_config<||> - Package['heat-api'] -> Class['heat::policy'] Package['heat-api'] -> Service['heat-api'] if $use_ssl { diff --git a/heat/manifests/api_cfn.pp b/heat/manifests/api_cfn.pp index 18d720a62..7e5940e9f 100644 --- a/heat/manifests/api_cfn.pp +++ b/heat/manifests/api_cfn.pp @@ -1,49 +1,13 @@ -# == Class: heat::api_cfn -# -# This class deprecates heat::api-cfn. -# # Installs & configure the heat CloudFormation API service # -# === Parameters -# -# [*enabled*] -# (optional) Should the service be enabled. -# Defaults to 'true'. -# -# [*manage_service*] -# (optional) Whether the service should be managed by Puppet. -# Defaults to 'true'. -# -# [*bind_host*] -# (Optional) Address to bind the server. Useful when -# selecting a particular network interface. -# Defaults to '0.0.0.0'. -# -# [*bind_port*] -# (Optional) The port on which the server will listen. -# Defaults to '8000'. -# -# [*workers*] -# (Optional) The port on which the server will listen. -# Defaults to '0'. -# -# [*use_ssl*] -# (Optional) Whether to use ssl or not. -# Defaults to 'false'. -# -# [*cert_file*] -# (Optional) Location of the SSL certificate file to use for SSL mode. -# Required when $use_ssl is set to 'true'. -# Defaults to 'false'. -# -# [*key_file*] -# (Optional) Location of the SSL key file to use for enabling SSL mode. -# Required when $use_ssl is set to 'true'. -# Defaults to 'false'. -# -# == Deprecated Parameters +# == Parameters +# [*enabled*] +# (optional) Should the service be enabled. +# Defaults to true # -# No Deprecated Parameters. +# [*manage_service*] +# (optional) Whether the service should be managed by Puppet. +# Defaults to true. # class heat::api_cfn ( $manage_service = true, @@ -58,13 +22,10 @@ include heat include heat::params - include heat::policy Heat_config<||> ~> Service['heat-api-cfn'] - Class['heat::policy'] -> Service['heat-api-cfn'] Package['heat-api-cfn'] -> Heat_config<||> - Package['heat-api-cfn'] -> Class['heat::policy'] Package['heat-api-cfn'] -> Service['heat-api-cfn'] if $use_ssl { diff --git a/heat/manifests/api_cloudwatch.pp b/heat/manifests/api_cloudwatch.pp index a527adcce..280bc29f6 100644 --- a/heat/manifests/api_cloudwatch.pp +++ b/heat/manifests/api_cloudwatch.pp @@ -1,48 +1,13 @@ -# == Class: heat::api_cloudwatch -# -# This class deprecates heat::api-cloudwatch -# # Installs & configure the heat CloudWatch API service # -# === Parameters -# [*enabled*] -# (optional) Should the service be enabled. -# Defaults to true. -# -# [*manage_service*] -# (optional) Whether the service should be managed by Puppet. -# Defaults to true. -# -# [*bind_host*] -# (Optional) Address to bind the server. Useful when -# selecting a particular network interface. -# Defaults to '0.0.0.0'. -# -# [*bind_port*] -# (Optional) The port on which the server will listen. -# Defaults to '8003'. -# -# [*workers*] -# (Optional) The port on which the server will listen. -# Defaults to '0'. -# -# [*use_ssl*] -# (Optional) Whether to use ssl or not. -# Defaults to 'false'. -# -# [*cert_file*] -# (Optional) Location of the SSL certificate file to use for SSL mode. -# Required when $use_ssl is set to 'true'. -# Defaults to 'false'. -# -# [*key_file*] -# (Optional) Location of the SSL key file to use for enabling SSL mode. -# Required when $use_ssl is set to 'true'. -# Defaults to 'false'. -# -# == Deprecated Parameters +# == Parameters +# [*enabled*] +# (optional) Should the service be enabled. +# Defaults to true # -# No Deprecated Parameters. +# [*manage_service*] +# (optional) Whether the service should be managed by Puppet. +# Defaults to true. # class heat::api_cloudwatch ( $manage_service = true, @@ -57,13 +22,10 @@ include heat include heat::params - include heat::policy Heat_config<||> ~> Service['heat-api-cloudwatch'] - Class['heat::policy'] -> Service['heat-api-cloudwatch'] Package['heat-api-cloudwatch'] -> Heat_config<||> - Package['heat-api-cloudwatch'] -> Class['heat::policy'] Package['heat-api-cloudwatch'] -> Service['heat-api-cloudwatch'] if $use_ssl { diff --git a/heat/manifests/client.pp b/heat/manifests/client.pp index 3e1248e5b..86fc4d889 100644 --- a/heat/manifests/client.pp +++ b/heat/manifests/client.pp @@ -1,11 +1,9 @@ -# == Class: heat::client # # Installs the heat python library. # -# === Parameters -# -# [*ensure*] -# (Optional) Ensure state for package. +# == parameters +# [*ensure*] +# ensure state for pachage. # class heat::client ( $ensure = 'present' diff --git a/heat/manifests/db/mysql.pp b/heat/manifests/db/mysql.pp index 2603c91a1..f477b5938 100644 --- a/heat/manifests/db/mysql.pp +++ b/heat/manifests/db/mysql.pp @@ -1,43 +1,35 @@ -# == Class: heat::db::mysql -# # The heat::db::mysql class creates a MySQL database for heat. # It must be used on the MySQL server # -# === Parameters -# -# [*password*] -# (Mandatory) Password to connect to the database. -# Defaults to 'false'. +# == Parameters # -# [*dbname*] -# (Optional) Name of the database. -# Defaults to 'heat'. +# [*password*] +# password to connect to the database. Mandatory. # -# [*user*] -# (Optional) User to connect to the database. -# Defaults to 'heat'. +# [*dbname*] +# name of the database. Optional. Defaults to heat. # -# [*host*] -# (Optional) The default source host user is allowed to connect from. -# Defaults to '127.0.0.1' +# [*user*] +# user to connect to the database. Optional. Defaults to heat. # -# [*allowed_hosts*] -# (Optional) Other hosts the user is allowed to connect from. -# Defaults to 'undef'. +# [*host*] +# the default source host user is allowed to connect from. +# Optional. Defaults to 'localhost' # -# [*charset*] -# (Optional) The database charset. -# Defaults to 'utf8' +# [*allowed_hosts*] +# other hosts the user is allowd to connect from. +# Optional. Defaults to undef. # -# [*collate*] -# (Optional) The database collate. -# Only used with mysql modules >= 2.2. -# Defaults to 'utf8_unicode_ci' +# [*charset*] +# the database charset. Optional. Defaults to 'utf8' # -# === Deprecated Parameters +# [*collate*] +# the database collate. Optional. Only used with mysql modules +# >= 2.2 +# Defaults to 'utf8_unicode_ci' # -# [*mysql_module*] -# (Optional) Does nothing. +# [*mysql_module*] +# (optional) Deprecated. Does nothing. # class heat::db::mysql( $password = false, diff --git a/heat/manifests/engine.pp b/heat/manifests/engine.pp index eabf5700d..02c94cb8d 100644 --- a/heat/manifests/engine.pp +++ b/heat/manifests/engine.pp @@ -1,52 +1,52 @@ -# == Class: heat::engine +# Class heat::engine # # Installs & configure the heat engine service # -# === Parameters -# [*auth_encryption_key*] -# (required) Encryption key used for authentication info in database +# == parameters +# [*enabled*] +# (optional) Should the service be enabled. +# Defaults to true # -# [*enabled*] -# (optional) Should the service be enabled. -# Defaults to 'true' +# [*manage_service*] +# (optional) Whether the service should be managed by Puppet. +# Defaults to true. # -# [*manage_service*] -# (optional) Whether the service should be managed by Puppet. -# Defaults to true. +# [*heat_stack_user_role*] +# (optional) Keystone role for heat template-defined users +# Defaults to 'heat_stack_user' # -# [*heat_stack_user_role*] -# (optional) Keystone role for heat template-defined users -# Defaults to 'heat_stack_user' +# [*heat_metadata_server_url*] +# (optional) URL of the Heat metadata server +# Defaults to 'http://127.0.0.1:8000' # -# [*heat_metadata_server_url*] -# (optional) URL of the Heat metadata server -# Defaults to 'http://127.0.0.1:8000' +# [*heat_waitcondition_server_url*] +# (optional) URL of the Heat waitcondition server +# Defaults to 'http://127.0.0.1:8000/v1/waitcondition' # -# [*heat_waitcondition_server_url*] -# (optional) URL of the Heat waitcondition server -# Defaults to 'http://127.0.0.1:8000/v1/waitcondition' +# [*heat_watch_server_url*] +# (optional) URL of the Heat cloudwatch server +# Defaults to 'http://127.0.0.1:8003' # -# [*heat_watch_server_url*] -# (optional) URL of the Heat cloudwatch server -# Defaults to 'http://127.0.0.1:8003' +# [*auth_encryption_key*] +# (required) Encryption key used for authentication info in database # -# [*engine_life_check_timeout*] -# (optional) RPC timeout (in seconds) for the engine liveness check that is -# used for stack locking -# Defaults to '2' +# [*engine_life_check_timeout*] +# (optional) RPC timeout (in seconds) for the engine liveness check that is +# used for stack locking +# Defaults to '2' # -# [*trusts_delegated_roles*] -# (optional) Array of trustor roles to be delegated to heat. -# Defaults to ['heat_stack_owner'] +# [*trusts_delegated_roles*] +# (optional) Array of trustor roles to be delegated to heat. +# Defaults to ['heat_stack_owner'] # -# [*deferred_auth_method*] -# (optional) Select deferred auth method. -# Can be "password" or "trusts". -# Defaults to 'trusts' +# [*deferred_auth_method*] +# (optional) Select deferred auth method. +# Can be "password" or "trusts". +# Defaults to 'trusts' # -# [*configure_delegated_roles*] -# (optional) Whether to configure the delegated roles. -# Defaults to true +# [*configure_delegated_roles*] +# (optional) Whether to configure the delegated roles. +# Defaults to true # class heat::engine ( $auth_encryption_key, diff --git a/heat/manifests/init.pp b/heat/manifests/init.pp index d199e99b1..a48218eac 100644 --- a/heat/manifests/init.pp +++ b/heat/manifests/init.pp @@ -1,154 +1,95 @@ -# == Class: heat -# -# Heat base package & configuration -# -# === Parameters -# -# [*package_ensure*] -# (Optional) Ensure state for package. -# Defaults to 'present' -# -# [*verbose*] -# (Optional) Should the daemons log verbose messages -# Defaults to 'false' -# -# [*debug*] -# (Optional) Should the daemons log debug messages -# Defaults to 'false' -# -# [*log_dir*] -# (Optional) Directory where logs should be stored -# If set to boolean 'false', it will not log to any directory -# Defaults to '/var/log/heat' -# -# [*rpc_backend*] -# (Optional) Use these options to configure the RabbitMQ message system. -# Defaults to 'heat.openstack.common.rpc.impl_kombu' -# -# [*rabbit_host*] -# (Optional) IP or hostname of the rabbit server. -# Defaults to '127.0.0.1' -# -# [*rabbit_port*] -# (Optional) Port of the rabbit server. -# Defaults to 5672. -# -# [*rabbit_hosts*] -# (Optional) Array of host:port (used with HA queues). -# If defined, will remove rabbit_host & rabbit_port parameters from config -# Defaults to undef. -# -# [*rabbit_userid*] -# (Optional) User to connect to the rabbit server. -# Defaults to 'guest' -# -# [*rabbit_password*] -# (Optional) Password to connect to the rabbit_server. -# Defaults to empty. -# -# [*rabbit_virtual_host*] -# (Optional) Virtual_host to use. -# Defaults to '/' -# -# [*rabbit_use_ssl*] -# (Optional) Connect over SSL for RabbitMQ. -# Defaults to false -# -# [*kombu_ssl_ca_certs*] -# (Optional) SSL certification authority file (valid only if SSL enabled). -# Defaults to undef -# -# [*kombu_ssl_certfile*] -# (Optional) SSL cert file (valid only if SSL enabled). -# Defaults to undef -# -# [*kombu_ssl_keyfile*] -# (Optional) SSL key file (valid only if SSL enabled). -# Defaults to undef -# -# [*kombu_ssl_version*] -# (Optional) SSL version to use (valid only if SSL enabled). -# Valid values are TLSv1, SSLv23 and SSLv3. SSLv2 may be -# available on some distributions. -# Defaults to 'SSLv3' -# -# [*amqp_durable_queues*] -# (Optional) Use durable queues in amqp. -# Defaults to false -# -# == keystone authentication options -# -# [*auth_uri*] -# (Optional) Specifies the Authentication URI for Heat to use. -# Located in heat.conf. -# Defaults to false, -# which uses: "${keystone_protocol}://${keystone_host}:5000/v2.0" - -# [*keystone_host*] -# -# [*keystone_port*] -# -# [*keystone_protocol*] -# -# [*keystone_user*] -# -# [*keystone_tenant*] -# -# [*keystone_password*] -# -# [*keystone_ec2_uri*] -# -# ==== Various QPID options (Optional) -# -# [*qpid_hostname*] -# -# [*qpid_port*] -# -# [*qpid_username*] -# -# [*qpid_password*] -# -# [*qpid_heartbeat*] -# -# [*qpid_protocol*] -# -# [*qpid_tcp_nodelay*] -# -# [*qpid_reconnect*] -# -# [*qpid_reconnect_timeout*] -# -# [*qpid_reconnect_limit*] -# -# [*qpid_reconnect_interval*] -# -# [*qpid_reconnect_interval_min*] -# -# [*qpid_reconnect_interval_max*] -# -# [*database_connection*] -# (Optional) Url used to connect to database. -# Defaults to 'sqlite:////var/lib/heat/heat.sqlite'. +# Class heat +# +# heat base package & configuration +# +# == parameters +# [*package_ensure*] +# ensure state for package. Optional. Defaults to 'present' +# [*verbose*] +# should the daemons log verbose messages. Optional. Defaults to 'False' +# [*debug*] +# should the daemons log debug messages. Optional. Defaults to 'False' +# +# [*log_dir*] +# (optional) Directory where logs should be stored. +# If set to boolean false, it will not log to any directory. +# Defaults to '/var/log/heat'. +# +# [*rabbit_host*] +# ip or hostname of the rabbit server. Optional. Defaults to '127.0.0.1' +# [*rabbit_port*] +# port of the rabbit server. Optional. Defaults to 5672. +# [*rabbit_hosts*] +# array of host:port (used with HA queues). Optional. Defaults to undef. +# If defined, will remove rabbit_host & rabbit_port parameters from config +# [*rabbit_userid*] +# user to connect to the rabbit server. Optional. Defaults to 'guest' +# [*rabbit_password*] +# password to connect to the rabbit_server. Optional. Defaults to empty. +# [*rabbit_virtual_host*] +# virtual_host to use. Optional. Defaults to '/' +# [*rabbit_use_ssl*] +# (optional) Connect over SSL for RabbitMQ +# Defaults to false +# [*kombu_ssl_ca_certs*] +# (optional) SSL certification authority file (valid only if SSL enabled). +# Defaults to undef +# [*kombu_ssl_certfile*] +# (optional) SSL cert file (valid only if SSL enabled). +# Defaults to undef +# [*kombu_ssl_keyfile*] +# (optional) SSL key file (valid only if SSL enabled). +# Defaults to undef +# [*kombu_ssl_version*] +# (optional) SSL version to use (valid only if SSL enabled). +# Valid values are TLSv1, SSLv23 and SSLv3. SSLv2 may be +# available on some distributions. +# Defaults to 'SSLv3' +# [*amqp_durable_queues*] +# Use durable queues in amqp. Defaults to false +# +# (keystone authentication options) +# [*auth_uri*] +# Specifies the Authentication URI for Heat to use. Located in heat.conf +# Optional. Defaults to false, which uses: +# "${keystone_protocol}://${keystone_host}:5000/v2.0" +# [*keystone_host*] +# [*keystone_port*] +# [*keystone_protocol*] +# [*keystone_user*] +# [*keystone_tenant*] +# [*keystone_password*] +# [*keystone_ec2_uri*] +# +# (optional) various QPID options +# [*qpid_hostname*] +# [*qpid_port*] +# [*qpid_username*] +# [*qpid_password*] +# [*qpid_heartbeat*] +# [*qpid_protocol*] +# [*qpid_tcp_nodelay*] +# [*qpid_reconnect*] +# [*qpid_reconnect_timeout*] +# [*qpid_reconnect_limit*] +# [*qpid_reconnect_interval*] +# [*qpid_reconnect_interval_min*] +# [*qpid_reconnect_interval_max*] # # [*database_idle_timeout*] -# (Optional) Timeout before idle db connections are reaped. -# Defaults to 3600. +# (optional) Timeout before idle db connections are reaped. +# Defaults to 3600 # # [*use_syslog*] -# (Optional) Use syslog for logging. -# Defaults to false. +# (optional) Use syslog for logging +# Defaults to false # # [*log_facility*] -# (Optional) Syslog facility to receive log lines. -# Defaults to LOG_USER. -# -# === Deprecated ParameterS +# (optional) Syslog facility to receive log lines +# Defaults to LOG_USER # # [*mysql_module*] -# Deprecated. Does nothing. -# -# [*sql_connection*] -# Deprecated. Use database_connection instead. +# (optional) Deprecated. Does nothing. # class heat( $auth_uri = false, @@ -189,13 +130,12 @@ $qpid_reconnect_interval_min = 0, $qpid_reconnect_interval_max = 0, $qpid_reconnect_interval = 0, - $database_connection = 'sqlite:////var/lib/heat/heat.sqlite', + $sql_connection = false, $database_idle_timeout = 3600, $use_syslog = false, $log_facility = 'LOG_USER', #Deprecated parameters $mysql_module = undef, - $sql_connection = undef, ) { include heat::params @@ -234,16 +174,16 @@ } file { '/etc/heat/': - ensure => directory, - owner => 'heat', - group => 'heat', - mode => '0750', + ensure => directory, + owner => 'heat', + group => 'heat', + mode => '0750', } file { '/etc/heat/heat.conf': - owner => 'heat', - group => 'heat', - mode => '0640', + owner => 'heat', + group => 'heat', + mode => '0640', } package { 'heat-common': @@ -372,17 +312,11 @@ } if $sql_connection { - warning('The sql_connection parameter is deprecated, use database_connection instead.') - $database_connection_real = $sql_connection - } else { - $database_connection_real = $database_connection - } - if $database_connection_real { - validate_re($database_connection_real, + validate_re($sql_connection, '(sqlite|mysql|postgresql):\/\/(\S+:\S+@\S+\/\S+)?') - case $database_connection_real { + case $sql_connection { /^mysql:\/\//: { $backend_package = false require mysql::bindings @@ -407,11 +341,8 @@ } heat_config { - 'database/connection': - value => $database_connection_real, - secret => true; - 'database/idle_timeout': - value => $database_idle_timeout; + 'database/connection': value => $sql_connection, secret => true; + 'database/idle_timeout': value => $database_idle_timeout; } Heat_config['database/connection'] ~> Exec['heat-dbsync'] diff --git a/heat/manifests/keystone/auth.pp b/heat/manifests/keystone/auth.pp index cb8fef84e..3302cca81 100644 --- a/heat/manifests/keystone/auth.pp +++ b/heat/manifests/keystone/auth.pp @@ -1,82 +1,56 @@ -# == Class: heat::keystone::auth +# == Class: heat::heat::auth # # Configures heat user, service and endpoint in Keystone. # # === Parameters +# # [*password*] -# (Required) Password for heat user. +# Password for heat user. Required. # # [*email*] -# (Optional) Email for heat user. -# Defaults to 'heat@localhost'. +# Email for heat user. Optional. Defaults to 'heat@localhost'. # # [*auth_name*] -# (Optional) Username for heat service. -# Defaults to 'heat'. +# Username for heat service. Optional. Defaults to 'heat'. # # [*configure_endpoint*] -# (Optional) Should heat endpoint be configured? -# Defaults to 'true'. +# Should heat endpoint be configured? Optional. Defaults to 'true'. # # [*configure_user*] -# (Optional) Whether to create the service user. -# Defaults to 'true'. +# Whether to create the service user. Defaults to 'true'. # # [*configure_user_role*] -# (Optional) Whether to configure the admin role for the service user. -# Defaults to 'true'. +# Whether to configure the admin role for teh service user. Defaults to 'true'. # # [*service_name*] -# (Optional) Name of the service. -# Defaults to the value of auth_name. +# Name of the service. Options. Defaults to the value of auth_name. # # [*service_type*] -# (Optional) Type of service. -# Defaults to 'orchestration'. +# Type of service. Optional. Defaults to 'orchestration'. # # [*public_address*] -# (Optional) Public address for endpoint. -# Defaults to '127.0.0.1'. +# Public address for endpoint. Optional. Defaults to '127.0.0.1'. # # [*admin_address*] -# (Optional) Admin address for endpoint. -# Defaults to '127.0.0.1'. +# Admin address for endpoint. Optional. Defaults to '127.0.0.1'. # # [*internal_address*] -# (Optional) Internal address for endpoint. -# Defaults to '127.0.0.1'. +# Internal address for endpoint. Optional. Defaults to '127.0.0.1'. # # [*version*] -# (Optional) Version of API to use. -# Defaults to 'v1' +# Version of API to use. Optional. Defaults to 'v1' # # [*port*] -# (Optional) Port for endpoint. -# Defaults to '8004'. +# Port for endpoint. Optional. Defaults to '8004'. # # [*region*] -# (Optional) Region for endpoint. -# Defaults to 'RegionOne'. +# Region for endpoint. Optional. Defaults to 'RegionOne'. # # [*tenant*] -# (Optional) Tenant for heat user. -# Defaults to 'services'. +# Tenant for heat user. Optional. Defaults to 'services'. # # [*protocol*] -# (Optional) Protocol for public endpoint. -# Defaults to 'http'. -# -# [*public_protocol*] -# (Optional) Protocol for public endpoint. -# Defaults to 'http'. -# -# [*admin_protocol*] -# (Optional) Protocol for admin endpoint -# Defaults to 'http'. -# -# [*internal_protocol*] -# (Optional) Protocol for internal endpoint -# Defaults to 'http' +# Protocol for public endpoint. Optional. Defaults to 'http'. # class heat::keystone::auth ( $password = false, @@ -121,8 +95,8 @@ Service <| name == 'heat-api' |> keystone_user_role { "${auth_name}@${tenant}": - ensure => present, - roles => ['admin'], + ensure => present, + roles => ['admin'], } } diff --git a/heat/manifests/keystone/auth_cfn.pp b/heat/manifests/keystone/auth_cfn.pp index ea685dd8e..36c0fa083 100644 --- a/heat/manifests/keystone/auth_cfn.pp +++ b/heat/manifests/keystone/auth_cfn.pp @@ -1,78 +1,56 @@ -# == Class: heat::keystone::auth_cfn +# == Class: heat::heat::auth_cfn # # Configures heat-api-cfn user, service and endpoint in Keystone. # # === Parameters +# # [*password*] -# (Mandatory) Password for heat-cfn user. +# Password for heat-cfn user. Required. # # [*email*] -# (Optional) Email for heat-cfn user. -# Defaults to 'heat@localhost'. +# Email for heat-cfn user. Optional. Defaults to 'heat@localhost'. # # [*auth_name*] -# (Optional) Username for heat-cfn service. -# Defaults to 'heat'. +# Username for heat-cfn service. Optional. Defaults to 'heat'. # # [*configure_endpoint*] -# (Optional) Should heat-cfn endpoint be configured? -# Defaults to 'true'. +# Should heat-cfn endpoint be configured? Optional. Defaults to 'true'. # # [*configure_user*] -# (Optional) Whether to create the service user. -# Defaults to 'true'. +# Whether to create the service user. Defaults to 'true'. # # [*configure_user_role*] -# (Optional) Whether to configure the admin role for the service user. -# Defaults to 'true'. +# Whether to configure the admin role for the service user. Defaults to 'true'. # # [*service_name*] -# (Optional) Name of the service. -# Defaults to the value of auth_name. +# Name of the service. Optional. Defaults to the value of auth_name. # # [*service_type*] -# (Optional) Type of service. -# Defaults to 'cloudformation'. +# Type of service. Optional. Defaults to 'cloudformation'. # # [*public_address*] -# (Optional) Public address for endpoint. -# Defaults to '127.0.0.1'. +# Public address for endpoint. Optional. Defaults to '127.0.0.1'. # # [*admin_address*] -# (Optional) Admin address for endpoint. -# Defaults to '127.0.0.1'. +# Admin address for endpoint. Optional. Defaults to '127.0.0.1'. # # [*internal_address*] -# (Optional) Internal address for endpoint. -# Defaults to '127.0.0.1'. +# Internal address for endpoint. Optional. Defaults to '127.0.0.1'. # # [*port*] -# (Optional) Port for endpoint. -# Defaults to '8000'. +# Port for endpoint. Optional. Defaults to '8000'. # # [*version*] -# (Optional) Version for API. -# Defaults to 'v1' +# Version for API. Optional. Defaults to 'v1' # [*region*] -# (Optional) Region for endpoint. -# Defaults to 'RegionOne'. +# Region for endpoint. Optional. Defaults to 'RegionOne'. # # [*tenant*] -# (Optional) Tenant for heat-cfn user. -# Defaults to 'services'. -# -# [*public_protocol*] -# (Optional) Protocol for public endpoint. -# Defaults to 'http'. -# -# [*admin_protocol*] -# (Optional) Protocol for admin endpoint. -# Defaults to 'http'. +# Tenant for heat-cfn user. Optional. Defaults to 'services'. # -# [*internal_protocol*] -# (Optional) Protocol for internal endpoint. -# Defaults to 'http'. +# [*protocol*] +# Protocol for public endpoint. Optional. Defaults to 'http'. # class heat::keystone::auth_cfn ( $password = false, @@ -117,8 +95,8 @@ Service <| name == 'heat-api-cfn' |> keystone_user_role { "${auth_name}@${tenant}": - ensure => present, - roles => ['admin'], + ensure => present, + roles => ['admin'], } } diff --git a/heat/manifests/logging.pp b/heat/manifests/logging.pp index 70af26351..d18b5c2a7 100644 --- a/heat/manifests/logging.pp +++ b/heat/manifests/logging.pp @@ -1,70 +1,70 @@ -# == Class heat::logging +# Class heat::logging # # heat extended logging configuration # -# === Parameters +# == parameters # -# [*logging_context_format_string*] -# (optional) Format string to use for log messages with context. -# Defaults to undef. -# Example: '%(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s\ -# [%(request_id)s %(user_identity)s] %(instance)s%(message)s' +# [*logging_context_format_string*] +# (optional) Format string to use for log messages with context. +# Defaults to undef. +# Example: '%(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s\ +# [%(request_id)s %(user_identity)s] %(instance)s%(message)s' # -# [*logging_default_format_string*] -# (optional) Format string to use for log messages without context. -# Defaults to undef. -# Example: '%(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s\ -# [-] %(instance)s%(message)s' +# [*logging_default_format_string*] +# (optional) Format string to use for log messages without context. +# Defaults to undef. +# Example: '%(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s\ +# [-] %(instance)s%(message)s' # -# [*logging_debug_format_suffix*] -# (optional) Formatted data to append to log format when level is DEBUG. -# Defaults to undef. -# Example: '%(funcName)s %(pathname)s:%(lineno)d' +# [*logging_debug_format_suffix*] +# (optional) Formatted data to append to log format when level is DEBUG. +# Defaults to undef. +# Example: '%(funcName)s %(pathname)s:%(lineno)d' # -# [*logging_exception_prefix*] -# (optional) Prefix each line of exception output with this format. -# Defaults to undef. -# Example: '%(asctime)s.%(msecs)03d %(process)d TRACE %(name)s %(instance)s' +# [*logging_exception_prefix*] +# (optional) Prefix each line of exception output with this format. +# Defaults to undef. +# Example: '%(asctime)s.%(msecs)03d %(process)d TRACE %(name)s %(instance)s' # -# [*log_config_append*] -# The name of an additional logging configuration file. -# Defaults to undef. -# See https://docs.python.org/2/howto/logging.html +# [*log_config_append*] +# The name of an additional logging configuration file. +# Defaults to undef. +# See https://docs.python.org/2/howto/logging.html # -# [*default_log_levels*] -# (optional) Hash of logger (keys) and level (values) pairs. -# Defaults to undef. -# Example: -# {'amqp' => 'WARN', 'amqplib' => 'WARN', 'boto' => 'WARN', -# 'qpid' => 'WARN', 'sqlalchemy' => 'WARN', 'suds' => 'INFO', -# 'iso8601' => 'WARN', -# 'requests.packages.urllib3.connectionpool' => 'WARN' } +# [*default_log_levels*] +# (optional) Hash of logger (keys) and level (values) pairs. +# Defaults to undef. +# Example: +# { 'amqp' => 'WARN', 'amqplib' => 'WARN', 'boto' => 'WARN', +# 'qpid' => 'WARN', 'sqlalchemy' => 'WARN', 'suds' => 'INFO', +# 'iso8601' => 'WARN', +# 'requests.packages.urllib3.connectionpool' => 'WARN' } # -# [*publish_errors*] -# (optional) Publish error events (boolean value). -# Defaults to undef (false if unconfigured). +# [*publish_errors*] +# (optional) Publish error events (boolean value). +# Defaults to undef (false if unconfigured). # -# [*fatal_deprecations*] -# (optional) Make deprecations fatal (boolean value) -# Defaults to undef (false if unconfigured). +# [*fatal_deprecations*] +# (optional) Make deprecations fatal (boolean value) +# Defaults to undef (false if unconfigured). # -# [*instance_format*] -# (optional) If an instance is passed with the log message, format it -# like this (string value). -# Defaults to undef. -# Example: '[instance: %(uuid)s] ' +# [*instance_format*] +# (optional) If an instance is passed with the log message, format it +# like this (string value). +# Defaults to undef. +# Example: '[instance: %(uuid)s] ' # -# [*instance_uuid_format*] -# (optional) If an instance UUID is passed with the log message, format -# It like this (string value). -# Defaults to undef. -# Example: instance_uuid_format='[instance: %(uuid)s] ' +# [*instance_uuid_format*] +# (optional) If an instance UUID is passed with the log message, format +# it like this (string value). +# Defaults to undef. +# Example: instance_uuid_format='[instance: %(uuid)s] ' + +# [*log_date_format*] +# (optional) Format string for %%(asctime)s in log records. +# Defaults to undef. +# Example: 'Y-%m-%d %H:%M:%S' -# [*log_date_format*] -# (optional) Format string for %%(asctime)s in log records. -# Defaults to undef. -# Example: 'Y-%m-%d %H:%M:%S' -# class heat::logging( $logging_context_format_string = undef, $logging_default_format_string = undef, diff --git a/heat/manifests/params.pp b/heat/manifests/params.pp index a1676fbc5..11504a390 100644 --- a/heat/manifests/params.pp +++ b/heat/manifests/params.pp @@ -1,5 +1,3 @@ -# == Class: heat::params -# # Parameters for puppet-heat # class heat::params { diff --git a/heat/manifests/policy.pp b/heat/manifests/policy.pp deleted file mode 100644 index 7a64f0081..000000000 --- a/heat/manifests/policy.pp +++ /dev/null @@ -1,34 +0,0 @@ -# == Class: heat::policy -# -# Configure the heat policies -# -# == Parameters -# -# [*policies*] -# (optional) Set of policies to configure for heat. -# Defaults to empty hash. -# -# Example: -# { -# 'heat-context_is_admin' => {'context_is_admin' => 'true'}, -# 'heat-default' => {'default' => 'rule:admin_or_owner'} -# } -# -# [*policy_path*] -# (optional) Path to the heat policy.json file. -# Defaults to '/etc/heat/policy.json'. -# -class heat::policy ( - $policies = {}, - $policy_path = '/etc/heat/policy.json', -) { - - validate_hash($policies) - - Openstacklib::Policy::Base { - file_path => $policy_path, - } - - create_resources('openstacklib::policy::base', $policies) - -} diff --git a/heat/metadata.json b/heat/metadata.json deleted file mode 100644 index 199c7626b..000000000 --- a/heat/metadata.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "stackforge-heat", - "version": "5.0.0", - "author": "eNovance and StackForge Contributors", - "summary": "Puppet module for OpenStack Heat", - "license": "Apache License 2.0", - "source": "git://github.com/stackforge/puppet-heat.git", - "project_page": "https://launchpad.net/puppet-heat", - "issues_url": "https://bugs.launchpad.net/puppet-heat", - "requirements": [ - { "name": "pe","version_requirement": "3.x" }, - { "name": "puppet","version_requirement": "3.x" } - ], - "operatingsystem_support": [ - { - "operatingsystem": "Debian", - "operatingsystemrelease": ["7"] - }, - { - "operatingsystem": "Fedora", - "operatingsystemrelease": ["20"] - }, - { - "operatingsystem": "RedHat", - "operatingsystemrelease": ["6.5","7"] - }, - { - "operatingsystem": "Ubuntu", - "operatingsystemrelease": ["12.04","14.04"] - } - ], - "description": "Installs and configures OpenStack Heat (Orchestration).", - "dependencies": [ - { "name": "puppetlabs/inifile", "version_requirement": ">=1.0.0 <2.0.0" }, - { "name": "stackforge/keystone", "version_requirement": ">=5.0.0 <6.0.0" }, - { "name": "puppetlabs/stdlib", "version_requirement": ">=4.0.0 <5.0.0" }, - { "name": "stackforge/openstacklib", "version_requirement": ">=5.0.0" } - ] -} diff --git a/heat/spec/classes/heat_api_cfn_spec.rb b/heat/spec/classes/heat_api_cfn_spec.rb index 11dadfbd4..53c3f7326 100644 --- a/heat/spec/classes/heat_api_cfn_spec.rb +++ b/heat/spec/classes/heat_api_cfn_spec.rb @@ -16,7 +16,6 @@ it { should contain_class('heat') } it { should contain_class('heat::params') } - it { should contain_class('heat::policy') } it { should contain_heat_config('heat_api_cfn/bind_host').with_value( params[:bind_host] ) } it { should contain_heat_config('heat_api_cfn/bind_port').with_value( params[:bind_port] ) } diff --git a/heat/spec/classes/heat_api_cloudwatch_spec.rb b/heat/spec/classes/heat_api_cloudwatch_spec.rb index 0fc412a88..dd9f078fc 100644 --- a/heat/spec/classes/heat_api_cloudwatch_spec.rb +++ b/heat/spec/classes/heat_api_cloudwatch_spec.rb @@ -16,7 +16,6 @@ it { should contain_class('heat') } it { should contain_class('heat::params') } - it { should contain_class('heat::policy') } it { should contain_heat_config('heat_api_cloudwatch/bind_host').with_value( params[:bind_host] ) } it { should contain_heat_config('heat_api_cloudwatch/bind_port').with_value( params[:bind_port] ) } diff --git a/heat/spec/classes/heat_api_spec.rb b/heat/spec/classes/heat_api_spec.rb index d9caab4b8..20d4caa16 100644 --- a/heat/spec/classes/heat_api_spec.rb +++ b/heat/spec/classes/heat_api_spec.rb @@ -16,7 +16,6 @@ it { should contain_class('heat') } it { should contain_class('heat::params') } - it { should contain_class('heat::policy') } it { should contain_heat_config('heat_api/bind_host').with_value( params[:bind_host] ) } it { should contain_heat_config('heat_api/bind_port').with_value( params[:bind_port] ) } diff --git a/heat/spec/classes/heat_init_spec.rb b/heat/spec/classes/heat_init_spec.rb index 36b1942de..82daadf4c 100644 --- a/heat/spec/classes/heat_init_spec.rb +++ b/heat/spec/classes/heat_init_spec.rb @@ -13,7 +13,7 @@ :rabbit_userid => 'guest', :rabbit_password => '', :rabbit_virtual_host => '/', - :database_connection => 'mysql://user@host/database', + :sql_connection => 'mysql://user@host/database', :database_idle_timeout => 3600, :auth_uri => 'http://127.0.0.1:5000/v2.0', :keystone_ec2_uri => 'http://127.0.0.1:5000/v2.0/ec2tokens', @@ -136,24 +136,19 @@ it { should contain_heat_config('DEFAULT/log_dir').with_ensure('absent') } end - it 'configures database_connection' do - should contain_heat_config('database/connection').with_value( params[:database_connection] ) + it 'configures sql_connection' do + should contain_heat_config('database/connection').with_value( params[:sql_connection] ) end it 'configures database_idle_timeout' do should contain_heat_config('database/idle_timeout').with_value( params[:database_idle_timeout] ) end - context("failing if database_connection is invalid") do - before { params[:database_connection] = 'foo://foo:bar@baz/moo' } + context("failing if sql_connection is invalid") do + before { params[:sql_connection] = 'foo://foo:bar@baz/moo' } it { expect { should raise_error(Puppet::Error) } } end - context("with deprecated sql_connection parameter") do - before { params[:sql_connection] = 'mysql://a:b@c/d' } - it { should contain_heat_config('database/connection').with_value( params[:sql_connection] )} - end - it 'configures keystone_ec2_uri' do should contain_heat_config('ec2authtoken/auth_uri').with_value( params[:keystone_ec2_uri] ) end diff --git a/heat/spec/classes/heat_policy_spec.rb b/heat/spec/classes/heat_policy_spec.rb deleted file mode 100644 index d0c974dbb..000000000 --- a/heat/spec/classes/heat_policy_spec.rb +++ /dev/null @@ -1,41 +0,0 @@ -require 'spec_helper' - -describe 'heat::policy' do - - shared_examples_for 'heat policies' do - let :params do - { - :policy_path => '/etc/heat/policy.json', - :policies => { - 'context_is_admin' => { - 'key' => 'context_is_admin', - 'value' => 'foo:bar' - } - } - } - end - - it 'set up the policies' do - should contain_openstacklib__policy__base('context_is_admin').with({ - :key => 'context_is_admin', - :value => 'foo:bar' - }) - end - end - - context 'on Debian platforms' do - let :facts do - { :osfamily => 'Debian' } - end - - it_configures 'heat policies' - end - - context 'on RedHat platforms' do - let :facts do - { :osfamily => 'RedHat' } - end - - it_configures 'heat policies' - end -end diff --git a/hiera/CHANGELOG b/hiera/CHANGELOG new file mode 100644 index 000000000..8cab63df8 --- /dev/null +++ b/hiera/CHANGELOG @@ -0,0 +1,27 @@ +2014-05-01 Release 1.0.2 +- Remove swap files from package + +2014-03-25 Release 1.0.1 +Bugfixes: +- Readme tweak +- Use template instance variables to remove warnings + +2014-02-27 Release 1.0.0 + +Features: +- `backends` parameter for an array of hiera backends +- `extra_config` parameter for a string of extra yaml config + +Bugfixes: +- Correct the yaml formatting + +2013-06-17 Release 0.3.1 +Bugfixes: +- Docs! + +2013-06-17 Release 0.3.0 +Features: +- PE + POSS support + +Bugfixes: +- Only ensure datadir if it does not have %{.*} diff --git a/hiera/Modulefile b/hiera/Modulefile new file mode 100644 index 000000000..1b92c5084 --- /dev/null +++ b/hiera/Modulefile @@ -0,0 +1,11 @@ +name 'hunner-hiera' +version '1.0.2' +source 'UNKNOWN' +author 'hunner' +license 'BSD' +summary 'Deploy hiera.yaml with hierarchy, and datadir' +description '' +project_page 'https://github.com/hunner/hunner-hiera' + +## Add dependencies, if any: +# dependency 'username/name', '>= 1.2.0' diff --git a/hiera/README.md b/hiera/README.md new file mode 100644 index 000000000..67e6f5421 --- /dev/null +++ b/hiera/README.md @@ -0,0 +1,33 @@ +# Hiera Puppet + +## Description +This module configures [Hiera](https://github.com/puppetlabs/hiera) for Puppet. + +## Usage +This class will write out a hiera.yaml file in either /etc/puppetlabs/puppet/hiera.yaml or /etc/puppet/hiera.yaml (depending on if the node is running Puppet Enterprise or not). + +```puppet +class { 'hiera': + hierarchy => [ + '%{environment}/%{calling_class}', + '%{environment}', + 'common', + ], +} +``` + +The resulting output in /etc/puppet/hiera.yaml: +```yaml +--- +:backends: + - yaml +:logger: console +:hierarchy: + - "%{environment}/%{calling_class}" + - "%{environment}" + - common + +:yaml: + :datadir: /etc/puppet/hieradata +``` + diff --git a/hiera/manifests/eyaml.pp b/hiera/manifests/eyaml.pp new file mode 100644 index 000000000..dedc65c81 --- /dev/null +++ b/hiera/manifests/eyaml.pp @@ -0,0 +1,53 @@ +# == Class: hiera::eyaml +# +# This class installs and configures hiera-eyaml +# +# === Authors: +# +# Terri Haber +# +# === Copyright: +# +# Copyright (C) 2014 Terri Haber, unless otherwise noted. +# +class hiera::eyaml ( + $provider = $hiera::params::provider, + $owner = $hiera::params::owner, + $group = $hiera::params::group, + $cmdpath = $hiera::params::cmdpath, + $confdir = $hiera::params::confdir +) inherits hiera::params { + + package { 'hiera-eyaml': + ensure => installed, + provider => $provider, + } + + file { "${confdir}/keys": + ensure => directory, + owner => $owner, + group => $group, + before => Exec['createkeys'], + } + + exec { 'createkeys': + user => $owner, + cwd => $confdir, + command => "${cmdpath}/eyaml createkeys", + path => $cmdpath, + creates => "${confdir}/keys/private_key.pkcs7.pem", + require => Package['hiera-eyaml'], + } + + $eyaml_files = [ + "${confdir}/keys/private_key.pkcs7.pem", + "${confdir}/keys/public_key.pkcs7.pem"] + + file { $eyaml_files: + ensure => file, + mode => '0604', + owner => $owner, + group => $group, + require => Exec['createkeys'], + } +} diff --git a/hiera/manifests/init.pp b/hiera/manifests/init.pp new file mode 100644 index 000000000..265e49cd5 --- /dev/null +++ b/hiera/manifests/init.pp @@ -0,0 +1,110 @@ +# == Class: hiera +# +# This class handles installing the hiera.yaml for Puppet's use. +# +# === Parameters: +# +# [*hierarchy*] +# Hiera hierarchy. +# Default: empty +# +# [*backends*] +# Hiera backends. +# Default: ['yaml'] +# +# [*hiera_yaml*] +# Heira config file. +# Default: auto-set, platform specific +# +# [*datadir*] +# Directory in which hiera will start looking for databases. +# Default: auto-set, platform specific +# +# [*owner*] +# Owner of the files. +# Default: auto-set, platform specific +# +# [*group*] +# Group owner of the files. +# Default: auto-set, platform specific +# +# [*extra_config*] +# An extra string fragment of YAML to append to the config file. +# Useful for configuring backend-specific parameters. +# Default: '' +# +# [*eyaml*] +# Install and configure hiera-eyaml +# Default: false +# +# [*eyaml_datadir*] +# Location of eyaml-specific data +# Default: Same as datadir +# +# === Actions: +# +# Installs either /etc/puppet/hiera.yaml or /etc/puppetlabs/puppet/hiera.yaml. +# Links /etc/hiera.yaml to the above file. +# Creates $datadir. +# +# === Requires: +# +# Nothing. +# +# === Sample Usage: +# +# class { 'hiera': +# hierarchy => [ +# '%{environment}', +# 'common', +# ], +# } +# +# === Authors: +# +# Hunter Haugen +# Mike Arnold +# Terri Haber +# +# === Copyright: +# +# Copyright (C) 2012 Hunter Haugen, unless otherwise noted. +# Copyright (C) 2013 Mike Arnold, unless otherwise noted. +# Copyright (C) 2014 Terri Haber, unless otherwise noted. +# +class hiera ( + $hierarchy = [], + $backends = $hiera::params::backends, + $hiera_yaml = $hiera::params::hiera_yaml, + $datadir = $hiera::params::datadir, + $owner = $hiera::params::owner, + $group = $hiera::params::group, + $eyaml = false, + $eyaml_datadir = $hiera::params::datadir, + $confdir = $hiera::params::confdir, + $extra_config = '', +) inherits hiera::params { + File { + owner => $owner, + group => $group, + mode => '0644', + } + if $datadir !~ /%\{.*\}/ { + file { $datadir: + ensure => directory, + } + } + if $eyaml { + require hiera::eyaml + } + # Template uses $hierarchy, $datadir + file { $hiera_yaml: + ensure => present, + content => template('hiera/hiera.yaml.erb'), + } + # Symlink for hiera command line tool + file { '/etc/hiera.yaml': + ensure => symlink, + target => $hiera_yaml, + } +} diff --git a/hiera/manifests/params.pp b/hiera/manifests/params.pp new file mode 100644 index 000000000..75119ae81 --- /dev/null +++ b/hiera/manifests/params.pp @@ -0,0 +1,35 @@ +# == Class: hiera::params +# +# This class handles OS-specific configuration of the hiera module. It +# looks for variables in top scope (probably from an ENC such as Dashboard). If +# the variable doesn't exist in top scope, it falls back to a hard coded default +# value. +# +# === Authors: +# +# Mike Arnold +# +# === Copyright: +# +# Copyright (C) 2013 Mike Arnold, unless otherwise noted. +# +class hiera::params { + if $::puppetversion =~ /Puppet Enterprise/ { + $hiera_yaml = '/etc/puppetlabs/puppet/hiera.yaml' + $datadir = '/etc/puppetlabs/puppet/hieradata' + $owner = 'pe-puppet' + $group = 'pe-puppet' + $provider = 'pe_gem' + $cmdpath = '/opt/puppet/bin' + $confdir = '/etc/puppetlabs/puppet' + } else { + $hiera_yaml = '/etc/puppet/hiera.yaml' + $datadir = '/etc/puppet/hieradata' + $owner = 'puppet' + $group = 'puppet' + $provider = 'gem' + $cmdpath = '/usr/bin/puppet' + $confdir = '/etc/puppet' + } + $backends = ['yaml'] +} diff --git a/hiera/templates/hiera.yaml.erb b/hiera/templates/hiera.yaml.erb new file mode 100644 index 000000000..825854d5e --- /dev/null +++ b/hiera/templates/hiera.yaml.erb @@ -0,0 +1,21 @@ +--- +:backends: +<%= @backends.to_yaml.split("\n")[1..-1].join("\n") %> +<% if @eyaml -%> + - eyaml +<% end -%> +:logger: console +:hierarchy: +<%= @hierarchy.to_yaml.split("\n")[1..-1].join("\n") %> + +:yaml: + :datadir: <%= @datadir %> + +<% if @eyaml %> +:eyaml: + :datadir: <%= @eyaml_datadir %> + :pkcs7_private_key: <%= @confdir %>/keys/private_key.pkcs7.pem + :pkcs7_public_key: <%= @confdir %>/keys/public_key.pkcs7.pem +<% end %> + +<%= @extra_config %> diff --git a/hiera/tests/init.pp b/hiera/tests/init.pp new file mode 100644 index 000000000..48d9986cb --- /dev/null +++ b/hiera/tests/init.pp @@ -0,0 +1,8 @@ +class { 'hiera': + datadir => '/etc/puppetlabs/puppet/hieradata', + hierarchy => [ + '%{environment}/%{calling_class}', + '%{environment}', + 'common', + ], +} diff --git a/horizon/manifests/wsgi/apache.pp b/horizon/manifests/wsgi/apache.pp index 935530090..4087f1959 100644 --- a/horizon/manifests/wsgi/apache.pp +++ b/horizon/manifests/wsgi/apache.pp @@ -142,8 +142,7 @@ require => [ File[$::horizon::params::logdir], Package['horizon'] ], } - $default_vhost_conf = { - ip => $bind_address, + $default_vhost_conf_no_ip = { servername => $servername, serveraliases => os_any2array($final_server_aliases), docroot => '/var/www/', @@ -170,8 +169,19 @@ redirectmatch_status => 'permanent', } + # Only add the 'ip' element to the $default_vhost_conf hash if it was explicitly + # specified in the instantiation of the class. This is because ip => undef gets + # changed to ip => '' via the Puppet function API when ensure_resource is called. + # See https://bugs.launchpad.net/puppet-horizon/+bug/1371345 + if $bind_address { + $default_vhost_conf = merge($default_vhost_conf_no_ip, { ip => $bind_address }) + } else { + $default_vhost_conf = $default_vhost_conf_no_ip + } + ensure_resource('apache::vhost', $vhost_conf_name, merge ($default_vhost_conf, $extra_params, { - redirectmatch_regexp => "${redirect_match} ${redirect_url}", + redirectmatch_regexp => $redirect_match, + redirectmatch_dest => $redirect_url, })) ensure_resource('apache::vhost', $vhost_ssl_conf_name, merge ($default_vhost_conf, $extra_params, { access_log_file => 'horizon_ssl_access.log', @@ -182,7 +192,8 @@ ensure => $ensure_ssl_vhost, wsgi_daemon_process => 'horizon-ssl', wsgi_process_group => 'horizon-ssl', - redirectmatch_regexp => "^/$ ${::horizon::params::root_url}" + redirectmatch_regexp => '^/$', + redirectmatch_dest => $::horizon::params::root_url, })) } diff --git a/horizon/metadata.json b/horizon/metadata.json index e7576515a..ce68ab167 100644 --- a/horizon/metadata.json +++ b/horizon/metadata.json @@ -31,7 +31,7 @@ ], "description": "Installs and configures OpenStack Horizon (Dashboard).", "dependencies": [ - { "name": "puppetlabs/apache", "version_requirement": ">=1.1.2 <2.0.0" }, + { "name": "puppetlabs/apache", "version_requirement": ">=1.2.0 <2.0.0" }, { "name": "puppetlabs/stdlib", "version_requirement": ">=4.0.0 <5.0.0" }, { "name": "saz/memcached", "version_requirement": ">=2.0.2 <3.0.0" } ] diff --git a/horizon/spec/classes/horizon_wsgi_apache_spec.rb b/horizon/spec/classes/horizon_wsgi_apache_spec.rb index 332eb311e..7a7b9ef15 100644 --- a/horizon/spec/classes/horizon_wsgi_apache_spec.rb +++ b/horizon/spec/classes/horizon_wsgi_apache_spec.rb @@ -44,7 +44,8 @@ 'docroot' => '/var/www/', 'ssl' => 'false', 'redirectmatch_status' => 'permanent', - 'redirectmatch_regexp' => "^/$ #{platforms_params[:root_url]}", + 'redirectmatch_regexp' => '^/$', + 'redirectmatch_dest' => platforms_params[:root_url], 'wsgi_script_aliases' => { platforms_params[:root_url] => '/usr/share/openstack-dashboard/openstack_dashboard/wsgi/django.wsgi' }, 'wsgi_process_group' => platforms_params[:wsgi_group], 'wsgi_daemon_process' => platforms_params[:wsgi_group], @@ -76,7 +77,8 @@ 'docroot' => '/var/www/', 'ssl' => 'false', 'redirectmatch_status' => 'permanent', - 'redirectmatch_regexp' => "^/$ #{platforms_params[:root_url]}", + 'redirectmatch_regexp' => '^/$', + 'redirectmatch_dest' => platforms_params[:root_url], 'wsgi_script_aliases' => { platforms_params[:root_url] => '/usr/share/openstack-dashboard/openstack_dashboard/wsgi/django.wsgi' }, 'wsgi_process_group' => platforms_params[:wsgi_group], 'wsgi_daemon_process' => platforms_params[:wsgi_group], @@ -112,7 +114,8 @@ 'ssl_key' => '/etc/pki/tls/private/httpd.key', 'ssl_ca' => '/etc/pki/tls/certs/ca.crt', 'redirectmatch_status' => 'permanent', - 'redirectmatch_regexp' => "^/$ #{platforms_params[:root_url]}", + 'redirectmatch_regexp' => '^/$', + 'redirectmatch_dest' => platforms_params[:root_url], 'wsgi_process_group' => 'horizon-ssl', 'wsgi_daemon_process' => 'horizon-ssl', 'wsgi_script_aliases' => { platforms_params[:root_url] => '/usr/share/openstack-dashboard/openstack_dashboard/wsgi/django.wsgi' } @@ -127,7 +130,8 @@ 'docroot' => '/var/www/', 'ssl' => 'false', 'redirectmatch_status' => 'permanent', - 'redirectmatch_regexp' => '(.*) https://some.host.tld', + 'redirectmatch_regexp' => '(.*)', + 'redirectmatch_dest' => 'https://some.host.tld', 'wsgi_process_group' => platforms_params[:wsgi_group], 'wsgi_daemon_process' => platforms_params[:wsgi_group], 'wsgi_script_aliases' => { platforms_params[:root_url] => '/usr/share/openstack-dashboard/openstack_dashboard/wsgi/django.wsgi' } diff --git a/inifile/README.markdown b/inifile/README.markdown index a2e1652ab..316077087 100644 --- a/inifile/README.markdown +++ b/inifile/README.markdown @@ -7,11 +7,11 @@ files. The main resource type is `ini_setting`, which is used to manage an individual setting in an INI file. Here's an example usage: ini_setting { "sample setting": + ensure => present, path => '/tmp/foo.ini', section => 'foo', setting => 'foosetting', value => 'FOO!', - ensure => present, } A supplementary resource type is `ini_subsetting`, which is used to manage diff --git a/inifile/lib/puppet/util/ini_file.rb b/inifile/lib/puppet/util/ini_file.rb index 53bed2d5f..523838504 100644 --- a/inifile/lib/puppet/util/ini_file.rb +++ b/inifile/lib/puppet/util/ini_file.rb @@ -5,7 +5,7 @@ module Puppet module Util class IniFile - @@SECTION_REGEX = /^\s*\[([\w\d\.\\\/\-\:]+)\]\s*$/ + @@SECTION_REGEX = /^\s*\[([\w\d\.\\\/\-\:\s]*[\w\d\.\\\/\-])\]\s*$/ @@SETTING_REGEX = /^(\s*)([\w\d\.\\\/\-\s]*[\w\d\.\\\/\-])([ \t]*=[ \t]*)([\S\s]*?)\s*$/ @@COMMENTED_SETTING_REGEX = /^(\s*)[#;]+(\s*)([\w\d\.\\\/\-]+)([ \t]*=[ \t]*)([\S\s]*?)\s*$/ diff --git a/inifile/metadata.json b/inifile/metadata.json new file mode 100644 index 000000000..156647337 --- /dev/null +++ b/inifile/metadata.json @@ -0,0 +1,25 @@ +{ + "name": "puppetlabs/inifile", + "version": "1.0.0", + "summary": "Resource types for managing settings in INI files", + "source": "git@github.com/puppetlabs/puppetlabs-inifile.git", + "project_page": "http://github.com/puppetlabs/puppetlabs-inifile", + "author": "Puppet Labs", + "license": "Apache-2.0", + "operatingsystem_support": [ + "RedHat", + "OpenSUSE", + "SLES", + "SLED", + "Debian", + "Ubuntu" + ], + "puppet_version": [ + 2.7, + 3.0, + 3.1, + 3.2, + 3.3 + ], + "dependencies": [] +} diff --git a/inifile/spec/unit/puppet/provider/ini_setting/ruby_spec.rb b/inifile/spec/unit/puppet/provider/ini_setting/ruby_spec.rb index 550dd72cd..5fee4f09e 100644 --- a/inifile/spec/unit/puppet/provider/ini_setting/ruby_spec.rb +++ b/inifile/spec/unit/puppet/provider/ini_setting/ruby_spec.rb @@ -965,6 +965,58 @@ def self.file_path end end + context "when sections have spaces and dashes" do + let(:orig_content) { + <<-EOS +# This is a comment +[section - one] +; This is also a comment +foo=foovalue + +bar = barvalue +master = true +[section - two] + +foo= foovalue2 +baz=bazvalue +url = http://192.168.1.1:8080 +[section:sub] +subby=bar + #another comment + ; yet another comment + EOS + } + + it "should add a missing setting to the correct section" do + resource = Puppet::Type::Ini_setting.new(common_params.merge( + :section => 'section - two', :setting => 'yahoo', :value => 'yippee')) + provider = described_class.new(resource) + provider.exists?.should be_nil + provider.create + validate_file(<<-EOS +# This is a comment +[section - one] +; This is also a comment +foo=foovalue + +bar = barvalue +master = true +[section - two] + +foo= foovalue2 +baz=bazvalue +url = http://192.168.1.1:8080 +yahoo = yippee +[section:sub] +subby=bar + #another comment + ; yet another comment + EOS + ) + end + + end + end end diff --git a/ipa/.gitignore b/ipa/.gitignore deleted file mode 100644 index 16c0d1ef6..000000000 --- a/ipa/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -tmp/ -old/ -pkg/ -hacking/ -.pmtignore -puppet-ipa-documentation.pdf diff --git a/ipa/.gitmodules b/ipa/.gitmodules deleted file mode 100644 index 24eadf7f2..000000000 --- a/ipa/.gitmodules +++ /dev/null @@ -1,21 +0,0 @@ -[submodule "vagrant/puppet/modules/puppet"] - path = vagrant/puppet/modules/puppet - url = https://github.com/purpleidea/puppet-puppet -[submodule "vagrant/puppet/modules/module-data"] - path = vagrant/puppet/modules/module-data - url = https://github.com/purpleidea/puppet-module-data -[submodule "vagrant/puppet/modules/stdlib"] - path = vagrant/puppet/modules/stdlib - url = https://github.com/purpleidea/puppetlabs-stdlib -[submodule "vagrant/puppet/modules/shorewall"] - path = vagrant/puppet/modules/shorewall - url = https://github.com/purpleidea/puppet-shorewall -[submodule "vagrant/puppet/modules/yum"] - path = vagrant/puppet/modules/yum - url = https://github.com/purpleidea/puppet-yum -[submodule "vagrant/puppet/modules/ssh"] - path = vagrant/puppet/modules/ssh - url = https://github.com/purpleidea/puppet-ssh -[submodule "vagrant/puppet/modules/keepalived"] - path = vagrant/puppet/modules/keepalived - url = https://github.com/purpleidea/puppet-keepalived diff --git a/ipa/.travis.yml b/ipa/.travis.yml deleted file mode 100644 index 354f4ef05..000000000 --- a/ipa/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: ruby -rvm: 1.8.7 -notifications: - email: - - travis-ci@shubin.ca -# TODO: do a full rake test once warnings are all gone! -#script: 'bundle exec rake test' -script: 'bundle exec rake syntax' -env: -# - PUPPET_VERSION=2.7.26 - - PUPPET_VERSION=3.0.2 - - PUPPET_VERSION=3.1.1 - - PUPPET_VERSION=3.2.4 - - PUPPET_VERSION=3.3.2 - - PUPPET_VERSION=3.4.3 - diff --git a/ipa/COPYING b/ipa/COPYING deleted file mode 100644 index dba13ed2d..000000000 --- a/ipa/COPYING +++ /dev/null @@ -1,661 +0,0 @@ - GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -our General Public Licenses are intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - Developers that use our General Public Licenses protect your rights -with two steps: (1) assert copyright on the software, and (2) offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - - A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - - The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - - An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing under -this license. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU Affero General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Remote Network Interaction; Use with the GNU General Public License. - - Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your version -supports such interaction) an opportunity to receive the Corresponding -Source of your version by providing access to the Corresponding Source -from a network server at no charge, through some standard or customary -means of facilitating copying of software. This Corresponding Source -shall include the Corresponding Source for any work covered by version 3 -of the GNU General Public License that is incorporated pursuant to the -following paragraph. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the work with which it is combined will remain governed by version -3 of the GNU General Public License. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU Affero General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU Affero General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU Affero General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU Affero General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If your software can interact with users remotely through a computer -network, you should also make sure that it provides a way for users to -get its source. For example, if your program is a web application, its -interface could display a "Source" link that leads users to an archive -of the code. There are many ways you could offer source, and different -solutions will be better for different programs; see section 13 for the -specific requirements. - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU AGPL, see -. diff --git a/ipa/COPYRIGHT b/ipa/COPYRIGHT deleted file mode 100644 index 480149fb7..000000000 --- a/ipa/COPYRIGHT +++ /dev/null @@ -1,16 +0,0 @@ -Copyright (C) 2012-2013+ James Shubin -Written by James Shubin - -This puppet module is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This puppet module is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . - diff --git a/ipa/DOCUMENTATION.md b/ipa/DOCUMENTATION.md deleted file mode 100644 index 19d143755..000000000 --- a/ipa/DOCUMENTATION.md +++ /dev/null @@ -1,851 +0,0 @@ -#Puppet-IPA - - - -##A FreeIPA Puppet module by [James](https://ttboj.wordpress.com/) -####Available from: -####[https://github.com/purpleidea/puppet-ipa/](https://github.com/purpleidea/puppet-ipa/) - -####Also available from: -####[https://gitorious.org/purpleidea/puppet-ipa/](https://gitorious.org/purpleidea/puppet-ipa/) - -####This documentation is available in: [Markdown](https://github.com/purpleidea/puppet-ipa/blob/master/DOCUMENTATION.md) or [PDF](https://pdfdoc-purpleidea.rhcloud.com/pdf/https://github.com/purpleidea/puppet-ipa/blob/master/DOCUMENTATION.md) format. - -####Table of Contents - -1. [Overview](#overview) -2. [Module description - What the module does](#module-description) -3. [Setup - Getting started with Puppet-IPA](#setup) - * [What can Puppet-IPA manage?](#what-can-puppet-ipa-manage) - * [Basic setup](#basic-setup) - * [Advanced setup](#advanced-setup) - * [Multi-master setup](#multi-master-setup) - * [Type setup](#type-setup) - * [Client setup](#client-setup) -4. [Usage/FAQ - Notes on management and frequently asked questions](#usage-and-frequently-asked-questions) -5. [Reference - Class and type reference](#reference) - * [ipa::server](#ipaserver) - * [ipa::server::host](#ipaserverhost) - * [ipa::server::service](#ipaserverservice) - * [ipa::server::user](#ipaserveruser) - * [ipa::client::deploy](#ipaclientdeploy) -6. [Examples - Example configurations](#examples) -7. [Limitations - Puppet versions, OS compatibility, etc...](#limitations) -8. [Development - Background on module development and reporting bugs](#development) -9. [Author - Author and contact information](#author) - -##Overview - -The Puppet-IPA module installs, configures, and manages a FreeIPA server, -replicas, and clients. - -##Module Description - -This Puppet-IPA module handles installation, configuration, and management -of FreeIPA across all of the replicas and clients in your infrastructure. - -##Setup - -###What can Puppet-IPA manage? - -Puppet-IPA is designed to be able to manage as much or as little of your -FreeIPA infrastructure as you wish. All features are optional. If there is a -feature that doesn't appear to be optional, and you believe it should be, -please let me know. Having said that, it makes good sense to me to have -Puppet-IPA manage as much of your FreeIPA infrastructure as it can. At the -moment, it cannot rack new servers, but I am accepting funding to explore this -feature ;) At the moment it can manage: - -* FreeIPA packages (rpm) -* FreeIPA server installation (ipa-server-install) -* FreeIPA configuration files (/etc/ipa/) -* FreeIPA replica peering (ipa-replica-{prepare,install}) -* FreeIPA replica topology (ipa-replica-manage) -* FreeIPA firewalling (whitelisting) -* FreeIPA host creation/modification/deletion (ipa host-{add,mod,del}) -* FreeIPA service creation/modification/deletion (ipa service-{add,mod,del}) -* FreeIPA user creation/modification/deletion (ipa user-{add,mod,del}) -* FreeIPA client installation (ipa-client-install, ipa-getkeytab) -* And much more... - -###Basic setup - -For a single host FreeIPA server, setup is quite simple and straight-forward. - -```puppet -node ipa1 { - class { '::ipa::server': - dm_password => 'changeme', # pick a dm password - admin_password => 'changeme2', # pick an admin password - shorewall => true, - } -} -``` - -Please be careful to keep the the _dm_ and _admin_ passwords secret. For a -better way to do this, please have a look at the [advanced setup](#advanced-setup). - -###Advanced setup - -Storing secrets in puppet is generally a bad idea. Because FreeIPA is usually -the security cornerstone of your infrastructure, care was taken to ensure that -this puppet modules does all that it can to survice even the harshest scrutiny. - -If desired, the local FreeIPA server can securely generate _dm_ and _admin_ -passwords, and encrypt them with your public GPG key. This way, you can have an -automatic FreeIPA deployment without ever having the secrets touch puppet. For -more information on this technique and how it works with puppet please read: -[Securely managing secrets for FreeIPA with Puppet](https://ttboj.wordpress.com/2014/06/06/securely-managing-secrets-for-freeipa-with-puppet/). - -Here is an example of how you can use the GPG parameters: - -```puppet -node ipa1 { - class { '::ipa::server': - # NOTE: email must exist in the public key if we use gpg_sendemail - #email => 'root@example.com', - gpg_recipient => '24090D66', # use your own GPG key id here! - #gpg_publickey => '...', # alternatively, paste it here! - gpg_keyserver => 'hkp://keys.gnupg.net', # pick your own - gpg_sendemail => false, # optional choice you can make. - shorewall => true, - } -} - -``` - -For information on how Puppet-IPA can be used for hybrid management of FreeIPA -types, please read: -[Hybrid management of FreeIPA types with Puppet](https://ttboj.wordpress.com/2014/07/24/hybrid-management-of-freeipa-types-with-puppet/). - -For information on additional advanced options, please see the -[reference](#reference) section below for the specifics. - -###Multi-master setup - -Puppet-IPA can be used to setup a cluster of FreeIPA servers. Each server in -the cluster becomes a FreeIPA replica. There are some additional requirements -and (unfortunately) limitations at this time. You will require: - -* An additional IP address to be used as a VIP. (It can be a private address.) -* More than one server or virtual machine. - -The limitation is that [Securely managing secrets for FreeIPA with Puppet](https://ttboj.wordpress.com/2014/06/06/securely-managing-secrets-for-freeipa-with-puppet/) -is not currently compatible with the automatic multi-master deployment. This is -due to a limitation in FreeIPA, and it will hopefully be fixed in a future -release. Help is appreciated! - -```puppet -node /^ipa\d+$/ { # ipa{1,2,..N} - class { '::ipa::server': - vip => '192.168.123.253', # pick an available VIP - topology => 'ring', # choose your favourite - dm_password => 'changeme', # pick a dm password - admin_password => 'changeme2', # pick an admin password - # NOTE: Unfortunately the gpg_* options are not currently - # compatible with automatic multi-master replication. This is - # due to a limitation in FreeIPA, but should hopefully get - # resolved in a future FreeIPA release. Help is appreciated! - vrrp => true, # setup keepalived - shorewall => true, - } -} - -``` - -The server configuration that you choose must be identical on each FreeIPA -server. As you can see in the above example, this is achieved by including a -_node_ pattern that matches ipa{1,2,...N} so that this is automatic. - -Another point of interest is the _topology_ parameter. This automatically -figures out the correct relationships for all of the hosts in your cluster -algorithmically, so that you can worry about other issues. Other algorithms can -be written and included, and contributions are encouraged. You can also specify -the topology manually if you want to be extremely hands on with your layout. - -###Type setup - -Naturally you'll probably want to define FreeIPA types on your server. The most -common ones are _host_, _service_, and _user_. These should be defined on all -the server hosts. Some examples include: - -```puppet -# create a managed user -ipa::server::user { 'ntesla': - first => 'Nikola', - last => 'Tesla', - city => 'Shoreham', - state => 'New York', - postalcode => '11786', -} -``` - -```puppet -# create a host -ipa::server::host { 'nfs': # NOTE: adding .${domain} is a good idea.... - domain => 'example.com', - macaddress => "00:11:22:33:44:55", - random => true, # set a one time password randomly - locality => 'Montreal, Canada', - location => 'Room 641A', - platform => 'Supermicro', - osstring => 'CentOS 6.5 x86_64', - comment => 'Simple NFSv4 Server', - watch => true, # read and understand the docs well -} -``` - -```puppet -# create a service for the above host -ipa::server::service { 'nfs': - service => 'nfs', - host => 'nfs', # needs to match ipa::server::host $name - domain => 'example.com', # used to figure out realm -} -``` - -For more information on FreeIPA type management, and the hybrid management -feature that Puppet-IPA supports, please read: -[Hybrid management of FreeIPA types with Puppet](https://ttboj.wordpress.com/2014/07/24/hybrid-management-of-freeipa-types-with-puppet/). - -###Client setup - -Getting a client host to enroll and work magically with the FreeIPA server is -particularly easy. Simply include the IPA client _deploy_ class: - -```puppet -# there is now a single "deploy" which can be used for both hosts and services! -include ipa::client::deploy -``` - -This will automatically pull down any host registration and service definitions -that were defined on the Puppet-IPA server. If you want to be more specific -about which you include, you can include each set of types separately: - -```puppet -# if you use fqdn's for the ipa:server:host $name's, then you can deploy with: -include ipa::client::host::deploy -``` - -or to only include services: - -```puppet -# pull down any defined FreeIPA services. -include ipa::client::service::deploy -``` - -For an NFS host (which is a FreeIPA client), you might want to use: - -```puppet -# and on the nfs server (an ipa client): -class { '::ipa::client::deploy': - nametag => 'nfs', # needs to match the ipa:server:host $name -} -``` - -All of this happens automatically through the magic of puppet and exported -resources. See the [examples](#examples) for more ideas. - -##Usage and frequently asked questions - -All management should be done by manipulating the arguments on the appropriate -Puppet-IPA classes and types. Hybrid management is also supported for certain -aspects of FreeIPA management. This is a stellar feature of Puppet-IPA. Please -read: [Hybrid management of FreeIPA types with Puppet](https://ttboj.wordpress.com/2014/07/24/hybrid-management-of-freeipa-types-with-puppet/). - -###Do I need to use a virtual IP? - -Using a virtual IP (VIP) is strongly recommended as a distributed lock manager -(DLM) for certain operations. For an article explaning the mechanism (but for a -different puppet module), please see: -[How to avoid cluster race conditions or: How to implement a distributed lock manager in puppet](https://ttboj.wordpress.com/2012/08/23/how-to-avoid-cluster-race-conditions-or-how-to-implement-a-distributed-lock-manager-in-puppet/) - -Remember that even if you're using a hosted solution (such as AWS) that doesn't -provide an additional IP address, or you want to avoid using an additional IP, -you can use an unused private RFC1918 IP address as the DLM VIP. Remember that -a layer 3 IP can co-exist on the same layer 2 network with the layer 3 network -that is used by your cluster. - -###Is it possible to have Puppet-IPA complete in a single run? - -No. This is a limitation of Puppet, and is related to how FreeIPA operates. It -is possible for a single FreeIPA server, but for many multi-host scenarios, -including the multi-master FreeIPA case, you will require more than one run. - -For example, - -###Can you integrate this with vagrant? - -Yes, see the -[vagrant/](https://github.com/purpleidea/puppet-ipa/tree/master/vagrant) -directory. This has been tested on Fedora 20, with vagrant-libvirt, as I have -no desire to use VirtualBox for fun. I have written many articles about this on -my [technical blog](https://ttboj.wordpress.com/?s=vagrant). In particular, I -would recommend: [Vagrant on Fedora with libvirt (reprise)](https://ttboj.wordpress.com/2014/05/13/vagrant-on-fedora-with-libvirt-reprise/). - -###Puppet runs fail with "Connection refused - connect(2)" errors. - -You may see a "_Connection refused - connect(2)_" message when running puppet. -This typically happens if your puppet vm guest is overloaded. When running high -guest counts on your laptop, or running without hardware virtualization support -this is quite common. Another common causes of this is if your domain type is -set to _qemu_ instead of the accelerated _kvm_. Since the _qemu_ domain type is -much slower, puppet timeouts and failures are common when it doesn't respond. - -###Will this work on my favourite OS? (eg: GNU/Linux F00bar OS v12 ?) -If it's a GNU/Linux based OS, can run FreeIPA, and Puppet, then it will -probably work. Typically, you might need to add a yaml data file to the _data/_ -folder so that Puppet-IPA knows where certain operating system specific -things are found. The multi-distro support has been designed to make it -particularly easy to add support for additional platforms. If your platform -doesn't work, please submit a yaml data file with the platform specific values. - -###Awesome work, but it's missing support for a feature and/or platform! - -Since this is an Open Source / Free Software project that I also give away for -free (as in beer, free as in gratis, free as in libre), I'm unable to provide -unlimited support. Please consider donating funds, hardware, virtual machines, -and other resources. For specific needs, you could perhaps sponsor a feature! - -###You didn't answer my question, or I have a question! - -Contact me through my [technical blog](https://ttboj.wordpress.com/contact/) -and I'll do my best to help. If you have a good question, please remind me to -add my answer to this documentation! - -##Reference -Please note that there are a number of undocumented options. For more -information on these options, please view the source at: -[https://github.com/purpleidea/puppet-ipa/](https://github.com/purpleidea/puppet-ipa/). -If you feel that a well used option needs documenting here, please contact me. - -###Overview of classes and types -Please note that the most common, user facing classes and types are documented, -while there may be many other undocumented classes and types that are used -internally by other classes and types. These will be documented as time allows. - -* [ipa::server](#ipaserver): Base class for server hosts. -* [ipa::server::host](#ipaserverhost): Host type for each FreeIPA client. -* [ipa::server::service](#ipaserverservice): Service type for each FreeIPA service. -* [ipa::server::user](#ipaserveruser): User type for each FreeIPA user. -* [ipa::client::deploy](#ipaclientdeploy): Client class to deploy types. - -###ipa::server -This is the main class to be used for IPA server installation. It is used for -both simple standalone FreeIPA servers, and for complex multi-master scenarios. - -####`hostname` -The hostname of the IPA server. This defaults to _$::hostname_. - -####`domain` -The domain of the IPA server and of the cluster. This defaults to _$::domain_. - -####`realm` -The realm of the cluster. This defaults to _upcase($domain)_. - -####`vip` -The virtual IP address to be used for the cluster distributed lock manager. -This option can be used in conjunction with the _vrrp_ option, but it does not -require it. If you don't want to provide a virtual ip, but you do want to -enforce that certain operations only run on one host, then you can set this -option to be the ip address of an arbitrary host in your cluster. Keep in mind -that if that host is down, certain options won't ever occur. - -####`peers` -Specify the peering topology manually in dictionary form. Each dictionary value -should be an array of the peers to connect to from the originating key. - -####`topology` -The topology algorithm to use when setting up mult-master cluster -automatically. A few default algorithms are included with Puppet-IPA. They are: -_ring_, _flat_. If you'd like to include an algorithm that generates a -different topology, it is easy to drop one in! Please contact me with the -details! - -####`topology_arguments` -A list of arguments to pass to the topology algorithm. Not all topology -algorithms support this, however it can be very useful so that it's easy to -generalize certain algorithms in the same function in terms of these variables. - -####`dm_password` -The dm_password in plaintext. It is recommended that you use a better mechanism -for handling secrets. Please see: -[Securely managing secrets for FreeIPA with Puppet](https://ttboj.wordpress.com/2014/06/06/securely-managing-secrets-for-freeipa-with-puppet/). - -####`admin_password` -The admin_password in plaintext. It is recommended that you use a better method -for handling secrets. Please see: -[Securely managing secrets for FreeIPA with Puppet](https://ttboj.wordpress.com/2014/06/06/securely-managing-secrets-for-freeipa-with-puppet/). - -####`gpg_recipient` -The GPG recipient ID of your public key goes here. This is a valid _-r_ value -for the _gpg_ command line tool. - -####`gpg_publickey` -This is the value of your GPG public key, or a _puppet:///_ uri pointing to the -file. This can't be used in conjunction with the `gpg_keyserver` option. - -####`gpg_keyserver` -This is the value of the GPG keyserver of your choice. You can use a public one -such as _hkp://keys.gnupg.net_ or you can use your own private keyserver. - -####`gpg_sendemail` -Do you want to mail out the _dm_ and _admin_ passwords encrypted with your GPG -key or not? Defaults to _false_. - -####`idstart` -The FreeIPA _idstart_ value to use. This is the starting id for users created. -This comes with a mostly sensible default, but recommendations are welcome. - -####`idmax` -The FreeIPA _idmax_ value to use. - -####`email_domain` -The email domain to use. This defaults to _$domain_. - -####`shell` -The default user shell to assign. This default to _/bin/sh_. - -####`homes` -The default home prefix. This defaults to _/home_. - -####`ntp` -Should we install an NTP server along with FreeIPA? This defaults to _false_. - -####`dns` -Should we install a DNS server along with FreeIPA? This defaults to _false_. -This must be set at install time to be used. - -####`dogtag` -Should we install a cert server along with FreeIPA? This defaults to _false_. -This is not currently managed by Puppet-IPA. - -####`email` -The email address to associate with the FreeIPA server. This defaults to -_root@$domain_. This is important for FreeIPA, and it is used to mail out -encrypted passwords depending on your `gpg_sendemail` settings. - -####`vrrp` -Whether to automatically deploy and manage _Keepalived_ for use as a _DLM_ and -for use in volume mounting, etc... Using this option requires the _vip_ option. - -####`shorewall` -Boolean to specify whether puppet-shorewall integration should be used or not. - -####`again` -Do you want to use _Exec['again']_ ? This helps build your cluster quickly! - -####`host_excludes` -This list matches and excludes certain _hosts_ from being removed by puppet. -The `host_excludes` are matched with bash regexp matching in: `[[ =~ ]]`. If -the string regexp passed contains quotes, string matching is done: - -`$string='"hostname.example.com"' vs $regexp='hostname.example.com'` - -Obviously, each pattern in the array is tried, and any match will do. Invalid -expressions might cause breakage! Use this at your own risk!! Remember that you -might be matching against strings which have dots. A value of true will -automatically add the `*` character to match all. For more information on this -option, please read: -[Hybrid management of FreeIPA types with Puppet](https://ttboj.wordpress.com/2014/07/24/hybrid-management-of-freeipa-types-with-puppet/). - -####`service_excludes` -This list matches and excludes certain _services_ from being removed by puppet. -The `service_excludes` are matched with bash regexp matching in: `[[ =~ ]]`. If -the string regexp passed contains quotes, string matching is done: - -`$string='"hostname.example.com"' vs $regexp='hostname.example.com'` - -Obviously, each pattern in the array is tried, and any match will do. Invalid -expressions might cause breakage! Use this at your own risk!! Remember that you -might be matching against strings which have dots. A value of true will -automatically add the `*` character to match all. For more information on this -option, please read: -[Hybrid management of FreeIPA types with Puppet](https://ttboj.wordpress.com/2014/07/24/hybrid-management-of-freeipa-types-with-puppet/). - -####`user_excludes` -This list matches and excludes certain _users_ from being removed by puppet. -The `user_excludes` are matched with bash regexp matching in: `[[ =~ ]]`. If -the string regexp passed contains quotes, string matching is done: - -`$string='"hostname.example.com"' vs $regexp='hostname.example.com'` - -Obviously, each pattern in the array is tried, and any match will do. Invalid -expressions might cause breakage! Use this at your own risk!! Remember that you -might be matching against strings which have dots. A value of true will -automatically add the `*` character to match all. For more information on this -option, please read: -[Hybrid management of FreeIPA types with Puppet](https://ttboj.wordpress.com/2014/07/24/hybrid-management-of-freeipa-types-with-puppet/). - -####`peer_excludes` -This list matches and excludes certain _peers_ from being removed by puppet. -The `peer_excludes` are matched with bash regexp matching in: `[[ =~ ]]`. If -the string regexp passed contains quotes, string matching is done: - -`$string='"hostname.example.com"' vs $regexp='hostname.example.com'` - -Obviously, each pattern in the array is tried, and any match will do. Invalid -expressions might cause breakage! Use this at your own risk!! Remember that you -might be matching against strings which have dots. A value of true will -automatically add the `*` character to match all. This particular option is -difference than the other excludes because it only prevents un-peering of the -listed hosts. - -###ipa::server::host -This is the main FreeIPA type that maps to host entries in FreeIPA. This gets -set on the FreeIPA server (or servers) and with the associated _ipa::client_ -types and classes, will automatically setup the FreeIPA client associated with -this host type. Here are a few examples: - -```puppet -# create a host -ipa::server::host { 'nfs': # NOTE: adding .${domain} is a good idea.... - domain => 'example.com', - macaddress => "00:11:22:33:44:55", - random => true, # set a one time password randomly - locality => 'Montreal, Canada', - location => 'Room 641A', - platform => 'Supermicro', - osstring => 'CentOS 6.5 x86_64', - comment => 'Simple NFSv4 Server', - watch => true, # read and understand the docs well -} -``` - -```puppet -ipa::server::host { 'test1': - domain => 'example.com', - password => 'password', - watch => true, # read and understand the docs well -} -``` - -####`domain` -The domain of the host. This defaults to the ipa server _$domain_ variable. - -####`server` -Where the client will find the IPA server. This has a sensible default. - -####`macaddress` -The [MAC address](https://en.wikipedia.org/wiki/MAC_address) of the host. - -####`sshpubkeys` -Leave this at the default for automatic public ssh keys to get transferred. - -####`password` -The one time password used for host provisioning. Not to be used with -`$random`. - -####`random` -Generate the one time password used for host provisioning. Conflicts with -`$password`. - -####`locality` -Host description parameter for _locality_. Example: "_Montreal, Canada_". - -####`location` -Host description parameter for _location_. Example: "_Lab 42_". - -####`platform` -Host description parameter for hardware _platform_. Example: "_Lenovo X201_". - -####`osstring` -Host description parameter for _os string_. Example: "_CentOS 6.5_". - -####`comments` -Host description parameter for _comments_. Example: "_NFS Server_". - -####`admin` -Should this client get the admin tools installed ? - -####`watch` -Manage all changes to this resource, reverting others if this is true. For more -information on this option please read: -[Hybrid management of FreeIPA types with Puppet](https://ttboj.wordpress.com/2014/07/24/hybrid-management-of-freeipa-types-with-puppet/). - -####`modify` -Modify this resource on puppet changes or not ? Do so if true. For more -information on this option please read: -[Hybrid management of FreeIPA types with Puppet](https://ttboj.wordpress.com/2014/07/24/hybrid-management-of-freeipa-types-with-puppet/). - -###ipa::server::service -This is the main FreeIPA type that maps to service entries in FreeIPA. This is -set on the FreeIPA server (or servers) and with the associated _ipa::client_ -types and classes, will automatically setup the FreeIPA service associated with -the correct host. Here is an example: - -```puppet -ipa::server::service { 'nfs': # this $name should match nfs::server => $ipa - service => 'nfs', - host => 'nfs', # needs to match ipa::server::host $name - domain => 'example.com', # used to figure out realm -} -``` - -####`service` -The service string. Common examples include: _nfs_, _HTTP_, _ldap_. - -####`host` -The hostname where the service will reside. - -####`domain` -The domain of the host where the service will reside. - -####`realm` -The realm of the host where the service will reside. - -####`principal` -Specify the desired principal of this service, overriding the principal that -can be ascertained by using the above values. - -####`server` -Where the client will find the IPA server. This has a sensible default. - -####`pactype` -The service _pac type_. Bad values are silently discarded, `[]` is _NONE_. - -####`watch` -Manage all changes to this resource, reverting others if this is true. For more -information on this option please read: -[Hybrid management of FreeIPA types with Puppet](https://ttboj.wordpress.com/2014/07/24/hybrid-management-of-freeipa-types-with-puppet/). - -####`modify` -Modify this resource on puppet changes or not ? Do so if true. For more -information on this option please read: -[Hybrid management of FreeIPA types with Puppet](https://ttboj.wordpress.com/2014/07/24/hybrid-management-of-freeipa-types-with-puppet/). - -####`comment` -A comment field that you can use however you want. - -###ipa::server::user -This is the main FreeIPA type that maps to user entries in FreeIPA. This is set -on the FreeIPA server (or servers) and creates user entries which can be seen -in the FreeIPA LDAP database, and which are then available on IPA clients. Here -are a few examples: - -```puppet -# create a managed user -ipa::server::user { 'ntesla': - first => 'Nikola', - last => 'Tesla', - city => 'Shoreham', - state => 'New York', - postalcode => '11786', -} -``` - -```puppet -# create a user by principal but without the instance set -ipa::server::user { 'arthur@EXAMPLE.COM': - first => 'Arthur', - last => 'Guyton', - jobtitle => 'Physiologist', - orgunit => 'Research', -} -``` - -```puppet -# create a user using a full principal as the primary key -# NOTE: the principal itself can't be edited without a remove/add -ipa::server::user { 'aturing/admin@EXAMPLE.COM': - first => 'Alan', - last => 'Turning', - random => true, # set a password randomly - password_file => true, # store the password in plain text ! (bad) -} -``` - -####`login` -The login of this user. In the principal, the pattern is: -`login/instance@REALM`. - -####`instance` -The instance of this user. In the principal, the pattern is: -`login/instance@REALM`. - -####`domain` -The domain associated with the user. Uppercase version can be used as the realm -default. - -####`realm` -The realm of this user. In the principal, the pattern is: -`login/instance@REALM`. - -####`principal` -Specify the desired principal of this user, overriding the principal that -can be ascertained by using the above values. - -####`first` -First name of user. Required. - -####`last` -Last name of user. Required. - -####`cn` -Full name of user. Defaults to: "_$first $last_". - -####`displayname` -Display name of user. Defaults to: "_$first $last_". - -####`initials` -Initials of user. Defaults to: "_$first[0] $last[0]_". - -####`email` -Email address of user. - -####`gecos` -Legacy field. Can be set manually if needed. - -####`uid` -UID value for user. By default this is automatically assigned. - -####`gid` -GID value for user. By default this is automatically assigned. - -####`shell` -Shell for user. By default this is automatically assigned. - -####`home` -Home dir for user. By default this is automatically assigned. - -####`sshpubkeys` -Public SSH keys for the user. - -####`random` -Set to _true_ to have the user password auto generated. - -####`password_file` -Save user password to a file. The file is in: _${vardir}/ipa/users/passwords/_. - -####`password_mail` -Mail out a GPG encrypted password to the admin. -*TODO*: this option is not yet implemented. - -####`street` -The users street address. - -####`city` -The users city. - -####`state` -The users state or province. - -####`postalcode` -The users zip or postal code. - -####`phone` -The users phone number. Can be an array of numbers. - -####`mobile` -The users mobile number. Can be an array of numbers. - -####`pager` -The users pager number. Can be an array of numbers. Users with pager numbers -are particularly cool. - -####`fax` -The users fax number. Can be an array of numbers. - -####`jobtitle` -The users job title. Silly titles are allowed. - -####`orgunit` -The users organization unit, otherwise known as a department. - -####`manager` -The users manager. Should match an existing FreeIPA user name. - -####`carlicense` -The users car license string. FreeIPA created these fields, I just wrap them. - -####`watch` -Manage all changes to this resource, reverting others if this is true. For more -information on this option please read: -[Hybrid management of FreeIPA types with Puppet](https://ttboj.wordpress.com/2014/07/24/hybrid-management-of-freeipa-types-with-puppet/). - -####`modify` -Modify this resource on puppet changes or not ? Do so if true. For more -information on this option please read: -[Hybrid management of FreeIPA types with Puppet](https://ttboj.wordpress.com/2014/07/24/hybrid-management-of-freeipa-types-with-puppet/). - -####`comment` -A comment field that you can use however you want. - -###ipa::client::deploy -Include this class to deploy the client host itself and any services. The -necessary information will automatically get exported from the FreeIPA server. -This class takes care of fetching this information through exported resources, -and safely running _ipa-getkeytab_ so that the admin sees an automatic process. - -##Examples -For example configurations, please consult the [examples/](https://github.com/purpleidea/puppet-ipa/tree/master/examples) -directory in the git source repository. It is available from: - -[https://github.com/purpleidea/puppet-ipa/tree/master/examples](https://github.com/purpleidea/puppet-ipa/tree/master/examples) - -It is also available from: - -[https://gitorious.org/purpleidea/puppet-ipa/source/examples](https://gitorious.org/purpleidea/puppet-ipa/source/examples) - -##Limitations - -This module has been tested against open source Puppet 3.2.4 and higher. - -The module is routinely tested on: - -* CentOS 6.5 - -It will probably work without incident or without major modification on: - -* CentOS 5.x/6.x -* RHEL 5.x/6.x - -It has patches to support: - -* Fedora 20+ - -It will most likely work with other Puppet versions and on other platforms, but -testing on those platforms has been minimal due to lack of time and resources. - -Testing is community supported! Please report any issues as there are a lot of -features, and in particular, support for additional distros isn't well tested. -The multi-distro architecture has been chosen to easily support new additions. -Most platforms and versions will only require a change to the yaml based data/ -folder. - -##Development - -This is my personal project that I work on in my free time. -Donations of funding, hardware, virtual machines, and other resources are -appreciated. Please contact me if you'd like to sponsor a feature, invite me to -talk/teach or for consulting. - -You can follow along [on my technical blog](https://ttboj.wordpress.com/). - -To report any bugs, please [contact me](https://ttboj.wordpress.com/contact/). - -##Author - -Copyright (C) 2012-2013+ James Shubin - -* [github](https://github.com/purpleidea/) -* [@purpleidea](https://twitter.com/#!/purpleidea) -* [https://ttboj.wordpress.com/](https://ttboj.wordpress.com/) - diff --git a/ipa/Gemfile b/ipa/Gemfile deleted file mode 100644 index 07dd7f4c2..000000000 --- a/ipa/Gemfile +++ /dev/null @@ -1,11 +0,0 @@ -source 'https://rubygems.org' - -puppet_version = ENV.key?('PUPPET_VERSION') ? "= #{ENV['PUPPET_VERSION']}" : ['>= 3.0'] - -gem 'rake' -gem 'puppet', puppet_version -gem 'puppet-lint' # style things, eg: tabs vs. spaces -gem 'rspec-puppet', :git => 'https://github.com/rodjek/rspec-puppet.git' -gem 'puppet-syntax' # syntax checking -gem 'puppetlabs_spec_helper' - diff --git a/ipa/INSTALL b/ipa/INSTALL deleted file mode 100644 index f497841f7..000000000 --- a/ipa/INSTALL +++ /dev/null @@ -1,18 +0,0 @@ -To install this puppet module, copy this folder to your puppet modulepath. - -You can usually find out where this is by running: - -$ puppet config print modulepath - -on your puppetmaster. In my case, this contains the directory: - -/etc/puppet/modules/ - -I keep all of my puppet modules in git managed directories named: - -puppet- - -You must remove the 'puppet-' prefix from the directory name for it to work! - -Happy hacking! - diff --git a/ipa/Makefile b/ipa/Makefile deleted file mode 100644 index 5ab177d33..000000000 --- a/ipa/Makefile +++ /dev/null @@ -1,155 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -.PHONY: all push docs test rpm srpm spec tar upload upload-sources upload-srpms upload-rpms -.SILENT: - -# version of the program -VERSION := $(shell gjs -c "print(`cat metadata.json`['version'])") -RELEASE = 1 -SPEC = rpmbuild/SPECS/puppet-ipa.spec -SOURCE = rpmbuild/SOURCES/puppet-ipa-$(VERSION).tar.bz2 -SRPM = rpmbuild/SRPMS/puppet-ipa-$(VERSION)-$(RELEASE).src.rpm -RPM = rpmbuild/RPMS/puppet-ipa-$(VERSION)-$(RELEASE).rpm -SERVER = 'download.gluster.org' -REMOTE_PATH = 'purpleidea/puppet-ipa' - -all: docs rpm - -push: - # use blacksmith to push module to forge - git checkout master && rake module:push - -docs: puppet-ipa-documentation.pdf - -puppet-ipa-documentation.pdf: DOCUMENTATION.md - pandoc DOCUMENTATION.md -o 'puppet-ipa-documentation.pdf' - -test: - # TODO: avoid exiting with non-zero when there are only warnings? - #rake syntax - rake test - -# -# aliases -# -# TODO: does making an rpm depend on making a .srpm first ? -rpm: $(SRPM) $(RPM) - # do nothing - -srpm: $(SRPM) - # do nothing - -spec: $(SPEC) - # do nothing - -tar: $(SOURCE) - # do nothing - -upload: upload-sources upload-srpms upload-rpms - # do nothing - -# -# rpmbuild -# -$(RPM): $(SPEC) $(SOURCE) - @echo Running rpmbuild -bb... - rpmbuild --define '_topdir $(shell pwd)/rpmbuild' -bb $(SPEC) && \ - mv rpmbuild/RPMS/noarch/puppet-ipa-$(VERSION)-$(RELEASE).*.rpm $(RPM) - -$(SRPM): $(SPEC) $(SOURCE) - @echo Running rpmbuild -bs... - rpmbuild --define '_topdir $(shell pwd)/rpmbuild' -bs $(SPEC) - # renaming is not needed because we aren't using the dist variable - #mv rpmbuild/SRPMS/puppet-ipa-$(VERSION)-$(RELEASE).*.src.rpm $(SRPM) - -# -# spec -# -$(SPEC): rpmbuild/ puppet-ipa.spec.in - @echo Running templater... - #cat puppet-ipa.spec.in > $(SPEC) - sed -e s/__VERSION__/$(VERSION)/ -e s/__RELEASE__/$(RELEASE)/ < puppet-ipa.spec.in > $(SPEC) - # append a changelog to the .spec file - git log --format="* %cd %aN <%aE>%n- (%h) %s%d%n" --date=local | sed -r 's/[0-9]+:[0-9]+:[0-9]+ //' >> $(SPEC) - -# -# archive -# -$(SOURCE): rpmbuild/ - @echo Running git archive... - # use HEAD if tag doesn't exist yet, so that development is easier... - git archive --prefix=puppet-ipa-$(VERSION)/ -o $(SOURCE) $(VERSION) 2> /dev/null || (echo 'Warning: $(VERSION) does not exist.' && git archive --prefix=puppet-ipa-$(VERSION)/ -o $(SOURCE) HEAD) - -# TODO: ensure that each sub directory exists -rpmbuild/: - mkdir -p rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} - -# -# sha256sum -# -rpmbuild/SOURCES/SHA256SUMS: rpmbuild/SOURCES/ $(SOURCE) - @echo Running SOURCES sha256sum... - cd rpmbuild/SOURCES/ && sha256sum *.tar.bz2 > SHA256SUMS; cd - - -rpmbuild/SRPMS/SHA256SUMS: rpmbuild/SRPMS/ $(SRPM) - @echo Running SRPMS sha256sum... - cd rpmbuild/SRPMS/ && sha256sum *src.rpm > SHA256SUMS; cd - - -rpmbuild/RPMS/SHA256SUMS: rpmbuild/RPMS/ $(RPM) - @echo Running RPMS sha256sum... - cd rpmbuild/RPMS/ && sha256sum *.rpm > SHA256SUMS; cd - - -# -# gpg -# -rpmbuild/SOURCES/SHA256SUMS.asc: rpmbuild/SOURCES/SHA256SUMS - @echo Running SOURCES gpg... - # the --yes forces an overwrite of the SHA256SUMS.asc if necessary - gpg2 --yes --clearsign rpmbuild/SOURCES/SHA256SUMS - -rpmbuild/SRPMS/SHA256SUMS.asc: rpmbuild/SRPMS/SHA256SUMS - @echo Running SRPMS gpg... - gpg2 --yes --clearsign rpmbuild/SRPMS/SHA256SUMS - -rpmbuild/RPMS/SHA256SUMS.asc: rpmbuild/RPMS/SHA256SUMS - @echo Running RPMS gpg... - gpg2 --yes --clearsign rpmbuild/RPMS/SHA256SUMS - -# -# upload -# -# upload to public server -upload-sources: rpmbuild/SOURCES/ rpmbuild/SOURCES/SHA256SUMS rpmbuild/SOURCES/SHA256SUMS.asc - if [ "`cat rpmbuild/SOURCES/SHA256SUMS`" != "`ssh $(SERVER) 'cd $(REMOTE_PATH)/SOURCES/ && cat SHA256SUMS'`" ]; then \ - echo Running SOURCES upload...; \ - rsync -avz rpmbuild/SOURCES/ $(SERVER):$(REMOTE_PATH)/SOURCES/; \ - fi - -upload-srpms: rpmbuild/SRPMS/ rpmbuild/SRPMS/SHA256SUMS rpmbuild/SRPMS/SHA256SUMS.asc - if [ "`cat rpmbuild/SRPMS/SHA256SUMS`" != "`ssh $(SERVER) 'cd $(REMOTE_PATH)/SRPMS/ && cat SHA256SUMS'`" ]; then \ - echo Running SRPMS upload...; \ - rsync -avz rpmbuild/SRPMS/ $(SERVER):$(REMOTE_PATH)/SRPMS/; \ - fi - -upload-rpms: rpmbuild/RPMS/ rpmbuild/RPMS/SHA256SUMS rpmbuild/RPMS/SHA256SUMS.asc - if [ "`cat rpmbuild/RPMS/SHA256SUMS`" != "`ssh $(SERVER) 'cd $(REMOTE_PATH)/RPMS/ && cat SHA256SUMS'`" ]; then \ - echo Running RPMS upload...; \ - rsync -avz --prune-empty-dirs rpmbuild/RPMS/ $(SERVER):$(REMOTE_PATH)/RPMS/; \ - fi - -# vim: ts=8 diff --git a/ipa/README b/ipa/README deleted file mode 100644 index 91bb599b8..000000000 --- a/ipa/README +++ /dev/null @@ -1,2 +0,0 @@ -Please see README.md - diff --git a/ipa/README.md b/ipa/README.md deleted file mode 100644 index dc9e4dcfb..000000000 --- a/ipa/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# *puppet-ipa*: a puppet module for FreeIPA - -[![Build Status](https://secure.travis-ci.org/purpleidea/puppet-ipa.png)](http://travis-ci.org/purpleidea/puppet-ipa) - -## Documentation: -Please see: [DOCUMENTATION.md](DOCUMENTATION.md) or [puppet-ipa-documentation.pdf](puppet-ipa-documentation.pdf). - -## Installation: -Please read the [INSTALL](INSTALL) file for instructions on getting this installed. - -## Examples: -Please look in the [examples/](examples/) folder for usage. If none exist, please contribute one! - -## Module specific notes: -* This is a rather elaborate module. Be sure to review the code before using. -* There are a number of (useful) lengthy comments and explanations in the code. -* This module plays nicely with my puppet-nfs module. Use them together :) - -## Dependencies: -* [puppetlabs-stdlib](https://github.com/puppetlabs/puppetlabs-stdlib) (required) -* my [puppet-shorewall](https://github.com/purpleidea/puppet-shorewall) module (optional) -* my [puppet-puppet](https://github.com/purpleidea/puppet-puppet) module (optional) -* my [puppet-nfs](https://github.com/purpleidea/puppet-nfs) module (optional) - -## Patches: -This code may be a work in progress. The interfaces may change without notice. -Patches are welcome, but please be patient. They are best received by email. -Please ping me if you have big changes in mind, before you write a giant patch. - -## - -Happy hacking! - diff --git a/ipa/Rakefile b/ipa/Rakefile deleted file mode 100644 index 72ce0318e..000000000 --- a/ipa/Rakefile +++ /dev/null @@ -1,48 +0,0 @@ -require 'puppetlabs_spec_helper/rake_tasks' -require 'puppet-lint/tasks/puppet-lint' -require 'puppet-syntax/tasks/puppet-syntax' - -# These two gems aren't always present, for instance -# on Travis with --without development -begin - require 'puppet_blacksmith/rake_tasks' -rescue LoadError -end - -PuppetLint.configuration.relative = true -PuppetLint.configuration.send('disable_2sp_soft_tabs') -PuppetLint.configuration.send('disable_hard_tabs') -PuppetLint.configuration.send('disable_arrow_alignment') -PuppetLint.configuration.send('disable_80chars') -PuppetLint.configuration.log_format = "%{path}:%{linenumber}:%{check}:%{KIND}:%{message}" -PuppetLint.configuration.fail_on_warnings = true - -# Forsake support for Puppet 2.6.2 for the benefit of cleaner code. -# http://puppet-lint.com/checks/class_parameter_defaults/ -PuppetLint.configuration.send('disable_class_parameter_defaults') -# http://puppet-lint.com/checks/class_inherits_from_params_class/ -PuppetLint.configuration.send('disable_class_inherits_from_params_class') - -exclude_paths = [ - "pkg/**/*", - "vendor/**/*", - "spec/**/*", - "tmp/**/*", - "rpmbuild/**/*", - "vagrant/**/*", # TODO: remove this, once we update vagrant/ to puppet4 -] -PuppetLint.configuration.ignore_paths = exclude_paths -PuppetSyntax.exclude_paths = exclude_paths - -desc 'Run acceptance tests' -RSpec::Core::RakeTask.new(:acceptance) do |t| - t.pattern = 'spec/acceptance' -end - -desc 'Run syntax, lint, and spec tests.' -task :test => [ - :syntax, - :lint, - :spec, -] - diff --git a/ipa/THANKS b/ipa/THANKS deleted file mode 100644 index 4578fc969..000000000 --- a/ipa/THANKS +++ /dev/null @@ -1,3 +0,0 @@ -Special thanks to the friendly folks in #kerberos and #freeipa for their time. -Special thanks to the freeipa developers for knowing how to set return codes! - diff --git a/ipa/docs/Red_Hat_Enterprise_Linux-6-Identity_Management_Guide-en-US.pdf b/ipa/docs/Red_Hat_Enterprise_Linux-6-Identity_Management_Guide-en-US.pdf deleted file mode 100644 index df36658b9..000000000 Binary files a/ipa/docs/Red_Hat_Enterprise_Linux-6-Identity_Management_Guide-en-US.pdf and /dev/null differ diff --git a/ipa/docs/adminkerberos.pdf b/ipa/docs/adminkerberos.pdf deleted file mode 100644 index ca0b4206d..000000000 Binary files a/ipa/docs/adminkerberos.pdf and /dev/null differ diff --git a/ipa/examples/host-excludes.pp b/ipa/examples/host-excludes.pp deleted file mode 100644 index 61eba49d8..000000000 --- a/ipa/examples/host-excludes.pp +++ /dev/null @@ -1,36 +0,0 @@ -# here is an example of how to use host excludes: -class { '::ipa::server': - shorewall => true, - host_excludes => [ - "'foo-42.example.com'", # exact string match - '"foo-bar.example.com"', # exact string match - "^[a-z0-9-]*\\-foo\\.example\\.com$", # *-foo.example.com or: - "^[[:alpha:]]{1}[[:alnum:]-]*\\-foo\\.example\\.com$", - "^foo\\-[0-9]{1,}\\.example\\.com" # foo-<\d>.example.com - ], -} - -# you'll see that you need double \\ to escape out the one we want in the match - -# if you just want to match most sane domain strings and avoid auto deletion: -class { '::ipa::server': - shorewall => true, - host_excludes => true, # automatically chooses a match all pattern -} - -# please remember that *any* match in the list will exclude a host deletion -# if you prefer to specify only one match, a single string will work too... -# if you want to be more dynamic, you can use something like: - -$match_domain = regsubst("${domain}", '\.', '\\.', 'G') -class { '::ipa::server': - domain => "${domain}", - shorewall => true, - host_excludes => [ - "^test[0-9]{1,}\\.${match_domain}\$", # test\d.domain - ], -} - -# i found some notes on available bracket expressions here: -# http://www.regular-expressions.info/posixbrackets.html - diff --git a/ipa/examples/simple-usage.pp b/ipa/examples/simple-usage.pp deleted file mode 100644 index 449105151..000000000 --- a/ipa/examples/simple-usage.pp +++ /dev/null @@ -1,36 +0,0 @@ -# here is some basic usage of the ipa module - -# on the ipa server: -$domain = $::domain -class { '::ipa::server': - domain => "${domain}", - shorewall => true, # uses my puppet-shorewall module -} - -ipa::server::host { 'nfs': # NOTE: adding .${domain} is a good idea.... - domain => "${domain}", - macaddress => "00:11:22:33:44:55", - random => true, # set a one time password randomly - locality => 'Montreal, Canada', - location => 'Room 641A', - platform => 'Supermicro', - osstring => 'CentOS 6.4 x86_64', - comment => 'Simple NFSv4 Server', - watch => true, # read and understand the docs well -} - -ipa::server::host { 'test1': - domain => "${domain}", - password => 'password', - watch => true, # read and understand the docs well -} - - -# and on the nfs server (an ipa client): -class { '::ipa::client::host::deploy': - nametag => 'nfs', # needs to match the ipa:server:host $name -} - -# if you use fqdn's for the ipa:server:host $name's, then you can deploy with: -include ipa::client::host::deploy - diff --git a/ipa/examples/simple-usage2.pp b/ipa/examples/simple-usage2.pp deleted file mode 100644 index e1288b02d..000000000 --- a/ipa/examples/simple-usage2.pp +++ /dev/null @@ -1,45 +0,0 @@ -# here is some basic usage of the ipa module, now with services! - -# on the ipa server: -$domain = $::domain -class { '::ipa::server': - domain => "${domain}", - shorewall => true, # uses my puppet-shorewall module -} - -ipa::server::host { 'nfs': # NOTE: adding .${domain} is a good idea.... - domain => "${domain}", - macaddress => "00:11:22:33:44:55", - random => true, # set a one time password randomly - locality => 'Montreal, Canada', - location => 'Room 641A', - platform => 'Supermicro', - osstring => 'CentOS 6.4 x86_64', - comment => 'Simple NFSv4 Server', - watch => true, # read and understand the docs well -} - -ipa::server::service { 'nfs': # this $name should match nfs::server => $ipa - service => 'nfs', - host => 'nfs', # needs to match ipa::server::host $name - domain => "${domain}", # used to figure out realm -} - -ipa::server::host { 'test1': - domain => "${domain}", - password => 'password', - watch => true, # read and understand the docs well -} - - -# and on the nfs server (an ipa client): -class { '::ipa::client::deploy': - nametag => 'nfs', # needs to match the ipa:server:host $name -} - -# if you use fqdn's for the ipa:server:host $name's, then you can deploy with: -#include ipa::client::host::deploy - -# there is now a single "deploy" which can be used for both hosts and services! -include ipa::client::deploy - diff --git a/ipa/examples/simple-usage3.pp b/ipa/examples/simple-usage3.pp deleted file mode 100644 index 30c6c8838..000000000 --- a/ipa/examples/simple-usage3.pp +++ /dev/null @@ -1,45 +0,0 @@ -# here is an example of how to use user excludes and types: - -# on the ipa server: -# NOTE: the 'admin' user is automatically excluded from being auto purged... -class { '::ipa::server': - shorewall => true, - user_excludes => [ - "^test[0-9]{1,}\$", # test\d - ], -} - -# create an unmanaged user -ipa::server::user { 'james': - first => 'James', - last => 'Shubin', - modify => false, - watch => false, -} - -# create a managed user -ipa::server::user { 'ntesla': - first => 'Nikola', - last => 'Tesla', - city => 'Shoreham', - state => 'New York', - postalcode => '11786', -} - -# create a user using a full principal as the primary key -# NOTE: the principal itself can't be edited without a remove/add -ipa::server::user { 'aturing/admin@EXAMPLE.COM': - first => 'Alan', - last => 'Turning', - random => true, # set a password randomly - password_file => true, # store the password in plain text ! (bad) -} - -# create a user by principal but without the instance set -ipa::server::user { 'arthur@EXAMPLE.COM': - first => 'Arthur', - last => 'Guyton', - jobtitle => 'Physiologist', - orgunit => 'Research', -} - diff --git a/ipa/files/diff.py b/ipa/files/diff.py deleted file mode 100644 index 6bdcfb7e5..000000000 --- a/ipa/files/diff.py +++ /dev/null @@ -1,587 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: this script should always be called from puppet, it's useless by itself -# NOTE: for manual command line viewing of data you can use: -# $ ipa host-show 'foo.example.com' --all --raw | cut -b 3- -# -# EXAMPLE: -# $ ./diff.py host ${valid_hostname} ${args} && echo TRUE || echo FALSE -# $ ./diff.py service ${valid_service} ${args} && echo TRUE || echo FALSE -# $ ./diff.py user ${valid_login} ${args} && echo TRUE || echo FALSE -# -# where ${} are puppet variables... -# -# NOTE: this is a particularly finicky piece of code. edit at your own risk... -# the reason it is so tricky, is because it has to cater to ipa's intricacies! - -import sys -import argparse -import ipalib -from ipapython.ssh import SSHPublicKey - -# -# helper functions -# -def p(x, f=lambda x: x): - """Pass None values through, otherwise apply function.""" - if x is None: return None - return f(x) - -def get(value, default): - if value is None: return default - else: return value - -# NOTE: a lot of places you'll see [0] because everything is wrapped in tuples! -# IPA does this for some reason, feel free to use the @left(untuple) decorator! -def untuple(x): - """Untuple a single value that was wrapped in a tuple.""" - assert type(x) == type(()), 'Expected tuple.' # safety check - assert len(x) == 1, 'Expected tuple singleton.' # safety check - return x[0] - -def listclean(x): - """Clean empty value lists to match puppets 'manage', but empty.""" - # NOTE: 0 length list values in ipa are None, ideally they should be (,) - # TODO: uncomment this first condition as well if it is ever needed... - #if x == []: return None # empties show up as 'None' in freeipa - if x == ['']: return None # this is the empty: --argument-value= - return x - -def lowerize(x): - """Transform input value into upper, recursively.""" - # TODO: dict ? - if type(x) == type([]): - return [lowerize(i) for i in x] # recurse - elif type(x) == type(()): - return tuple([lowerize(i) for i in x]) # recurse - elif isinstance(x, basestring): - return x.lower() # don't recurse - else: - return x # int's, etc... - -def upperize(x): - """Transform input value into upper, recursively.""" - # TODO: dict ? - if type(x) == type([]): - return [upperize(i) for i in x] # recurse - elif type(x) == type(()): - return tuple([upperize(i) for i in x]) # recurse - elif isinstance(x, basestring): - return x.upper() # don't recurse - else: - return x # int's, etc... - -def unicodeize(x): - """Transform input value into unicode, recursively.""" - # TODO: dict ? - if type(x) == type([]): - return [unicodeize(i) for i in x] # recurse - elif type(x) == type(()): - return tuple([unicodeize(i) for i in x]) # recurse - elif isinstance(x, basestring): - return unicode(x) # don't recurse - else: - return x # int's, etc... - -def sshfp(x): - """Transform a public ssh key into the ipa style fingerprint.""" - if type(x) == type([]): - return [sshfp(i) for i in x] # recurse - - # this code is the algorithm used in: ipalib/util.py - pubkey = SSHPublicKey(x) - fp = pubkey.fingerprint_hex_md5() - comment = pubkey.comment() - if comment: fp = u'%s %s' % (fp, comment) - fp = u'%s (%s)' % (fp, pubkey.keytype()) - return fp - -def sshdsp(x): - """Transform a public ssh key into the ipa style display.""" - if type(x) == type([]): - return [sshdsp(i) for i in x] # recurse - - # this code is the algorithm used in: ipalib/util.py - return SSHPublicKey(x).openssh() # normalize_sshpubkey - -# -# function decorators to wrap cmp functions -# -def debug(f): - """Function decorator to help debug cmp values.""" - def r(x, y): - if args.debug: - # NOTE: f.func_name works if it is closest to function! - print 'f:', f.func_name, 'x:', x, 'y:', y - return f(x, y) - - return r # we're a function decorator - -def lower(f): - """Function decorator to lower case x and y inputs.""" - # NOTE: this shows the longer versions of the decorator... - #def r(x, y): - # #_x = None if x is None else lowerize(x) - # #_y = None if y is None else lowerize(y) - # #return f(_x, _y) - # return f(p(x, lowerize), p(y, lowerize)) - #return r - return lambda x, y: f(p(x, lowerize), p(y, lowerize)) - -def upper(f): - """Function decorator to upper case x and y inputs.""" - return lambda x, y: f(p(x, upperize), p(y, upperize)) - -# TODO: is this unused because of @left(list) ? -def lists(f): - """Function decorator to ensure both inputs are lists.""" - return lambda x, y: f(p(x, list), p(y, list)) - -def sort(f): - """Function decorator to sort x and y inputs.""" - return lambda x, y: f(p(x, sorted), p(y, sorted)) - -def unique(f): - """Function decorator to remove duplicates in x and y inputs.""" - d = lambda z: list(set(z)) # remove duplicates - return lambda x, y: f(p(x, d), p(y, d)) - -def unicoded(f): - """Function decorator to unicode x and y inputs including lists, and - tuples. Recurses into compound types like lists.""" - return lambda x, y: f(p(x, unicodeize), p(y, unicodeize)) - -def left(l=lambda z: z): - """Return a function decorator using a lambda l for the left only.""" - def inner_left(f): - """Function decorator to ensure l is applied on the left.""" - return lambda x, y: f(p(x, l), y) - return inner_left - -def right(l=lambda z: z): - """Return a function decorator using a lambda l for the right only.""" - def inner_right(f): - """Function decorator to ensure l is applied on the right.""" - return lambda x, y: f(x, p(y, l)) - return inner_right - -# NOTE: we could rewrite lower,upper,lists,sort,unique and etc in terms of this -def both(l=lambda z: z): - """Return a function decorator using a lambda l for both x and y.""" - def inner_both(f): - """Function decorator to ensure l is applied to both x and y.""" - return lambda x, y: f(p(x, l), p(y, l)) - return inner_both - -# -# composed decorators -# -# http://docs.python.org/2/reference/compound_stmts.html#grammar-token-decorated -def ipalist(f): - # equivalent to decorating with: - # @left(list) - # @right(listclean) - # @unicoded - return left(list)(right(listclean)(unicoded(f))) - -def ipastr(f): - # @left(untuple) - # @unicoded - return left(untuple)(unicoded(f)) - -# -# cmp functions -# -@unicoded -def cmp_default(x, y): - return x == y - -# -# host cmp functions -# -@left(untuple) -@lower # TODO: should we drop the @lower ? -@unicoded -def cmp_host_primarykey(x, y): - return x == y - -@left(list) -@right(listclean) -@sort -@upper # ipa expects uppercase mac addresses -@unicoded -def cmp_host_macaddress(x, y): - return x == y - -#@left(list) -#@right(listclean) -#@right(sshfp) -#@unicoded -#@debug # should usually be closest to the cmp function -#def cmp_host_sshpubkeyfp(x, y): -# # in comes lists of ssh keys. we need to transform each one into the -# # format as returned by freeipa. freeipa returns a tuple of strings! -# # eg x is usually something like: -# # (u'AB:98:62:82:C0:74:47:5E:FC:36:F7:5A:D7:8F:8E:FF (ssh-dss)', -# # u'62:6D:8B:7B:3F:E3:EA:4C:50:4D:86:AA:BF:17:9D:8B (ssh-rsa)') -# return x == y - -@left(list) -@right(listclean) -@right(sshdsp) -@unicoded -@debug # should usually be closest to the cmp function -def cmp_host_ipasshpubkey(x, y): - # this is only seen when using all=True - return x == y - -@ipastr -def cmp_host_l(x, y): - return x == y - -@ipastr -def cmp_host_nshostlocation(x, y): - return x == y - -@ipastr -def cmp_host_nshardwareplatform(x, y): - return x == y - -@ipastr -def cmp_host_nsosversion(x, y): - return x == y - -@ipastr -def cmp_host_description(x, y): - return x == y - -# -# service cmp functions -# -@left(untuple) -@unicoded -def cmp_service_primarykey(x, y): - return x == y - -@left(list) -@unicoded -def cmp_service_ipakrbauthzdata(x, y): - # TODO: is it possible that instead of (u'NONE',) some return None ? - return x == y - -# -# user cmp functions -# -@left(untuple) -@unicoded -def cmp_user_primarykey(x, y): - return x == y - -@ipastr -def cmp_user_givenname(x, y): - return x == y - -@ipastr -def cmp_user_sn(x, y): - return x == y - -@ipastr -def cmp_user_cn(x, y): - return x == y - -@ipastr -def cmp_user_displayname(x, y): - return x == y - -@ipastr -def cmp_user_initials(x, y): - return x == y - -@ipastr -def cmp_user_krbprincipalname(x, y): - return x == y - -@ipalist -def cmp_user_mail(x, y): - return x == y - -@ipastr -def cmp_user_gecos(x, y): - return x == y - -@ipastr -def cmp_user_uidnumber(x, y): - return x == y - -@ipastr -def cmp_user_gidnumber(x, y): - return x == y - -@ipastr -def cmp_user_loginshell(x, y): - return x == y - -@ipastr -def cmp_user_homedirectory(x, y): - return x == y - -@left(list) -@right(listclean) -@right(sshdsp) -@unicoded -def cmp_user_ipasshpubkey(x, y): - return x == y - -@ipastr -def cmp_user_street(x, y): - return x == y - -@ipastr -def cmp_user_l(x, y): - return x == y - -@ipastr -def cmp_user_st(x, y): - return x == y - -@ipastr -def cmp_user_postalcode(x, y): - return x == y - -@ipalist -def cmp_user_telephonenumber(x, y): - return x == y - -@ipalist -def cmp_user_mobile(x, y): - return x == y - -@ipalist -def cmp_user_pager(x, y): - return x == y - -@ipalist -def cmp_user_facsimiletelephonenumber(x, y): - return x == y - -@ipastr -def cmp_user_title(x, y): - return x == y - -@ipastr -def cmp_user_ou(x, y): - return x == y - -@ipastr -def cmp_user_manager(x, y): - return x == y - -@ipastr -def cmp_user_carlicense(x, y): - return x == y - -# -# initialize ipa -# -ipalib.api.bootstrap() -ipalib.api.load_plugins() -ipalib.api.finalize() -ipalib.api.Backend.xmlclient.connect() - -# -# parser to match ipa arguments -# -parser = argparse.ArgumentParser(description='ipa difference engine') - -parser.add_argument('--debug', dest='debug', action='store_true', default=False) -parser.add_argument('--not', dest='n', action='store_true', default=False) - -subparsers = parser.add_subparsers(dest='subparser_name') - -# parent parser (contains common subparser arguments) -parent_parser = argparse.ArgumentParser(add_help=False) -parent_parser.add_argument('primarykey', action='store') # positional arg - -# NOTE: this is a mapping with dest being the --raw key to look for the data in -# NOTE: this --raw key to dest values can be seen by looking in the ipa API.txt - -# -# 'host' parser -# -parser_host = subparsers.add_parser('host', parents=[parent_parser]) -parser_host.add_argument('--macaddress', dest='macaddress', action='append') # list -# this is actually part of DNS, ignore it... -#parser_host.add_argument('--ip-address', dest='ip?', action='store') -#parser_host.add_argument('--sshpubkey', dest='sshpubkeyfp', action='append') -parser_host.add_argument('--sshpubkey', dest='ipasshpubkey', action='append') -parser_host.add_argument('--locality', dest='l', action='store') -parser_host.add_argument('--location', dest='nshostlocation', action='store') -parser_host.add_argument('--platform', dest='nshardwareplatform', action='store') -parser_host.add_argument('--os', dest='nsosversion', action='store') -parser_host.add_argument('--desc', dest='description', action='store') - -# -# 'service' parser -# -parser_service = subparsers.add_parser('service', parents=[parent_parser]) -parser_service.add_argument('--pac-type', dest='ipakrbauthzdata', action='append') - -# -# 'user' parser -# -parser_user = subparsers.add_parser('user', parents=[parent_parser]) -parser_user.add_argument('--first', dest='givenname', action='store') -parser_user.add_argument('--last', dest='sn', action='store') -parser_user.add_argument('--cn', dest='cn', action='store') -parser_user.add_argument('--displayname', dest='displayname', action='store') -parser_user.add_argument('--initials', dest='initials', action='store') -parser_user.add_argument('--principal', dest='krbprincipalname', action='store') -parser_user.add_argument('--email', dest='mail', action='append') -parser_user.add_argument('--gecos', dest='gecos', action='store') -parser_user.add_argument('--uid', dest='uidnumber', action='store') -parser_user.add_argument('--gidnumber', dest='gidnumber', action='store') -parser_user.add_argument('--shell', dest='loginshell', action='store') -parser_user.add_argument('--homedir', dest='homedirectory', action='store') -parser_user.add_argument('--sshpubkey', dest='ipasshpubkey', action='append') -parser_user.add_argument('--street', dest='street', action='store') -parser_user.add_argument('--city', dest='l', action='store') -parser_user.add_argument('--state', dest='st', action='store') -parser_user.add_argument('--postalcode', dest='postalcode', action='store') -parser_user.add_argument('--phone', dest='telephonenumber', action='append') -parser_user.add_argument('--mobile', dest='mobile', action='append') -parser_user.add_argument('--pager', dest='pager', action='append') -parser_user.add_argument('--fax', dest='facsimiletelephonenumber', action='append') -parser_user.add_argument('--title', dest='title', action='store') -parser_user.add_argument('--orgunit', dest='ou', action='store') -parser_user.add_argument('--manager', dest='manager', action='store') -parser_user.add_argument('--carlicense', dest='carlicense', action='store') - -args = parser.parse_args() - -# TODO: the process dictionaries could probably be generated by argparse data -if args.subparser_name == 'host': - process = { - 'macaddress': cmp_host_macaddress, - #'sshpubkeyfp': cmp_host_sshpubkeyfp, - 'ipasshpubkey': cmp_host_ipasshpubkey, # only seen with --all - 'l': cmp_host_l, - 'nshostlocation': cmp_host_nshostlocation, - 'nshardwareplatform': cmp_host_nshardwareplatform, - 'nsosversion': cmp_host_nsosversion, - 'description': cmp_host_description, - } - -elif args.subparser_name == 'service': - process = { - 'ipakrbauthzdata': cmp_service_ipakrbauthzdata, - } - -elif args.subparser_name == 'user': - process = { - 'givenname': cmp_user_givenname, - 'sn': cmp_user_sn, - 'cn': cmp_user_cn, - 'displayname': cmp_user_displayname, - 'initials': cmp_user_initials, - 'krbprincipalname': cmp_user_krbprincipalname, - 'mail': cmp_user_mail, - 'gecos': cmp_user_gecos, - 'uidnumber': cmp_user_uidnumber, - 'gidnumber': cmp_user_gidnumber, - 'loginshell': cmp_user_loginshell, - 'homedirectory': cmp_user_homedirectory, - 'ipasshpubkey': cmp_user_ipasshpubkey, - 'street': cmp_user_street, - 'l': cmp_user_l, - 'st': cmp_user_st, - 'postalcode': cmp_user_postalcode, - 'telephonenumber': cmp_user_telephonenumber, - 'mobile': cmp_user_mobile, - 'pager': cmp_user_pager, - 'facsimiletelephonenumber': cmp_user_facsimiletelephonenumber, - 'title': cmp_user_title, - 'ou': cmp_user_ou, - 'manager': cmp_user_manager, - 'carlicense': cmp_user_carlicense, - } - -try: - #output = ipalib.api.Command.host_show(fqdn=unicode(args.hostname)) - if args.subparser_name == 'host': - output = ipalib.api.Command.host_show(unicode(args.primarykey), all=True) - elif args.subparser_name == 'service': - output = ipalib.api.Command.service_show(unicode(args.primarykey), all=True) - elif args.subparser_name == 'user': - output = ipalib.api.Command.user_show(unicode(args.primarykey), all=True) - -except ipalib.errors.NotFound, e: - if args.debug: - print >> sys.stderr, 'Not found' - # NOTE: if we exit here, it's a bug in the puppet module because puppet - # should only be running this script for hosts that it believe exist... - sys.exit(2) - -result = output.get('result', {}) # the freeipa api returns a result key! -if args.debug: - print args - print result - -if args.subparser_name == 'host': - compare = cmp_host_primarykey - pk = result.get('fqdn', '') -elif args.subparser_name == 'service': - compare = cmp_service_primarykey - pk = result.get('krbprincipalname', '') -elif args.subparser_name == 'user': - compare = cmp_user_primarykey - pk = result.get('uid', '') - -# the pk gets untuples by the @left(untuple) compare decorators -assert compare(pk, args.primarykey), 'Primary key does not match!' - -# -# loop through all the keys to validate -# -for i in process.keys(): - - a = result.get(i, None) # value from ipa (in unicode) - b = getattr(args, i) # value from command line arg - - compare = process.get(i, cmp_default) # cmp function - compare = get(compare, cmp_default) # default None - - # NOTE: the a value (left) must always be the ipa data - # the b value (right) must correspond to the arg value - watch = (b is not None) # values of None are unmanaged - if watch and not(compare(a, b)): # run the cmp! - if args.debug: - # TODO: compare could return the post decorated x and y - # which we're actually comparing and print them here... - # this would give us more information about the unmatch - print >> sys.stderr, ('Unmatched on %s between %s and %s' % (i, a, b)) - if args.n: - sys.exit(0) - else: - sys.exit(1) - -if args.n: - sys.exit(1) -else: - sys.exit(0) # everything matches - diff --git a/ipa/lib/facter/ipa_host.rb b/ipa/lib/facter/ipa_host.rb deleted file mode 100644 index b91ec65cc..000000000 --- a/ipa/lib/facter/ipa_host.rb +++ /dev/null @@ -1,35 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -require 'facter' -require 'resolv' - -# try and pick the _right_ ip that ipa should use by default... -fqdn = Facter.value('fqdn') -if not fqdn.nil? - ip = Resolv.getaddress "#{fqdn}" - if not ip.nil? - Facter.add('ipa_host_ip') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - ip - } - end - end -end - -# vim: ts=8 diff --git a/ipa/lib/facter/ipa_installed.rb b/ipa/lib/facter/ipa_installed.rb deleted file mode 100644 index d236a459a..000000000 --- a/ipa/lib/facter/ipa_installed.rb +++ /dev/null @@ -1,57 +0,0 @@ -# Simple ipa templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: this fact creates boolean string values that others can read for status -require 'facter' - -# find the module_vardir -dir = Facter.value('puppet_vardirtmp') # nil if missing -if dir.nil? # let puppet decide if present! - dir = Facter.value('puppet_vardir') - if dir.nil? - var = nil - else - var = dir.gsub(/\/$/, '')+'/'+'tmp/' # ensure trailing slash - end -else - var = dir.gsub(/\/$/, '')+'/' -end - -if var.nil? - # if we can't get a valid vardirtmp, then we can't collect... - valid_dir = nil -else - module_vardir = var+'ipa/' - valid_dir = module_vardir.gsub(/\/$/, '')+'/' -end - -if not(valid_dir.nil?) and File.directory?(valid_dir) - ['ipa_client_installed', 'ipa_server_installed'].each do |key| - f = valid_dir+''+key # the full file path - if File.exists?(f) - # NOTE: sadly, empty string facts don't work :( - Facter.add(key) do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode do - Facter::Util::Resolution.exec("/bin/cat '"+f+"'") - end - end - end - end -end - -# vim: ts=8 diff --git a/ipa/lib/facter/ipa_master.rb b/ipa/lib/facter/ipa_master.rb deleted file mode 100644 index 833013e9d..000000000 --- a/ipa/lib/facter/ipa_master.rb +++ /dev/null @@ -1,98 +0,0 @@ -# Simple ipa templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: this fact creates boolean string values that others can read for status -require 'facter' - -# regexp to match an fqdn pattern, eg: ipa1.example.com -regexp = /^[a-zA-Z]{1}[a-zA-Z0-9\.\-]{0,}$/ # TODO: is this right ? -prefix = 'master_' - -# find the module_vardir -dir = Facter.value('puppet_vardirtmp') # nil if missing -if dir.nil? # let puppet decide if present! - dir = Facter.value('puppet_vardir') - if dir.nil? - var = nil - else - var = dir.gsub(/\/$/, '')+'/'+'tmp/' # ensure trailing slash - end -else - var = dir.gsub(/\/$/, '')+'/' -end - -if var.nil? - # if we can't get a valid vardirtmp, then we can't collect... - valid_dir = nil - masterdir = nil -else - module_vardir = var+'ipa/' - valid_dir = module_vardir.gsub(/\/$/, '')+'/' - masterdir = module_vardir+'replica/master/' -end - -# create fact from self-proclaimed master... needed to know who first installed -found = '' - -if not(masterdir.nil?) and File.directory?(masterdir) - Dir.glob(masterdir+prefix+'*').each do |f| - - b = File.basename(f) - # strip off leading prefix - fqdn = b[prefix.length, b.length-prefix.length] - - master = File.open(f, 'r').read.strip.downcase # read into str - if master.length > 0 and regexp.match(master) - # avoid: http://projects.puppetlabs.com/issues/22455 - - if master != fqdn - # FIXME: error: i think these should match... - puts 'ERROR' - end - - if found != '' - # FIXME: error, already found... - puts 'ERROR' - end - - found = master # save - #break # there should only be one, so no need to break - # TODO: print warning on else... - end - end -end - -# put the fqdn of the zero-th replica (the master) into a fact... look first in -# the local installation tag, and then in the found exported file if one exists -# it's inefficient to read the exported file a second time, but simpler to code -["#{valid_dir}ipa_server_replica_master", "#{masterdir}#{prefix}#{found}"].each do |key| - if File.exists?(key) - master = File.open(key, 'r').read.strip.downcase # read into str - if master.length > 0 and regexp.match(master) - - Facter.add('ipa_server_replica_master') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode do - Facter::Util::Resolution.exec("/bin/cat '"+key+"'") - end - end - break # there can only be one... - end - end -end - -# vim: ts=8 diff --git a/ipa/lib/facter/ipa_passwords.rb b/ipa/lib/facter/ipa_passwords.rb deleted file mode 100644 index 61cd7595f..000000000 --- a/ipa/lib/facter/ipa_passwords.rb +++ /dev/null @@ -1,78 +0,0 @@ -# Simple ipa templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: this fact creates the one time password facts, needed to be exported... -require 'facter' - -suffix = '.password' -found = [] # create list of values - -# find the module_vardir -dir = Facter.value('puppet_vardirtmp') # nil if missing -if dir.nil? # let puppet decide if present! - dir = Facter.value('puppet_vardir') - if dir.nil? - var = nil - else - var = dir.gsub(/\/$/, '')+'/'+'tmp/' # ensure trailing slash - end -else - var = dir.gsub(/\/$/, '')+'/' -end - -if var.nil? - # if we can't get a valid vardirtmp, then we can't collect... - valid_hostpassdir = nil -else - module_vardir = var+'ipa/' - hostpassdir = module_vardir+'hosts/passwords/' - valid_hostpassdir = hostpassdir.gsub(/\/$/, '')+'/' -end - -if not(valid_hostpassdir.nil?) and File.directory?(valid_hostpassdir) - Dir.glob(valid_hostpassdir+'*'+suffix).each do |f| - b = File.basename(f) - g = b.split('.') # $name.password - if g.length >= 2 and ('.'+g.pop()) == suffix - x = g.join('.') # in case value had dots in it. - - #has_password = Facter::Util::Resolution.exec("/usr/bin/ipa host-show '"+x+"' --raw | /usr/bin/tr -d ' ' | /bin/grep '^has_password:' | /bin/cut -b 14- | /usr/bin/tr '[A-Z]' '[a-z]'") or 'nil' - key = ('ipa_host_'+x+'_password').gsub(/\./, '_') - found.push(key) # make a list of keys - - # NOTE: sadly, empty string facts don't work :( - Facter.add(key) do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode do - Facter::Util::Resolution.exec("/bin/cat '"+f+"'") - # single equals sign for test ! - #Facter::Util::Resolution.exec("/usr/bin/test \"`/usr/bin/ipa host-show '"+x+"' --raw | /usr/bin/tr -d ' ' | /bin/grep '^has_password:' | /bin/cut -b 14-`\" = 'True' && /bin/cat '"+f+"'") - end - end - end - end -end - -# make a list of keys... might be useful and helps to know this fact is working -Facter.add('ipa_host_passwords') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - found.join(',') - } -end - -# vim: ts=8 diff --git a/ipa/lib/facter/ipa_peering.rb b/ipa/lib/facter/ipa_peering.rb deleted file mode 100644 index 648c53ecb..000000000 --- a/ipa/lib/facter/ipa_peering.rb +++ /dev/null @@ -1,161 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -require 'facter' - -# uuid regexp -regexp = /^[a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12}$/ -prefix = 'peer_' - -# find the module_vardir -dir = Facter.value('puppet_vardirtmp') # nil if missing -if dir.nil? # let puppet decide if present! - dir = Facter.value('puppet_vardir') - if dir.nil? - var = nil - else - var = dir.gsub(/\/$/, '')+'/'+'tmp/' # ensure trailing slash - end -else - var = dir.gsub(/\/$/, '')+'/' -end - -if var.nil? - # if we can't get a valid vardirtmp, then we can't continue - module_vardir = nil - peerdir = nil - uuidfile = nil -else - module_vardir = var+'ipa/' - peerdir = module_vardir+'replica/peering/' - uuidfile = peerdir+'uuid' -end - -# NOTE: module specific mkdirs, needed to ensure there is no blocking/deadlock! -if not(var.nil?) and not File.directory?(var) - Dir::mkdir(var) -end - -if not(module_vardir.nil?) and not File.directory?(module_vardir) - Dir::mkdir(module_vardir) -end - -if not(peerdir.nil?) and not File.directory?(peerdir) - Dir::mkdir(File.expand_path('..', peerdir)) - Dir::mkdir(peerdir) -end - -# NOTE: each host is given a "uuidgen -t" based create time and they are sorted -# according to that time first, and alphabetically second. the idea is that the -# chronological order provides a consistent, but decentralized ordering that is -# needed so that subsequent joins are always sorted to the end of the uuid list - -# generate uuid and parent directory if they don't already exist... -if not(module_vardir.nil?) and File.directory?(module_vardir) - - create = false - # create a uuid and store it in our vardir if it doesn't already exist! - if File.directory?(peerdir) - - if File.exist?(uuidfile) - test = File.open(uuidfile, 'r').read.strip.downcase # read into str - # skip over uuid's of the wrong length or that don't match (security!!) - if test.length == 36 and regexp.match(test) - create = false - else - create = true - end - else - create = true - end - end - - if create - # NOTE: this is a time based uuid ! - result = system("/usr/bin/uuidgen -t > '" + uuidfile + "'") - if not(result) - # TODO: print warning - end - end -end - -# create the fact if the uuid file contains a valid uuid -if not(uuidfile.nil?) and File.exist?(uuidfile) - uuid = File.open(uuidfile, 'r').read.strip.downcase # read into str - # skip over uuid's of the wrong length or that don't match (security!!) - if uuid.length == 36 and regexp.match(uuid) - Facter.add('ipa_server_replica_uuid') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - # don't reuse uuid variable to avoid bug #: - # http://projects.puppetlabs.com/issues/22455 - uuid - } - end - # TODO: print warning on else... - end -end - -# create facts from externally collected peer files -peer = '' -found = {} -if not(peerdir.nil?) and File.directory?(peerdir) - Dir.glob(peerdir+prefix+'*').each do |f| - - b = File.basename(f) - # strip off leading prefix - fqdn = b[prefix.length, b.length-prefix.length] - - peer = File.open(f, 'r').read.strip.downcase # read into str - if peer.length > 0 and regexp.match(peer) - # avoid: http://projects.puppetlabs.com/issues/22455 - found[fqdn] = peer - # TODO: print warning on else... - end - end -end - -# FIXME: ensure that this properly sorts by uuidgen -t times... -# sort chronologically by time based uuid -# thanks to: Pádraig Brady for the sort implementation... -sorted = found.inject({}){ |h,(k,v)| h[k]=v.split('-'); h }.sort_by { |k,v| [v[2], v[1], v[0], v[3], v[4]] }.map { |x| x[0] } - -sorted.each do |x| - Facter.add('ipa_server_replica_uuid_'+x) do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - found[x] - } - end -end - -# list of generated ipa_server_replica_uuid's -Facter.add('ipa_server_replica_uuid_facts') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - sorted.collect {|x| 'ipa_server_replica_uuid_'+x }.join(',') - } -end - -Facter.add('ipa_server_replica_peers') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - sorted.join(',') - } -end - -# vim: ts=8 diff --git a/ipa/lib/facter/ipa_replica.rb b/ipa/lib/facter/ipa_replica.rb deleted file mode 100644 index 4e53bd855..000000000 --- a/ipa/lib/facter/ipa_replica.rb +++ /dev/null @@ -1,71 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -require 'facter' - -# regexp to match an fqdn pattern, eg: ipa1.example.com -regexp = /^[a-zA-Z]{1}[a-zA-Z0-9\.\-]{0,}$/ # TODO: is this right ? -prefix = 'replica-info-' -ending = '.gpg' - -# find the module_vardir -dir = Facter.value('puppet_vardirtmp') # nil if missing -if dir.nil? # let puppet decide if present! - dir = Facter.value('puppet_vardir') - if dir.nil? - var = nil - else - var = dir.gsub(/\/$/, '')+'/'+'tmp/' # ensure trailing slash - end -else - var = dir.gsub(/\/$/, '')+'/' -end - -if var.nil? - # if we can't get a valid vardirtmp, then we can't continue - valid_replicadir = nil -else - module_vardir = var+'ipa/' - valid_replicadir = module_vardir.gsub(/\/$/, '')+'/replica/install/' -end - -found = [] - -if not(valid_replicadir.nil?) and File.directory?(valid_replicadir) - Dir.glob(valid_replicadir+prefix+'*'+ending).each do |f| - b = File.basename(f) - - g = b.slice(prefix.length, b.length-prefix.length-ending.length) - - if g.length > 0 and regexp.match(g) - if not found.include?(g) - found.push(g) - end - # TODO: print warning on else... - end - end -end - -Facter.add('ipa_replica_prepared_fqdns') do - #confine :operatingsystem => %w{CentOS, RedHat, Fedora} - setcode { - # TODO: facter should support native list types :) - found.sort.join(',') - } -end - -# vim: ts=8 diff --git a/ipa/lib/facter/ipa_version.rb b/ipa/lib/facter/ipa_version.rb deleted file mode 100644 index f19761ec5..000000000 --- a/ipa/lib/facter/ipa_version.rb +++ /dev/null @@ -1,45 +0,0 @@ -# Simple ipa templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -require 'facter' - -# get the yum path. this fact can come from an external fact set in: params.pp -yum = Facter.value('ipa_program_yum').to_s.chomp -if yum == '' - yum = `which yum 2> /dev/null`.chomp - if yum == '' - yum = '/usr/bin/yum' - end -end - -ipa = Facter.value('ipa_package_ipa_server').to_s.chomp -if ipa == '' - ipa = 'ipa-server' -end - -#confine :operatingsystem => %w{CentOS, RedHat, Fedora} -# TODO: add a long TTL to avoid repeated yum noise -cmdout = Facter::Util::Resolution.exec(yum+" info "+ipa+" 2> /dev/null | /bin/grep '^Version' | /bin/awk -F ':' '{print $2}'") -if cmdout != nil - Facter.add('ipa_version') do - setcode { - cmdout.strip - } - end -end - -# vim: ts=8 diff --git a/ipa/lib/puppet/parser/functions/ipa_topology_flat.rb b/ipa/lib/puppet/parser/functions/ipa_topology_flat.rb deleted file mode 100644 index 299a1fb9a..000000000 --- a/ipa/lib/puppet/parser/functions/ipa_topology_flat.rb +++ /dev/null @@ -1,77 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# TODO: is 'flat' the correct topological name for what this algorithm outputs? - -module Puppet::Parser::Functions - newfunction(:ipa_topology_flat, :type => :rvalue, :doc => <<-'ENDHEREDOC') do |args| - Return an ipa N-N topology from a sorted list of hosts - - Example: - - $valid_peers = ipa_topology_flat($peers) - notice("valid peers is: ${valid_peers}") - - This function is used internally for building automatic topologies. - - ENDHEREDOC - - Puppet::Parser::Functions.function('warning') # load function - # signature: replica, bricks -> bricks - unless args.length == 1 - raise Puppet::ParseError, "ipa_topology_flat(): wrong number of arguments (#{args.length}; must be 1)" - end - if not(args[0].is_a?(Array)) - raise Puppet::ParseError, "ipa_topology_flat(): expects the first argument to be an array, got #{args[0].inspect} which is of type #{args[0].class}" - end - - peers = args[0] - - if peers.uniq.length != peers.length # there are duplicates! - raise Puppet::ParseError, "ipa_topology_flat(): duplicates were found in the first argument!" - end - - # NOTE: need at least one - if peers.length < 1 - function_warning(["ipa_topology_flat(): peer list is empty"]) - return {} - end - - # if we only have one peer, and it's me, then topology is empty - if peers.length == 1 and peers[0] == lookupvar('fqdn') - return {} - end - - result = {} - - peers.each do |x| - - same = peers.dup # copy... to not destroy peers! - if same.delete(x).nil? # normally returns the value... - # TODO: return programming error: delete failed - end - - # connect to every peer except yourself - result[x] = same - - end - - result # return - end -end - -# vim: ts=8 diff --git a/ipa/lib/puppet/parser/functions/ipa_topology_ring.rb b/ipa/lib/puppet/parser/functions/ipa_topology_ring.rb deleted file mode 100644 index 6a6c35a7b..000000000 --- a/ipa/lib/puppet/parser/functions/ipa_topology_ring.rb +++ /dev/null @@ -1,81 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# TODO: is 'ring' the correct topological name for what this algorithm outputs? - -module Puppet::Parser::Functions - newfunction(:ipa_topology_ring, :type => :rvalue, :doc => <<-'ENDHEREDOC') do |args| - Return an ipa ring topology from a sorted list of hosts - - Example: - - $valid_peers = ipa_topology_ring($peers) - notice("valid peers is: ${valid_peers}") - - This function is used internally for building automatic topologies. - - ENDHEREDOC - - Puppet::Parser::Functions.function('warning') # load function - # signature: replica, bricks -> bricks - unless args.length == 1 - raise Puppet::ParseError, "ipa_topology_ring(): wrong number of arguments (#{args.length}; must be 1)" - end - if not(args[0].is_a?(Array)) - raise Puppet::ParseError, "ipa_topology_ring(): expects the first argument to be an array, got #{args[0].inspect} which is of type #{args[0].class}" - end - - peers = args[0] - - if peers.uniq.length != peers.length # there are duplicates! - raise Puppet::ParseError, "ipa_topology_ring(): duplicates were found in the first argument!" - end - - # NOTE: need at least one - if peers.length < 1 - function_warning(["ipa_topology_ring(): peer list is empty"]) - return {} - end - - # if we only have one peer, and it's me, then topology is empty - if peers.length == 1 and peers[0] == lookupvar('fqdn') - return {} - end - - result = {} - - i = 0 - while i < peers.length do - - x = peers[i] # from - if i < peers.length-1 - y = peers[i+1] # to - else - y = peers[0] # wrap around - end - - # store value as a list, in the ring case of length 1 - result[x] = [y] - - i+=1 # i++ - end - - result # return - end -end - -# vim: ts=8 diff --git a/ipa/manifests/client.pp b/ipa/manifests/client.pp deleted file mode 100644 index bae6a883d..000000000 --- a/ipa/manifests/client.pp +++ /dev/null @@ -1,213 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class ipa::client( - $name = '', # what define was called with... - $hostname = $::hostname, - $domain = $::domain, - $realm = '', # defaults to upcase($domain) - $server = '', # ipa server - $password = '', # seemingly no password restrictions... - - $admin = false, # should we get admin tools installed ? - $ssh = false, - $sshd = false, - $ntp = false, - $ntp_server = '', - - $shorewall = false, # TODO ? - $zone = 'net', - $allow = 'all', - $debug = false, - $ensure = present # TODO: support uninstall with 'absent' -) { - include ipa::vardir - - case $::osfamily { - 'RedHat': { - if $::operatingsystem == 'Fedora' { - $ipa_client_pkgname = 'freeipa-client' - $ipa_admintools_pkgname = 'freeipa-admintools' - } else { - $ipa_client_pkgname = 'ipa-client' - $ipa_admintools_pkgname = 'ipa-admintools' - } - } - default: { - fail('Unsupported OS') - } - } - - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - $valid_domain = downcase($domain) # TODO: validate ? - $valid_realm = $realm ? { - '' => upcase($valid_domain), - default => upcase($realm), - } - - $valid_server = "${server}" ? { - '' => "ipa.${valid_domain}", # default if unspecified... - default => "${server}", - } - - if "${hostname}" != delete("${hostname}", '.') { - fail('The $hostname value must not contain periods. It is not the FQDN.') - } - - if "${valid_domain}" == '' { - fail('A $domain value is required.') - } - - $valid_name = "${name}" ? { - '' => "${hostname}.${domain}", # defaults to fqdn if empty... - default => "${name}", # this could be fqdn or not... - } - - if $debug { - # just used for debugging - $valid_fqdn = "${hostname}.${valid_domain}" - $valid_principal = "host/${valid_fqdn}@${valid_realm}" - notify { "ipa-client-host-${name}": - message => "Host: '${name}', principal: '${valid_principal}'", - } - } - - package { 'ipa-client': - name => $ipa_client_pkgname, - ensure => present, - } - - # an administrator machine requires the ipa-admintools package as well: - package { 'ipa-admintools': - ensure => $admin ? { - true => present, - false => absent, - }, - name => $ipa_admintools_pkgname, - require => Package['ipa-client'], - } - - # store the passwords in text files instead of having them on cmd line! - # TODO: storing plain text passwords is not good, so what should we do? - file { "${vardir}/password": - content => "${password}\n", # temporarily secret... - owner => root, - group => nobody, - mode => 600, # u=rw,go= - backup => false, - require => File["${vardir}/"], - ensure => present, - } - # these are the arguments to ipa-server-install in the prompted order - $args01 = "--hostname='${hostname}.${valid_domain}'" - $args02 = "--domain='${valid_domain}'" - $args03 = "--realm='${valid_realm}'" - $args04 = "--server='${valid_server}'" - #$args05 = "--password='${password}'" # password to join IPA realm - $args05 = "--password=`/bin/cat '${vardir}/password'`" - - $args06 = $ssh ? { - true => '', - default => '--no-ssh', - } - - $args07 = $sshd ? { - true => '', - default => '--no-sshd', - } - - $args08 = $ntp ? { - true => '', - default => '--no-ntp', - } - - $args09 = $ntp_server ? { - '' => '', - default => $ntp ? { - true => "--ntp-server=${ntp_server}", - default => '', - }, - } - - $arglist = ["${args01}", "${args02}", "${args03}", "${args04}", "${args05}", "${args06}", "${args07}", "${args08}", "${args09}"] - #$args = inline_template('<%= arglist.delete_if {|x| x.empty? }.join(" ") %>') - $args = join(delete($arglist, ''), ' ') - - # this makes the install wait if a valid password hasn't been exported! - # this happens because it takes a second run of the ipa puppet after it - # has configured the host, because, on this second puppet run, the fact - # will finally now see the password, and it can be properly exported... - $has_auth = "${password}" ? { - '' => 'false', - default => 'true', - } - $onlyif = "/usr/bin/test '${has_auth}' = 'true'" - $unless = "/usr/bin/python -c 'import sys,ipapython.sysrestore; sys.exit(0 if ipapython.sysrestore.FileStore(\"/var/lib/ipa-client/sysrestore\").has_files() else 1)'" - exec { "/usr/sbin/ipa-client-install ${args} --unattended": - logoutput => on_failure, - onlyif => "${onlyif}", # needs a password or authentication... - unless => "${unless}", # can't install if already installed... - require => [ - Package['ipa-client'], - File["${vardir}/password"], - ], - alias => 'ipa-install', # same alias as server to prevent both! - } - - # this file is a tag that lets nfs know that the ipa host is now ready! - file { "${vardir}/ipa_client_installed": - content => "true\n", - owner => root, - group => nobody, - mode => 600, # u=rw,go= - backup => false, - require => [ - File["${vardir}/"], - Exec['ipa-install'], - ], - ensure => present, - } - - # normally when this resource is created by collection, the password is - # exported which allows the client to boostrap itself without a ticket. - # once this host gets built, the password gets "used" on the ipa server - # which causes it to show 'has_password: False', which would cause that - # password to get regenerated, however this exported resource will stop - # that from happening when it gets collected on the server as a tag. if - # this client dissapears, then, the exported resource should eventually - # get removed when a client runs puppet, which will cause a new pass to - # be created for the new ipa client install if we happen to want one... - #if "${password}" == '' { - @@ipa::server::host::pwtag { "${valid_name}": - tag => "${valid_name}", # collection by name is buggy, use tag! - } - #} - - # send ssh keys back so that server updates its database if they change - @@ipa::server::host::sshpubkeys { "${valid_name}": - # FIXME: redo this resource so that we specify an array instead - # this is needed in case we decide to export other keys perhaps - # it's more important because static things aren't very elegant - rsa => "${::sshrsakey}", # built in fact - dsa => "${::sshdsakey}", # built in fact - tag => "${valid_name}", # same name as ipa::server::host - } -} - -# vim: ts=8 diff --git a/ipa/manifests/client/deploy.pp b/ipa/manifests/client/deploy.pp deleted file mode 100644 index 8e04a3fb3..000000000 --- a/ipa/manifests/client/deploy.pp +++ /dev/null @@ -1,66 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: use this to deploy all the @@ipa::client::* exported resources on clients -# the $nametag variable should match the $name value of the server/client::host -class ipa::client::deploy( - $hostname = $::hostname, - $domain = $::domain, - $server = '', - $nametag = '', # pick a tag to collect... - $debug = false -) { - $valid_domain = downcase($domain) # TODO: validate ? - - # if $hostname has dots, then assume it's a fqdn, if not, we add $domain - $valid_fqdn = delete("${hostname}", '.') ? { - "${hostname}" => "${hostname}.${valid_domain}", # had no dots present - default => "${hostname}", # had dots present... - } - - # NOTE: the resource collects by fqdn; one good reason to use the fqdn! - # sure you can override this by choosing your own $name value, but why? - $valid_tag = "${nametag}" ? { - '' => "${valid_fqdn}", - default => "${nametag}", - } - - # TODO: if i had more than one arg to decide to override, then i would - # have to build a big tree of nested choices... this is one more place - # where puppet shows it's really not a mature language yet. oh well... - # the host field is also the argument passed to the exported resource, - # and it is the $valid_host variable that came from the server service - if "${server}" == '' { - Ipa::Client::Host <<| tag == "${valid_tag}" |>> { - debug => $debug, - } - Ipa::Client::Service <<| host == "${valid_tag}" |>> { - debug => $debug, - } - } else { - Ipa::Client::Host <<| tag == "${valid_tag}" |>> { - server => "${server}", # override... - debug => $debug, - } - Ipa::Client::Service <<| host == "${valid_tag}" |>> { - server => "${server}", # override... - debug => $debug, - } - } -} - -# vim: ts=8 diff --git a/ipa/manifests/client/host.pp b/ipa/manifests/client/host.pp deleted file mode 100644 index bdbf017f6..000000000 --- a/ipa/manifests/client/host.pp +++ /dev/null @@ -1,80 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -define ipa::client::host( - # NOTE: this should be a copy of most of the params from ipa::client - $domain = '', - $realm = '', - $server = '', - $password = '', - $admin = false, - $ssh = false, - $sshd = false, - $ntp = false, - $ntp_server = '', - $shorewall = false, - $zone = 'net', - $allow = 'all', - $debug = false, - $ensure = present # TODO -) { - # $name should be a fqdn, split it into the $hostname and $domain args! - # NOTE: a regexp wizard could possibly write something to match better! - #$r = '^([a-z][a-z0-9\-]*)\.([a-z0-9\.\-]*)$' - $r = '^([a-z][a-z0-9\-]*)(\.{0,1})([a-z0-9\.\-]*)$' - $h = regsubst("${name}", $r, '\1') - $x = regsubst("${name}", $r, '\2') # the dot - $d = regsubst("${name}", $r, '\3') - - $valid_hostname = "${h}" - $valid_domain = "${d}" ? { - '' => "${domain}" ? { - '' => "${::domain}", - default => "${domain}", - }, - default => "${d}" ? { # we need to check this matches $domain - "${domain}" => "${d}", # they match, okay phew - default => '', # no match, set '' to trigger an error! - }, - } - # this error condition is very important because '' is used as trigger! - if "${valid_domain}" == '' { - fail('A $domain inconsistency was found.') - } - - class { '::ipa::client': - # NOTE: this should transfer most of the params from ipa::client - name => $name, # often the fqdn, but necessarily - hostname => $valid_hostname, - domain => $valid_domain, - realm => $realm, - server => $server, - password => $password, - admin => $admin, - ssh => $ssh, - sshd => $sshd, - ntp => $ntp, - ntp_server => $ntp_server, - shorewall => $shorewall, - zone => $zone, - allow => $allow, - debug => $debug, - ensure => $ensure, - } -} - -# vim: ts=8 diff --git a/ipa/manifests/client/host/deploy.pp b/ipa/manifests/client/host/deploy.pp deleted file mode 100644 index 6dd89b35d..000000000 --- a/ipa/manifests/client/host/deploy.pp +++ /dev/null @@ -1,57 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: use this to deploy the exported resource @@ipa::client::host on clients -#define ipa::client::host::deploy( -class ipa::client::host::deploy( - $hostname = $::hostname, - $domain = $::domain, - $server = '', - $nametag = '', # pick a tag to collect... - $debug = false -) { - $valid_domain = downcase($domain) # TODO: validate ? - - # if $hostname has dots, then assume it's a fqdn, if not, we add $domain - $valid_fqdn = delete("${hostname}", '.') ? { - "${hostname}" => "${hostname}.${valid_domain}", # had no dots present - default => "${hostname}", # had dots present... - } - - # NOTE: the resource collects by fqdn; one good reason to use the fqdn! - # sure you can override this by choosing your own $name value, but why? - $valid_tag = "${nametag}" ? { - '' => "${valid_fqdn}", - default => "${nametag}", - } - - # TODO: if i had more than one arg to decide to override, then i would - # have to build a big tree of nested choices... this is one more place - # where puppet shows it's really not a mature language yet. oh well... - if "${server}" == '' { - Ipa::Client::Host <<| tag == "${valid_tag}" |>> { - debug => $debug, - } - } else { - Ipa::Client::Host <<| tag == "${valid_tag}" |>> { - server => "${server}", # override... - debug => $debug, - } - } -} - -# vim: ts=8 diff --git a/ipa/manifests/client/service.pp b/ipa/manifests/client/service.pp deleted file mode 100644 index 98ae77b1b..000000000 --- a/ipa/manifests/client/service.pp +++ /dev/null @@ -1,186 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# FIXME: if this resource is removed, how do we revoke the key from the keytab? -# FIXME: it seems that after a kdestroy/kinit cycle happens, it is then revoked -# FIXME: a freeipa expert should verify and confirm that it's safe/ok this way! -# this runs ipa-getkeytab magic, to setup the keytab, for a service on a client -define ipa::client::service( - $service = '', # nfs, HTTP, ldap - $host = '', # should match $name of ipa::client::host - $domain = '', # must be the empty string by default - $realm = '', - $principal = '', # after all that, you can override principal... - $server = '', # where the client will find the ipa server... - $keytab = '', # defaults to /etc/krb5.keytab - $comment = '', - $debug = false, - $ensure = present -) { - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - # NOTE: much of the following code is almost identical to that up above - # TODO: a better regexp magician could probably do a better job :) - # nfs/nfs.example.com@EXAMPLE.COM - $r = '^([a-zA-Z][a-zA-Z0-9]*)(/([a-z][a-z\.\-]*)(@([A-Z][A-Z\.\-]*)){0,1}){0,1}$' - - $a = regsubst("${name}", $r, '\1') # service (nfs) - $b = regsubst("${name}", $r, '\3') # fqdn (nfs.example.com) - $c = regsubst("${name}", $r, '\5') # realm (EXAMPLE.COM) - - # service: first try to get value from arg, then fall back to $a (name) - $valid_service = "${service}" ? { - '' => "${a}", # get from $name regexp - default => "${service}", - } - if "${valid_service}" == '' { - # NOTE: if we see this message it might be a regexp pattern bug - fail('The $service must be specified.') - } - - # host: first try to get value from arg, then fall back to $b - # this is not necessarily the fqdn, but it could be. both are possible! - $valid_host = "${host}" ? { - '' => "${b}", # get from $name regexp - default => "${host}", - } - # this error will probably prevent a later error in $valid_domain - if "${valid_host}" == '' { - fail('The $host must be specified.') - } - - # parse the fqdn from $valid_host - $r2 = '^([a-z][a-z0-9\-]*)(\.{0,1})([a-z0-9\.\-]*)$' - #$h = regsubst("${valid_host}", $r2, '\1') # hostname - $d = regsubst("${valid_host}", $r2, '\3') # domain - - $valid_domain = delete("${valid_host}", '.') ? { - "${valid_host}" => "${domain}" ? { # no dots, not an fqdn! - '' => "${ipa::client::domain}" ? { # NOTE: client! - '' => "${::domain}", # default to global val - default => "${ipa::client::domain}", # main! - }, - default => "${domain}", - }, - default => "${domain}" ? { # dots, it's an fqdn... - '' => "${d}", # okay, used parsed value, it had dots! - "${d}" => "${domain}", # they match, okay phew - default => '', # no match, set '' to trigger an error! - }, - } - - # this error condition is very important because '' is used as trigger! - if "${valid_domain}" == '' { - fail('The $domain must be specified.') - } - - $valid_fqdn = delete("${valid_host}", '.') ? { # does it have any dots - "${valid_host}" => "${valid_host}.${valid_domain}", - default => "${valid_host}", # it had dot(s) present - } - - $valid_realm = "${realm}" ? { - '' => "${c}" ? { # get from $name regexp - '' => upcase($valid_domain), # a backup plan default - default => "${c}", # got from $name regexp - }, - default => "${realm}", - } - - # sanity checking, this should probably not happen - if "${valid_realm}" == '' { - fail('The $realm must be specified.') - } - - $valid_server = "${server}" ? { - '' => "${ipa::client::valid_server}", - default => "${server}", - } - - # sanity checking, this should probably not happen - if "${valid_server}" == '' { - fail('The $server must be specified.') - } - - $valid_principal = "${principal}" ? { - '' => "${valid_service}/${valid_fqdn}@${valid_realm}", - default => "${principal}", # just do what you want - } - - $valid_keytab = "${keytab}" ? { # TODO: validate - '' => '/etc/krb5.keytab', - default => "${keytab}", - } - - if $debug { - notify { "ipa-client-service-${name}": - message => "Service: '${name}', principal: '${valid_principal}'", - } - } - - # TODO: it would be great to put this kinit code into a single class to - # be used by each service, but it's not easily possible if puppet stops - # us from declaring identical class objects when they're seen as dupes! - # there is ensure_resource, but it's a hack and class might not work... - # NOTE: i added a lifetime of 1 hour... no sense needing any longer - $rr = "krbtgt/${valid_realm}@${valid_realm}" - $tl = '900' # 60*15 => 15 minutes - $admin = "host/${valid_fqdn}@${valid_realm}" # use this principal... - exec { "/usr/bin/kinit -k -t '${valid_keytab}' ${admin} -l 1h": - logoutput => on_failure, - #unless => "/usr/bin/klist -s", # is there a credential cache - # NOTE: we need to check if the ticket has at least a certain - # amount of time left. if not, it could expire mid execution! - # this should definitely get patched, but in the meantime, we - # check that the current time is greater than the valid start - # time (in seconds) and that we have within $tl seconds left! - unless => "/usr/bin/klist -s && /usr/bin/test \$(( `/bin/date +%s` - `/usr/bin/klist | /bin/grep -F '${rr}' | /bin/awk '{print \$1\" \"\$2}' | /bin/date --file=- +%s` )) -gt 0 && /usr/bin/test \$(( `/usr/bin/klist | /bin/grep -F '${rr}' | /bin/awk '{print \$3\" \"\$4}' | /bin/date --file=- +%s` - `/bin/date +%s` )) -gt ${tl}", - require => [ - Package['ipa-client'], - Exec['ipa-install'], - Ipa::Client::Host["${valid_host}"], - ], - alias => "ipa-client-service-kinit-${name}", - } - - $args01 = "--server='${valid_server}'" # contact this KDC server (ipa) - $args02 = "--principal='${valid_principal}'" # the service principal - $args03 = "--keytab='${valid_keytab}'" - - $arglist = ["${args01}", "${args02}", "${args03}"] - $args = join(delete($arglist, ''), ' ') - - $kvno_bool = "/usr/bin/kvno -q '${valid_principal}'" - exec { "/usr/sbin/ipa-getkeytab ${args}": - logoutput => on_failure, - # check that the KDC has a valid ticket available there - # check that the ticket version no. matches our keytab! - unless => "${kvno_bool} && /usr/bin/klist -k -t '${valid_keytab}' | /bin/awk '{print \$4\": kvno = \"\$1}' | /bin/sort | /usr/bin/uniq | /bin/grep -F '${valid_principal}' | /bin/grep -qxF \"`/usr/bin/kvno '${valid_principal}'`\"", - require => [ - # these deps are done in the kinit - #Package['ipa-client'], - #Exec['ipa-install'], - #Ipa::Client::Host["${valid_host}"], - Exec["ipa-client-service-kinit-${name}"], - ], - #alias => "ipa-getkeytab-${name}", - } -} - -# vim: ts=8 diff --git a/ipa/manifests/client/service/deploy.pp b/ipa/manifests/client/service/deploy.pp deleted file mode 100644 index 9c8e053f3..000000000 --- a/ipa/manifests/client/service/deploy.pp +++ /dev/null @@ -1,46 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: use this to deploy the exported resource @@ipa::client::service -class ipa::client::service::deploy( - $server = '', - $nametag = '', # pick a tag to collect... - $debug = false -) { - - # NOTE: the resource collects by fqdn; one good reason to use the fqdn! - # sure you can override this by choosing your own $name value, but why? - $valid_tag = "${nametag}" ? { - '' => "${::fqdn}", # if we're smart, this is what is used! - default => "${nametag}", - } - - # the host field is also the argument passed to the exported resource, - # and it is the $valid_host variable that came from the server service - if "${server}" == '' { - Ipa::Client::Service <<| host == "${valid_tag}" |>> { - debug => $debug, - } - } else { - Ipa::Client::Service <<| host == "${valid_tag}" |>> { - server => "${server}", # override... - debug => $debug, - } - } -} - -# vim: ts=8 diff --git a/ipa/manifests/common.pp b/ipa/manifests/common.pp deleted file mode 100644 index 8d0f16722..000000000 --- a/ipa/manifests/common.pp +++ /dev/null @@ -1,27 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class ipa::common( - -) { - - # TODO: patch freeipa to provide this in a stable way... - $ipa_installed = "/usr/bin/python -c 'import sys,ipaserver.install.installutils; sys.exit(0 if ipaserver.install.installutils.is_ipa_configured() else 1)'" - -} - -# vim: ts=8 diff --git a/ipa/manifests/init.pp b/ipa/manifests/init.pp deleted file mode 100644 index 1fbc15f99..000000000 --- a/ipa/manifests/init.pp +++ /dev/null @@ -1,60 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# README: this is a rather complicated module to understand. read the comments! - -# NOTE: if you ever see a puppet error where an ipa exec returns with: -# ipa: ERROR: no modifications to be performed -# then please report this as a bug. This puppet module is (supposed to be) -# smart enough to only run exec's when they are actually necessary. - -# NOTE: to hack your way into the ipa web ui with ssh port forwarding, when the -# computer you are using is completely isolated from the actual ipa server, you -# could fake the dns entry in your /etc/hosts file by adding/ensuring the line: -# 127.0.0.1 ipa.example.com ipa localhost.localdomain localhost -# exists (replace example.com with your ipa domain of course) and then running: -# sudo ssh root@ipa -L 80:localhost:80 -L 443:localhost:443 # (as root !) -# to force forwarding on priviledged ports, and then point your web browser to: -# https://ipa.example.com/ipa/ui/ -# and then accept the certificate. but don't do any of this, it's an evil hack! - -# NOTE: this expects mit kerberos: http://web.mit.edu/kerberos/krb5-latest/doc/ - -# NOTE: useful ipa docs at: https://access.redhat.com/site/documentation/en-US/ -# Red_Hat_Enterprise_Linux/6/html-single/Identity_Management_Guide/index.html - -# NOTE: if on client reinstall ipa-client-install complains with: -# freeipa LDAP Error: Connect error: TLS error -8054: You are attempting -# to import a cert with the same issuer/serial as an existing cert, but -# that is not the same cert. -# just: 'rm /etc/ipa/ca.crt', bug: https://fedorahosted.org/freeipa/ticket/3537 - -# NOTE: if you wish to use the $dns option, it must be enabled at first install -# subsequent enabling/disabling is currently not supported. this is because of: -# https://fedorahosted.org/freeipa/ticket/3726 -# (ipa-dns-install needs a --uninstall option) -# and also because the DM_PASSWORD might not be available if we gpg encrypt and -# email it out after randomly generating it. This is a security feature! (TODO) <- CHANGE TO (DONE) when finished! -# we could actually support install and uninstall if that bug was resolved, and -# if we either regenerated the password, or were able to circumvent it with our -# root powers somehow. this is actually quite plausible, but not worth the time - -# TODO: maybe we could have an exported resource that creates a .k5login in the -# root home dirs of machines to give access to other admins with their tickets? - -# TODO: a ...host::dns type or similar needs to be added to manage and host ips - diff --git a/ipa/manifests/rulewrapper.pp b/ipa/manifests/rulewrapper.pp deleted file mode 100644 index d237113a4..000000000 --- a/ipa/manifests/rulewrapper.pp +++ /dev/null @@ -1,47 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: this wraps shorewall::rule so that we can add on additional fake 'tags' -define ipa::rulewrapper( - $action = '', - $source = '', - $source_ips = [], - $dest = '', - $dest_ips = [], - $proto = '', - $port = [], - $sport = [], - $original = [], - $comment = '', - $ensure = present, - $match = '' # additional tag parameter -) { - shorewall::rule { "${name}": - action => "${action}", - source => "${source}", - source_ips => $source_ips, - dest => "${dest}", - dest_ips => $dest_ips, - proto => "${proto}", - port => $port, - sport => $sport, - comment => "${comment}", - ensure => $ensure, - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server.pp b/ipa/manifests/server.pp deleted file mode 100644 index 3e148ab16..000000000 --- a/ipa/manifests/server.pp +++ /dev/null @@ -1,754 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class ipa::server( - $hostname = $::hostname, - $domain = $::domain, - $ipa_ipaddress = '', - $realm = '', # defaults to upcase($domain) - $vip = '', # virtual ip of the replica master host - $peers = {}, # specify the peering topology by fqdns - $topology = '', # specify the peering algorithm to use! - $topology_arguments = [], # list of additional arguments for algo - - # we generate these passwords locally to use for the install, but then - # we gpg encrypt and store locally and/or email to the root user. this - # requires an admin's public gpg key which is a sensible thing to have - # thanks to Jpmh from #gnupg for helping me find things in the manual! - $dm_password = '', # eight char minimum or auto-generated - $admin_password = '', # eight char minimum or auto-generated - - # if one of the above passwords is blank, you must use: $gpg_recipient - # with: $gpg_recipient, you must use: $gpg_publickey or $gpg_keyserver - $gpg_recipient = '', # must specify a valid -r value to use - $gpg_publickey = '', # can be the value or a puppet:/// uri - $gpg_keyserver = '', # use a uri like: hkp://keys.gnupg.net - $gpg_sendemail = false, # mail out the gpg encrypted password? - - $idstart = '16777216', # TODO: what is sensible? i picked 2^24 - $idmax = '', - $email_domain = '', # defaults to domain - $shell = true, # defaults to /bin/sh - $homes = true, # defaults to /home - - # packages products to install ? - $ntp = false, # opposite of ipa-server-install default - $dns = false, # must be set at install time to be used - $dogtag = false, - - $email = '', # defaults to root@domain, important... - - $vrrp = false, - $shorewall = false, - $zone = 'net', - $allow = 'all', - - # special - # NOTE: host_excludes is matched with bash regexp matching in: [[ =~ ]] - # if the string regexp passed contains quotes, string matching is done: - # $string='"hostname.example.com"' vs: $regexp='hostname.example.com' ! - # obviously, each pattern in the array is tried, and any match will do. - # invalid expressions might cause breakage! use this at your own risk!! - # remember that you are matching against the fqdn's, which have dots... - # a value of true, will automatically add the * character to match all. - $host_excludes = [], # never purge these host excludes... - $service_excludes = [], # never purge these service excludes... - $user_excludes = [], # never purge these user excludes... - $peer_excludes = [], # never purge these peer excludes... - $ensure = present # TODO: support uninstall with 'absent' -) { - $FW = '$FW' # make using $FW in shorewall easier... - - # TODO: should we always include the replica peering or only when used? - include ipa::server::replica::peering - include ipa::server::replica::master - include ipa::common - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - case $::osfamily { - 'RedHat': { - if $::operatingsystem == 'Fedora' { - $ipa_server_pkgname = 'freeipa-server' - } else { - $ipa_server_pkgname = 'ipa-server' - package { 'python-argparse': # used by diff.py - ensure => present, - before => [ - Package['ipa-server'], - File["${vardir}/diff.py"], - ], - } - } - } - default: { - fail('Unsupported OS') - } - } - - if "${vip}" != '' { - if ! ($vip =~ /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/) { - fail('You must specify a valid VIP to use.') - } - } - $valid_vip = "${vip}" - $vipif = inline_template("<%= @interfaces.split(',').find_all {|x| '${valid_vip}' == scope.lookupvar('ipaddress_'+x) }[0,1].join('') %>") - - # automatically setup vrrp on each host... - if $vrrp { - class { '::keepalived::simple': - #ip => '', - vip => "${valid_vip}", - shorewall => $shorewall, - zone => $zone, - #allow => $allow, - #password => '', - } - } - - # this is used for automatic peering... this is a list of every server! - $replica_peers_fact = "${::ipa_server_replica_peers}" # fact! - $replica_peers = split($replica_peers_fact, ',') # list! - - # NOTE: this algorithm transforms a sorted list of peers into a set of: - # from -> to pairs (as a hash), or from -> to and to -> from pairs that - # are symmetrical since peering is bi-directional... this list of hosts - # could either be determined automatically with "exported resources" or - # specified manually. just select an algorithm for automatic peering... - # the $key in the hash is the from value. the $value of the hash is the - # list of whichever hosts we should peer with, ordered by preference... - - # run the appropriate topology function here - $empty_hash = {} - $valid_peers = $topology ? { - 'flat' => ipa_topology_flat($replica_peers), - 'ring' => ipa_topology_ring($replica_peers), - #'manual' => $peers, - default => type($peers) ? { # 'manual' (default) peering... - 'hash' => $peers, # TODO: validate this data type - default => $empty_hash, # invalid data... - }, - } - - notice(inline_template('valid_peers: <%= @valid_peers.inspect %>')) - - # export the required firewalls... - if $shorewall { - # in the single host case, the topology should be an empty hash - if has_key($valid_peers, "${::fqdn}") { - ipa::server::replica::firewall { $valid_peers["${::fqdn}"]: - peer => "${::fqdn}", # match the manage type pattern - } - } - } - - $valid_hostname = "${hostname}" # TODO: validate ? - $valid_domain = downcase($domain) # TODO: validate ? - $valid_realm = $realm ? { - '' => upcase($valid_domain), - default => upcase($realm), - } - - $default_email_domain = "${email_domain}" ? { - '' => "${valid_domain}", - default => "${email_domain}", - } - ipa::server::config { 'emaildomain': - value => "${default_email_domain}", - } - - $default_shell = type($shell) ? { - 'boolean' => $shell ? { - false => false, # unmanaged - default => '/bin/sh', # the default - }, - default => "${shell}", - } - # we don't manage if value is false, otherwise it's good to go! - if ! (type($shell) == 'boolean' and (! $shell)) { - ipa::server::config { 'shell': - value => "${default_shell}", - } - } - - # TODO: the home stuff seems to not use trailing slashes. can i add it? - $default_homes = type($homes) ? { - 'boolean' => $homes ? { - false => false, # unmanaged - default => '/home', # the default - }, - default => "${homes}", - } - if ! (type($homes) == 'boolean' and (! $homes)) { - ipa::server::config { 'homes': - value => "${default_homes}", # XXX: remove trailing slash if present ? - } - } - - $valid_email = $email ? { - '' => "root@${default_email_domain}", - default => "${email}", - } - - if "${valid_hostname}" == '' { - fail('A $hostname value is required.') - } - - if "${valid_domain}" == '' { - fail('A $domain value is required.') - } - - $valid_fqdn = "${valid_hostname}.${valid_domain}" - - if $dns { - package { ['bind', 'bind-dyndb-ldap']: - ensure => present, - before => Package['ipa-server'], - } - } - - package { 'pwgen': # used to generate passwords - ensure => present, - before => Package['ipa-server'], - } - - package { 'ipa-server': - name => $ipa_server_pkgname, - ensure => present, - } - - file { "${vardir}/diff.py": # used by a few child classes - source => 'puppet:///modules/ipa/diff.py', - owner => root, - group => nobody, - mode => 700, # u=rwx - backup => false, # don't backup to filebucket - ensure => present, - require => [ - Package['ipa-server'], - File["${vardir}/"], - ], - } - - if "${dm_password}" == '' and "${gpg_recipient}" == '' { - fail('You must specify either a dm_password or a GPG id.') - } - - if "${admin_password}" == '' and "${gpg_recipient}" == '' { - fail('You must specify either an admin_password or a GPG id.') - } - - if "${gpg_recipient}" != '' { - if "${gpg_publickey}" == '' and "${gpg_keyserver}" == '' { - fail('You must specify either a keyserver or a public key.') - } - - if "${gpg_publickey}" != '' and "${gpg_keyserver}" != '' { - fail('You cannot specify a keyserver and a public key.') - } - } - - if "${gpg_recipient}" != '' { - file { "${vardir}/gpg/": - ensure => directory, # make sure this is a directory - recurse => true, # don't recurse into directory - purge => true, # don't purge unmanaged files - force => true, # don't purge subdirs and links - # group and other must not have perms or gpg complains! - mode => 600, # u=rw,go= - backup => false, - require => File["${vardir}/"], - } - - # tag - $dm_password_filename = "${vardir}/gpg/dm_password.gpg" - file { "${dm_password_filename}": - owner => root, - group => nobody, - mode => 600, # u=rw,go= - backup => false, - require => File["${vardir}/gpg/"], - ensure => present, - } - - # tag - $admin_password_filename = "${vardir}/gpg/admin_password.gpg" - file { "${admin_password_filename}": - owner => root, - group => nobody, - mode => 600, # u=rw,go= - backup => false, - require => File["${vardir}/gpg/"], - ensure => present, - } - - # tag - file { "${vardir}/gpg/pubring.gpg": - owner => root, - group => nobody, - mode => 600, # u=rw,go= - backup => false, - require => File["${vardir}/gpg/"], - ensure => present, - } - - file { "${vardir}/gpg/secring.gpg": - owner => root, - group => nobody, - mode => 600, # u=rw,go= - backup => false, - require => File["${vardir}/gpg/"], - ensure => present, - } - - # tag this file too, because the gpg 'unless' commands cause it - # get added when gpg sees that it's missing from the --homedir! - file { "${vardir}/gpg/trustdb.gpg": - owner => root, - group => nobody, - mode => 600, # u=rw,go= - backup => false, - require => File["${vardir}/gpg/"], - ensure => present, - } - } - - if "${gpg_publickey}" != '' { - $gpg_source = inline_template('<%= @gpg_publickey.start_with?("puppet:///") ? "true":"false" %>') - file { "${vardir}/gpg/pub.gpg": - content => "${gpg_source}" ? { - 'true' => undef, - default => "${gpg_publickey}", - }, - source => "${gpg_source}" ? { - 'true' => "${gpg_publickey}", - default => undef, - }, - owner => root, - group => nobody, - mode => 600, # u=rw,go= - backup => false, - before => Exec['ipa-gpg-import'], - require => File["${vardir}/gpg/"], - ensure => present, - } - } - - $gpg_cmd = "/usr/bin/gpg --homedir '${vardir}/gpg/'" # base gpg cmd! - - $gpg_import = "${gpg_publickey}" ? { - '' => "--keyserver '${gpg_keyserver}' --recv-keys '${gpg_recipient}'", - default => "--import '${vardir}/gpg/pub.gpg'", - } - - if "${gpg_recipient}" != '' { - - # check if key is already imported - $gpg_unless = "${gpg_cmd} --with-colons --fast-list-mode --list-public-keys '${gpg_recipient}'" - - exec { "${gpg_cmd} ${gpg_import}": - logoutput => on_failure, - unless => $gpg_unless, - before => Exec['ipa-install'], - require => File["${vardir}/gpg/"], - alias => 'ipa-gpg-import', - } - - # TODO: add checks - # * is key revoked ? - # * other sanity checks ? - - if $gpg_sendemail { - # if we email out the encrypted password, make sure its - # public key has the correct email address to match it! - $gpg_check_email = "${gpg_cmd} --with-colons --list-public-keys '${gpg_recipient}' | /bin/awk -F ':' '\$1 = /uid/ {print \$10}' | /bin/grep -qF '<${valid_email}>'" - exec { "${gpg_check_email}": - logoutput => on_failure, - unless => $gpg_unless, - before => Exec['ipa-install'], - require => Exec['ipa-gpg-import'], - alias => 'ipa-gpg-check', - } - } - } - - $pwgen_cmd = "/usr/bin/pwgen 16 1" - - $valid_dm_password = "${dm_password}" ? { - '' => "${pwgen_cmd}", - default => "/bin/cat '${vardir}/dm.password'", - } - - $valid_admin_password = "${admin_password}" ? { - '' => "${pwgen_cmd}", - default => "/bin/cat '${vardir}/admin.password'", - } - - # NOTE: we have to use '--trust-model always' or it prompts with: - # It is NOT certain that the key belongs to the person named - # in the user ID. If you *really* know what you are doing, - # you may answer the next question with yes. - $gpg_encrypt = "${gpg_cmd} --encrypt --trust-model always --recipient '${gpg_recipient}'" - $mail_send = "/bin/mailx -s 'Password for: ${valid_hostname}.${valid_domain}' '${valid_email}'" - - $dm_password_file = "${gpg_recipient}" ? { - '' => '/bin/cat', # pass through, no gpg key exists... - default => "/usr/bin/tee >( ${gpg_encrypt} > '${dm_password_filename}' )", - } - if "${gpg_recipient}" != '' and $gpg_sendemail { - $dm_password_mail = "/usr/bin/tee >( ${gpg_encrypt} | (/bin/echo 'GPG(DM password):'; /bin/cat) | ${mail_send} > /dev/null )" - } else { - $dm_password_mail = '/bin/cat' - } - $dm_password_exec = "${valid_dm_password} | ${dm_password_file} | ${dm_password_mail} | /bin/cat" - - $admin_password_file = "${gpg_recipient}" ? { - '' => '/bin/cat', - default => "/usr/bin/tee >( ${gpg_encrypt} > '${admin_password_filename}' )", - } - if "${gpg_recipient}" != '' and $gpg_sendemail { - $admin_password_mail = "/usr/bin/tee >( ${gpg_encrypt} | (/bin/echo 'GPG(admin password):'; /bin/cat) | ${mail_send} > /dev/null )" - } else { - $admin_password_mail = '/bin/cat' - } - $admin_password_exec = "${valid_admin_password} | ${admin_password_file} | ${admin_password_mail} | /bin/cat" - - # store the passwords in text files instead of having them on cmd line! - # even better is to let them get automatically generated and encrypted! - if "${dm_password}" != '' { - $dm_bool = inline_template('<%= @dm_password.length < 8 ? "false":"true" %>') - if "${dm_bool}" != 'true' { - fail('The dm_password must be at least eight characters in length.') - } - file { "${vardir}/dm.password": - content => "${dm_password}\n", # top top secret! - owner => root, - group => nobody, - mode => 600, # u=rw,go= - backup => false, - before => Exec['ipa-install'], - require => File["${vardir}/"], - ensure => present, - } - } - - if "${admin_password}" != '' { - $admin_bool = inline_template('<%= @admin_password.length < 8 ? "false":"true" %>') - if "${admin_bool}" != 'true' { - fail('The admin_password must be at least eight characters in length.') - } - file { "${vardir}/admin.password": - content => "${admin_password}\n", # top secret! - owner => root, - group => nobody, - mode => 600, # u=rw,go= - backup => false, - before => Exec['ipa-install'], - require => File["${vardir}/"], - ensure => present, - } - } - - # these are the arguments to ipa-server-install in the prompted order - $args01 = "--hostname='${valid_fqdn}'" - $args02 = "--domain='${valid_domain}'" - $args03 = "--realm='${valid_realm}'" - $args04 = "--ds-password=`${dm_password_exec}`" # Directory Manager - $args05 = "--admin-password=`${admin_password_exec}`" # IPA admin - # TODO: reconcile these options with the range settings: EXAMPLE.COM_id_range - # if that range is changed, should we watch for it and reset? yes we should if we specified one here... - $args06 = $idstart ? { - '' => '', - default => "--idstart=${idstart}", - } - $args07 = $idmax ? { - '' => '', - default => "--idmax=${idmax}", - } - - $args08 = $ntp ? { - true => '', # create ntp server... - default => '--no-ntp', - } - - $args09 = $dns ? { - true => '--setup-dns --no-forwarders', - default => '', - } - - $args10 = $dns ? { - true => "--zonemgr=${valid_email}", - default => '', - } - - # we check the version because the --selfsign option vanishes in 3.2.0 - # http://www.freeipa.org/page/Releases/3.2.0#Dropped_--selfsign_option - $versioncmp = versioncmp("${::ipa_version}", '3.2.0') - $args11 = $dogtag ? { - true => '', # TODO: setup dogtag - default => "${versioncmp}" ? { - # pre 3.2.0, you have to disable dogtag manually - '-1' => '--selfsign', # disable dogtag - # post 3.2.0, dogtag is not setup by default...! - default => '', - }, - } - - $args12 = $ipa_ipaddress ? { - '' => '', - default => $dns ? { - true => "--ip-address=${ipa_ipaddress} --no-host-dns", - default => "--ip-address=${ipa_ipaddress}", - }, - } - - $arglist = [ - "${args01}", - "${args02}", - "${args03}", - "${args04}", - "${args05}", - "${args06}", - "${args07}", - "${args08}", - "${args09}", - "${args10}", - "${args11}", - "${args12}", - ] - #$args = inline_template('<%= arglist.delete_if {|x| x.empty? }.join(" ") %>') - $args = join(delete($arglist, ''), ' ') - - # split ipa-server-install command into a separate file so that it runs - # as bash, and also so that it's available to run manually and inspect! - # if this installs successfully, tag it so we know which host was first - file { "${vardir}/ipa-server-install.sh": - content => inline_template("#!/bin/bash\n/usr/sbin/ipa-server-install ${args} --unattended && /bin/echo '${::fqdn}' > ${vardir}/ipa_server_replica_master\n"), - owner => root, - group => root, - mode => 700, - ensure => present, - require => File["${vardir}/"], - } - - if ("${valid_vip}" == '' or "${vipif}" != '') { - - exec { "${vardir}/ipa-server-install.sh": - logoutput => on_failure, - unless => "${::ipa::common::ipa_installed}", # can't install if installed... - timeout => 3600, # hope it doesn't take more than 1 hour - require => [ - Package['ipa-server'], - File["${vardir}/ipa-server-install.sh"], - ], - alias => 'ipa-install', # same alias as client to prevent both! - } - - # NOTE: this is useful to collect only on hosts that are installed or - # which are replicas that have been installed. ensure the type checks - # this prepares for any host we prepare for to potentially join us... - Ipa::Server::Replica::Prepare <<| title != "${::fqdn}" |>> { - - } - - } else { - - # NOTE: this is useful to export from any host that didn't install !!! - # this sends the message: "prepare for me to potentially join please!" - @@ipa::server::replica::prepare { "${valid_fqdn}": - } - - class { '::ipa::server::replica::install': - peers => $valid_peers, - } - - } - - # this file is a tag that lets you know which server was the first one! - file { "${vardir}/ipa_server_replica_master": - owner => root, - group => nobody, - mode => 600, # u=rw,go= - backup => false, - require => [ - File["${vardir}/"], - Exec['ipa-install'], - ], - ensure => present, - alias => 'ipa-server-master-flag', - } - - # this file is a tag that lets notify know it only needs to run once... - file { "${vardir}/ipa_server_installed": - #content => "true\n", - owner => root, - group => nobody, - mode => 600, # u=rw,go= - backup => false, - require => [ - File["${vardir}/"], - Exec['ipa-install'], - ], - ensure => present, - alias => 'ipa-server-installed-flag', - } - - # this sets the true value so that we know that ipa is installed first! - exec { "/bin/echo true > ${vardir}/ipa_server_installed": - logoutput => on_failure, - unless => "/usr/bin/test \"`/bin/cat ${vardir}/ipa_server_installed`\" = 'true'", - onlyif => "${::ipa::common::ipa_installed}", - require => File['ipa-server-installed-flag'], - } - - # check if we changed the dns state after initial install (unsupported) - # this is needed, because if dns was once setup, but the param is false - # then the host resource won't use --force and we'll get errors... this - # happens because of bug#: https://fedorahosted.org/freeipa/ticket/3726 - if ! $dns { - exec { '/bin/false': # fail so that we know about the change - logoutput => on_failure, - # thanks to 'ab' in #freeipa for help with the ipa api! - onlyif => "/usr/bin/python -c 'import sys,ipalib;ipalib.api.bootstrap_with_global_options(context=\"puppet\");ipalib.api.finalize();(ipalib.api.Backend.ldap2.connect(ccache=ipalib.api.Backend.krb.default_ccname()) if ipalib.api.env.in_server else ipalib.api.Backend.xmlclient.connect());sys.exit(0 if ipalib.api.Command.dns_is_enabled().get(\"result\") else 1)'", - require => Package['ipa-server'], - alias => 'ipa-dns-check', - } - } - - # TODO: add management of ipa services (ipa, httpd, krb5kdc, kadmin, etc...) run: ipactl status or service ipa status for more info - # TODO: add management (augeas?) of /etc/ipa/default.conf - - class { 'ipa::server::kinit': - realm => "${valid_realm}", - } - - # FIXME: consider allowing only certain ip's to the ipa server - # TODO: we could open ports per host when added with ipa::server::host - if $shorewall { - if $allow == 'all' or "${allow}" == '' { - $net = "${zone}" - } else { - $net = is_array($allow) ? { - true => sprintf("${zone}:%s", join($allow, ',')), - default => "${zone}:${allow}", - } - } - #################################################################### - #ACTION SOURCE DEST PROTO DEST SOURCE ORIGINAL - # PORT PORT(S) DEST - shorewall::rule { 'http': rule => " - HTTP/ACCEPT ${net} $FW - ", comment => 'Allow HTTP for webui'} - - shorewall::rule { 'https': rule => " - HTTPS/ACCEPT ${net} $FW - ", comment => 'Allow HTTPS for webui'} - - shorewall::rule { 'ldap': rule => " - LDAP/ACCEPT ${net} $FW - ", comment => 'Allow LDAP for 389 server on tcp port 389.'} - - shorewall::rule { 'ldaps': rule => " - LDAPS/ACCEPT ${net} $FW - ", comment => 'Allow LDAPS for 389 server on tcp port 636.'} - - shorewall::rule { 'kerberos': rule => " - Kerberos/ACCEPT ${net} $FW - ", comment => 'Allow Kerberos for krb5 server on tcp/udp port 88.'} - - # TODO: should i propose this as a shorewall macro ? - shorewall::rule { 'kpasswd': rule => " - ACCEPT ${net} $FW tcp 464 - ACCEPT ${net} $FW udp 464 - ", comment => 'Allow Kerberos for kpasswd on tcp/udp port 464.'} - - if $ntp { - shorewall::rule { 'ntp': rule => " - NTP/ACCEPT ${net} $FW - ", comment => 'Allow NTP on udp port 123.'} - } - - if $dns { - shorewall::rule { 'dns': rule => " - DNS/ACCEPT ${net} $FW - ", comment => 'Allow DNS on tcp/udp port 53.'} - } - - if $dogtag { - shorewall::rule { 'dogtag': rule => " - ACCEPT ${net} $FW tcp 7389 - ", comment => 'Allow dogtag certificate system on tcp port 7389.'} - } - } - - # in the single host case, the topology should be an empty hash - if has_key($valid_peers, "${::fqdn}") { - # ensure the topology has the right shape... - ipa::server::replica::manage { $valid_peers["${::fqdn}"]: # magic - peer => "${::fqdn}", - } - } - - # this fact gets created once the installation is complete... the first - # time that puppet runs, it won't be set. after installation it will :) - # this mechanism provides a way to only run the 'helpful' notifies once - if "${ipa_server_installed}" != 'true' { - # notify about password locations to be helpful - if "${gpg_recipient}" != '' { - if "${dm_password}" == '' { - $dm_password_msg = "The dm_password should be found in: ${dm_password_filename}." - notice("${dm_password_msg}") - notify {'ipa-notify-dm_password': - message => "${dm_password_msg}", - #stage => last, # TODO - require => Exec['ipa-install'], - } - if $gpg_sendemail { - $dm_password_email_msg = "The dm_password should be emailed to: ${valid_email}." - notice("${dm_password_email_msg}") - notify {'ipa-notify-email-dm_password': - message => "${dm_password_email_msg}", - #stage => last, # TODO - require => Exec['ipa-install'], - } - } - } - - if "${admin_password}" == '' { - $admin_password_msg = "The admin_password should be found in: ${admin_password_filename}." - notice("${admin_password_msg}") - notify {'ipa-notify-admin_password': - message => "${admin_password_msg}", - #stage => last, # TODO - require => Exec['ipa-install'], - } - if $gpg_sendemail { - $admin_password_email_msg = "The admin_password should be emailed to: ${valid_email}." - notice("${admin_password_email_msg}") - notify {'ipa-notify-email-admin_password': - message => "${admin_password_email_msg}", - #stage => last, # TODO - require => Exec['ipa-install'], - } - } - } - } - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/config.pp b/ipa/manifests/server/config.pp deleted file mode 100644 index ccaa9f2d2..000000000 --- a/ipa/manifests/server/config.pp +++ /dev/null @@ -1,142 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# FIXME: some values have not been filled in yet. some are missing: --arguments -define ipa::server::config( - $value -) { - include ipa::common - - $key = "${name}" - - $etype = "${key}" ? { # expected type - #'?' => '', # FIXME: dn - #'?' => '', # --maxusername - 'homes' => 'string', - 'shell' => 'string', - #'?' => '', # --defaultgroup - 'emaildomain' => 'string', - #'?' => '', # --searchtimelimit - #'?' => '', # --searchrecordslimit - 'usersearch' => 'array', - 'groupsearch' => 'array', - 'migration' => 'boolean', - #'?' => '', # FIXME: ipacertificatesubjectbase - #'?' => '', # --groupobjectclasses - #'?' => '', # --userobjectclasses - #'?' => '', # --pwdexpnotify - #'?' => '', # --ipaconfigstring - #'?' => '', # --ipaselinuxusermaporder - #'?' => '', # --ipaselinuxusermapdefault - #'?' => '', # --pac-type - #'?' => '', # FIXME: cn - #'?' => '', # FIXME: objectclass - default => '', # missing - } - - $option = "${key}" ? { - #'?' => 'dn', FIXME - #'?' => '--maxusername=', - 'homes' => '--homedirectory=', - 'shell' => '--defaultshell=', - #'?' => '--defaultgroup=', - 'emaildomain' => '--emaildomain=', - #'?' => '--searchtimelimit=', - #'?' => '--searchrecordslimit=', - 'usersearch' => '--usersearch=', - 'groupsearch' => '--groupsearch=', - 'migration' => '--enable-migration=', - #'?' => 'ipacertificatesubjectbase', FIXME - #'?' => '--groupobjectclasses=', - #'?' => '--userobjectclasses=', - #'?' => '--pwdexpnotify=', - #'?' => '--ipaconfigstring=', - #'?' => '--ipaselinuxusermaporder=', - #'?' => '--ipaselinuxusermapdefault=', - #'?' => '--pac-type=', - #'?' => 'cn', FIXME - #'?' => 'objectclass', FIXME - default => '', # missing - } - - $rawkey = "${key}" ? { - #'?' => 'dn', - #'?' => 'ipamaxusernamelength', - 'homes' => 'ipahomesrootdir', - 'shell' => 'ipadefaultloginshell', - #'?' => 'ipadefaultprimarygroup', - 'emaildomain' => 'ipadefaultemaildomain', - #'?' => 'ipasearchtimelimit', - #'?' => 'ipasearchrecordslimit', - 'usersearch' => 'ipausersearchfields', - 'groupsearch' => 'ipagroupsearchfields', - 'migration' => 'ipamigrationenabled', - #'?' => 'ipacertificatesubjectbase', - #'?' => 'ipagroupobjectclasses', - #'?' => 'ipauserobjectclasses', - #'?' => 'ipapwdexpadvnotify', - #'?' => 'ipaconfigstring', - #'?' => 'ipaselinuxusermaporder', - #'?' => 'ipaselinuxusermapdefault', - #'?' => 'ipakrbauthzdata', - #'?' => 'cn', - #'?' => 'objectclass', - default => '', # missing - } - - if "${option}" == '' or "${etype}" == '' or "${rawkey}" == '' { - fail("Key '${key}' is invalid.") - } - - if type($value) != "${etype}" { - fail("Ipa::Server::Config[${key}] must be type: ${etype}.") - } - - # convert to correct type - if "${etype}" == 'string' { - $safe_value = shellquote($value) # TODO: is this right ? - $jchar = '' # pass through the paste binary - } elsif "${etype}" == 'array' { - $jchar = "${key}" ? { # join char - 'usersearch' => ',', - 'groupsearch' => ',', - default => '', - } - $safe_value = inline_template('<%= value.join(jchar) %>') - } elsif "${etype}" == 'boolean' { - $safe_value = $value ? { - true => 'TRUE', - default => 'FALSE', - } - $jchar = '' # pass through the paste binary - } else { - fail("Unknown type: ${etype}.") - } - - $cutlength = inline_template('<%= (rawkey.length+2).to_s %>') - exec { "/usr/bin/ipa config-mod ${option}'${safe_value}'": - logoutput => on_failure, - onlyif => "${::ipa::common::ipa_installed}", - unless => "/usr/bin/test \"`/usr/bin/ipa config-show --raw --all | /usr/bin/tr -d ' ' | /bin/grep '^${rawkey}:' | /bin/cut -b ${cutlength}- | /usr/bin/paste -sd '${jchar}'`\" = '${safe_value}'", - require => [ - Exec['ipa-install'], - Exec['ipa-server-kinit'], - ], - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/host.pp b/ipa/manifests/server/host.pp deleted file mode 100644 index 4d13d9596..000000000 --- a/ipa/manifests/server/host.pp +++ /dev/null @@ -1,404 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -define ipa::server::host( - $domain = $ipa::server::domain, # default to main domain - $server = '', # where the client will find the ipa server... - $macaddress = '', # TODO: this should be a list... - #$ipaddress = '', # NOTE: this is a bad fit here... - $sshpubkeys = true, # leave this at the default to get auto sshkeys - #$certificate = ???, # TODO ? - - $password = '', # one time password used for host provisioning! - $random = false, # or set this to true to have us generate it... - - # comment parameters... - $locality = '', # host locality (e.g. "Montreal, Canada") - $location = '', # host location (e.g. "Lab 42") - $platform = '', # host hardware platform (e.g. "Lenovo X201") - $osstring = '', # host operating system and version (e.g. "CentOS 6.4") - $comments = '', # host description (e.g. "NFS server") - - #$hosts = [], # TODO: add hosts managed by support - - # client specific parameters... - $admin = false, # should client get admin tools installed ? - - # special parameters... - $watch = true, # manage all changes to this resource, reverting others - $modify = true # modify this resource on puppet changes or not ? -) { - include ipa::server - include ipa::server::host::base - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - $dns = $ipa::server::dns # boolean from main obj - - $valid_domain = downcase($domain) - - $valid_server = "${server}" ? { - '' => "${::hostname}.${::domain}", - default => "${server}", - } - - # NOTE: the valid_fqdn is actually what ipa calls a hostname internally - # if $name has dots, then we assume it's a fqdn, if not, we add $domain - $valid_fqdn = delete("${name}", '.') ? { - "${name}" => "${name}.${valid_domain}", # had no dots present - default => "${name}", # had dots present... - } - - $valid_sshpubkeys = type($sshpubkeys) ? { - 'string' => "${sshpubkeys}" ? { - # BUG: lol: https://projects.puppetlabs.com/issues/15813 - '' => [], # assume managed but empty (rm sshkeys) - default => ["${sshpubkeys}"], - }, - 'boolean' => $sshpubkeys, - 'array' => $sshpubkeys, - default => '', # set an error... - } - if "${valid_sshpubkeys}" == '' { - fail('You must specify a valid type for $sshpubkeys.') - } - - if $watch and (! $modify) { - fail('You must be able to $modify to be able to $watch.') - } - - # NOTE: this is not a good fit for host-* it is part of the dns system, - # and not the host, and should be managed separately - #if $dns { - # $args00 = "${ipaddress}" ? { - # '' => '', - # default => "--ip-address='${ipaddress}'", - # } - #} else { - # $args00 = '' - # # TODO: allow this silently for now... - # #warning("Host: '${valid_fqdn}' is setting an IP without DNS.") - #} - - $args01 = "${macaddress}" ? { - '' => '', - default => "--macaddress='${macaddress}'", - } - - # array means: managed, set these keys exactly, and remove when it's [] - # boolean false means: unmanaged, don't set or get anything... empty '' - # boolean true means: managed, get the keys automatically (super magic) - $args02 = type($valid_sshpubkeys) ? { - # we always have to at least specify the '--sshpubkey=' if this - # is empty, because otherwise we have no way to remove old keys - 'array' => inline_template('<% if valid_sshpubkeys == [] %>--sshpubkey=<% else %><%= valid_sshpubkeys.map {|x| "--sshpubkey=\'"+x+"\'" }.join(" ") %><% end %>'), - default => $valid_sshpubkeys ? { # boolean - false => '', # unmanaged, do nothing - # this large beast loops through all the collected dirs - # and cats the contents of each file into an individual - # --sshpubkey argument. if no keys are found, the empty - # --sshpubkey argument is returned. this is all used to - # build the ipa commands. i hope this doesn't overflow! - default => "`a=(); for i in ${vardir}/hosts/sshpubkeys/${name}/*.pub; do [ -e \"\$i\" ] || break; a+=(\"--sshpubkey='\$(/bin/cat \$i)'\"); done; if [ \"\${a[*]}\" == '' ]; then /bin/echo \"--sshpubkey=\"; else /bin/echo \${a[@]}; fi`", - }, - } - - $args03 = "${locality}" ? { - '' => '', - default => "--locality='${locality}'", - } - $args04 = "${location}" ? { - '' => '', - default => "--location='${location}'", - } - $args05 = "${platform}" ? { - '' => '', - default => "--platform='${platform}'", - } - $args06 = "${osstring}" ? { - '' => '', - default => "--os='${osstring}'", - } - $args07 = "${comments}" ? { - '' => '', - default => "--desc='${comments}'", - } - - $arglist = ["${args01}", "${args02}", "${args03}", "${args04}", "${args05}", "${args06}", "${args07}"] - $args = join(delete($arglist, ''), ' ') - - if $random and ("${password}" != '') { - fail('Specify $random or $password, but not both.') - } - $argspass = "${password}" ? { - '' => $random ? { - true => '--random', - default => '', # no password specified - }, - #default => "--password='${password}'", # direct mode, (bad)! - default => "--password=`/bin/cat '${vardir}/hosts/passwords/${valid_fqdn}.password'`", - } - - $qarglist = ["${argspass}"] # NOTE: add any silent arg changes here - $qargs = join(delete($qarglist, ''), ' ') - - # if we're not modifying, we need to add on the qargs stuff to the add! - $xarglist = $modify ? { - false => concat($arglist, $qarglist), - default => $arglist, - } - $xargs = join(delete($xarglist, ''), ' ') - - # NOTE: this file is the subscribe destination for the modify exec when - # not using watch mode. it is separate from the qhost file (which is - # used for unwatchable changes), because if we had only one notify - # source, then a configuration transition from watch to unwatched would - # actually trigger a modification. this file is also the official file - # that is used by the clean script for determining which hosts need to - # be erased. please keep in mind that on accidental notification, or on - # system rebuild, the differing changes will be erased. - file { "${vardir}/hosts/${valid_fqdn}.host": - content => "${valid_fqdn}\n${args}\n", - owner => root, - group => nobody, - mode => 600, # u=rw,go= - require => File["${vardir}/hosts/"], - ensure => present, - } - - file { "${vardir}/hosts/${valid_fqdn}.qhost": - content => "${valid_fqdn}\n${qargs}\n", - owner => root, - group => nobody, - mode => 600, # u=rw,go= - require => File["${vardir}/hosts/"], - ensure => present, - } - - # NOTE: a custom fact, reads from these dirs and collects the passwords - if $random { - file { "${vardir}/hosts/passwords/${valid_fqdn}.password": - # no content! this is a tag, content comes in by echo ! - owner => root, - group => nobody, - mode => 600, # u=rw,go= - backup => false, - notify => $modify ? { - false => undef, # can't notify if not modifying - default => Exec["ipa-server-host-qmod-${name}"], - }, - require => File["${vardir}/hosts/passwords/"], - ensure => present, - } - } elsif "${password}" != '' { - file { "${vardir}/hosts/passwords/${valid_fqdn}.password": - content => "${password}\n", # top secret (briefly!) - owner => root, - group => nobody, - mode => 600, # u=rw,go= - backup => false, - notify => $modify ? { - false => undef, # can't notify if not modifying - default => Exec["ipa-server-host-qmod-${name}"], - }, - before => $modify ? { - false => undef, - default => Exec["ipa-server-host-qmod-${name}"], - }, - require => File["${vardir}/hosts/passwords/"], - ensure => present, - } - } - - file { "${vardir}/hosts/sshpubkeys/${name}/": # store host ssh keys - ensure => directory, # make sure this is a directory - recurse => true, # recursively manage directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, group => nobody, mode => 600, backup => false, - require => File["${vardir}/hosts/sshpubkeys/"], - } - - # collect host specific ssh keys - Ipa::Server::Host::Sshpubkeys <<| tag == "${name}" |>> { - #realname => "${name}", - #basedir => "${vardir}/hosts/sshpubkeys/${name}/", - } - - $exists = "/usr/bin/ipa host-show '${valid_fqdn}' > /dev/null 2>&1" - # NOTE: we don't need to set the password in the host-add, because the - # host-mod that deals specifically with password stuff will trigger it - # NOTE: --force is needed when dns is configured for ipa but we're not - # setting an ip address on host-add. this makes ipa sad, and it fails! - # NOTE: we don't seem to need --force for host-mod, as it hasn't erred - $force = "${xargs}" ? { # if args is empty - '' => '--force', # we have no args! - default => "${xargs} --force", # pixel perfect... - } - $fargs = $dns ? { # without the dns, - true => "${force}", # we don't need to - default => "${xargs}", # force everything - } - # NOTE: this runs when no host is present... - #exec { "/usr/bin/ipa host-add '${valid_fqdn}' ${fargs}": - exec { "ipa-server-host-add-${name}": # alias - # this has to be here because the command string gets too long - # for a puppet $name var and strange things start to happen... - command => "/usr/bin/ipa host-add '${valid_fqdn}' ${fargs}", - logoutput => on_failure, - unless => "${exists}", - require => $dns ? { - true => [ - Exec['ipa-server-kinit'], - File["${vardir}/hosts/sshpubkeys/${name}/"], - ], - default => [ - Exec['ipa-dns-check'], # avoid --force errors! - Exec['ipa-server-kinit'], - File["${vardir}/hosts/sshpubkeys/${name}/"], - ], - }, - #alias => "ipa-server-host-add-${name}", - } - - # NOTE: this runs when we detect that the attributes don't match (diff) - if $modify and ("${args}" != '') { # if there are changes to do... - #exec { "/usr/bin/ipa host-mod '${valid_fqdn}' ${args}": - exec { "ipa-server-host-mod-${name}": - command => "/usr/bin/ipa host-mod '${valid_fqdn}' ${args}", - logoutput => on_failure, - refreshonly => $watch ? { - false => true, # when not watching, we - default => undef, # refreshonly to change - }, - subscribe => $watch ? { - false => File["${vardir}/hosts/${valid_fqdn}.host"], - default => undef, - }, - onlyif => "${exists}", - unless => $watch ? { - false => undef, # don't run the diff checker... - default => "${exists} && ${vardir}/diff.py host '${valid_fqdn}' ${args}", - }, - before => "${qargs}" ? { # only if exec exists ! - '' => undef, - default => Exec["ipa-server-host-qmod-${name}"], - }, - require => [ - File["${vardir}/diff.py"], - Exec['ipa-server-kinit'], - Exec["ipa-server-host-add-${name}"], - File["${vardir}/hosts/sshpubkeys/${name}/"], - ], - #alias => "ipa-server-host-mod-${name}", - } - } - - # NOTE: this runs when there should be an attribute change we can't see - if $modify and ("${qargs}" != '') { # quiet q changes to do - - # this is a bonus to double check that a password entry exists! - # once a host is provisioned, it will reset the single use pass - # and this script would normally try and create a new one back, - # however if a pwtag is collected, then it won't run the notify - # this is pretty advanced stuff to understand, but it's useful! - if $random or ("${password}" != '') { - - # collect any password tags. note i used $name exactly! - Ipa::Server::Host::Pwtag <<| tag == "${name}" |>> { - } - exec { "ipa-host-verify-password-exists-${name}": # uid - command => '/bin/true', # i'm just here for the notify! - # do not run this if the password tag exists... - # if it dissapears, that means the host is gone - unless => "/usr/bin/test -e '${vardir}/hosts/passwords/${name}.pwtag'", - # only do this if machine is unenrolled, eg see - # https://git.fedorahosted.org/cgit/freeipa.git - # /tree/ipalib/plugins/host.py#n642 (approx...) - # NOTE: this uses a single equals sign for test - onlyif => [ - "/usr/bin/test \"`/usr/bin/ipa host-show '${valid_fqdn}' --raw | /usr/bin/tr -d ' ' | /bin/grep '^has_password:' | /bin/cut -b 14-`\" = 'False'", - "/usr/bin/test \"`/usr/bin/ipa host-show '${valid_fqdn}' --raw | /usr/bin/tr -d ' ' | /bin/grep '^has_keytab:' | /bin/cut -b 12-`\" = 'False'", - ], - logoutput => on_failure, - notify => Exec["ipa-server-host-qmod-${name}"], - # TODO: notify: Exec['again'] so that the facts - # get refreshed right away, and the password is - # exported without delay! now go and hack away! - before => Exec["ipa-server-host-qmod-${name}"], - require => [ - Exec['ipa-server-kinit'], - Exec["ipa-server-host-add-${name}"], - # this file require ensures that if the - # pwtag disappears (by that dir purge), - # that right away the new pass is made! - File["${vardir}/hosts/passwords/"], - ], - } - } - - # NOTE: if this runs before a pwtag can prevent it, on a random - # password it will succeed without error and wipe the password: - # invalid 'password': Password cannot be set on enrolled host. - # this isn't a big deal, it just has the side effect of erasing - # the stored temporary password from locally where it's unused. - # if this runs before a pwtag can prevent it, on a static pass, - # this will cause a transient error until the pwtag gets saved. - # to avoid both of these scenarios, the above exec runs a check - # to see if the host is unenrolled before running the notify :) - $qextra = $random ? { # save the generated password to a file - true => " --raw | /usr/bin/tr -d ' ' | /bin/grep '^randompassword:' | /bin/cut -b 16- > ${vardir}/hosts/passwords/${valid_fqdn}.password", - default => '', - } - exec { "/usr/bin/ipa host-mod '${valid_fqdn}' ${qargs}${qextra}": - logoutput => on_failure, - refreshonly => true, # needed because we can't "see" - subscribe => File["${vardir}/hosts/${valid_fqdn}.qhost"], - onlyif => "${exists}", - require => [ - Exec['ipa-server-kinit'], - Exec["ipa-server-host-add-${name}"], - ], - alias => "ipa-server-host-qmod-${name}", - } - } - - # use this password in an exported resource to deploy the ipa client... - $passfact = regsubst("ipa_host_${valid_fqdn}_password", '\.', '_', 'G') - $pass = getvar("${passfact}") - # NOTE: 'include ipa::client::host::deploy' to deploy the ipa client... - @@ipa::client::host { "${name}": # this is usually the fqdn - # NOTE: this should set all the client args it can safely assume - domain => $valid_domain, - realm => $realm, - server => "${valid_server}", - password => "${pass}", - admin => $admin, - #ssh => $ssh, - #sshd => $sshd, - #ntp => $ntp, - #ntp_server => $ntp_server, - #shorewall => $shorewall, - #zone => $zone, - #allow => $allow, - #ensure => $ensure, - tag => "${name}", # bonus - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/host/base.pp b/ipa/manifests/server/host/base.pp deleted file mode 100644 index 5c8706719..000000000 --- a/ipa/manifests/server/host/base.pp +++ /dev/null @@ -1,113 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class ipa::server::host::base { - include ipa::server - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - # we don't want to purge the freeipa entry, so we need to exclude it... - $valid_hostname = $ipa::server::valid_hostname - $valid_domain = $ipa::server::valid_domain - $host_always_ignore = ["${valid_hostname}.${valid_domain}"] - $host_excludes = $ipa::server::host_excludes - $valid_host_excludes = type($host_excludes) ? { - 'string' => [$host_excludes], - 'array' => $host_excludes, - 'boolean' => $host_excludes ? { - # TODO: there's probably a better fqdn match expression - # this is an expression to prevent all fqdn deletion... - #true => ['^[a-zA-Z0-9\.\-]*$'], - true => ['^[[:alpha:]]{1}[[:alnum:]-.]*$'], - default => false, - }, - default => false, # trigger error... - } - - if type($valid_host_excludes) != 'array' { - fail('The $host_excludes must be an array.') - } - - # directory of system tags which should exist (as managed by puppet) - file { "${vardir}/hosts/": - ensure => directory, # make sure this is a directory - recurse => true, # recursively manage directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, group => nobody, mode => 600, backup => false, - notify => Exec['ipa-clean-hosts'], - require => File["${vardir}/"], - } - - # these are template variables for the clean.sh.erb script - $id_dir = 'hosts' - $ls_cmd = '/usr/bin/ipa host-find --pkey-only --raw | /usr/bin/tr -d " " | /bin/grep "^fqdn:" | /bin/cut -b 6-' # show ipa hosts - # TODO: i don't understand all the implications of the --updatedns arg! - # we should probably change the dns arg based on if dns is on or not... - $rm_cmd = $dns ? { # delete ipa hosts - true => '/usr/bin/ipa host-del --updatedns ', - default => '/usr/bin/ipa host-del ', - } - $fs_chr = ' ' - $suffix = '.host' - $regexp = $valid_host_excludes - $ignore = $host_always_ignore - - # build the clean script - file { "${vardir}/clean-hosts.sh": - content => template('ipa/clean.sh.erb'), - owner => root, - group => nobody, - mode => 700, # u=rwx - backup => false, # don't backup to filebucket - ensure => present, - require => File["${vardir}/"], - } - - # run the cleanup - exec { "${vardir}/clean-hosts.sh": - logoutput => on_failure, - refreshonly => true, - require => [ - Exec['ipa-server-kinit'], - File["${vardir}/clean-hosts.sh"], - ], - alias => 'ipa-clean-hosts', - } - - # NOTE: it doesn't cause a problem that this dir is inside the hosts dir - file { "${vardir}/hosts/passwords/": - ensure => directory, # make sure this is a directory - recurse => true, # recursively manage directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, group => nobody, mode => 600, backup => false, - require => File["${vardir}/hosts/"], - } - - file { "${vardir}/hosts/sshpubkeys/": - ensure => directory, # make sure this is a directory - recurse => true, # recursively manage directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, group => nobody, mode => 600, backup => false, - require => File["${vardir}/hosts/"], - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/host/pwtag.pp b/ipa/manifests/server/host/pwtag.pp deleted file mode 100644 index a8c5e747f..000000000 --- a/ipa/manifests/server/host/pwtag.pp +++ /dev/null @@ -1,42 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: this should only be used by a freeipa client and as an exported resource -define ipa::server::host::pwtag() { - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - # the existence of this file means that an ipa client has exported it, - # and that the ipa server collected it and it means that a provisioned - # ipa client host is notifying the server that a new one time password - # does not need to be generated at this time. to reprovision the host, - # you must erase the exported resource that is sending this file here, - # or rather, in doing so, the ipa server will generate a new password! - file { "${vardir}/hosts/passwords/${name}.pwtag": - content => "# This is a password tag for: ${name}\n", - owner => root, - group => nobody, - mode => 600, # u=rw,go= - backup => false, - before => Exec["ipa-host-verify-password-exists-${name}"], - require => File["${vardir}/hosts/passwords/"], - ensure => present, - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/host/sshpubkeys.pp b/ipa/manifests/server/host/sshpubkeys.pp deleted file mode 100644 index d34396905..000000000 --- a/ipa/manifests/server/host/sshpubkeys.pp +++ /dev/null @@ -1,63 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# NOTE: this is called by ipa::client internally and shouldn't be used manually - -define ipa::server::host::sshpubkeys( # $name matches ipa::server::host $name - $rsa = '', - $dsa = '' -) { - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - # FIXME: if i really cared, i would just have one argument, an array of - # keys, and i would loop through them creating each file... tempting... - if "${rsa}" != '' { - file { "${vardir}/hosts/sshpubkeys/${name}/rsa.pub": - content => "${rsa}\n", - owner => root, - group => nobody, - mode => 600, # u=rw,go= - backup => false, - # this before is essential, and it implies that it will also go - # before the "ipa-server-host-mod-${name}" exec, because of the - # relationship between those two types. mod might not always be - # present (if $modify is false) so don't directly reference it. - before => Exec["ipa-server-host-add-${name}"], - require => File["${vardir}/hosts/sshpubkeys/${name}/"], - ensure => present, - } - } - if "${dsa}" != '' { - file { "${vardir}/hosts/sshpubkeys/${name}/dsa.pub": - content => "${dsa}\n", - owner => root, - group => nobody, - mode => 600, # u=rw,go= - backup => false, - # this before is essential, and it implies that it will also go - # before the "ipa-server-host-mod-${name}" exec, because of the - # relationship between those two types. mod might not always be - # present (if $modify is false) so don't directly reference it. - before => Exec["ipa-server-host-add-${name}"], - require => File["${vardir}/hosts/sshpubkeys/${name}/"], - ensure => present, - } - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/kinit.pp b/ipa/manifests/server/kinit.pp deleted file mode 100644 index acd997a1a..000000000 --- a/ipa/manifests/server/kinit.pp +++ /dev/null @@ -1,57 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# FIXME: can we rename this to ipa::kinit and merge with the ipa client kinit ? - -class ipa::server::kinit( - $realm -) { - - include ipa::common - - $valid_realm = "${realm}" - - # since we're on the kdc, we can use our root access to get a ticket... - # < me> kaduk_: [...] is this an evil hack? [...] - # < kaduk_> [...] It's not really a hack, but things running on the KDC - # are always a bit special. - #exec { "/bin/cat '${vardir}/admin.password' | /usr/bin/kinit admin": - # NOTE: i added a lifetime of 1 hour... no sense needing any longer - $rr = "krbtgt/${valid_realm}@${valid_realm}" - $tl = '900' # 60*15 => 15 minutes - exec { "/usr/bin/kinit -k -t KDB: admin -l 1h": # thanks to: kaduk_ - logoutput => on_failure, - #unless => "/usr/bin/klist -s", # is there a credential cache - # NOTE: we need to check if the ticket has at least a certain - # amount of time left. if not, it could expire mid execution! - # this should definitely get patched, but in the meantime, we - # check that the current time is greater than the valid start - # time (in seconds) and that we have within $tl seconds left! - unless => "/usr/bin/klist -s && /usr/bin/test \$(( `/bin/date +%s` - `/usr/bin/klist | /bin/grep -F '${rr}' | /bin/awk '{print \$1\" \"\$2}' | /bin/date --file=- +%s` )) -gt 0 && /usr/bin/test \$(( `/usr/bin/klist | /bin/grep -F '${rr}' | /bin/awk '{print \$3\" \"\$4}' | /bin/date --file=- +%s` - `/bin/date +%s` )) -gt ${tl}", - onlyif => "${::ipa::common::ipa_installed}", - # NOTE: we need the 'require' to actually be a 'before', coming - # from the ipa-install exec since that may not exist right away - # if it's a replica that pulls in the exported resource exec... - require => [ - Exec['ipa-install'], - #File["${vardir}/admin.password"], - ], - alias => 'ipa-server-kinit', - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/replica/base.pp b/ipa/manifests/server/replica/base.pp deleted file mode 100644 index 49df9a52d..000000000 --- a/ipa/manifests/server/replica/base.pp +++ /dev/null @@ -1,34 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class ipa::server::replica::base { - - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - file { "${vardir}/replica/": - ensure => directory, # make sure this is a directory - recurse => true, # recursively manage directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, group => nobody, mode => 600, backup => false, - require => File["${vardir}/"], - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/replica/firewall.pp b/ipa/manifests/server/replica/firewall.pp deleted file mode 100644 index 1fd375430..000000000 --- a/ipa/manifests/server/replica/firewall.pp +++ /dev/null @@ -1,186 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: all replication agreements are bi-directional for now due to FreeIPA... -# NOTE: in the future, it would be quite cool to allow uni-directional replicas -# NOTE: this type has been engineered to fit easily with the topology datatype: -# $ring = { # example flat topology as expressed in the std. format -# 'fqdn1': ['fqdn2', 'fqdn3'], -# 'fqdn2': ['fqdn3', 'fqdn1'], -# 'fqdn3': ['fqdn1', 'fqdn2'], -# } -# -# ipa::server::replica::firewall { $ring["${::fqdn}"]: # all automatic -# peer => "${::fqdn}", -# } -define ipa::server::replica::firewall( # to - $peer = '', # from (usually we run this on itself!) - $ip = '' # you can specify which ip address to use (if multiple) -) { - - include ipa::server::replica::firewall::base - - # NOTE: the peer vs. valid_peer names are by convention (but confusing) - $self = "${peer}" # from (a) - if "${self}" != "${::fqdn}" { - fail('Are you sure you want to run this on a different host ?') - } - $valid_peer = "${name}" # to (b) - - $zone = $::ipa::server::zone # firewall zone - $valid_ip = "${ip}" ? { - '' => "${::ipa_host_ip}" ? { # smart fact... - '' => "${::ipaddress}", # puppet picks! - default => "${::ipa_host_ip}", # smart - }, - default => "${ip}", # user selected - } - if "${valid_ip}" == '' { - fail('No valid IP exists!') - } - - # NOTE: an exported resource here says: "i would like to connect to you" - # this means the collector's (receiver) perspective source ip is *my* ip - - # NOTE: we need to add the $fqdn so that exported resources - # don't conflict... I'm not sure they should anyways though - - # Directory Service: Unsecure port (389) - @@ipa::rulewrapper { "ipa-server-replica-ldap-${name}-${::fqdn}": - action => 'LDAP/ACCEPT', - source => "${zone}", # override this on collect... - source_ips => ["${valid_ip}"], # i am the source ! - dest => '$FW', - #proto => 'tcp', - #port => '', # comma separated string or list - comment => "Allow incoming tcp:389 from ${::fqdn}.", - tag => 'ipa-server-replica', - match => "${name}", # used for collection - ensure => present, - } - - # Directory Service: Secure port (636) - @@ipa::rulewrapper { "ipa-server-replica-ldaps-${name}-${::fqdn}": - action => 'LDAPS/ACCEPT', - source => "${zone}", - source_ips => ["${valid_ip}"], - dest => '$FW', - comment => "Allow incoming tcp:636 from ${::fqdn}.", - tag => 'ipa-server-replica', - match => "${name}", - ensure => present, - } - - # TODO: this should work in a future version of shorewall... - # Kerberos KDC: TCP (88) / Kerberos KDC: UDP (88) - #@@ipa::rulewrapper { "ipa-server-replica-kerberos-${name}-${::fqdn}": - # action => 'Kerberos/ACCEPT', - # source => "${zone}", - # source_ips => ["${valid_ip}"], - # dest => '$FW', - # comment => "Allow incoming tcp/udp:88 from ${::fqdn}.", - # tag => 'ipa-server-replica', - # match => "${name}", - # ensure => present, - #} - - # TODO: until the Kerberos macro exists in shorewall, we do it manually - # Kerberos KDC: TCP (88) - @@ipa::rulewrapper { "ipa-server-replica-kerberos-tcp-${name}-${::fqdn}": - action => 'ACCEPT', - source => "${zone}", - source_ips => ["${valid_ip}"], - dest => '$FW', - proto => 'tcp', - port => ['88'], - comment => "Allow incoming tcp:88 from ${::fqdn}.", - tag => 'ipa-server-replica', - match => "${name}", - ensure => present, - } - - # Kerberos KDC: UDP (88) - @@ipa::rulewrapper { "ipa-server-replica-kerberos-udp-${name}-${::fqdn}": - action => 'ACCEPT', - source => "${zone}", - source_ips => ["${valid_ip}"], - dest => '$FW', - proto => 'udp', - port => ['88'], - comment => "Allow incoming udp:88 from ${::fqdn}.", - tag => 'ipa-server-replica', - match => "${name}", - ensure => present, - } - - # TODO: create a kpasswd macro, or use the 'macro.ActiveDir' one... - # Kerberos Kpasswd: TCP (464) - @@ipa::rulewrapper { "ipa-server-replica-kpasswd-tcp-${name}-${::fqdn}": - action => 'ACCEPT', - source => "${zone}", - source_ips => ["${valid_ip}"], - dest => '$FW', - proto => 'tcp', - port => ['464'], - comment => "Allow incoming tcp:464 from ${::fqdn}.", - tag => 'ipa-server-replica', - match => "${name}", - ensure => present, - } - - # Kerberos Kpasswd: UDP (464) - @@ipa::rulewrapper { "ipa-server-replica-kpasswd-udp-${name}-${::fqdn}": - action => 'ACCEPT', - source => "${zone}", - source_ips => ["${valid_ip}"], - dest => '$FW', - proto => 'udp', - port => ['464'], - comment => "Allow incoming udp:464 from ${::fqdn}.", - tag => 'ipa-server-replica', - match => "${name}", - ensure => present, - } - - # HTTP Server: Unsecure port (80) - @@ipa::rulewrapper { "ipa-server-replica-http-${name}-${::fqdn}": - action => 'HTTP/ACCEPT', - source => "${zone}", - source_ips => ["${valid_ip}"], - dest => '$FW', - comment => "Allow incoming tcp:80 from ${::fqdn}.", - tag => 'ipa-server-replica', - match => "${name}", - ensure => present, - } - - # HTTP Server: Secure port (443) - @@ipa::rulewrapper { "ipa-server-replica-https-${name}-${::fqdn}": - action => 'HTTPS/ACCEPT', - source => "${zone}", - source_ips => ["${valid_ip}"], - dest => '$FW', - comment => "Allow incoming tcp:443 from ${::fqdn}.", - tag => 'ipa-server-replica', - match => "${name}", - ensure => present, - } - - # FIXME: are all the necessary ports for ipa replication include here ? -} - -# vim: ts=8 diff --git a/ipa/manifests/server/replica/firewall/base.pp b/ipa/manifests/server/replica/firewall/base.pp deleted file mode 100644 index 4f95c5589..000000000 --- a/ipa/manifests/server/replica/firewall/base.pp +++ /dev/null @@ -1,42 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class ipa::server::replica::firewall::base { - include ipa::server - - $zone = $::ipa::server::zone # firewall zone - $shorewall = $::ipa::server::shorewall # enable fw...? - - # open the firewall so that replicas can connect to what they will need - Ipa::Rulewrapper <<| tag == 'ipa-server-replica' and match == "${::fqdn}" |>> { - #Shorewall::Rule <<| tag == 'ipa-server-replica' and match == "${::fqdn}" |>> { - source => "${zone}", # use our source zone - # TODO: this below before is basically untested for usefulness! - before => Exec['ipa-install'], # open bi-directional fw first! - # TODO: the below require is basically untested for usefulness! - require => Exec['ipa-clean-peers'], # let the peers clean up first! - ensure => $shorewall ? { - absent => absent, - 'absent' => absent, - present => present, - 'present' => present, - default => present, - }, - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/replica/install.pp b/ipa/manifests/server/replica/install.pp deleted file mode 100644 index f8e786b9f..000000000 --- a/ipa/manifests/server/replica/install.pp +++ /dev/null @@ -1,107 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: this has to be a singleton (eg: class) because we can only install one! -# NOTE: topology connections and peering information can be non-singleton types TODO -class ipa::server::replica::install( - $peers = {} -) { - - include ipa::server::replica::install::base - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - # process possible replica masters that are available... - $replica_fqdns_fact = "${::ipa_replica_prepared_fqdns}" # fact! - $replica_fqdns = split($replica_fqdns_fact, ',') # list! - - # peering is always bidirectional for now :) - # $peers is a hash of fqdn1 => fqdn2 pairs... - - #if has_key($peers, "${::fqdn}") and member($replica_fqdns, $peers["${::fqdn}"]) { - # $valid_fqdn = $peers["${::fqdn}"] - if has_key($peers, "${::fqdn}") { - $intersection = intersection($replica_fqdns, $peers["${::fqdn}"]) - # NOTE use empty() because 'if []' returns true! - if empty($intersection) { - $valid_fqdn = '' - } else { - # pick the first in the list if there is more than one! - $valid_fqdn = pick($intersection, '') # first - } - } else { - $valid_fqdn = '' - } - - if "${ipa_server_installed}" != 'true' { - if "${valid_fqdn}" == '' { - warning("The requested peer: '${valid_fqdn}', isn't ready yet.") - } else { - info("The requested peer is: '${valid_fqdn}'.") - } - } - - $filename = "replica-info-${valid_fqdn}.gpg" - $filefrom = "replica-info-${::fqdn}.gpg" # name it with our fqdn - $valid_file = "${vardir}/replica/install/${filename}" - $valid_from = "${vardir}/replica/prepare/${filefrom}" - - # send to all prepared hosts, so the keys don't flip flop if vip moves! - ssh::send { $replica_fqdns: # fqdn of where i got this from... - - } - - # TODO: tag can be used as grouping - # NOTE: this could pull down multiple files... - # NOTE: this also matches against the file parameter from the exporting - # side. we do this so that we only pull in what is intended for us, and - # as a result, this avoids real duplicate resource conflicts. but NOTE: - # this currently depends on all hosts sharing the same value of $vardir - Ssh::File::Pull <<| tag == 'ipa-replica-prepare' and file == "${valid_from}" |>> { - path => "${vardir}/replica/install/", - verify => false, # rely on mtime - pair => false, # do it now so it happens fast! - # tag this file so it doesn't get purged - ensure => present, - owner => root, - group => nobody, - mode => 600, # u=rw - backup => false, # don't backup to filebucket - before => Exec['ipa-install'], - require => File["${vardir}/replica/install/"], - } - - # this exec is purposefully very similar to the ipa-server-install exec - # NOTE: the --admin-password is only useful for the connection check... - exec { "/usr/sbin/ipa-replica-install --password=`/bin/cat '${vardir}/dm.password'` --admin-password=`/bin/cat '${vardir}/admin.password'` --unattended ${valid_file}": - logoutput => on_failure, - onlyif => [ - "/usr/bin/test '${valid_fqdn}' != ''", # bonus safety! - "/usr/bin/test -s ${valid_file}", - ], - unless => "${::ipa::common::ipa_installed}", # can't install if installed... - timeout => 3600, # hope it doesn't take more than 1 hour - require => [ - File["${vardir}/"], - Package['ipa-server'], - ], - alias => 'ipa-install', # same alias as server to prevent both! - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/replica/install/base.pp b/ipa/manifests/server/replica/install/base.pp deleted file mode 100644 index 32d1f8060..000000000 --- a/ipa/manifests/server/replica/install/base.pp +++ /dev/null @@ -1,35 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class ipa::server::replica::install::base { - - include ipa::server::replica::base - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - file { "${vardir}/replica/install/": - ensure => directory, # make sure this is a directory - recurse => true, # recursively manage directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, group => nobody, mode => 600, backup => false, - require => File["${vardir}/replica/"], - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/replica/manage.pp b/ipa/manifests/server/replica/manage.pp deleted file mode 100644 index fd7af9757..000000000 --- a/ipa/manifests/server/replica/manage.pp +++ /dev/null @@ -1,89 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: all replication agreements are bi-directional for now due to FreeIPA... -# NOTE: in the future, it would be quite cool to allow uni-directional replicas -# NOTE: this type has been engineered to fit easily with the topology datatype: -# $ring = { # example flat topology as expressed in the std. format -# 'fqdn1': ['fqdn2', 'fqdn3'], -# 'fqdn2': ['fqdn3', 'fqdn1'], -# 'fqdn3': ['fqdn1', 'fqdn2'], -# } -# -# ipa::server::replica::manage { $ring["${::fqdn}"]: # all automatic -# peer => "${::fqdn}", -# } -define ipa::server::replica::manage( # to - $peer = '' # from -) { - # TODO: this type could grow fancy name parsing to specify: to and from - - include ipa::server::replica::manage::base - include ipa::common - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - # NOTE: the peer vs. valid_peer names are by convention (but confusing) - $args = "${peer}" # from (a) - $valid_peer = "${name}" # to (b) - - # switch bad characters for file name friendly characters (unused atm!) - # this could be useful if we allow peers's with $ and others in them... - $valid_peer_file = regsubst("${valid_peer}", '\$', '-', 'G') - file { "${vardir}/replica/manage/peers/${valid_peer_file}.peer": - content => "${valid_peer}\n${args}\n", - owner => root, - group => nobody, - mode => 600, # u=rw,go= - require => File["${vardir}/replica/manage/peers/"], - ensure => present, - } - - # NOTE: this shouldn't depend on the VIP because it runs on each host... - exec { "/usr/sbin/ipa-replica-manage connect '${peer}' '${valid_peer}'": - logoutput => on_failure, - onlyif => [ - "${::ipa::common::ipa_installed}", # i am ready - # this check is used to see if my peer is "ready" to - # accept any ipa-replica-manage connect commands. if - # it is, then it must mean that ipa is installed and - # running, even though this check tool isn't exactly - # designed for this particular type of check case... - # NOTE: this avoids unnecessary 'ipa-replica-manage' - # calls which would error in 3.0.0 with the message: - # You cannot connect to a previously deleted master. - # INFO: https://fedorahosted.org/freeipa/ticket/3105 - "/usr/sbin/ipa-replica-conncheck -R '${valid_peer}'", - ], - unless => "/usr/sbin/ipa-replica-manage list '${peer}' | /bin/awk -F ':' '{print \$1}' | /bin/grep -qxF '${valid_peer}'", - timeout => 900, # hope it doesn't take more than 15 min - before => Exec['ipa-clean-peers'], # try to connect first! - require => [ - Exec['ipa-install'], # show for readability! - Exec['ipa-server-kinit'], # needs auth to work... - ], - # NOTE: these two aliases can be used to prevent reverse dupes! - # NOTE: remove these if FreeIPA ever supports unidirectionality - alias => [ - "${peer} -> ${valid_peer}", - "${valid_peer} -> ${peer}", - ], - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/replica/manage/base.pp b/ipa/manifests/server/replica/manage/base.pp deleted file mode 100644 index 235718e1e..000000000 --- a/ipa/manifests/server/replica/manage/base.pp +++ /dev/null @@ -1,97 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class ipa::server::replica::manage::base { - include ipa::server - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - # TODO: do we need this extra nesting here, or should we use it below ? - file { "${vardir}/replica/manage/": - ensure => directory, # make sure this is a directory - recurse => true, # recursively manage directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, group => nobody, mode => 600, backup => false, - require => File["${vardir}/replica/"], - } - - # since we don't want to purge them, we need to exclude them... - $peer_always_ignore = ["${::fqdn}"] # never try and purge yourself! - $peer_excludes = $ipa::server::peer_excludes - $valid_peer_excludes = type($peer_excludes) ? { - 'string' => [$peer_excludes], - 'array' => $peer_excludes, - 'boolean' => $peer_excludes ? { - # TODO: there's probably a better peer match expression - # this is an expression to prevent all peer deletion... - #true => ['^[a-zA-Z0-9]*$'], - true => ['^[[:alpha:]]{1}[[:alnum:]]*$'], - default => false, - }, - default => false, # trigger error... - } - - if type($valid_peer_excludes) != 'array' { - fail('The $peer_excludes must be an array.') - } - - # directory of system tags which should exist (as managed by puppet) - file { "${vardir}/replica/manage/peers/": - ensure => directory, # make sure this is a directory - recurse => true, # recursively manage directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, group => nobody, mode => 600, backup => false, - notify => Exec['ipa-clean-peers'], - require => File["${vardir}/replica/manage/"], - } - - # these are template variables for the clean.sh.erb script - $id_dir = 'replica/manage/peers' - $ls_cmd = "/usr/sbin/ipa-replica-manage list '${::fqdn}'" # show ipa peers - $rm_cmd = "/usr/sbin/ipa-replica-manage disconnect '${::fqdn}' " # disconnect ipa peers - $fs_chr = ':' # remove the ':replica' suffix - $suffix = '.peer' - $regexp = $valid_peer_excludes - $ignore = $peer_always_ignore - - # build the clean script - file { "${vardir}/clean-peers.sh": - content => template('ipa/clean.sh.erb'), - owner => root, - group => nobody, - mode => 700, # u=rwx - backup => false, # don't backup to filebucket - ensure => present, - require => File["${vardir}/"], - } - - # run the cleanup - exec { "${vardir}/clean-peers.sh": - logoutput => on_failure, - refreshonly => true, - require => [ - Exec['ipa-server-kinit'], - File["${vardir}/clean-peers.sh"], - ], - alias => 'ipa-clean-peers', - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/replica/master.pp b/ipa/manifests/server/replica/master.pp deleted file mode 100644 index 34c32a84e..000000000 --- a/ipa/manifests/server/replica/master.pp +++ /dev/null @@ -1,43 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class ipa::server::replica::master( -) { - - include ipa::server::replica::master::base - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - # fact from data in: ${vardir}/ipa_server_replica_master - $valid_master = "${::ipa_server_replica_master}" - - @@file { "${vardir}/replica/master/master_${::fqdn}": - content => "${valid_master}\n", - tag => 'ipa-server-replica-master', - owner => root, - group => nobody, - mode => 600, - ensure => present, - } - - # collect to make facts - File <<| tag == 'ipa-server-replica-master' |>> { - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/replica/master/base.pp b/ipa/manifests/server/replica/master/base.pp deleted file mode 100644 index 94c39882c..000000000 --- a/ipa/manifests/server/replica/master/base.pp +++ /dev/null @@ -1,35 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class ipa::server::replica::master::base { - - include ipa::server::replica::base - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - file { "${vardir}/replica/master/": - ensure => directory, # make sure this is a directory - recurse => true, # recursively manage directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, group => nobody, mode => 600, backup => false, - require => File["${vardir}/replica/"], - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/replica/peering.pp b/ipa/manifests/server/replica/peering.pp deleted file mode 100644 index fbe9ff3dd..000000000 --- a/ipa/manifests/server/replica/peering.pp +++ /dev/null @@ -1,69 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class ipa::server::replica::peering( - # NOTE: these are *time* based uuid's, eg as generated with: uuidgen -t - $uuid = '', # if empty, puppet will attempt to use the uuidgen fact -) { - - include ipa::server::replica::peering::base - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - if ("${uuid}" != '') and (! ("${uuid}" =~ /^[a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12}$/)) { - fail("The chosen UUID: '${uuid}' is not valid.") - } - - # if we manually *pick* a uuid, then store it too, so that it - # sticks if we ever go back to using automatic uuids. this is - # useful if a user wants to initially import uuids by picking - # them manually, and then letting puppet take over afterwards - file { "${vardir}/replica/peering/uuid": - # this file object needs to always exist to avoid us purging... - content => "${uuid}" ? { - '' => undef, - default => "${uuid}\n", - }, - owner => root, - group => nobody, - mode => 600, # might as well... - ensure => present, - require => File["${vardir}/replica/peering/"], - } - - $valid_uuid = "${uuid}" ? { - # fact from data generated in: ${vardir}/replica/peering/uuid - '' => "${::ipa_server_replica_uuid}", - default => "${uuid}", - } - - @@file { "${vardir}/replica/peering/peer_${::fqdn}": - content => "${valid_uuid}\n", - tag => 'ipa-server-replica-peering', - owner => root, - group => nobody, - mode => 600, - ensure => present, - } - - # collect to make facts - File <<| tag == 'ipa-server-replica-peering' |>> { - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/replica/peering/base.pp b/ipa/manifests/server/replica/peering/base.pp deleted file mode 100644 index d192faf0c..000000000 --- a/ipa/manifests/server/replica/peering/base.pp +++ /dev/null @@ -1,35 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class ipa::server::replica::peering::base { - - include ipa::server::replica::base - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - file { "${vardir}/replica/peering/": - ensure => directory, # make sure this is a directory - recurse => true, # recursively manage directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, group => nobody, mode => 600, backup => false, - require => File["${vardir}/replica/"], - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/replica/prepare.pp b/ipa/manifests/server/replica/prepare.pp deleted file mode 100644 index c86ecf710..000000000 --- a/ipa/manifests/server/replica/prepare.pp +++ /dev/null @@ -1,74 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# $name is the fqdn of the server we are preparing for -define ipa::server::replica::prepare( -) { - - include ipa::server::replica::prepare::base - include ipa::common - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - $valid_fqdn = "${name}" # TODO: validate - - $filename = "replica-info-${valid_fqdn}.gpg" - $filedest = "replica-info-${::fqdn}.gpg" # name it with our fqdn - $prepared = "/var/lib/ipa/${filename}" - $valid_file = "${vardir}/replica/prepare/${filename}" - - # TODO: ipa-replica-prepare should allow you to pick output path/file - exec { "/usr/sbin/ipa-replica-prepare --password=`/bin/cat '${vardir}/dm.password'` ${valid_fqdn} && /bin/mv -f '${prepared}' '${valid_file}'": - logoutput => on_failure, - creates => "${valid_file}", - onlyif => "${::ipa::common::ipa_installed}", - # ipa-server-install or ipa-replica-install must execute first! - require => Exec['ipa-install'], # same alias for either install - alias => "ipa-replica-prepare-${name}", - } - - # tag this file so it doesn't get purged - file { "${valid_file}": - owner => root, - group => nobody, - mode => 600, # u=rw - backup => false, # don't backup to filebucket - ensure => present, - require => Exec["ipa-replica-prepare-${name}"], - } - - # add this manually so we don't have to wait for the exported resources - ssh::recv { "${valid_fqdn}": - - } - - # use a pull, so the remote path is decided over *there* - # export (@@) the pull, so that it knows the file is already here... - @@ssh::file::pull { "ipa-replica-prepare-${::fqdn}-${name}": - user => 'root', # src user - host => "${::fqdn}", # src host - file => "${valid_file}", # src file - path => "${vardir}/replica/install/", # dest path; overridden - dest => "${filedest}", # dest file - verify => false, # rely on mtime - pair => false, # do it now so it happens fast! - tag => 'ipa-replica-prepare', # TODO: can be used as grouping - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/replica/prepare/base.pp b/ipa/manifests/server/replica/prepare/base.pp deleted file mode 100644 index 69503dac2..000000000 --- a/ipa/manifests/server/replica/prepare/base.pp +++ /dev/null @@ -1,35 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class ipa::server::replica::prepare::base { - - include ipa::server::replica::base - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - file { "${vardir}/replica/prepare/": - ensure => directory, # make sure this is a directory - recurse => true, # recursively manage directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, group => nobody, mode => 600, backup => false, - require => File["${vardir}/replica/"], - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/service.pp b/ipa/manifests/server/service.pp deleted file mode 100644 index b12e545f8..000000000 --- a/ipa/manifests/server/service.pp +++ /dev/null @@ -1,230 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -define ipa::server::service( - $service = '', # nfs, HTTP, ldap - $host = '', # should match $name of ipa::server::host - $domain = '', # must be the empty string by default - $realm = '', - $principal = '', # after all that, you can override principal... - $server = '', # where the client will find the ipa server... - - # args - $pactype = [], # bad values are silently discarded, [] is NONE - - #$hosts = [], # TODO: add hosts managed by support - - # special parameters... - $watch = true, # manage all changes to this resource, reverting others - $modify = true, # modify this resource on puppet changes or not ? - $comment = '', - $ensure = present # TODO -) { - include ipa::server - include ipa::server::service::base - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - $dns = $ipa::server::dns # boolean from main obj - - # TODO: a better regexp magician could probably do a better job :) - # nfs/nfs.example.com@EXAMPLE.COM - $r = '^([a-zA-Z][a-zA-Z0-9]*)(/([a-z0-9][a-z0-9\.\-]*)(@([A-Z][A-Z\.\-]*)){0,1}){0,1}$' - - $a = regsubst("${name}", $r, '\1') # service (nfs) - $b = regsubst("${name}", $r, '\3') # fqdn (nfs.example.com) - $c = regsubst("${name}", $r, '\5') # realm (EXAMPLE.COM) - - # service: first try to get value from arg, then fall back to $a (name) - $valid_service = "${service}" ? { - '' => "${a}", # get from $name regexp - default => "${service}", - } - if "${valid_service}" == '' { - # NOTE: if we see this message it might be a regexp pattern bug - fail('The $service must be specified.') - } - - # host: first try to get value from arg, then fall back to $b - # this is not necessarily the fqdn, but it could be. both are possible! - $valid_host = "${host}" ? { - '' => "${b}", # get from $name regexp - default => "${host}", - } - # this error will probably prevent a later error in $valid_domain - if "${valid_host}" == '' { - fail('The $host must be specified.') - } - - # parse the fqdn from $valid_host - $r2 = '^([a-z][a-z0-9\-]*)(\.{0,1})([a-z0-9\.\-]*)$' - #$h = regsubst("${valid_host}", $r2, '\1') # hostname - $d = regsubst("${valid_host}", $r2, '\3') # domain - - $valid_domain = delete("${valid_host}", '.') ? { - "${valid_host}" => "${domain}" ? { # no dots, not an fqdn! - '' => "${ipa::server::domain}" ? { # NOTE: server! - '' => "${::domain}", # default to global val - default => "${ipa::server::domain}", # main! - }, - default => "${domain}", - }, - default => "${domain}" ? { # dots, it's an fqdn... - '' => "${d}", # okay, used parsed value, it had dots! - "${d}" => "${domain}", # they match, okay phew - default => '', # no match, set '' to trigger an error! - }, - } - - # this error condition is very important because '' is used as trigger! - if "${valid_domain}" == '' { - fail('The $domain must be specified.') - } - - $valid_fqdn = delete("${valid_host}", '.') ? { # does it have any dots - "${valid_host}" => "${valid_host}.${valid_domain}", - default => "${valid_host}", # it had dot(s) present - } - - $valid_realm = "${realm}" ? { - '' => "${c}" ? { # get from $name regexp - '' => upcase($valid_domain), # a backup plan default - default => "${c}", # got from $name regexp - }, - default => "${realm}", - } - - # sanity checking, this should probably not happen - if "${valid_realm}" == '' { - fail('The $realm must be specified.') - } - - $valid_server = "${server}" ? { - '' => "${::hostname}.${::domain}", - default => "${server}", - } - - # sanity checking, this should probably not happen - if "${valid_server}" == '' { - fail('The $server must be specified.') - } - - $valid_principal = "${principal}" ? { - '' => "${valid_service}/${valid_fqdn}@${valid_realm}", - default => "${principal}", # just do what you want - } - - if $watch and (! $modify) { - fail('You must be able to $modify to be able to $watch.') - } - - $pactype_valid = ['MS-PAC', 'PAD'] # or 'NONE' - $pactype_array = type($pactype) ? { - 'array' => $pactype, - 'string' => ["${pactype}"], - default => [], # will become 'NONE' - } - $valid_pactype = split(inline_template('<%= ((pactype_array.delete_if {|x| not pactype_valid.include?(x)}.length == 0) ? ["NONE"] : pactype_array.delete_if {|x| not pactype_valid.include?(x)}).join("#") %>'), '#') - - $args01 = sprintf("--pac-type='%s'", join($valid_pactype, ',')) - - $arglist = ["${args01}"] # future expansion available :) - $args = join(delete($arglist, ''), ' ') - - # switch the slashes for a file name friendly character - $valid_principal_file = regsubst("${valid_principal}", '/', '-', 'G') - file { "${vardir}/services/${valid_principal_file}.service": - content => "${valid_principal}\n${args}\n", - owner => root, - group => nobody, - mode => 600, # u=rw,go= - require => File["${vardir}/services/"], - ensure => present, - } - - $exists = "/usr/bin/ipa service-show '${valid_principal}' > /dev/null 2>&1" - $force = "${args}" ? { # if args is empty - '' => '--force', # we have no args! - default => "${args} --force", # pixel perfect... - } - $fargs = $dns ? { # without the dns, - true => "${force}", # we don't need to - default => "${args}", # force everything - } - # NOTE: this runs when no service is present... - exec { "ipa-server-service-add-${name}": # alias - # this has to be here because the command string gets too long - # for a puppet $name var and strange things start to happen... - command => "/usr/bin/ipa service-add '${valid_principal}' ${fargs}", - logoutput => on_failure, - unless => "${exists}", - require => $dns ? { - true => [ - Exec['ipa-server-kinit'], - ], - default => [ - Exec['ipa-dns-check'], # avoid --force errors! - Exec['ipa-server-kinit'], - ], - }, - } - - # NOTE: this runs when we detect that the attributes don't match (diff) - if $modify and ("${args}" != '') { # if there are changes to do... - #exec { "/usr/bin/ipa service-mod '${valid_principal}' ${args}": - exec { "ipa-server-service-mod-${name}": - command => "/usr/bin/ipa service-mod '${valid_principal}' ${args}", - logoutput => on_failure, - refreshonly => $watch ? { - false => true, # when not watching, we - default => undef, # refreshonly to change - }, - subscribe => $watch ? { - false => File["${vardir}/services/${valid_principal_file}.service"], - default => undef, - }, - onlyif => "${exists}", - unless => $watch ? { - false => undef, # don't run the diff checker... - default => "${exists} && ${vardir}/diff.py service '${valid_principal}' ${args}", - }, - require => [ - File["${vardir}/diff.py"], - Exec['ipa-server-kinit'], - Exec["ipa-server-service-add-${name}"], - ], - #alias => "ipa-server-service-mod-${name}", - } - } - - @@ipa::client::service { "${name}": # this is usually the principal - # NOTE: this should set all the client args it can safely assume - service => "${valid_service}", - host => "${valid_host}", # this value is used to collect - domain => "${valid_domain}", - realm => "${valid_realm}", - principal => "${valid_principal}", - server => "${valid_server}", - comment => "${comment}", - ensure => $ensure, - require => Ipa::Client::Host["${name}"], # should match! -# TODO: FIXME: tag => "${name}", # bonus - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/service/base.pp b/ipa/manifests/server/service/base.pp deleted file mode 100644 index 94e4399f2..000000000 --- a/ipa/manifests/server/service/base.pp +++ /dev/null @@ -1,98 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class ipa::server::service::base { - include ipa::server - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - # by default, the following services get installed with freeipa: - # DNS/ipa.example.com@EXAMPLE.COM - # dogtagldap/ipa.example.com@EXAMPLE.COM - # HTTP/ipa.example.com@EXAMPLE.COM - # ldap/ipa.example.com@EXAMPLE.COM - # since we don't want to purge them, we need to exclude them... - $prefix = ['DNS', 'dogtagldap', 'HTTP', 'ldap'] - $valid_hostname = $ipa::server::valid_hostname - $valid_domain = $ipa::server::valid_domain - $valid_realm = $ipa::server::valid_realm - $append = "/${valid_hostname}.${valid_domain}@${valid_realm}" - $service_always_ignore = suffix($prefix, $append) - - $service_excludes = $ipa::server::service_excludes - $valid_service_excludes = type($service_excludes) ? { - 'string' => [$service_excludes], - 'array' => $service_excludes, - 'boolean' => $service_excludes ? { - # TODO: there's probably a better fqdn match expression - # this is an expression to prevent all fqdn deletion... - #true => ['^[a-zA-Z0-9\.\-]*$'], - true => ['^[[:alpha:]]{1}[[:alnum:]-.]*$'], - default => false, - }, - default => false, # trigger error... - } - - if type($valid_service_excludes) != 'array' { - fail('The $service_excludes must be an array.') - } - - # directory of system tags which should exist (as managed by puppet) - file { "${vardir}/services/": - ensure => directory, # make sure this is a directory - recurse => true, # recursively manage directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, group => nobody, mode => 600, backup => false, - notify => Exec['ipa-clean-services'], - require => File["${vardir}/"], - } - - # these are template variables for the clean.sh.erb script - $id_dir = 'services' - $ls_cmd = '/usr/bin/ipa service-find --pkey-only --raw | /usr/bin/tr -d " " | /bin/grep "^krbprincipalname:" | /bin/cut -b 18-' # show ipa services - $rm_cmd = '/usr/bin/ipa service-del ' # delete ipa services - $fs_chr = ' ' - $suffix = '.service' - $regexp = $valid_service_excludes - $ignore = $service_always_ignore - - # build the clean script - file { "${vardir}/clean-services.sh": - content => template('ipa/clean.sh.erb'), - owner => root, - group => nobody, - mode => 700, # u=rwx - backup => false, # don't backup to filebucket - ensure => present, - require => File["${vardir}/"], - } - - # run the cleanup - exec { "${vardir}/clean-services.sh": - logoutput => on_failure, - refreshonly => true, - require => [ - Exec['ipa-server-kinit'], - File["${vardir}/clean-services.sh"], - ], - alias => 'ipa-clean-services', - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/user.pp b/ipa/manifests/server/user.pp deleted file mode 100644 index 4a288313e..000000000 --- a/ipa/manifests/server/user.pp +++ /dev/null @@ -1,543 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -define ipa::server::user( # $login or principal as a unique id - $login = '', # usually the same as $name, but set manually - $instance = '', # as in: user/instance@REALM - $domain = '', # must be the empty string by default - $realm = '', - $principal = true, # after all that, you can override principal... - - # name args - $first = '', # required - $last = '', # required - $cn = true, # full name, defaults to "$first $last" - $displayname = true, # defaults to "$first $last" - $initials = true, # defaults to $first[0]+$last[0] - - # some of these parameters can be strings, arrays, or boolean specials! - $email = true, # comes with a sensible default (false = no) - $gecos = true, # old style passwd field, can be set manually - - # special characteristics - $uid = true, # either pick a value, or let system assign it! - $gid = true, # true means try to match $uid value on create! - $shell = true, - $home = true, - $sshpubkeys = false, - - # password - $random = false, # set to true to have the password generated... - $password_file = false, # save to file in ${vardir}/ipa/users/passwords/ - $password_mail = false, # TODO: mail a gpg encrypted password to admin! - - # mailing address section (just plain strings, false is unmanaged) - $street = false, # street address - $city = false, # city - $state = false, # state/province - $postalcode = false, # zip/postal code - - # these four accept arrays or a string. false means unmanaged... - $phone = false, # telephone number - $mobile = false, # mobile telephone number - $pager = false, # pager number - $fax = false, # fax number - - # other information - $jobtitle = false, # job title - $orgunit = false, # org. unit (department) - $manager = false, # manager (should match an existing user $name) - $carlicense = false, # car license (who cares?) - - #$hosts = [], # TODO: add hosts managed by support if exists! - - # special parameters... - $watch = true, # manage all changes to this resource, reverting others - $modify = true, # modify this resource on puppet changes or not ? - $comment = '', - $ensure = present # TODO -) { - include ipa::server - include ipa::server::user::base - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - # TODO: a better regexp magician could probably do a better job :) - # james/admin@EXAMPLE.COM - # james@EXAMPLE.COM - # james - $r = '^([a-zA-Z][a-zA-Z0-9]*)((/([a-zA-Z][a-zA-Z0-9]*)){0,1}@([A-Z][A-Z\.\-]*)){0,1}$' - - $a = regsubst("${name}", $r, '\1') # login (james) - $b = regsubst("${name}", $r, '\4') # instance (admin) - $c = regsubst("${name}", $r, '\5') # realm (EXAMPLE.COM) - - # user: first try to get value from arg, then fall back to $a (name) - $valid_login = "${login}" ? { - '' => "${a}", # get from $name regexp - default => "${login}", - } - if "${valid_login}" == '' { - # NOTE: if we see this message it might be a regexp pattern bug - fail('The $login must be specified.') - } - - # host: first try to get value from arg, then fall back to $b - # this is not necessarily the group, but it could be. both are possible - # empty values are allowed and possibly even common :) - $valid_instance = "${instance}" ? { - '' => "${b}", # get from $name regexp - default => "${instance}", - } - - $valid_domain = "${domain}" ? { - '' => "${ipa::server::domain}" ? { # NOTE: server! - '' => "${::domain}", # default to global val - default => "${ipa::server::domain}", # main! - }, - default => "${domain}", - } - - # this error condition is very important because '' is used as trigger! - if "${valid_domain}" == '' { - fail('The $domain must be specified.') - } - - $valid_realm = "${realm}" ? { - '' => "${c}" ? { # get from $name regexp - '' => upcase($valid_domain), # a backup plan default - default => "${c}", # got from $name regexp - }, - default => "${realm}", - } - - # sanity checking, this should probably not happen - if "${valid_realm}" == '' { - fail('The $realm must be specified.') - } - - # to be used if principal is generated from the available entered data! - $auto_principal = "${valid_instance}" ? { - '' => "${valid_login}@${valid_realm}", # no instance ! - default => "${valid_login}/${valid_instance}@${valid_realm}", - } - - $valid_principal = type($principal) ? { - 'string' => "${principal}" ? { - '' => "${auto_principal}", - default => "${principal}", # just do what you want - }, - 'boolean' => $principal ? { - false => '', # don't use a principal - default => "${auto_principal}", - }, - default => '', - } - - if $watch and (! $modify) { - fail('You must be able to $modify to be able to $watch.') - } - - if "${first}" == '' { - fail("The first name is required for: '${valid_login}'.") - } - if "${last}" == '' { - fail("The last name is required for: '${valid_login}'.") - } - - $args01 = "${first}" ? { - '' => '', - default => "--first='${first}'", - } - $args02 = "${last}" ? { - '' => '', - default => "--last='${last}'", - } - - $args03 = type($cn) ? { - 'string' => "--cn='${cn}'", - 'boolean' => $cn ? { - false => '', - default => "--cn='${first} ${last}'", - }, - default => '', - } - - $args04 = type($displayname) ? { - 'string' => "--displayname='${displayname}'", - 'boolean' => $displayname ? { - false => '', - default => "--displayname='${first} ${last}'", - }, - default => '', - } - - $args05 = type($initials) ? { - 'string' => "--initials='${displayname}'", - 'boolean' => $initials ? { - false => '', - # NOTE: [0,1] is a version robust way to get index 0... - default => sprintf("--initials='%s'", inline_template('<%= first[0,1]+last[0,1] %>')), - }, - default => '', - } - - # email can provide a sensible default - $default_email_domain = $ipa::server::default_email_domain - $valid_email = type($email) ? { - 'string' => "${email}" ? { - '' => [], # assume managed but empty (rm values) - default => ["${email}"], - }, - 'array' => $email, - 'boolean' => $email ? { - false => '', # unmanaged - default => ["${valid_login}@${default_email_domain}"], # sensible default - }, - default => '', # unmanaged - } - $args06 = type($valid_email) ? { - 'array' => inline_template('<% if valid_email == [] %>--email=<% else %><%= valid_email.map {|x| "--email=\'"+x+"\'" }.join(" ") %><% end %>'), - default => '', # unmanaged - } - - $args07 = type($gecos) ? { - 'string' => "--gecos='${gecos}'", - 'boolean' => $gecos ? { - false => '', - default => "--gecos='${first} ${last}'", - }, - default => '', - } - - # TODO: validate id ranges ? - $args08 = type($uid) ? { - 'string' => "--uid='${uid}'", - 'integer' => "--uid='${uid}'", - default => '', - } - - # TODO: validate id ranges ? - $args09 = type($gid) ? { - 'string' => "--gidnumber='${gid}'", - 'integer' => "--gidnumber='${gid}'", - 'boolean' => $gid ? { - false => '', - default => type($uid) ? { # auto try to match uid - 'string' => "--gidnumber='${uid}'", # uid ! - 'integer' => "--gidnumber='${uid}'", # uid ! - default => '', # auto - }, - }, - default => '', - } - - $default_shell = $ipa::server::default_shell - $args10 = type($shell) ? { - 'string' => "--shell='${shell}'", - 'boolean' => $shell ? { - false => '', - default => "--shell='${default_shell}'", - }, - default => '', - } - - # TODO: the home stuff seems to not use trailing slashes. can i add it? - $default_homes = $ipa::server::default_homes - $args11 = type($home) ? { - 'string' => sprintf("--homedir='%s'", regsubst("${home}" , '\/$', '')), - 'boolean' => $home ? { - false => '', - default => type($default_homes) ? { - 'string' => sprintf("--homedir='%s/${valid_login}'", regsubst("${default_homes}" , '\/$', '')), - # TODO: warning ? - default => '', # can't manage, parent is false - }, - }, - default => '', - } - - # users individual ssh public keys - $valid_sshpubkeys = type($sshpubkeys) ? { - 'string' => "${sshpubkeys}" ? { - '' => [], # assume managed but empty (rm values) - default => ["${sshpubkeys}"], - }, - 'array' => $sshpubkeys, - default => '', # unmanaged - } - $args12 = type($valid_sshpubkeys) ? { - 'array' => inline_template('<% if valid_sshpubkeys == [] %>--sshpubkey=<% else %><%= valid_sshpubkeys.map {|x| "--sshpubkey=\'"+x+"\'" }.join(" ") %><% end %>'), - default => '', # unmanaged - } - - # mailing address section - $args13 = type($street) ? { - 'string' => "--street='${street}'", - 'boolean' => $street ? { - true => '--street=', # managed - default => '', # unmanaged - }, - default => '', # whatever and unmanaged - } - - $args14 = type($city) ? { - 'string' => "--city='${city}'", - 'boolean' => $city ? { - true => '--city=', - default => '', - }, - default => '', - } - - $args15 = type($state) ? { # or province - 'string' => "--state='${state}'", - 'boolean' => $state ? { - true => '--state=', - default => '', - }, - default => '', - } - - $args16 = type($postalcode) ? { - 'string' => "--postalcode='${postalcode}'", - 'boolean' => $postalcode ? { - true => '--postalcode=', - default => '', - }, - default => '', - } - - # the following four phone number types can be arrays - $valid_phone = type($phone) ? { - 'string' => "${phone}" ? { - '' => [], # assume managed but empty (rm values) - default => ["${phone}"], - }, - 'array' => $phone, - default => '', # unmanaged - } - $args17 = type($valid_phone) ? { - 'array' => inline_template('<% if valid_phone == [] %>--phone=<% else %><%= valid_phone.map {|x| "--phone=\'"+x+"\'" }.join(" ") %><% end %>'), - default => '', # unmanaged - } - - $valid_mobile = type($mobile) ? { - 'string' => "${mobile}" ? { - '' => [], # assume managed but empty (rm values) - default => ["${mobile}"], - }, - 'array' => $mobile, - default => '', # unmanaged - } - $args18 = type($valid_mobile) ? { - 'array' => inline_template('<% if valid_mobile == [] %>--mobile=<% else %><%= valid_mobile.map {|x| "--mobile=\'"+x+"\'" }.join(" ") %><% end %>'), - default => '', # unmanaged - } - - $valid_pager = type($pager) ? { - 'string' => "${pager}" ? { - '' => [], # assume managed but empty (rm values) - default => ["${pager}"], - }, - 'array' => $pager, - default => '', # unmanaged - } - $args19 = type($valid_pager) ? { - 'array' => inline_template('<% if valid_pager == [] %>--pager=<% else %><%= valid_pager.map {|x| "--pager=\'"+x+"\'" }.join(" ") %><% end %>'), - default => '', # unmanaged - } - - $valid_fax = type($fax) ? { - 'string' => "${fax}" ? { - '' => [], # assume managed but empty (rm values) - default => ["${fax}"], - }, - 'array' => $fax, - default => '', # unmanaged - } - $args20 = type($valid_fax) ? { - 'array' => inline_template('<% if valid_fax == [] %>--fax=<% else %><%= valid_fax.map {|x| "--fax=\'"+x+"\'" }.join(" ") %><% end %>'), - default => '', # unmanaged - } - - # other information - $args21 = type($jobtitle) ? { # job title - 'string' => "--title='${jobtitle}'", - 'boolean' => $jobtitle ? { - true => '--title=', - default => '', - }, - default => '', - } - - $args22 = type($orgunit) ? { - 'string' => "--orgunit='${orgunit}'", - 'boolean' => $orgunit ? { - true => '--orgunit=', - default => '', - }, - default => '', - } - - # manager requires user exists... this lets us match a user principal - $valid_manager = regsubst("${manager}", $r, '\1') # login (james) - $args23 = type($manager) ? { # this has to match an existing user... - 'string' => "--manager='${valid_manager}'", - 'boolean' => $manager ? { - true => '--manager=', - default => '', - }, - default => '', - } - - $args24 = type($carlicense) ? { - 'string' => "--carlicense='${carlicense}'", - 'boolean' => $carlicense ? { - true => '--carlicense=', - default => '', - }, - default => '', - } - - $arglist = ["${args01}", "${args02}", "${args03}", "${args04}", "${args05}", "${args06}", "${args07}", "${args08}", "${args09}", "${args10}", "${args11}", "${args12}", "${args13}", "${args14}", "${args15}", "${args16}", "${args17}", "${args18}", "${args19}", "${args20}", "${args21}", "${args22}", "${args23}", "${args24}"] - $args = join(delete($arglist, ''), ' ') - - # switch bad characters for file name friendly characters (unused atm!) - # this could be useful if we allow login's with $ and others in them... - $valid_login_file = regsubst("${valid_login}", '\$', '-', 'G') - file { "${vardir}/users/${valid_login_file}.user": - content => "${valid_login}\n${args}\n", - owner => root, - group => nobody, - mode => 600, # u=rw,go= - require => File["${vardir}/users/"], - ensure => present, - } - - if $random and $password_file { - file { "${vardir}/users/passwords/${valid_login}.password": - # no content! this is a tag, content comes in by echo ! - owner => root, - group => nobody, - mode => 600, # u=rw,go= - backup => false, - require => File["${vardir}/users/passwords/"], - ensure => present, - } - } - - $exists = "/usr/bin/ipa user-show '${valid_login}' > /dev/null 2>&1" - # this requires ensures the $manager user exists when we can check that - # this melds together the kinit require which is needed by the user add - $requires = type($manager) ? { - 'string' => "${manager}" ? { - '' => Exec['ipa-server-kinit'], - default => $watch ? { - false => Exec['ipa-server-kinit'], - default => [ - Exec['ipa-server-kinit'], - Ipa::Server::User["${manager}"], - ], - }, - }, - default => Exec['ipa-server-kinit'], - } - - # principal is only set on user add... it can't be edited afaict - $principal_arg = "${valid_principal}" ? { # not shown in ipa gui! - '' => '', - default => "--principal='${valid_principal}'", - } - - $aargs = "${principal_arg}" ? { # principal exists - '' => "${args}", # just normal args - default => "${principal_arg} ${args}", # pixel perfect... - } - - # NOTE: this runs when no user is present... - exec { "ipa-server-user-add-${name}": # alias - # this has to be here because the command string gets too long - # for a puppet $name var and strange things start to happen... - command => "/usr/bin/ipa user-add '${valid_login}' ${aargs}", - logoutput => on_failure, - unless => "${exists}", - require => $requires, - } - - # NOTE: this runs when we detect that the attributes don't match (diff) - if $modify and ("${args}" != '') { # if there are changes to do... - #exec { "/usr/bin/ipa user-mod '${valid_login}' ${args}": - exec { "ipa-server-user-mod-${name}": - command => "/usr/bin/ipa user-mod '${valid_login}' ${args}", - logoutput => on_failure, - refreshonly => $watch ? { - false => true, # when not watching, we - default => undef, # refreshonly to change - }, - subscribe => $watch ? { - false => File["${vardir}/users/${valid_login_file}.user"], - default => undef, - }, - onlyif => "${exists}", - unless => $watch ? { - false => undef, # don't run the diff checker... - default => "${exists} && ${vardir}/diff.py user '${valid_login}' ${args}", - }, - require => [ - File["${vardir}/diff.py"], - Exec['ipa-server-kinit'], - # this user-add exec pulls in manager $requires - Exec["ipa-server-user-add-${name}"], - ], - #alias => "ipa-server-user-mod-${name}", - } - } - - $prog01 = $password_file ? { - true => "/bin/cat > ${vardir}/users/passwords/${valid_login}.password", - default => '', - } - - $gpg_email = $ipa::server::valid_email # admin email - #$gpg_key = $ipa::server::TODO - $prog02 = $password_mail ? { - #true => "/bin/cat | /usr/bin/gpg TODO | /bin/mailx -s 'GPG encrypted password' '${gpg_email}'", # FIXME: add this code! - default => '', - } - - if $modify and $random { - $proglist = ["${prog01}", "${prog02}"] - # eg /usr/bin/tee /dev/null >(prog1) >(prog2) >(progN) - $progs = join(suffix(prefix(delete($proglist, ''), '>('), ')'), ' ') - exec { "ipa-server-user-qmod-${name}": - # bash -c is needed because this command uses bashisms! - command => "/bin/bash -c \"/usr/bin/ipa user-mod '${valid_login}' --raw --random | /usr/bin/tr -d ' ' | /bin/grep '^randompassword:' | /bin/cut -b 16- | /usr/bin/tee /dev/null ${progs}\"", - logoutput => on_failure, - onlyif => "/usr/bin/test \"`/usr/bin/ipa user-show '${valid_login}' --raw | /usr/bin/tr -d ' ' | /bin/grep '^has_password:' | /bin/cut -b 14-`\" = 'False'", - require => [ - Exec['ipa-server-kinit'], - Exec["ipa-server-user-add-${name}"], - #Exec["ipa-server-user-mod-${name}"], # not needed... - ], - #alias => "ipa-server-user-qmod-${name}", - } - } -} - -# vim: ts=8 diff --git a/ipa/manifests/server/user/base.pp b/ipa/manifests/server/user/base.pp deleted file mode 100644 index 542e5be5c..000000000 --- a/ipa/manifests/server/user/base.pp +++ /dev/null @@ -1,98 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class ipa::server::user::base { - include ipa::server - include ipa::vardir - #$vardir = $::ipa::vardir::module_vardir # with trailing slash - $vardir = regsubst($::ipa::vardir::module_vardir, '\/$', '') - - # by default, the following users get installed with freeipa: - # admin - # since we don't want to purge them, we need to exclude them... - $user_always_ignore = ['admin'] - $user_excludes = $ipa::server::user_excludes - $valid_user_excludes = type($user_excludes) ? { - 'string' => [$user_excludes], - 'array' => $user_excludes, - 'boolean' => $user_excludes ? { - # TODO: there's probably a better user match expression - # this is an expression to prevent all user deletion... - #true => ['^[a-zA-Z0-9]*$'], - true => ['^[[:alpha:]]{1}[[:alnum:]]*$'], - default => false, - }, - default => false, # trigger error... - } - - if type($valid_user_excludes) != 'array' { - fail('The $user_excludes must be an array.') - } - - # directory of system tags which should exist (as managed by puppet) - file { "${vardir}/users/": - ensure => directory, # make sure this is a directory - recurse => true, # recursively manage directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, group => nobody, mode => 600, backup => false, - notify => Exec['ipa-clean-users'], - require => File["${vardir}/"], - } - - # these are template variables for the clean.sh.erb script - $id_dir = 'users' - $ls_cmd = '/usr/bin/ipa user-find --pkey-only --raw | /usr/bin/tr -d " " | /bin/grep "^uid:" | /bin/cut -b 5-' # show ipa users - $rm_cmd = '/usr/bin/ipa user-del ' # delete ipa users - $fs_chr = ' ' - $suffix = '.user' - $regexp = $valid_user_excludes - $ignore = $user_always_ignore - - # build the clean script - file { "${vardir}/clean-users.sh": - content => template('ipa/clean.sh.erb'), - owner => root, - group => nobody, - mode => 700, # u=rwx - backup => false, # don't backup to filebucket - ensure => present, - require => File["${vardir}/"], - } - - # run the cleanup - exec { "${vardir}/clean-users.sh": - logoutput => on_failure, - refreshonly => true, - require => [ - Exec['ipa-server-kinit'], - File["${vardir}/clean-users.sh"], - ], - alias => 'ipa-clean-users', - } - - file { "${vardir}/users/passwords/": # for storing random passwords - ensure => directory, # make sure this is a directory - recurse => true, # recursively manage directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, group => nobody, mode => 600, backup => false, - require => File["${vardir}/users/"], - } -} - -# vim: ts=8 diff --git a/ipa/manifests/vardir.pp b/ipa/manifests/vardir.pp deleted file mode 100644 index d048c2ba2..000000000 --- a/ipa/manifests/vardir.pp +++ /dev/null @@ -1,51 +0,0 @@ -# FreeIPA templating module by James -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -class ipa::vardir { # module vardir snippet - $module_vardir = sprintf("%s/ipa/", regsubst($tmp, '\/$', '')) - file { "${module_vardir}": # /var/lib/puppet/tmp/ipa/ - ensure => directory, # make sure this is a directory - recurse => true, # recursively manage directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, group => nobody, mode => 600, backup => false, - } - if "${::puppet_vardirtmp}" == '' { - if "${::puppet_vardir}" == '' { - # here, we require that the puppetlabs fact exist! - fail('Fact: $puppet_vardir is missing!') - } - $tmp = sprintf("%s/tmp/", regsubst($::puppet_vardir, '\/$', '')) - # base directory where puppet modules can work and namespace in - file { "${tmp}": - ensure => directory, # make sure this is a directory - recurse => false, # don't recurse into directory - purge => true, # purge all unmanaged files - force => true, # also purge subdirs and links - owner => root, - group => nobody, - mode => 600, - backup => false, # don't backup to filebucket - before => File["${module_vardir}"], - #require => Package['puppet'], # no puppet module seen - } - } else { - $tmp = sprintf("%s/", regsubst($::puppet_vardirtmp, '\/$', '')) - } -} - -# vim: ts=8 diff --git a/ipa/metadata.json b/ipa/metadata.json deleted file mode 100644 index 89d5c0939..000000000 --- a/ipa/metadata.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "purpleidea-ipa", - "version": "0.0.4", - "author": "James Shubin", - "summary": "FreeIPA templating module by James", - "license": "GNU Affero General Public License, Version 3.0+", - "source": "https://github.com/purpleidea/puppet-ipa/", - "project_page": "https://github.com/purpleidea/puppet-ipa/", - "issues_url": null, - "description": "UNKNOWN", - "dependencies": [ - { "name": "puppetlabs/stdlib", "version_requirement": ">= 4.0.0" } - ] -} - diff --git a/ipa/puppet-ipa.spec.in b/ipa/puppet-ipa.spec.in deleted file mode 100644 index dca48da16..000000000 --- a/ipa/puppet-ipa.spec.in +++ /dev/null @@ -1,49 +0,0 @@ -# special thanks to kkeithley for using his wizard rpm skills to get this going -%global puppet_module_version __VERSION__ - -Name: puppet-ipa -Version: __VERSION__ -#Release: __RELEASE__%{?dist} # use this to make dist specific builds -Release: __RELEASE__ -Summary: A puppet module for FreeIPA -License: AGPLv3+ -URL: https://github.com/purpleidea/puppet-ipa -Source0: https://download.gluster.org/pub/gluster/purpleidea/puppet-ipa/SOURCES/puppet-ipa-%{puppet_module_version}.tar.bz2 -BuildArch: noarch - -Requires: puppet >= 3.0.0 -Requires: puppetlabs-stdlib >= 4.1.0 - -# these should be 'Suggests:' or similar, since they aren't absolutely required -#Requires: puppetlabs-apt >= 1.4.0 -Requires: puppet-keepalived >= 0.0.1 -Requires: puppet-puppet >= 0.0.1 -Requires: puppet-shorewall >= 0.0.1 -Requires: puppet-ssh >= 0.0.1 -Requires: puppet-yum >= 0.0.1 - -%description -A puppet module used for installing, configuring and managing FreeIPA. - -%prep -%setup -c -q -T -D -a 0 - -find %{_builddir} -type f -name ".*" -exec rm {} + -find %{_builddir} -size 0 -exec rm {} + -find %{_builddir} \( -name "*.pl" -o -name "*.sh" \) -exec chmod +x {} + -find %{_builddir} \( -name "*.pp" -o -name "*.py" \) -exec chmod -x {} + -find %{_builddir} \( -name "*.rb" -o -name "*.erb" \) -exec chmod -x {} + -exec sed -i "/^#!/{d;q}" {} + - -%build - -%install -rm -rf %{buildroot} -# _datadir is typically /usr/share/ -install -d -m 0755 %{buildroot}/%{_datadir}/puppet/modules/ -cp -r puppet-ipa-%{puppet_module_version} %{buildroot}/%{_datadir}/puppet/modules/ipa - -%files -%{_datadir}/puppet/modules/* - -# this changelog is auto-generated by git log -%changelog diff --git a/ipa/spec/spec_helper.rb b/ipa/spec/spec_helper.rb deleted file mode 100644 index b51de3a7e..000000000 --- a/ipa/spec/spec_helper.rb +++ /dev/null @@ -1,18 +0,0 @@ -dir = File.expand_path(File.dirname(__FILE__)) -$LOAD_PATH.unshift File.join(dir, 'lib') - -require 'mocha' -require 'puppet' -require 'rspec' -require 'spec/autorun' - -Spec::Runner.configure do |config| - config.mock_with :mocha -end - -# We need this because the RAL uses 'should' as a method. This -# allows us the same behaviour but with a different method name. -class Object - alias :must :should -end - diff --git a/ipa/templates/clean.sh.erb b/ipa/templates/clean.sh.erb deleted file mode 100644 index 6c1382be5..000000000 --- a/ipa/templates/clean.sh.erb +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/bash - -# README: -# This script loops through listed items and removes those which are not known. -# This is so that when puppet removes the 'tag' file the actual item gets rm'd. -# An optional list of bash matches can be provided to spare removal if matched. -# -# NOTE: -# The following variables were used to build this script: -# id_dir: <%= id_dir %> -# ls_cmd: <%= ls_cmd %> -# rm_cmd: <%= rm_cmd %> -# fs_chr: <%= fs_chr %> -# suffix: <%= suffix %> -# regexp: <%= regexp.join(' ') %> -# ignore: <%= ignore.join(' ') %> -# - -for i in `<%= ls_cmd %> | /bin/awk -F '<%= fs_chr %>' '{print $1}'`; do - #echo "$i" - found=false - # this section is essentially an in_array() - for j in <%= scope.lookupvar('::ipa::vardir::module_vardir').gsub(/\/$/, '') %>/<%= id_dir %>/*<%= suffix %>; do - [ -e "$j" ] || break # loop in bash properly - #echo "found tag: $j" - # compare against first line of the file - n=`/usr/bin/head -1 "$j"` - if [ "$i" == "$n" ]; then - found=true # found it -- it's safe - break - fi - done - -<% if ignore.is_a?(Array) and ignore != [] -%> - # check against built in ignores -<% ignore.each do |x| -%> - if [ "$i" == '<%= x %>' ]; then - found=true - fi -<% end -%> - -<% end -%> -<% if regexp.is_a?(Array) and regexp != [] -%> - # quoting the match pattern indicate a string match (bash v3.2+) -<% regexp.each do |m| -%> - if [[ "$i" =~ <%= m %> ]]; then - found=true - fi -<% end -%> - -<% end -%> - # if not found, not matched, and not ignored, then it should be removed - if ! $found; then - # echo "Removing: $i" - <%= rm_cmd %>"$i" - fi -done - diff --git a/ipa/vagrant/.gitignore b/ipa/vagrant/.gitignore deleted file mode 100644 index cb41b90cb..000000000 --- a/ipa/vagrant/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -puppet-ipa.yaml -.vagrant/ -.ssh/ diff --git a/ipa/vagrant/Vagrantfile b/ipa/vagrant/Vagrantfile deleted file mode 100644 index 64246847d..000000000 --- a/ipa/vagrant/Vagrantfile +++ /dev/null @@ -1,570 +0,0 @@ -# -*- mode: ruby -*- -# vi: set ft=ruby : - -# Vagrantfile for FreeIPA using Puppet-Ipa -# Copyright (C) 2012-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: vagrant-libvirt needs to run in series (not in parallel) to avoid trying -# to create the network twice, eg: 'vagrant up --no-parallel'. alternatively you -# can build the first host (puppet server) manually, and then the hosts next eg: -# 'vagrant up puppet && vagrant up' which ensures the puppet server is up first! -# NOTE: https://github.com/pradels/vagrant-libvirt/issues/104 is the open bug... - -# README: https://ttboj.wordpress.com/2013/12/09/vagrant-on-fedora-with-libvirt/ -# README: https://ttboj.wordpress.com/2013/12/21/vagrant-vsftp-and-other-tricks/ -# ALSO: https://ttboj.wordpress.com/2014/01/02/vagrant-clustered-ssh-and-screen/ - -# NOTE: this will not work properly on Fedora 19 or anything that does not have -# the libvirt broadcast patch included. You can check which libvirt version you -# have and see if that version tag is in the libvirt git or in your distro. eg: -# git tag --contains 51e184e9821c3740ac9b52055860d683f27b0ab6 | grep -# this is because old libvirt broke the vrrp broadcast packets from keepalived! - -# TODO: the /etc/hosts DNS setup is less than ideal, but I didn't implement -# anything better yet. Please feel free to suggest something else! - -# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! -VAGRANTFILE_API_VERSION = '2' - -require 'ipaddr' -require 'yaml' - -# -# globals -# -domain = 'example.com' -network = IPAddr.new '192.168.144.0/24' -range = network.to_range.to_a -cidr = (32-(Math.log(range.length)/Math.log(2))).to_i -offset = 100 # start hosts after here -#puts range[0].to_s # network -#puts range[1].to_s # router (reserved) -#puts range[2].to_s # puppetmaster -#puts range[3].to_s # vip - -network2 = IPAddr.new '192.168.145.0/24' -range2 = network2.to_range.to_a -cidr2 = (32-(Math.log(range2.length)/Math.log(2))).to_i -offset2 = 2 -netmask2 = IPAddr.new('255.255.255.255').mask(cidr2).to_s - -# mutable by ARGV and settings file -count = 1 # default number of ipa hosts to build -# NOTE: this is the default topology that makes sense for puppet-ipa+vagrant... -# FIXME: what topology makes the most sense for most replica host counts of N ? -topology = 'ring' # default topology -version = '' # default ipa version (empty string means latest!) -firewall = false # default firewall enabled (FIXME: default to true when keepalived bug is fixed) -recipient = '' # default gpg recipient to use -clients = 1 # default number of XXX clients to build -sync = 'rsync' # default sync type -cachier = false # default cachier usage - -# -# ARGV parsing -# -projectdir = File.expand_path File.dirname(__FILE__) # vagrant project dir!! -f = File.join(projectdir, 'puppet-ipa.yaml') - -# load settings -if File.exist?(f) - settings = YAML::load_file f - count = settings[:count] - topology = settings[:topology] - version = settings[:version] - firewall = settings[:firewall] - recipient = settings[:recipient] - clients = settings[:clients] - sync = settings[:sync] - cachier = settings[:cachier] -end - -# ARGV parser -skip = 0 -while skip < ARGV.length - #puts "#{skip}, #{ARGV[skip]}" # debug - if ARGV[skip].start_with?(arg='--ipa-count=') - v = ARGV.delete_at(skip).dup - v.slice! arg - #puts "#{arg}, #{v}" # debug - - count = v.to_i # set ipa host count - - elsif ARGV[skip].start_with?(arg='--ipa-topology=') - v = ARGV.delete_at(skip).dup - v.slice! arg - - topology = v.to_s # set ipa topology - - elsif ARGV[skip].start_with?(arg='--ipa-version=') - v = ARGV.delete_at(skip).dup - v.slice! arg - - version = v.to_s # set ipa version - - elsif ARGV[skip].start_with?(arg='--ipa-firewall=') - v = ARGV.delete_at(skip).dup - v.slice! arg - - firewall = v.to_s # set firewall flag - if ['false', 'no'].include?(firewall.downcase) - firewall = false - else - firewall = true - end - - elsif ARGV[skip].start_with?(arg='--ipa-recipient=') - v = ARGV.delete_at(skip).dup - v.slice! arg - - recipient = v.to_s # set gpg recipient - - elsif ARGV[skip].start_with?(arg='--ipa-clients=') - v = ARGV.delete_at(skip).dup - v.slice! arg - - clients = v.to_i # set ipa client count - - elsif ARGV[skip].start_with?(arg='--ipa-sync=') - v = ARGV.delete_at(skip).dup - v.slice! arg - - sync = v.to_s # set sync type - - elsif ARGV[skip].start_with?(arg='--ipa-cachier=') - v = ARGV.delete_at(skip).dup - v.slice! arg - - cachier = v.to_s # set cachier flag - if ['true', 'yes'].include?(cachier.downcase) - cachier = true - else - cachier = false - end - - else # skip over "official" vagrant args - skip = skip + 1 - end -end - -# save settings (ARGV overrides) -settings = { - :count => count, - :topology => topology, - :version => version, - :firewall => firewall, - :recipient => recipient, - :clients => clients, - :sync => sync, - :cachier => cachier -} -File.open(f, 'w') do |file| - file.write settings.to_yaml -end - -#puts "ARGV: #{ARGV}" # debug - -# erase host information from puppet so that the user can do partial rebuilds -snoop = ARGV.select { |x| !x.start_with?('-') } -if snoop.length > 1 and snoop[0] == 'destroy' - snoop.shift # left over array snoop should be list of hosts - if snoop.include?('puppet') # doesn't matter then... - snoop = [] - end -else - # important! clear snoop because we're not using 'destroy' - snoop = [] -end - -# figure out which hosts are getting destroyed -destroy = ARGV.select { |x| !x.start_with?('-') } -if destroy.length > 0 and destroy[0] == 'destroy' - destroy.shift # left over array destroy should be list of hosts or [] - if destroy.length == 0 - destroy = true # destroy everything - end -else - destroy = false # destroy nothing -end - -# figure out which hosts are getting provisioned -provision = ARGV.select { |x| !x.start_with?('-') } -if provision.length > 0 and ['up', 'provision'].include?(provision[0]) - provision.shift # left over array provision should be list of hosts or [] - if provision.length == 0 - provision = true # provision everything - end -else - provision = false # provision nothing -end - -# XXX: workaround for: https://github.com/mitchellh/vagrant/issues/2447 -# only run on 'vagrant init' or if it's the first time running vagrant -if sync == 'nfs' and ((ARGV.length > 0 and ARGV[0] == 'init') or not(File.exist?(f))) - `sudo systemctl restart nfs-server` - `firewall-cmd --permanent --zone public --add-service mountd` - `firewall-cmd --permanent --zone public --add-service rpc-bind` - `firewall-cmd --permanent --zone public --add-service nfs` - `firewall-cmd --reload` -end - -Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| - - #config.landrush.enable # TODO ? - - # - # box (pre-built base image) - # - config.vm.box = 'centos-6' # i built it - - # box source url - # TODO: this box should be GPG signed - config.vm.box_url = 'https://download.gluster.org/pub/gluster/purpleidea/vagrant/centos-6/centos-6.box' - - # - # sync type - # - config.vm.synced_folder './', '/vagrant', type: sync # nfs, rsync - - # - # cache - # - # NOTE: you should probably erase the cache between rebuilds if you are - # installing older package versions. This is because the newer packages - # will get cached, and then subsequently might get silently installed!! - if cachier - # TODO: this doesn't cache metadata, full offline operation not possible - config.cache.auto_detect = true - config.cache.enable :yum - #config.cache.enable :apt - if not ARGV.include?('--no-parallel') # when running in parallel, - config.cache.scope = :machine # use the per machine cache - end - if sync == 'nfs' # TODO: support other sync types here... - config.cache.enable_nfs = true # sets nfs => true on the synced_folder - # the nolock option is required, otherwise the NFSv3 client will try to - # access the NLM sideband protocol to lock files needed for /var/cache/ - # all of this can be avoided by using NFSv4 everywhere. die NFSv3, die! - config.cache.mount_options = ['rw', 'vers=3', 'tcp', 'nolock'] - end - end - - # - # vip - # - vip_ip = range[3].to_s - vip_hostname = 'ipa' - - # - # puppetmaster - # - puppet_ip = range[2].to_s - puppet_hostname = 'puppet' - fv = File.join(projectdir, '.vagrant', "#{puppet_hostname}-hosts.done") - if destroy.is_a?(TrueClass) or (destroy.is_a?(Array) and destroy.include?(puppet_hostname)) - if File.exists?(fv) # safety - puts "Unlocking shell provisioning for: #{puppet_hostname}..." - File.delete(fv) # delete hosts token - end - end - - #puppet_fqdn = "#{puppet_hostname}.#{domain}" - config.vm.define :puppet, :primary => true do |vm| - vm.vm.hostname = puppet_hostname - # red herring network so that management happens here... - vm.vm.network :private_network, - :ip => range2[2].to_s, - :libvirt__netmask => netmask2, - #:libvirt__dhcp_enabled => false, # XXX: not allowed here - :libvirt__network_name => 'default' - - # this is the real network that we'll use... - vm.vm.network :private_network, - :ip => puppet_ip, - :libvirt__dhcp_enabled => false, - :libvirt__network_name => 'ipa' - - #vm.landrush.host puppet_hostname, puppet_ip # TODO ? - - # ensure the ipa module is present for provisioning... - if provision.is_a?(TrueClass) or (provision.is_a?(Array) and provision.include?(puppet_hostname)) - cwd = `pwd` - mod = File.join(projectdir, 'puppet', 'modules') - `cd #{mod} && make ipa &> /dev/null; cd #{cwd}` - end - - # - # shell - # - if not File.exists?(fv) # only modify /etc/hosts once - if provision.is_a?(TrueClass) or (provision.is_a?(Array) and provision.include?(puppet_hostname)) - File.open(fv, 'w') {} # touch - end - vm.vm.provision 'shell', inline: 'puppet resource host localhost.localdomain ip=127.0.0.1 host_aliases=localhost' - vm.vm.provision 'shell', inline: "puppet resource host #{puppet_hostname} ensure=absent" # so that fqdn works - - vm.vm.provision 'shell', inline: "puppet resource host #{vip_hostname}.#{domain} ip=#{vip_ip} host_aliases=#{vip_hostname} ensure=present" - vm.vm.provision 'shell', inline: "puppet resource host #{puppet_hostname}.#{domain} ip=#{puppet_ip} host_aliases=#{puppet_hostname} ensure=present" - (1..count).each do |i| - h = "ipa#{i}" - ip = range[offset+i].to_s - vm.vm.provision 'shell', inline: "puppet resource host #{h}.#{domain} ip=#{ip} host_aliases=#{h} ensure=present" - end - - # hosts entries for all clients - (1..clients).each do |i| - h = "client#{i}" - ip = range[offset+count+i].to_s - vm.vm.provision 'shell', inline: "puppet resource host #{h}.#{domain} ip=#{ip} host_aliases=#{h} ensure=present" - end - end - # - # puppet (apply) - # - vm.vm.provision :puppet do |puppet| - puppet.module_path = 'puppet/modules' - puppet.manifests_path = 'puppet/manifests' - puppet.manifest_file = 'site.pp' - # custom fact - puppet.facter = { - 'vagrant' => '1', - 'vagrant_ipa_firewall' => firewall ? 'true' : 'false', - 'vagrant_ipa_allow' => (1..count).map{|z| range[offset+z].to_s}.join(','), - } - puppet.synced_folder_type = sync - end - - vm.vm.provider :libvirt do |libvirt| - libvirt.cpus = 2 - libvirt.memory = 1024 - end - end - - # - # ipa, et al... - # - (1..count).each do |i| - h = "ipa#{i}" - ip = range[offset+i].to_s - #fqdn = "#{h}.#{domain}" - fvx = File.join(projectdir, '.vagrant', "#{h}-hosts.done") - if destroy.is_a?(TrueClass) or (destroy.is_a?(Array) and destroy.include?(h)) - if File.exists?(fvx) # safety - puts "Unlocking shell provisioning for: #{h}..." - File.delete(fvx) # delete hosts token - end - end - - if snoop.include?(h) # should we clean this machine? - cmd = "puppet cert clean #{h}.#{domain}" - puts "Running 'puppet cert clean' for: #{h}..." - `vagrant ssh #{puppet_hostname} -c 'sudo #{cmd}'` - cmd = "puppet node deactivate #{h}.#{domain}" - puts "Running 'puppet node deactivate' for: #{h}..." - `vagrant ssh #{puppet_hostname} -c 'sudo #{cmd}'` - end - - config.vm.define h.to_sym do |vm| - vm.vm.hostname = h - # red herring network so that management happens here... - vm.vm.network :private_network, - :ip => range2[offset2+i].to_s, - :libvirt__netmask => netmask2, - :libvirt__network_name => 'default' - - # this is the real network that we'll use... - vm.vm.network :private_network, - :ip => ip, - :libvirt__dhcp_enabled => false, - :libvirt__network_name => 'ipa' - - # use a specialized freeipa box if it exists :) - if `vagrant box list | grep -q '^centos-6-freeipa' && echo -n found` != '' - vm.vm.box = 'centos-6-freeipa' - end - # vagrant won't bind to these ports if not run as root! - # if vagrant-libvirt includes this patch, it is better: - # https://github.com/purpleidea/vagrant-libvirt/tree/feat/sudo-forward - vm.vm.network 'forwarded_port', guest: 80, host: 80 - vm.vm.network 'forwarded_port', guest: 443, host: 443 - - #vm.landrush.host h, ip # TODO ? - - # - # shell - # - if not File.exists?(fvx) # only modify /etc/hosts once - if provision.is_a?(TrueClass) or (provision.is_a?(Array) and provision.include?(h)) - File.open(fvx, 'w') {} # touch - end - vm.vm.provision 'shell', inline: 'puppet resource host localhost.localdomain ip=127.0.0.1 host_aliases=localhost' - vm.vm.provision 'shell', inline: "puppet resource host #{h} ensure=absent" # so that fqdn works - - vm.vm.provision 'shell', inline: "puppet resource host #{vip_hostname}.#{domain} ip=#{vip_ip} host_aliases=#{vip_hostname} ensure=present" - vm.vm.provision 'shell', inline: "puppet resource host #{puppet_hostname}.#{domain} ip=#{puppet_ip} host_aliases=#{puppet_hostname} ensure=present" - #vm.vm.provision 'shell', inline: "[ ! -e /root/puppet-cert-is-clean ] && ssh -o 'StrictHostKeyChecking=no' #{puppet_hostname} puppet cert clean #{h}.#{domain} ; touch /root/puppet-cert-is-clean" - # hosts entries for all hosts - (1..count).each do |j| - oh = "ipa#{j}" - oip = range[offset+j].to_s # eg: "192.168.142.#{100+i}" - vm.vm.provision 'shell', inline: "puppet resource host #{oh}.#{domain} ip=#{oip} host_aliases=#{oh} ensure=present" - end - - # hosts entries for all clients - (1..clients).each do |j| - oh = "client#{j}" - oip = range[offset+count+j].to_s - vm.vm.provision 'shell', inline: "puppet resource host #{oh}.#{domain} ip=#{oip} host_aliases=#{oh} ensure=present" - end - end - # - # puppet (agent) - # - vm.vm.provision :puppet_server do |puppet| - #puppet.puppet_node = "#{h}" # redundant - #puppet.puppet_server = "#{puppet_hostname}.#{domain}" - puppet.puppet_server = puppet_hostname - #puppet.options = '--verbose --debug' - puppet.options = '--test' # see the output - puppet.facter = { - 'vagrant' => '1', - 'vagrant_ipa_vip' => vip_ip, - 'vagrant_ipa_vip_fqdn' => "#{vip_hostname}.#{domain}", - 'vagrant_ipa_firewall' => firewall ? 'true' : 'false', - 'vagrant_ipa_topology' => topology.to_s, - 'vagrant_ipa_recipient' => recipient.to_s, - 'vagrant_ipa_version' => version, - } - end - - vm.vm.provider :libvirt do |libvirt| - # add additional disks to the os - #(1..disks).each do |j| # if disks is 0, this passes :) - # #print "disk: #{j}" - # libvirt.storage :file, - # #:path => '', # auto! - # #:device => 'vdb', # auto! - # #:size => '10G', # auto! - # :type => 'qcow2' - # - #end - end - end - end - - # - # client - # - (1..clients).each do |i| - h = "client#{i}" - ip = range[offset+count+i].to_s - #fqdn = "ipa#{i}.#{domain}" - fvy = File.join(projectdir, '.vagrant', "#{h}-hosts.done") - if destroy.is_a?(TrueClass) or (destroy.is_a?(Array) and destroy.include?(h)) - if File.exists?(fvy) # safety - puts "Unlocking shell provisioning for: #{h}..." - File.delete(fvy) # delete hosts token - end - end - - if snoop.include?(h) # should we clean this machine? - cmd = "puppet cert clean #{h}.#{domain}" - puts "Running 'puppet cert clean' for: #{h}..." - `vagrant ssh #{puppet_hostname} -c 'sudo #{cmd}'` - cmd = "puppet node deactivate #{h}.#{domain}" - puts "Running 'puppet node deactivate' for: #{h}..." - `vagrant ssh #{puppet_hostname} -c 'sudo #{cmd}'` - end - - config.vm.define h.to_sym do |vm| - vm.vm.hostname = h - # red herring network so that management happens here... - vm.vm.network :private_network, - :ip => range2[offset2+count+i].to_s, - :libvirt__netmask => netmask2, - :libvirt__network_name => 'default' - - # this is the real network that we'll use... - vm.vm.network :private_network, - :ip => ip, - :libvirt__dhcp_enabled => false, - :libvirt__network_name => 'ipa' - - #vm.landrush.host h, ip # TODO ? - - # - # shell - # - if not File.exists?(fvy) # only modify /etc/hosts once - if provision.is_a?(TrueClass) or (provision.is_a?(Array) and provision.include?(h)) - File.open(fvy, 'w') {} # touch - end - vm.vm.provision 'shell', inline: 'puppet resource host localhost.localdomain ip=127.0.0.1 host_aliases=localhost' - vm.vm.provision 'shell', inline: "puppet resource host #{h} ensure=absent" # so that fqdn works - - vm.vm.provision 'shell', inline: "puppet resource host #{vip_hostname}.#{domain} ip=#{vip_ip} host_aliases=#{vip_hostname} ensure=present" - vm.vm.provision 'shell', inline: "puppet resource host #{puppet_hostname}.#{domain} ip=#{puppet_ip} host_aliases=#{puppet_hostname} ensure=present" - # hosts entries for all hosts - (1..count).each do |j| - oh = "ipa#{j}" - oip = range[offset+j].to_s # eg: "192.168.142.#{100+i}" - vm.vm.provision 'shell', inline: "puppet resource host #{oh}.#{domain} ip=#{oip} host_aliases=#{oh} ensure=present" - end - - # hosts entries for all clients - (1..clients).each do |j| - oh = "client#{j}" - oip = range[offset+count+j].to_s - vm.vm.provision 'shell', inline: "puppet resource host #{oh}.#{domain} ip=#{oip} host_aliases=#{oh} ensure=present" - end - end - # - # puppet (agent) - # - vm.vm.provision :puppet_server do |puppet| - #puppet.puppet_node = "#{h}" # redundant - #puppet.puppet_server = "#{puppet_hostname}.#{domain}" - puppet.puppet_server = puppet_hostname - #puppet.options = '--verbose --debug' - puppet.options = '--test' # see the output - puppet.facter = { - 'vagrant' => '1', - 'vagrant_ipa_vip' => vip_ip, - 'vagrant_ipa_vip_fqdn' => "#{vip_hostname}.#{domain}", - 'vagrant_ipa_firewall' => firewall ? 'true' : 'false', - 'vagrant_ipa_version' => version, - } - end - end - end - - # - # libvirt - # - config.vm.provider :libvirt do |libvirt| - libvirt.driver = 'kvm' # needed for kvm performance benefits ! - # leave out to connect directly with qemu:///system - #libvirt.host = 'localhost' - libvirt.connect_via_ssh = false - libvirt.username = 'root' - libvirt.storage_pool_name = 'default' - #libvirt.default_network = 'default' # XXX: this does nothing - libvirt.default_prefix = 'ipa' # set a prefix for your vm's... - end - -end - diff --git a/ipa/vagrant/puppet/files/README b/ipa/vagrant/puppet/files/README deleted file mode 100644 index ae49d54bf..000000000 --- a/ipa/vagrant/puppet/files/README +++ /dev/null @@ -1,2 +0,0 @@ -This is Puppet-Ipa+Vagrant! (https://ttboj.wordpress.com/) - diff --git a/ipa/vagrant/puppet/hiera.yaml b/ipa/vagrant/puppet/hiera.yaml deleted file mode 100644 index 5aaf25d18..000000000 --- a/ipa/vagrant/puppet/hiera.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -:backends: - - yaml -:yaml: - :datadir: /etc/puppet/hieradata/ -:hierarchy: - - common diff --git a/ipa/vagrant/puppet/hieradata/common.yaml b/ipa/vagrant/puppet/hieradata/common.yaml deleted file mode 100644 index 03416fb3d..000000000 --- a/ipa/vagrant/puppet/hieradata/common.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -welcome: 'This is Puppet-Ipa+Vagrant! (https://ttboj.wordpress.com/)' -# vim:expandtab ts=8 sw=8 sta diff --git a/ipa/vagrant/puppet/manifests/site.pp b/ipa/vagrant/puppet/manifests/site.pp deleted file mode 100644 index aa788acab..000000000 --- a/ipa/vagrant/puppet/manifests/site.pp +++ /dev/null @@ -1,166 +0,0 @@ -node default { - # this will get put on every host... - $url = 'https://ttboj.wordpress.com/' - file { '/etc/motd': - content => "This is Puppet-Ipa+Vagrant! (${url})\n", - } -} - -# puppetmaster -node puppet inherits default { - - if "${::vagrant_ipa_firewall}" != 'false' { - include firewall - } - - $allow = split("${::vagrant_ipa_allow}", ',') # ip list fact - - class { '::puppet::server': - pluginsync => true, # do we want to enable pluginsync? - storeconfigs => true, # do we want to enable storeconfigs? - autosign => [ - '*', # FIXME: this is a temporary solution - #"*.${domain}", # FIXME: this is a temporary solution - ], - #allow_duplicate_certs => true, # redeploy without cert clean - allow => $allow, # also used in fileserver.conf - repo => true, # automatic repos - shorewall => "${::vagrant_ipa_firewall}" ? { - 'false' => false, - default => true, - }, - start => true, - } - - class { '::puppet::deploy': - path => '/vagrant/puppet/', # puppet folder is put here... - backup => false, # don't use puppet to backup... - } -} - -node /^ipa\d+$/ inherits default { # ipa{1,2,..N} - - if "${::vagrant_ipa_firewall}" != 'false' { - include firewall - } - - class { '::puppet::client': - #start => true, - start => false, # useful for testing manually... - } - - if "${::vagrant_ipa_recipient}" == '' { - # if no recipient is specified, we use a password of 'password' - warning("The IPA recipient is empty. This is unsafe!") - } - - $domain = $::domain - class { '::ipa::server': - domain => "${domain}", - vip => "${::vagrant_ipa_vip}", - topology => "${::vagrant_ipa_topology}" ? { - '' => undef, - default => "${::vagrant_ipa_topology}", - }, - dm_password => "${::vagrant_ipa_recipient}" ? { - '' => 'password', # unsafe !!! - default => undef, - }, - admin_password => "${::vagrant_ipa_recipient}" ? { - '' => 'password', # unsafe !!! - default => undef, - }, - # NOTE: email must exist in the public key if we use gpg_sendemail - #email => 'root@example.com', - gpg_recipient => "${::vagrant_ipa_recipient}" ? { - '' => undef, - default => "${::vagrant_ipa_recipient}", - }, - #gpg_publickey => '', - gpg_keyserver => 'hkp://keys.gnupg.net', # TODO: variable - gpg_sendemail => false, - vrrp => true, - shorewall => "${::vagrant_ipa_firewall}" ? { - 'false' => false, - default => true, - }, - } - -} - -node /^client\d+$/ inherits default { # client{1,2,..N} - - if "${::vagrant_ipa_firewall}" != 'false' { - include firewall - } - - class { '::puppet::client': - #start => true, - start => false, # useful for testing manually... - } - -} - -class firewall { - - $FW = '$FW' # make using $FW in shorewall easier - - class { '::shorewall::configuration': - # NOTE: no configuration specifics are needed at the moment - } - - shorewall::zone { ['net', 'man']: - type => 'ipv4', - options => [], # these aren't really needed right now - } - - # management zone interface used by vagrant-libvirt - shorewall::interface { 'man': - interface => 'MAN_IF', - broadcast => 'detect', - physical => 'eth0', # XXX: set manually! - options => ['dhcp', 'tcpflags', 'routefilter', 'nosmurfs', 'logmartians'], - comment => 'Management zone.', # FIXME: verify options - } - - # XXX: eth1 'dummy' zone to trick vagrant-libvirt into leaving me alone - # - - # net zone that ipa uses to communicate - shorewall::interface { 'net': - interface => 'NET_IF', - broadcast => 'detect', - physical => 'eth2', # XXX: set manually! - options => ['tcpflags', 'routefilter', 'nosmurfs', 'logmartians'], - comment => 'Public internet zone.', # FIXME: verify options - } - - # TODO: is this policy really what we want ? can we try to limit this ? - shorewall::policy { '$FW-net': - policy => 'ACCEPT', # TODO: shouldn't we whitelist? - } - - shorewall::policy { '$FW-man': - policy => 'ACCEPT', # TODO: shouldn't we whitelist? - } - - #################################################################### - #ACTION SOURCE DEST PROTO DEST SOURCE ORIGINAL - # PORT PORT(S) DEST - shorewall::rule { 'ssh': rule => " - SSH/ACCEPT net $FW - SSH/ACCEPT man $FW - ", comment => 'Allow SSH'} - - shorewall::rule { 'ping': rule => " - #Ping/DROP net $FW - Ping/ACCEPT net $FW - Ping/ACCEPT man $FW - ", comment => 'Allow ping from the `bad` net zone'} - - shorewall::rule { 'icmp': rule => " - ACCEPT $FW net icmp - ACCEPT $FW man icmp - ", comment => 'Allow icmp from the firewall zone'} -} - diff --git a/ipa/vagrant/puppet/modules/.gitignore b/ipa/vagrant/puppet/modules/.gitignore deleted file mode 100644 index bf8c6ef16..000000000 --- a/ipa/vagrant/puppet/modules/.gitignore +++ /dev/null @@ -1 +0,0 @@ -ipa/ diff --git a/ipa/vagrant/puppet/modules/Makefile b/ipa/vagrant/puppet/modules/Makefile deleted file mode 100644 index 3737d497a..000000000 --- a/ipa/vagrant/puppet/modules/Makefile +++ /dev/null @@ -1,63 +0,0 @@ -# Makefile for pulling in git modules for Vagrant deployment for Puppet-Ipa -# Copyright (C) 2010-2013+ James Shubin -# Written by James Shubin -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# NOTE: if we remove a module, it won't get purged from the destination! - -# NOTE: this script can sync puppet-ipa to a specific sha1sum commit, or it -# can sync all of the repos to git master. This option can be useful for devel. - -BASE = 'https://github.com/purpleidea' -MODULES := \ - puppet-ipa \ - puppet-module-data \ - puppet-puppet \ - puppet-shorewall \ - puppetlabs-stdlib -# NOTE: set to a git commit id if we need an specific commit for vagrant builds -# NOTE: remember that new commits to master should change this to a specific id -# if they will break the vagrant build process. hopefully we don't forget this! -#SHA1SUM := master -SHA1SUM := $(shell git rev-parse --verify HEAD) # goto whatever the main tree is at - -.PHONY: all modules ipa -.SILENT: all modules ipa - -all: - -# -# modules -# -# clone, and then pull -modules: - basename `pwd` | grep -q '^modules' || exit 1 # run in a modules dir! - for i in $(MODULES); do \ - j=`echo $$i | awk -F '-' '{print $$2}'`; \ - [ -d "$$j" ] || git clone --depth 1 $(BASE)/$$i.git $$j; \ - [ -d "$$j" ] && cd $$j && git pull; cd ..; \ - done - -# -# ipa -# -# just clone and pull this one -ipa: - basename `pwd` | grep -q '^modules' || exit 1 # run in a modules dir! - i='puppet-ipa'; \ - j=`echo $$i | awk -F '-' '{print $$2}'`; \ - [ -d "$$j" ] || git clone ../../../. $$j; \ - [ -d "$$j" ] && cd $$j && git checkout master && git pull && git checkout $(SHA1SUM); cd .. - diff --git a/ipa/vagrant/puppet/modules/keepalived b/ipa/vagrant/puppet/modules/keepalived deleted file mode 160000 index 4e3609580..000000000 --- a/ipa/vagrant/puppet/modules/keepalived +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4e3609580f9e2e2e73a386f1dd52d83dd1b37b84 diff --git a/ipa/vagrant/puppet/modules/module-data b/ipa/vagrant/puppet/modules/module-data deleted file mode 160000 index 4b3ad1cc2..000000000 --- a/ipa/vagrant/puppet/modules/module-data +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4b3ad1cc239d7831616e69796184e400de0f5fe4 diff --git a/ipa/vagrant/puppet/modules/puppet b/ipa/vagrant/puppet/modules/puppet deleted file mode 160000 index f139d0b7c..000000000 --- a/ipa/vagrant/puppet/modules/puppet +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f139d0b7cfe6d55c0848d0d338e19fe640a961f2 diff --git a/ipa/vagrant/puppet/modules/shorewall b/ipa/vagrant/puppet/modules/shorewall deleted file mode 160000 index fbc7c6576..000000000 --- a/ipa/vagrant/puppet/modules/shorewall +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fbc7c6576092ceaf81b837989e086cb7fcc071d8 diff --git a/ipa/vagrant/puppet/modules/ssh b/ipa/vagrant/puppet/modules/ssh deleted file mode 160000 index 0063501b6..000000000 --- a/ipa/vagrant/puppet/modules/ssh +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0063501b6b8d58961b7b7273780973d95fd6d2e3 diff --git a/ipa/vagrant/puppet/modules/stdlib b/ipa/vagrant/puppet/modules/stdlib deleted file mode 160000 index 44c181ec0..000000000 --- a/ipa/vagrant/puppet/modules/stdlib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 44c181ec0e230768b8dce10de57f9b32638e66e1 diff --git a/ipa/vagrant/puppet/modules/yum b/ipa/vagrant/puppet/modules/yum deleted file mode 160000 index d098f6de9..000000000 --- a/ipa/vagrant/puppet/modules/yum +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d098f6de9a38055b3482325d371722835b576976 diff --git a/java/.fixtures.yml b/java/.fixtures.yml new file mode 100644 index 000000000..514489409 --- /dev/null +++ b/java/.fixtures.yml @@ -0,0 +1,5 @@ +fixtures: + repositories: + stdlib: http://github.com/puppetlabs/puppetlabs-stdlib.git + symlinks: + java: "#{source_dir}" diff --git a/java/.gitignore b/java/.gitignore new file mode 100644 index 000000000..608cc8ed1 --- /dev/null +++ b/java/.gitignore @@ -0,0 +1,6 @@ +pkg/ +.DS_Store +spec/fixtures +*.idea +*.swp +*.tmp diff --git a/java/.travis.yml b/java/.travis.yml new file mode 100644 index 000000000..befea3f63 --- /dev/null +++ b/java/.travis.yml @@ -0,0 +1,40 @@ +--- +branches: + only: + - master +language: ruby +bundler_args: --without development +script: "bundle exec rake spec SPEC_OPTS='--format documentation'" +after_success: + - git clone -q git://github.com/puppetlabs/ghpublisher.git .forge-releng + - .forge-release/publish +rvm: +- 1.8.7 +- 1.9.3 +- 2.0.0 +env: + matrix: + - PUPPET_GEM_VERSION="~> 2.7.0" + - PUPPET_GEM_VERSION="~> 3.0.0" + - PUPPET_GEM_VERSION="~> 3.1.0" + - PUPPET_GEM_VERSION="~> 3.2.0" + global: + - PUBLISHER_LOGIN=puppetlabs + - secure: |- + InMPZJVSGFC/AfcTbM6eKpYjPU3L1m1/P8BiXwcwreeyz0elsX2qmXk80A1h + \nakUY6VxLtzcFGBHV1V8NvpRHovUBSzRGuhSFUm72XL62OB6TMl+Wg30coJ + /N\nXjEfy7N5TQ3ThTKf1gfph0x/3iJBPgqIuIIELJCQJusTRDPKWYQ= +matrix: + exclude: + - rvm: 1.9.3 + env: PUPPET_GEM_VERSION="~> 2.7.0" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 2.7.0" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 3.0.0" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 3.1.0" + - rvm: 1.8.7 + env: PUPPET_GEM_VERSION="~> 3.2.0" +notifications: + email: false diff --git a/java/CHANGELOG b/java/CHANGELOG new file mode 100644 index 000000000..3d74e4184 --- /dev/null +++ b/java/CHANGELOG @@ -0,0 +1,99 @@ +2014-08-25 - Supported Version 1.1.2 + +Summary: This release begins the support coverage of the puppetlabs-java +module. + +Bugfixes: +- Update java alternative values from deprecated names +- Readme updated +- Testing updated + +2014-05-02 - Version 1.1.1 + +Summary: + +Add support for new versions of Debian and Ubuntu! + +Features: +- Add support for Ubuntu Trusty (14.04) +- Add support for Debian Jessie (8.x) + +2014-01-06 - Version 1.1.0 + +Summary: + +Primarily a release for Ubuntu users! + +Features: +- Add support for Ubuntu Saucy (13.10) +- Add `java_home` parameter for centralized setting of JAVA_HOME. + +Bugfixes: +- Plus signs are valid in debian/ubuntu package names. + + + +1.0.2 () + +Brett Porter +* Support Scientific Linux + +1.0.1 (2013-08-01) + +Matthaus Owens +* Update java packages for Fedora systems + +1.0.0 (2013-07-29) + +Krzysztof Suszyński +* Adding support for Oracle Enterprise Linux + +Peter Drake +* Add support for natty + +Robert Munteanu +* Add support for OpenSUSE + +Martin Jackson +* Added support Amazon Linux using facter >= 1.7.x + +Gareth Rushgrove +Brett Porter +* Fixes for older versions of CentOS +* Improvements to module build and tests + +Nathan R Valentine +* Add support for Ubuntu quantal and raring + +Sharif Nassar +* Add support for Debian alternatives, and more than one JDK/JRE per platform. + +2013-04-04 Reid Vandewiele - 0.3.0 +* Refactor, introduce params pattern + +2012-11-15 Scott Schneider - 0.2.0 +* Add Solaris support + +2011-06-16 Jeff McCune - 0.1.5 +* Add Debian based distro (Lucid) support + +2011-06-02 Jeff McCune - 0.1.4 +* Fix class composition ordering problems + +2011-05-28 Jeff McCune - 0.1.3 +* Remove stages + +2011-05-26 Jeff McCune - 0.1.2 +* Changes JRE/JDK selection class parameter to $distribution + +2011-05-25 Jeff McCune - 0.1.1 +* Re-did versioning to follow semantic versioning + +2011-05-25 Jeff McCune - 1.0.1 +* Add validation of class parameters + +2011-05-24 Jeff McCune - 1.0.0 +* Default to JDK version 6u25 + +2011-05-24 Jeff McCune - 0.0.1 +* Initial release diff --git a/java/Gemfile b/java/Gemfile new file mode 100644 index 000000000..e960f7c4b --- /dev/null +++ b/java/Gemfile @@ -0,0 +1,27 @@ +source ENV['GEM_SOURCE'] || "https://rubygems.org" + +group :development, :test do + gem 'rake', :require => false + gem 'rspec-puppet', :require => false + gem 'puppetlabs_spec_helper', :require => false + gem 'serverspec', :require => false + gem 'puppet-lint', :require => false + gem 'beaker', :require => false + gem 'beaker-rspec', :require => false + gem 'pry', :require => false + gem 'simplecov', :require => false +end + +if facterversion = ENV['FACTER_GEM_VERSION'] + gem 'facter', facterversion, :require => false +else + gem 'facter', :require => false +end + +if puppetversion = ENV['PUPPET_GEM_VERSION'] + gem 'puppet', puppetversion, :require => false +else + gem 'puppet', :require => false +end + +# vim:ft=ruby diff --git a/java/LICENSE b/java/LICENSE new file mode 100644 index 000000000..a33e81211 --- /dev/null +++ b/java/LICENSE @@ -0,0 +1,17 @@ +Puppet Java Module - Puppet module for managing Java + +Copyright (C) 2011 Puppet Labs Inc + +Puppet Labs can be contacted at: info@puppetlabs.com + +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. diff --git a/java/README.markdown b/java/README.markdown new file mode 100644 index 000000000..a1c73a8a6 --- /dev/null +++ b/java/README.markdown @@ -0,0 +1,96 @@ +#java + +####Table of Contents + +1. [Overview](#overview) +2. [Module Description - What the module does and why it is useful](#module-description) +3. [Setup - The basics of getting started with the java module](#setup) + * [Beginning with the java module](#beginning-with-the-java-module) +4. [Usage - Configuration options and additional functionality](#usage) +5. [Reference - An under-the-hood peek at what the module is doing and how](#reference) +5. [Limitations - OS compatibility, etc.](#limitations) +6. [Development - Guide for contributing to the module](#development) + +##Overview + +Installs the correct Java package on various platforms. + +##Module Description + +The java module can automatically install Java jdk or jre on a wide variety of systems. Java is a base component for many software platforms, but Java system packages don't always follow packaging conventions. The java module simplifies the Java installation process. + +##Setup + +###Beginning with the java module +To install the correct Java package on your system, include the `java` class: `include java`. + +##Usage + +The java module installs the correct jdk or jre package on a wide variety of systems. By default, the module will install the jdk package, but you can set different installation parameters as needed. For example, to install jre instead of jdk, you would set the distribution parameter: + +``` +class { 'java': + distribution => 'jre', +} +``` + +##Reference + +###Classes + +####Public classes + +* `java`: This is the module's main class, which installs and manages the Java package. + +####Private classes + +* `java::params`: Builds a hash of jdk/jre packages for all compatible operating systems. + +* `java::config`: Configures the Java alternatives on Debian systems. + +###Parameters: + +The following parameters are available in the java module: + +* `distribution`: The Java distribution to install. Can be 'jdk','jre', or, where the platform supports alternative packages, 'sun-jdk', 'sun-jre', 'oracle-jdk', 'oracle-jre'. Defaults to 'jdk'. + +*`version`: The version of Java to install, if you want to ensure a particular version. By default, the module ensures that Java is present but does not require a specific version. + +* `package`: The name of the Java package. This is configurable in case you want to install a non-standard Java package. If not set, the module will install the appropriate package for the `distribution` parameter and target platform. If you set `package`, the `distribution` parameter will do nothing. + +* `java_alternative`: The name of the Java alternative to use on Debian systems. The command 'update-java-alternatives -l' will show which choices are available. If you specify a particular package, you will usually want to specify which Java alternative to use. If you set this parameter, you also need to set the `java_alternative_path`. + +* `java_alternative_path`: The path to the 'java' command on Debian systems. Since the alternatives system makes it difficult to verify which alternative is actually enabled, this is required to ensure the correct JVM is enabled. + +##Limitations + +This module cannot guarantee installation of Java versions that are not available on platform repositories. + +Oracle Java packages are not included in Debian 7 and Ubuntu 12.04/14.04 repositories. To install Java on those systems, you'll need to package Oracle JDK/JRE, and then the module will be able to install the package. For more information on how to package Oracle JDK/JRE, see the [Debian wiki](http://wiki.debian.org/JavaPackage). + +This module is officially [supported](https://forge.puppetlabs.com/supported) for the following Java versions and platforms: + +OpenJDK is supported on: +* Red Hat Enterprise Linux (RHEL) 5, 6, 7 +* CentOS 5, 6, 7 +* Oracle Linux 6, 7 +* Scientific Linux 5, 6 +* Debian 6, 7 +* Ubuntu 10.04, 12.04, 14.04 +* Solaris 11 +* SLES 11 SP1 + +Sun Java is supported on: +* Debian 6 + +##Development + +Puppet Labs modules on the Puppet Forge are open projects, and community contributions are essential for keeping them great. We can’t access the huge number of platforms and myriad of hardware, software, and deployment configurations that Puppet is intended to serve. + +We want to keep it as easy as possible to contribute changes so that our modules work in your environment. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things. + +You can read the complete module contribution guide on the [Puppet Labs wiki](http://projects.puppetlabs.com/projects/module-site/wiki/Module_contributing). + +##Contributors + +The list of contributors can be found at: [https://github.com/puppetlabs/puppetlabs-java/graphs/contributors](https://github.com/puppetlabs/puppetlabs-java/graphs/contributors). diff --git a/java/Rakefile b/java/Rakefile new file mode 100644 index 000000000..bb968be71 --- /dev/null +++ b/java/Rakefile @@ -0,0 +1,5 @@ +require 'puppetlabs_spec_helper/rake_tasks' +require 'puppet-lint/tasks/puppet-lint' + +PuppetLint.configuration.send("disable_80chars") +PuppetLint.configuration.log_format = "%{path}:%{linenumber}:%{check}:%{KIND}:%{message}" diff --git a/java/manifests/config.pp b/java/manifests/config.pp new file mode 100644 index 000000000..adb1fcb34 --- /dev/null +++ b/java/manifests/config.pp @@ -0,0 +1,17 @@ +# On Debian systems, if alternatives are set, manually assign them. +class java::config ( ) { + case $::osfamily { + Debian: { + if $java::use_java_alternative != undef and $java::use_java_alternative_path != undef { + exec { 'update-java-alternatives': + path => '/usr/bin:/usr/sbin:/bin:/sbin', + command => "update-java-alternatives --set ${java::use_java_alternative} --jre", + unless => "test /etc/alternatives/java -ef '${java::use_java_alternative_path}'", + } + } + } + default: { + # Do nothing. + } + } +} diff --git a/java/manifests/init.pp b/java/manifests/init.pp new file mode 100644 index 000000000..2b11483a3 --- /dev/null +++ b/java/manifests/init.pp @@ -0,0 +1,95 @@ +# Class: java +# +# This module manages the Java runtime package +# +# Parameters: +# +# [*distribution*] +# The java distribution to install. Can be one of "jdk" or "jre", +# or other platform-specific options where there are multiple +# implementations available (eg: OpenJDK vs Oracle JDK). +# +# +# [*version*] +# The version of java to install. By default, this module simply ensures +# that java is present, and does not require a specific version. +# +# [*package*] +# The name of the java package. This is configurable in case a non-standard +# java package is desired. +# +# [*java_alternative*] +# The name of the java alternative to use on Debian systems. +# "update-java-alternatives -l" will show which choices are available. +# If you specify a particular package, you will almost always also +# want to specify which java_alternative to choose. If you set +# this, you also need to set the path below. +# +# [*java_alternative_path*] +# The path to the "java" command on Debian systems. Since the +# alternatives system makes it difficult to verify which +# alternative is actually enabled, this is required to ensure the +# correct JVM is enabled. +# +# Actions: +# +# Requires: +# +# Sample Usage: +# +class java( + $distribution = 'jdk', + $version = 'present', + $package = undef, + $java_alternative = undef, + $java_alternative_path = undef +) { + include java::params + + validate_re($version, 'present|installed|latest|^[.+_0-9a-zA-Z:-]+$') + + if has_key($java::params::java, $distribution) { + $default_package_name = $java::params::java[$distribution]['package'] + $default_alternative = $java::params::java[$distribution]['alternative'] + $default_alternative_path = $java::params::java[$distribution]['alternative_path'] + $java_home = $java::params::java[$distribution]['java_home'] + } else { + fail("Java distribution ${distribution} is not supported.") + } + + $use_java_package_name = $package ? { + default => $package, + undef => $default_package_name, + } + + ## If $java_alternative is set, use that. + ## Elsif the DEFAULT package is being used, then use $default_alternative. + ## Else undef + $use_java_alternative = $java_alternative ? { + default => $java_alternative, + undef => $use_java_package_name ? { + $default_package_name => $default_alternative, + default => undef, + } + } + + ## Same logic as $java_alternative above. + $use_java_alternative_path = $java_alternative_path ? { + default => $java_alternative_path, + undef => $use_java_package_name ? { + $default_package_name => $default_alternative_path, + default => undef, + } + } + + anchor { 'java::begin:': } + -> + package { 'java': + ensure => $version, + name => $use_java_package_name, + } + -> + class { 'java::config': } + -> anchor { 'java::end': } + +} diff --git a/java/manifests/params.pp b/java/manifests/params.pp new file mode 100644 index 000000000..865ebd08d --- /dev/null +++ b/java/manifests/params.pp @@ -0,0 +1,133 @@ +# Class: java::params +# +# This class builds a hash of JDK/JRE packages and (for Debian) +# alternatives. For wheezy/precise, we provide Oracle JDK/JRE +# options, even though those are not in the package repositories. +# +# For more info on how to package Oracle JDK/JRE, see the Debian wiki: +# http://wiki.debian.org/JavaPackage +# +# Because the alternatives system makes it very difficult to tell +# which Java alternative is enabled, we hard code the path to bin/java +# for the config class to test if it is enabled. +class java::params { + + case $::osfamily { + default: { fail("unsupported platform ${::osfamily}") } + 'RedHat': { + case $::operatingsystem { + default: { fail("unsupported os ${::operatingsystem}") } + 'RedHat', 'CentOS', 'OracleLinux', 'Scientific': { + if (versioncmp($::operatingsystemrelease, '5.0') < 0) { + $jdk_package = 'java-1.6.0-sun-devel' + $jre_package = 'java-1.6.0-sun' + } + elsif (versioncmp($::operatingsystemrelease, '6.3') < 0) { + $jdk_package = 'java-1.6.0-openjdk-devel' + $jre_package = 'java-1.6.0-openjdk' + } + else { + $jdk_package = 'java-1.7.0-openjdk-devel' + $jre_package = 'java-1.7.0-openjdk' + } + } + 'Fedora': { + $jdk_package = 'java-1.7.0-openjdk-devel' + $jre_package = 'java-1.7.0-openjdk' + } + 'Amazon': { + $jdk_package = 'java-1.7.0-openjdk-devel' + $jre_package = 'java-1.7.0-openjdk' + } + } + $java = { + 'jdk' => { 'package' => $jdk_package, }, + 'jre' => { 'package' => $jre_package, }, + } + } + 'Debian': { + case $::lsbdistcodename { + default: { fail("unsupported release ${::lsbdistcodename}") } + 'lenny', 'squeeze', 'lucid', 'natty': { + $java = { + 'jdk' => { + 'package' => 'openjdk-6-jdk', + 'alternative' => "java-6-openjdk-${architecture}", + 'alternative_path' => '/usr/lib/jvm/java-6-openjdk/jre/bin/java', + 'java_home' => '/usr/lib/jvm/java-6-openjdk/jre/', + }, + 'jre' => { + 'package' => 'openjdk-6-jre-headless', + 'alternative' => "java-6-openjdk-${architecture}", + 'alternative_path' => '/usr/lib/jvm/java-6-openjdk/jre/bin/java', + 'java_home' => '/usr/lib/jvm/java-6-openjdk/jre/', + }, + 'sun-jre' => { + 'package' => 'sun-java6-jre', + 'alternative' => 'java-6-sun', + 'alternative_path' => '/usr/lib/jvm/java-6-sun/jre/bin/java', + 'java_home' => '/usr/lib/jvm/java-6-sun/jre/', + }, + 'sun-jdk' => { + 'package' => 'sun-java6-jdk', + 'alternative' => 'java-6-sun', + 'alternative_path' => '/usr/lib/jvm/java-6-sun/jre/bin/java', + 'java_home' => '/usr/lib/jvm/java-6-sun/jre/', + }, + } + } + 'wheezy', 'jessie', 'precise','quantal','raring','saucy', 'trusty': { + $java = { + 'jdk' => { + 'package' => 'openjdk-7-jdk', + 'alternative' => "java-1.7.0-openjdk-${::architecture}", + 'alternative_path' => "/usr/lib/jvm/java-1.7.0-openjdk-${::architecture}/bin/java", + 'java_home' => "/usr/lib/jvm/java-1.7.0-openjdk-${::architecture}/", + }, + 'jre' => { + 'package' => 'openjdk-7-jre-headless', + 'alternative' => "java-1.7.0-openjdk-${::architecture}", + 'alternative_path' => "/usr/lib/jvm/java-1.7.0-openjdk-${::architecture}/bin/java", + 'java_home' => "/usr/lib/jvm/java-1.7.0-openjdk-${::architecture}/", + }, + 'oracle-jre' => { + 'package' => 'oracle-j2re1.7', + 'alternative' => 'j2re1.7-oracle', + 'alternative_path' => '/usr/lib/jvm/j2re1.7-oracle/bin/java', + 'java_home' => '/usr/lib/jvm/j2re1.7-oracle/', + }, + 'oracle-jdk' => { + 'package' => 'oracle-j2sdk1.7', + 'alternative' => 'j2sdk1.7-oracle', + 'alternative_path' => '/usr/lib/jvm/j2sdk1.7-oracle/jre/bin/java', + 'java_home' => '/usr/lib/jvm/j2sdk1.7-oracle/jre/', + }, + } + } + } + } + 'Solaris': { + $java = { + 'jdk' => { 'package' => 'developer/java/jdk-7', }, + 'jre' => { 'package' => 'runtime/java/jre-7', }, + } + } + 'Suse': { + case $::operatingsystem { + default: { + $jdk_package = 'java-1_6_0-ibm-devel' + $jre_package = 'java-1_6_0-ibm' + } + + 'OpenSuSE': { + $jdk_package = 'java-1_7_0-openjdk-devel' + $jre_package = 'java-1_7_0-openjdk' + } + } + $java = { + 'jdk' => { 'package' => $jdk_package, }, + 'jre' => { 'package' => $jre_package, }, + } + } + } +} diff --git a/java/metadata.json b/java/metadata.json new file mode 100644 index 000000000..81fa0b52b --- /dev/null +++ b/java/metadata.json @@ -0,0 +1,85 @@ +{ + "name": "puppetlabs-java", + "version": "1.1.2", + "author": "puppetlabs", + "summary": "Manage the official Java runtime", + "license": "Apache", + "source": "git://github.com/puppetlabs/puppetlabs-java", + "project_page": "https://github.com/puppetlabs/puppetlabs-java", + "issues_url": "https://tickets.puppetlabs.com/browse/MODULES", + "operatingsystem_support": [ + { + "operatingsystem": "RedHat", + "operatingsystemrelease": [ + "5", + "6", + "7" + ] + }, + { + "operatingsystem": "CentOS", + "operatingsystemrelease": [ + "5", + "6", + "7" + ] + }, + { + "operatingsystem": "OracleLinux", + "operatingsystemrelease": [ + "6", + "7" + ] + }, + { + "operatingsystem": "Scientific", + "operatingsystemrelease": [ + "5", + "6" + ] + }, + { + "operatingsystem": "Debian", + "operatingsystemrelease": [ + "6", + "7" + ] + }, + { + "operatingsystem": "Ubuntu", + "operatingsystemrelease": [ + "10.04", + "12.04", + "14.04" + ] + }, + { + "operatingsystem": "SLES", + "operatingsystemrelease": [ + "11 SP1" + ] + }, + { + "operatingsystem": "Solaris", + "operatingsystemrelease": [ + "11" + ] + } + ], + "requirements": [ + { + "name": "pe", + "version_requirement": "3.x" + }, + { + "name": "puppet", + "version_requirement": "3.x" + } + ], + "dependencies": [ + { + "name": "puppetlabs/stdlib", + "version_requirement": ">= 2.4.0" + } + ] +} diff --git a/java/spec/acceptance/install_spec.rb b/java/spec/acceptance/install_spec.rb new file mode 100644 index 000000000..efc4a9b67 --- /dev/null +++ b/java/spec/acceptance/install_spec.rb @@ -0,0 +1,212 @@ +require 'spec_helper_acceptance' + +#RedHat, CentOS, Scientific, Oracle prior to 5.0 : Sun Java JDK/JRE 1.6 +#RedHat, CentOS, Scientific, Oracle 5.0 < x < 6.3 : OpenJDK Java JDK/JRE 1.6 +#RedHat, CentOS, Scientific, Oracle after 6.3 : OpenJDK Java JDK/JRE 1.7 +#Debian 5/6 & Ubuntu 10.04/11.04 : OpenJDK Java JDK/JRE 1.6 or Sun Java JDK/JRE 1.6 +#Debian 7/Jesse & Ubuntu 12.04 - 14.04 : OpenJDK Java JDK/JRE 1.7 or Oracle Java JDK/JRE 1.6 +#Solaris (what versions?) : Java JDK/JRE 1.7 +#OpenSuSE : OpenJDK Java JDK/JRE 1.7 +#SLES : IBM Java JDK/JRE 1.6 + +# C14677 +# C14678 +# C14679 +# C14680 +# C14681 +# C14682 +# C14684 +# C14687 +# C14692 +# C14696 +# C14697 +# C14700 check on solaris 11 +# C14701 check on sles 11 +# C14703 +# C14723 Where is oracle linux 5? +# C14724 Where is oracle linux 5? +# C14771 Where is redhat 7? Centos 7? +describe "installing java", :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do + describe "jre" do + it 'should install jre' do + pp = <<-EOS + class { 'java': + distribution => 'jre', + } + EOS + + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + end + describe "jdk" do + it 'should install jdk' do + pp = <<-EOS + class { 'java': } + EOS + + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + end +end +# C14686 +describe 'sun', :if => (fact('operatingsystem') == 'Debian' and fact('operatingsystemrelease').match(/(5|6)/)) do + before :all do + pp = <<-EOS + file_line { 'non-free source': + path => '/etc/apt/sources.list', + match => "deb http://osmirror.delivery.puppetlabs.net/debian/ ${::lsbdistcodename} main", + line => "deb http://osmirror.delivery.puppetlabs.net/debian/ ${::lsbdistcodename} main non-free", + } + EOS + apply_manifest(pp) + shell('apt-get update') + shell('echo "sun-java6-jdk shared/accepted-sun-dlj-v1-1 select true" | debconf-set-selections') + shell('echo "sun-java6-jre shared/accepted-sun-dlj-v1-1 select true" | debconf-set-selections') + end + describe 'jre' do + it 'should install sun-jre' do + pp = <<-EOS + class { 'java': + distribution => 'sun-jre', + } + EOS + + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + end + describe 'jdk' do + it 'should install sun-jdk' do + pp = <<-EOS + class { 'java': + distribution => 'sun-jdk', + } + EOS + + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + end +end + +# C14704 +# C14705 +# C15006 +describe 'oracle', :if => ( + (fact('operatingsystem') == 'Debian') and (fact('operatingsystemrelease').match(/^7/)) or + (fact('operatingsystem') == 'Ubuntu') and (fact('operatingsystemrelease').match(/^12\.04/)) or + (fact('operatingsystem') == 'Ubuntu') and (fact('operatingsystemrelease').match(/^14\.04/)) +) do + # not supported + # The package is not available from any sources, but if a customer + # custom-builds the package using java-package and adds it to a local + # repository, that is the intention of this version ability + describe 'jre' do + it 'should install oracle-jre' do + pp = <<-EOS + class { 'java': + distribution => 'oracle-jre', + } + EOS + + apply_manifest(pp, :expect_failures => true) + end + end + describe 'jdk' do + it 'should install oracle-jdk' do + pp = <<-EOS + class { 'java': + distribution => 'oracle-jdk', + } + EOS + + apply_manifest(pp, :expect_failures => true) + end + end +end + +describe 'failure cases' do + # C14711 + it 'should fail to install java with an incorrect version' do + pp = <<-EOS + class { 'java': + version => '14.5', + } + EOS + + apply_manifest(pp, :expect_failures => true) + end + + # C14712 + it 'should fail to install java with a blank version' do + pp = <<-EOS + class { 'java': + version => '', + } + EOS + + apply_manifest(pp, :expect_failures => true) + end + + # C14713 + it 'should fail to install java with an incorrect distribution' do + pp = <<-EOS + class { 'java': + distribution => 'xyz', + } + EOS + + apply_manifest(pp, :expect_failures => true) + end + + # C14714 + it 'should fail to install java with a blank distribution' do + pp = <<-EOS + class { 'java': + distribution => '', + } + EOS + + apply_manifest(pp, :expect_failures => true) + end + + # C14715 + it 'should fail to install java with an incorrect package' do + pp = <<-EOS + class { 'java': + package => 'xyz', + } + EOS + + apply_manifest(pp, :expect_failures => true) + end + # C14715 + it 'should fail to install java with an incorrect package' do + pp = <<-EOS + class { 'java': + package => 'xyz', + } + EOS + + apply_manifest(pp, :expect_failures => true) + end + # C14717 + # C14719 + # C14725 + it 'should fail on debian when passed fake java_alternative and path' do + pp = <<-EOS + class { 'java': + java_alternative => 'whatever', + java_alternative_path => '/whatever', + } + EOS + + if fact('osfamily') == 'Debian' + apply_manifest(pp, :expect_failures => true) + else + apply_manifest(pp, :catch_failures => true) + end + end +end diff --git a/java/spec/acceptance/nodesets/centos-5-vcloud.yml b/java/spec/acceptance/nodesets/centos-5-vcloud.yml new file mode 100644 index 000000000..377d0eec5 --- /dev/null +++ b/java/spec/acceptance/nodesets/centos-5-vcloud.yml @@ -0,0 +1,15 @@ +HOSTS: + 'centos-5-vcloud': + roles: + - master + platform: el-5-x86_64 + hypervisor: vcloud + template: centos-5-x86_64 +CONFIG: + type: foss + ssh: + keys: "~/.ssh/id_rsa-acceptance" + datastore: instance0 + folder: Delivery/Quality Assurance/Enterprise/Dynamic + resourcepool: delivery/Quality Assurance/Enterprise/Dynamic + pooling_api: http://vcloud.delivery.puppetlabs.net/ diff --git a/java/spec/acceptance/nodesets/centos-59-x64-pe.yml b/java/spec/acceptance/nodesets/centos-59-x64-pe.yml new file mode 100644 index 000000000..3a6470bea --- /dev/null +++ b/java/spec/acceptance/nodesets/centos-59-x64-pe.yml @@ -0,0 +1,12 @@ +HOSTS: + centos-59-x64: + roles: + - master + - database + - console + platform: el-5-x86_64 + box : centos-59-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-59-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: pe diff --git a/mysql/spec/acceptance/nodesets/centos-59-x64.yml b/java/spec/acceptance/nodesets/centos-59-x64.yml similarity index 100% rename from mysql/spec/acceptance/nodesets/centos-59-x64.yml rename to java/spec/acceptance/nodesets/centos-59-x64.yml diff --git a/java/spec/acceptance/nodesets/centos-6-vcloud.yml b/java/spec/acceptance/nodesets/centos-6-vcloud.yml new file mode 100644 index 000000000..ca9c1d329 --- /dev/null +++ b/java/spec/acceptance/nodesets/centos-6-vcloud.yml @@ -0,0 +1,15 @@ +HOSTS: + 'centos-6-vcloud': + roles: + - master + platform: el-6-x86_64 + hypervisor: vcloud + template: centos-6-x86_64 +CONFIG: + type: foss + ssh: + keys: "~/.ssh/id_rsa-acceptance" + datastore: instance0 + folder: Delivery/Quality Assurance/Enterprise/Dynamic + resourcepool: delivery/Quality Assurance/Enterprise/Dynamic + pooling_api: http://vcloud.delivery.puppetlabs.net/ diff --git a/java/spec/acceptance/nodesets/centos-64-x64-fusion.yml b/java/spec/acceptance/nodesets/centos-64-x64-fusion.yml new file mode 100644 index 000000000..d5166735e --- /dev/null +++ b/java/spec/acceptance/nodesets/centos-64-x64-fusion.yml @@ -0,0 +1,10 @@ +HOSTS: + centos-64-x64: + roles: + - master + platform: el-6-x86_64 + box : centos-64-x64-fusion503-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-fusion503-nocm.box + hypervisor : fusion +CONFIG: + type: foss diff --git a/mongodb/spec/acceptance/nodesets/centos-64-x64-pe.yml b/java/spec/acceptance/nodesets/centos-64-x64-pe.yml similarity index 100% rename from mongodb/spec/acceptance/nodesets/centos-64-x64-pe.yml rename to java/spec/acceptance/nodesets/centos-64-x64-pe.yml diff --git a/mongodb/spec/acceptance/nodesets/centos-64-x64.yml b/java/spec/acceptance/nodesets/centos-64-x64.yml similarity index 100% rename from mongodb/spec/acceptance/nodesets/centos-64-x64.yml rename to java/spec/acceptance/nodesets/centos-64-x64.yml diff --git a/ntp/spec/acceptance/nodesets/centos-65-x64.yml b/java/spec/acceptance/nodesets/centos-65-x64.yml similarity index 100% rename from ntp/spec/acceptance/nodesets/centos-65-x64.yml rename to java/spec/acceptance/nodesets/centos-65-x64.yml diff --git a/java/spec/acceptance/nodesets/centos-7-vcloud.yml b/java/spec/acceptance/nodesets/centos-7-vcloud.yml new file mode 100644 index 000000000..1cecb6b28 --- /dev/null +++ b/java/spec/acceptance/nodesets/centos-7-vcloud.yml @@ -0,0 +1,15 @@ +HOSTS: + 'centos-7-vcloud': + roles: + - master + platform: el-7-x86_64 + hypervisor: vcloud + template: centos-7-x86_64 +CONFIG: + type: foss + ssh: + keys: "~/.ssh/id_rsa-acceptance" + datastore: instance0 + folder: Delivery/Quality Assurance/Enterprise/Dynamic + resourcepool: delivery/Quality Assurance/Enterprise/Dynamic + pooling_api: http://vcloud.delivery.puppetlabs.net/ diff --git a/java/spec/acceptance/nodesets/debian-6-vcloud.yml b/java/spec/acceptance/nodesets/debian-6-vcloud.yml new file mode 100644 index 000000000..658c7d4c0 --- /dev/null +++ b/java/spec/acceptance/nodesets/debian-6-vcloud.yml @@ -0,0 +1,15 @@ +HOSTS: + 'debian-6-amd64': + roles: + - master + platform: debian-6-amd64 + hypervisor: vcloud + template: debian-6-x86_64 +CONFIG: + type: foss + ssh: + keys: "~/.ssh/id_rsa-acceptance" + datastore: instance0 + folder: Delivery/Quality Assurance/Enterprise/Dynamic + resourcepool: delivery/Quality Assurance/Enterprise/Dynamic + pooling_api: http://vcloud.delivery.puppetlabs.net/ diff --git a/java/spec/acceptance/nodesets/debian-607-x64.yml b/java/spec/acceptance/nodesets/debian-607-x64.yml new file mode 100644 index 000000000..4c8be42d0 --- /dev/null +++ b/java/spec/acceptance/nodesets/debian-607-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + debian-607-x64: + roles: + - master + platform: debian-6-amd64 + box : debian-607-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-607-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: git diff --git a/java/spec/acceptance/nodesets/debian-7-vcloud.yml b/java/spec/acceptance/nodesets/debian-7-vcloud.yml new file mode 100644 index 000000000..9323473e3 --- /dev/null +++ b/java/spec/acceptance/nodesets/debian-7-vcloud.yml @@ -0,0 +1,15 @@ +HOSTS: + 'debian-7-amd64': + roles: + - master + platform: debian-7-amd64 + hypervisor: vcloud + template: debian-7-x86_64 +CONFIG: + type: foss + ssh: + keys: "~/.ssh/id_rsa-acceptance" + datastore: instance0 + folder: Delivery/Quality Assurance/Enterprise/Dynamic + resourcepool: delivery/Quality Assurance/Enterprise/Dynamic + pooling_api: http://vcloud.delivery.puppetlabs.net/ diff --git a/mongodb/spec/acceptance/nodesets/debian-70rc1-x64.yml b/java/spec/acceptance/nodesets/debian-70rc1-x64.yml similarity index 95% rename from mongodb/spec/acceptance/nodesets/debian-70rc1-x64.yml rename to java/spec/acceptance/nodesets/debian-70rc1-x64.yml index 750d299eb..19181c123 100644 --- a/mongodb/spec/acceptance/nodesets/debian-70rc1-x64.yml +++ b/java/spec/acceptance/nodesets/debian-70rc1-x64.yml @@ -7,4 +7,4 @@ HOSTS: box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-70rc1-x64-vbox4210-nocm.box hypervisor : vagrant CONFIG: - type: git \ No newline at end of file + type: git diff --git a/ntp/spec/acceptance/nodesets/default.yml b/java/spec/acceptance/nodesets/default.yml similarity index 100% rename from ntp/spec/acceptance/nodesets/default.yml rename to java/spec/acceptance/nodesets/default.yml diff --git a/java/spec/acceptance/nodesets/fedora-18-x64.yml b/java/spec/acceptance/nodesets/fedora-18-x64.yml new file mode 100644 index 000000000..624b53716 --- /dev/null +++ b/java/spec/acceptance/nodesets/fedora-18-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + fedora-18-x64: + roles: + - master + platform: fedora-18-x86_64 + box : fedora-18-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/fedora-18-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: git diff --git a/java/spec/acceptance/nodesets/redhat-7-vcloud.yml b/java/spec/acceptance/nodesets/redhat-7-vcloud.yml new file mode 100644 index 000000000..cf8f79a97 --- /dev/null +++ b/java/spec/acceptance/nodesets/redhat-7-vcloud.yml @@ -0,0 +1,15 @@ +HOSTS: + 'redhat-7-vcloud': + roles: + - master + platform: el-7-x86_64 + hypervisor: vcloud + template: redhat-7-x86_64 +CONFIG: + type: foss + ssh: + keys: "~/.ssh/id_rsa-acceptance" + datastore: instance0 + folder: Delivery/Quality Assurance/Enterprise/Dynamic + resourcepool: delivery/Quality Assurance/Enterprise/Dynamic + pooling_api: http://vcloud.delivery.puppetlabs.net/ diff --git a/java/spec/acceptance/nodesets/sles-11-vcloud.yml b/java/spec/acceptance/nodesets/sles-11-vcloud.yml new file mode 100644 index 000000000..3f6114a56 --- /dev/null +++ b/java/spec/acceptance/nodesets/sles-11-vcloud.yml @@ -0,0 +1,16 @@ +HOSTS: + 'sles-11-vcloud': + roles: + - master + platform: sles-11-x86_64 + hypervisor: vcloud + template: sles-11-x86_64 + +CONFIG: + type: foss + ssh: + keys: "~/.ssh/id_rsa-acceptance" + datastore: instance0 + folder: Delivery/Quality Assurance/Enterprise/Dynamic + resourcepool: delivery/Quality Assurance/Enterprise/Dynamic + pooling_api: http://vcloud.delivery.puppetlabs.net/ diff --git a/java/spec/acceptance/nodesets/sles-11sp1-x64.yml b/java/spec/acceptance/nodesets/sles-11sp1-x64.yml new file mode 100644 index 000000000..554c37a50 --- /dev/null +++ b/java/spec/acceptance/nodesets/sles-11sp1-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + sles-11sp1-x64: + roles: + - master + platform: sles-11-x86_64 + box : sles-11sp1-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/sles-11sp1-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: git diff --git a/java/spec/acceptance/nodesets/solaris-11-vcloud.yml b/java/spec/acceptance/nodesets/solaris-11-vcloud.yml new file mode 100644 index 000000000..f3912ab2d --- /dev/null +++ b/java/spec/acceptance/nodesets/solaris-11-vcloud.yml @@ -0,0 +1,15 @@ +HOSTS: + 'solaris-11-vcloud': + roles: + - master + platform: solaris-11-i386 + hypervisor: vcloud + template: solaris-11-x86_64 +CONFIG: + type: foss + ssh: + keys: "~/.ssh/id_rsa-acceptance" + datastore: instance0 + folder: Delivery/Quality Assurance/Enterprise/Dynamic + resourcepool: delivery/Quality Assurance/Enterprise/Dynamic + pooling_api: http://vcloud.delivery.puppetlabs.net/ diff --git a/java/spec/acceptance/nodesets/ubuntu-1004-vcloud.yml b/java/spec/acceptance/nodesets/ubuntu-1004-vcloud.yml new file mode 100644 index 000000000..19a918f54 --- /dev/null +++ b/java/spec/acceptance/nodesets/ubuntu-1004-vcloud.yml @@ -0,0 +1,15 @@ +HOSTS: + 'ubuntu-1004-amd64': + roles: + - master + platform: ubuntu-10.04-amd64 + hypervisor: vcloud + template: ubuntu-1004-x86_64 +CONFIG: + type: foss + ssh: + keys: "~/.ssh/id_rsa-acceptance" + datastore: instance0 + folder: Delivery/Quality Assurance/Enterprise/Dynamic + resourcepool: delivery/Quality Assurance/Enterprise/Dynamic + pooling_api: http://vcloud.delivery.puppetlabs.net/ diff --git a/java/spec/acceptance/nodesets/ubuntu-1404-vcloud.yml b/java/spec/acceptance/nodesets/ubuntu-1404-vcloud.yml new file mode 100644 index 000000000..d84b18e50 --- /dev/null +++ b/java/spec/acceptance/nodesets/ubuntu-1404-vcloud.yml @@ -0,0 +1,15 @@ +HOSTS: + 'ubuntu-1404-amd64': + roles: + - master + platform: ubuntu-14.04-amd64 + hypervisor: vcloud + template: ubuntu-1404-x86_64 +CONFIG: + type: foss + ssh: + keys: "~/.ssh/id_rsa-acceptance" + datastore: instance0 + folder: Delivery/Quality Assurance/Enterprise/Dynamic + resourcepool: delivery/Quality Assurance/Enterprise/Dynamic + pooling_api: http://vcloud.delivery.puppetlabs.net/ diff --git a/mongodb/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml b/java/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml similarity index 100% rename from mongodb/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml rename to java/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml diff --git a/mongodb/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml b/java/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml similarity index 100% rename from mongodb/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml rename to java/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml diff --git a/ntp/spec/acceptance/nodesets/ubuntu-server-1404-x64.yml b/java/spec/acceptance/nodesets/ubuntu-server-1404-x64.yml similarity index 100% rename from ntp/spec/acceptance/nodesets/ubuntu-server-1404-x64.yml rename to java/spec/acceptance/nodesets/ubuntu-server-1404-x64.yml diff --git a/java/spec/classes/java_spec.rb b/java/spec/classes/java_spec.rb new file mode 100644 index 000000000..de1eda34c --- /dev/null +++ b/java/spec/classes/java_spec.rb @@ -0,0 +1,154 @@ +require 'spec_helper' + +describe 'java', :type => :class do + + context 'select openjdk for Centos 5.8' do + let(:facts) { {:osfamily => 'RedHat', :operatingsystem => 'Centos', :operatingsystemrelease => '5.8'} } + it { should contain_package('java').with_name('java-1.6.0-openjdk-devel') } + end + + context 'select openjdk for Centos 6.3' do + let(:facts) { {:osfamily => 'RedHat', :operatingsystem => 'Centos', :operatingsystemrelease => '6.3'} } + it { should contain_package('java').with_name('java-1.7.0-openjdk-devel') } + end + + context 'select openjdk for Centos 6.2' do + let(:facts) { {:osfamily => 'RedHat', :operatingsystem => 'Centos', :operatingsystemrelease => '6.2'} } + it { should contain_package('java').with_name('java-1.6.0-openjdk-devel') } + end + + context 'select openjdk for Fedora' do + let(:facts) { {:osfamily => 'RedHat', :operatingsystem => 'Fedora'} } + it { should contain_package('java').with_name('java-1.7.0-openjdk-devel') } + end + + context 'select passed value for Fedora' do + let(:facts) { {:osfamily => 'RedHat', :operatingsystem => 'Fedora'} } + let(:params) { { 'distribution' => 'jre' } } + it { should contain_package('java').with_name('java-1.7.0-openjdk') } + end + + context 'select passed value for Centos 5.3' do + let(:facts) { {:osfamily => 'RedHat', :operatingsystem => 'Centos', :operatingsystemrelease => '5.3'} } + let(:params) { { 'package' => 'jdk' } } + it { should contain_package('java').with_name('jdk') } + it { should_not contain_exec('update-java-alternatives') } + end + + context 'select default for Centos 5.3' do + let(:facts) { {:osfamily => 'RedHat', :operatingsystem => 'Centos', :operatingsystemrelease => '5.3'} } + it { should contain_package('java').with_name('java-1.6.0-openjdk-devel') } + it { should_not contain_exec('update-java-alternatives') } + end + + context 'select default for Debian Wheezy' do + let(:facts) { {:osfamily => 'Debian', :operatingsystem => 'Debian', :lsbdistcodename => 'wheezy', :operatingsystemrelease => '7.1', :architecture => 'amd64',} } + it { should contain_package('java').with_name('openjdk-7-jdk') } + it { should contain_exec('update-java-alternatives').with_command('update-java-alternatives --set java-1.7.0-openjdk-amd64 --jre') } + end + + context 'select Oracle JRE for Debian Wheezy' do + let(:facts) { {:osfamily => 'Debian', :operatingsystem => 'Debian', :lsbdistcodename => 'wheezy', :operatingsystemrelease => '7.1', } } + let(:params) { { 'distribution' => 'oracle-jre' } } + it { should contain_package('java').with_name('oracle-j2re1.7') } + it { should contain_exec('update-java-alternatives').with_command('update-java-alternatives --set j2re1.7-oracle --jre') } + end + + context 'select default for Debian Squeeze' do + let(:facts) { {:osfamily => 'Debian', :operatingsystem => 'Debian', :lsbdistcodename => 'squeeze', :operatingsystemrelease => '6.0.5', :architecture => 'amd64',} } + it { should contain_package('java').with_name('openjdk-6-jdk') } + it { should contain_exec('update-java-alternatives').with_command('update-java-alternatives --set java-6-openjdk-amd64 --jre') } + end + + context 'select Oracle JRE for Debian Squeeze' do + let(:facts) { {:osfamily => 'Debian', :operatingsystem => 'Debian', :lsbdistcodename => 'squeeze', :operatingsystemrelease => '6.0.5'} } + let(:params) { { 'distribution' => 'sun-jre', } } + it { should contain_package('java').with_name('sun-java6-jre') } + it { should contain_exec('update-java-alternatives').with_command('update-java-alternatives --set java-6-sun --jre') } + end + + context 'select random alternative for Debian Wheezy' do + let(:facts) { {:osfamily => 'Debian', :operatingsystem => 'Debian', :lsbdistcodename => 'wheezy', :operatingsystemrelease => '7.1'} } + let(:params) { { 'java_alternative' => 'bananafish' } } + it { should contain_package('java').with_name('openjdk-7-jdk') } + it { should contain_exec('update-java-alternatives').with_command('update-java-alternatives --set bananafish --jre') } + end + + context 'select openjdk for Amazon Linux' do + let(:facts) { {:osfamily => 'RedHat', :operatingsystem => 'Amazon', :operatingsystemrelease => '3.4.43-43.43.amzn1.x86_64'} } + it { should contain_package('java').with_name('java-1.7.0-openjdk-devel') } + end + + context 'select passed value for Amazon Linux' do + let(:facts) { {:osfamily => 'RedHat', :operatingsystem => 'Amazon', :operatingsystemrelease => '5.3.4.43-43.43.amzn1.x86_64'} } + let(:params) { { 'distribution' => 'jre' } } + it { should contain_package('java').with_name('java-1.7.0-openjdk') } + end + + context 'select openjdk for Oracle Linux' do + let(:facts) { {:osfamily => 'RedHat', :operatingsystem => 'OracleLinux', :operatingsystemrelease => '6.4'} } + it { should contain_package('java').with_name('java-1.7.0-openjdk-devel') } + end + + context 'select openjdk for Oracle Linux 6.2' do + let(:facts) { {:osfamily => 'RedHat', :operatingsystem => 'OracleLinux', :operatingsystemrelease => '6.2'} } + it { should contain_package('java').with_name('java-1.6.0-openjdk-devel') } + end + + context 'select passed value for Oracle Linux' do + let(:facts) { {:osfamily => 'RedHat', :operatingsystem => 'OracleLinux', :operatingsystemrelease => '6.3'} } + let(:params) { { 'distribution' => 'jre' } } + it { should contain_package('java').with_name('java-1.7.0-openjdk') } + end + + context 'select passed value for Scientific Linux' do + let(:facts) { {:osfamily => 'RedHat', :operatingsystem => 'Scientific', :operatingsystemrelease => '6.4'} } + let(:params) { { 'distribution' => 'jre' } } + it { should contain_package('java').with_name('java-1.7.0-openjdk') } + end + + context 'select default for OpenSUSE 12.3' do + let(:facts) { {:osfamily => 'Suse', :operatingsystem => 'OpenSUSE', :operatingsystemrelease => '12.3'}} + it { should contain_package('java').with_name('java-1_7_0-openjdk-devel')} + end + + describe 'incompatible OSs' do + [ + { + # C14706 + :osfamily => 'windows', + :operatingsystem => 'windows', + :operatingsystemrelease => '8.1', + }, + { + # C14707 + :osfamily => 'Darwin', + :operatingsystem => 'Darwin', + :operatingsystemrelease => '13.3.0', + }, + { + # C14708 + :osfamily => 'AIX', + :operatingsystem => 'AIX', + :operatingsystemrelease => '7100-02-00-000', + }, + { + # C14708 + :osfamily => 'AIX', + :operatingsystem => 'AIX', + :operatingsystemrelease => '6100-07-04-1216', + }, + { + # C14708 + :osfamily => 'AIX', + :operatingsystem => 'AIX', + :operatingsystemrelease => '5300-12-01-1016', + }, + ].each do |facts| + let(:facts) { facts } + it "should fail on #{facts[:operatingsystem]} #{facts[:operatingsystemrelease]}" do + expect { subject }.to raise_error Puppet::Error, /unsupported platform/ + end + end + end +end diff --git a/java/spec/spec_helper.rb b/java/spec/spec_helper.rb new file mode 100644 index 000000000..2c6f56649 --- /dev/null +++ b/java/spec/spec_helper.rb @@ -0,0 +1 @@ +require 'puppetlabs_spec_helper/module_spec_helper' diff --git a/java/spec/spec_helper_acceptance.rb b/java/spec/spec_helper_acceptance.rb new file mode 100644 index 000000000..37ad8eefb --- /dev/null +++ b/java/spec/spec_helper_acceptance.rb @@ -0,0 +1,41 @@ +require 'beaker-rspec' + +UNSUPPORTED_PLATFORMS = [ "Darwin", "windows" ] + +unless ENV["RS_PROVISION"] == "no" or ENV["BEAKER_provision"] == "no" + # This will install the latest available package on el and deb based + # systems fail on windows and osx, and install via gem on other *nixes + foss_opts = { :default_action => 'gem_install' } + + if default.is_pe?; then install_pe; else install_puppet( foss_opts ); end + + hosts.each do |host| + if host["platform"] =~ /solaris/ + on host, "echo 'export PATH=/opt/puppet/bin:/var/ruby/1.8/gem_home/bin:${PATH}' >> ~/.bashrc" + end + unless host.is_pe? + on host, "/bin/mkdir -p #{host["puppetpath"]}" + on host, "/bin/echo '' > #{host["hieraconf"]}" + end + on host, "mkdir -p #{host["distmoduledir"]}" + on host, "puppet module install puppetlabs-stdlib", :acceptable_exit_codes => [0,1] + # For test support + on host, "puppet module install puppetlabs-apt", :acceptable_exit_codes => [0,1] + end +end + +RSpec.configure do |c| + # Project root + proj_root = File.expand_path(File.join(File.dirname(__FILE__), "..")) + + # Readable test descriptions + c.formatter = :documentation + + # Configure all nodes in nodeset + c.before :suite do + # Install module + hosts.each do |host| + copy_module_to(host, :source => proj_root, :module_name => "java") + end + end +end diff --git a/java/tests/init.pp b/java/tests/init.pp new file mode 100644 index 000000000..bdd923e51 --- /dev/null +++ b/java/tests/init.pp @@ -0,0 +1,4 @@ +class { 'java': + distribution => 'jdk', + version => 'latest', +} diff --git a/keepalived/.fixtures.yml b/keepalived/.fixtures.yml new file mode 100644 index 000000000..634302fcd --- /dev/null +++ b/keepalived/.fixtures.yml @@ -0,0 +1,7 @@ +fixtures: + repositories: + "concat": + repo: "git://github.com/puppetlabs/puppetlabs-concat.git" + ref: '1.0.0' + symlinks: + "keepalived": "#{source_dir}" diff --git a/keepalived/.gitignore b/keepalived/.gitignore new file mode 100644 index 000000000..66ebfdbf3 --- /dev/null +++ b/keepalived/.gitignore @@ -0,0 +1,2 @@ +pkg +*~ diff --git a/keepalived/Gemfile b/keepalived/Gemfile new file mode 100644 index 000000000..3d6545e57 --- /dev/null +++ b/keepalived/Gemfile @@ -0,0 +1,15 @@ +source 'https://rubygems.org' + +group :development, :test do + gem 'rake', :require => false + gem 'puppetlabs_spec_helper', :require => false + gem 'puppet-lint', :require => false +end + +if puppetversion = ENV['PUPPET_GEM_VERSION'] + gem 'puppet', puppetversion, :require => false +else + gem 'puppet', :require => false +end + +# vim:ft=ruby diff --git a/keepalived/LICENSE b/keepalived/LICENSE new file mode 100644 index 000000000..435dd9d4a --- /dev/null +++ b/keepalived/LICENSE @@ -0,0 +1,13 @@ + Copyright 2012 Bruno LEON + + 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. diff --git a/keepalived/Modulefile b/keepalived/Modulefile new file mode 100644 index 000000000..3d2be7872 --- /dev/null +++ b/keepalived/Modulefile @@ -0,0 +1,10 @@ +name 'unyonsys-keepalived' +version '0.0.2' +author 'Bruno LEON' +license 'Apache License, Version 2.0' +summary 'Keepalived module' +description 'Manage keepalived instances' +project_page 'https://github.com/Unyonsys/puppet-module-keepalived/' + +## Add dependencies, if any: +dependency 'puppetlabs/concat', '>=1.0.0 <2.0.0' diff --git a/keepalived/README.md b/keepalived/README.md new file mode 100644 index 000000000..90aba3bed --- /dev/null +++ b/keepalived/README.md @@ -0,0 +1,56 @@ +keepalived + +This is the keepalived module. + +License +------- + +Apache2 + +Contact +------- + +bruno.leon@unyonsys.com + +Parameters +========== + +[*notification_email_to*] = [ "root@${domain}" ] + An array of emails to send notifications to + +[*notification_email_from*] = "keepalived@${domain}" + The source adress of notification messages + +[*smtp_server*] = 'localhost' + The SMTP server to use to send notifications. + +[*smtp_connect_timeout*] = '30' + The SMTP server to use to send notifications. + +[*router_id*] = $::hostname + The router_id identifies us on the network. + +Variables +========= + +[*$keepalived::variables::keepalived_conf*] + Path to keepalived.conf configuration file + +Examples +======== + + class { keepalived: } + keepalived::instance { '50': + interface => 'eth0', + virtual_ips => [ '192.168.200.17 dev eth0' ], + state => hiera( "keepalived_50_state" ), + priority => hiera( "keepalived_50_priority" ), + } + +Developement +============ + +You can run the test-suite with the following commands: + + bundle exec rake spec + bundle exec rake lint diff --git a/keepalived/Rakefile b/keepalived/Rakefile new file mode 100644 index 000000000..4c2b2ed07 --- /dev/null +++ b/keepalived/Rakefile @@ -0,0 +1,6 @@ +require 'puppetlabs_spec_helper/rake_tasks' +require 'puppet-lint/tasks/puppet-lint' + +PuppetLint.configuration.fail_on_warnings = true +PuppetLint.configuration.send('disable_80chars') +PuppetLint.configuration.send('disable_class_parameter_defaults') diff --git a/keepalived/manifests/config.pp b/keepalived/manifests/config.pp new file mode 100644 index 000000000..763727262 --- /dev/null +++ b/keepalived/manifests/config.pp @@ -0,0 +1,19 @@ +class keepalived::config ( + $notification_email_to, + $notification_email_from, + $smtp_server, + $smtp_connect_timeout, + $router_id, + $static_ipaddress +) { + + concat { $keepalived::variables::keepalived_conf: + warn => true, + } + + concat::fragment { 'global_config': + target => $keepalived::variables::keepalived_conf, + content => template( "${module_name}/global_config.erb" ), + order => '01', + } +} diff --git a/keepalived/manifests/init.pp b/keepalived/manifests/init.pp new file mode 100644 index 000000000..955308130 --- /dev/null +++ b/keepalived/manifests/init.pp @@ -0,0 +1,64 @@ +# == Class: keepalived +# +# This class setup the basis for keeapalive, knowingly package installation +# and some global variables. +# +# The keepalived::instance define depends on this class. +# +# === Parameters +# +# [*notification_email_to*] = [ "root@${domain}" ] +# An array of emails to send notifications to +# +# [*notification_from*] = "keepalived@${domain}" +# The source adress of notification messages +# +# [*smtp_server*] = 'localhost' +# The SMTP server to use to send notifications. +# +# [*smtp_connect_timeout*] = '30' +# The SMTP server to use to send notifications. +# +# [*router_id*] = $::hostname +# The router_id identifies us on the network. +# +# === Variables +# +# [*$keepalived::variables::keepalived_conf*] +# Path to keepalived.conf configuration file +# +# === Examples +# +# class { keepalived: } +# +# === Authors +# +# Author Name +# +# === Copyright +# +# Copyright 2012 Bruno LEON, unless otherwise noted. +# +class keepalived ( + $notification_email_to = [ "root@${domain}" ], + $notification_email_from = "keepalived@${domain}", + $smtp_server = 'localhost', + $smtp_connect_timeout = '30', + $router_id = $::hostname, + $static_ipaddress = [], +) { + + Class[ "${module_name}::install" ] -> Class[ "${module_name}::config" ] ~> Class[ "${module_name}::service" ] + + include "${module_name}::variables" + class { "${module_name}::install": } + class { "${module_name}::config": + notification_email_to => $keepalived::notification_email_to, + notification_email_from => $keepalived::notification_email_from, + smtp_server => $keepalived::smtp_server, + smtp_connect_timeout => $keepalived::smtp_connect_timeout, + router_id => $keepalived::router_id, + static_ipaddress => $keepalived::static_ipaddress, + } + class { "${module_name}::service": } +} diff --git a/keepalived/manifests/install.pp b/keepalived/manifests/install.pp new file mode 100644 index 000000000..5f4a50c20 --- /dev/null +++ b/keepalived/manifests/install.pp @@ -0,0 +1,5 @@ +class keepalived::install { + package { 'keepalived': + ensure => present, + } +} diff --git a/keepalived/manifests/instance.pp b/keepalived/manifests/instance.pp new file mode 100644 index 000000000..a4538cff2 --- /dev/null +++ b/keepalived/manifests/instance.pp @@ -0,0 +1,30 @@ +define keepalived::instance ( + $interface, + $virtual_ips, + $state, + $priority, + $track_script = [], + $notify = undef, + $notify_master = undef, + $notify_backup = undef, + $notify_fault = undef, + $smtp_alert = false, + $nopreempt = false, + $advert_int = '1', + $auth_type = undef, + $auth_pass = undef, + $virtual_router_id = $name, +) { + + include keepalived::variables + + Keepalived::Vrrp_script[ $track_script ] -> Keepalived::Instance[ $name ] + + concat::fragment { "keepalived_${name}": + target => $keepalived::variables::keepalived_conf, + content => template( 'keepalived/keepalived_instance.erb' ), + order => '50', + notify => Class[ 'keepalived::service' ], + require => Class[ 'keepalived::install' ], + } +} diff --git a/keepalived/manifests/service.pp b/keepalived/manifests/service.pp new file mode 100644 index 000000000..770797070 --- /dev/null +++ b/keepalived/manifests/service.pp @@ -0,0 +1,8 @@ +class keepalived::service { + service { 'keepalived': + ensure => running, + enable => true, + hasstatus => false, + pattern => 'keepalived', + } +} diff --git a/keepalived/manifests/variables.pp b/keepalived/manifests/variables.pp new file mode 100644 index 000000000..386c04f22 --- /dev/null +++ b/keepalived/manifests/variables.pp @@ -0,0 +1,3 @@ +class keepalived::variables { + $keepalived_conf = '/etc/keepalived/keepalived.conf' +} diff --git a/keepalived/manifests/vrrp_script.pp b/keepalived/manifests/vrrp_script.pp new file mode 100644 index 000000000..82b6f2539 --- /dev/null +++ b/keepalived/manifests/vrrp_script.pp @@ -0,0 +1,28 @@ +define keepalived::vrrp_script ( + $name_is_process = false, + $script = undef, + $interval = '2', + $weight = '2', +) { + + include keepalived::variables + + if (! $name_is_process) and (! $script) { + fail('You must pass either name_is_process or script.') + } + if ($name_is_process) and ($script) { + fail('You must pass either name_is_process or script, not both.') + } + $script_real = $name_is_process ? { + true => "killall -0 ${name}", + false => $script, + } + + concat::fragment { "keepalived_vrrp_script_${name}": + target => $keepalived::variables::keepalived_conf, + content => template( 'keepalived/keepalived_vrrp_script.erb' ), + order => '20', + notify => Class[ 'keepalived::service' ], + require => Class[ 'keepalived::install' ], + } +} diff --git a/keepalived/spec/defines/instance_spec.rb b/keepalived/spec/defines/instance_spec.rb new file mode 100644 index 000000000..08246d3ec --- /dev/null +++ b/keepalived/spec/defines/instance_spec.rb @@ -0,0 +1,64 @@ +require 'spec_helper' + +describe 'keepalived::instance', :type => :define do + + let :title do + 'something' + end + + let :default_params do + { + :interface => 'eth0', + :virtual_ips => [ '10.0.0.1 dev bond0.X' ], + :state => 'BACKUP', + :state => 'BACKUP', + :priority => 1, + :name => 'something', + } + end + + let :fragment_file do + '/var/lib/puppet/concat/_etc_keepalived_keepalived.conf/fragments/50_keepalived_something' + end + + let :facts do + { + :concat_basedir => '/var/lib/puppet/concat' + } + end + + let :pre_condition do + 'class { "concat::setup": } + concat { "/etc/keepalived/keepalived.conf": }' + end + + + describe 'when passing default parameters' do + let :params do + default_params + end + + it 'should build the fragment with correct content' do + verify_contents(subject, fragment_file, + [ +"vrrp_instance something {", " virtual_router_id something", "", " # Advert interval", " advert_int 1", " # for electing MASTER, highest priority wins.", " priority 1", " state BACKUP", " interface eth0", " virtual_ipaddress {", " ", " 10.0.0.1 dev bond0.X", " }", "}" + ] + ) + end + end + + + describe 'when passing duplicated IP addresses' do + let :params do + default_params.merge(:virtual_ips => [ '10.0.0.1 dev bond0.X', '10.0.0.1 dev bond0.X', '10.0.0.2 dev bond1.X' ]) + end + it 'it should only keep the first one' do + should contain_file(fragment_file)\ + .with_content( + "vrrp_instance something {\n virtual_router_id something\n\n # Advert interval\n advert_int 1\n\n # for electing MASTER, highest priority wins.\n priority 1\n state BACKUP\n\n interface eth0\n\n virtual_ipaddress {\n \n 10.0.0.1 dev bond0.X\n 10.0.0.2 dev bond1.X\n \n \n }\n \n \n \n \n\n \n \n}\n" + ) + end + end + +end + diff --git a/keepalived/spec/spec_helper.rb b/keepalived/spec/spec_helper.rb new file mode 100644 index 000000000..f64ca3ea9 --- /dev/null +++ b/keepalived/spec/spec_helper.rb @@ -0,0 +1,3 @@ +require 'rubygems' +require 'puppetlabs_spec_helper/module_spec_helper' + diff --git a/keepalived/templates/global_config.erb b/keepalived/templates/global_config.erb new file mode 100644 index 000000000..81982cca3 --- /dev/null +++ b/keepalived/templates/global_config.erb @@ -0,0 +1,17 @@ +global_defs { + notification_email { + <% notification_email_to.each do |email| -%> + <%= email %> + <% end -%>} + notification_email_from <%= @notification_email_from %> + smtp_server <%= @smtp_server %> + smtp_connect_timeout <%= @smtp_connect_timeout %> + router_id <%= @router_id %> +} + +static_ipaddress { + <% @static_ipaddress.each do |vip| -%> + <%= vip %> + <% end %> +} + diff --git a/keepalived/templates/keepalived_instance.erb b/keepalived/templates/keepalived_instance.erb new file mode 100644 index 000000000..5076b330a --- /dev/null +++ b/keepalived/templates/keepalived_instance.erb @@ -0,0 +1,43 @@ +vrrp_instance <%= @name %> { + virtual_router_id <%= @virtual_router_id %> + + # Advert interval + advert_int <%= @advert_int %> + + # for electing MASTER, highest priority wins. + priority <%= @priority %> + state <%= @state %> + + interface <%= @interface %> + + virtual_ipaddress { + <% if @virtual_ips.is_a? Hash then %> + <% @virtual_ips.each do |vip, label| -%> + <%= vip -%> label <%= @interface -%>:<%= label %> + <% end %> + <% else %> + <% @virtual_ips.uniq.each do |vip| -%> + <%= vip %> + <% end %> + <% end %> + } + <% if @track_script.length > 0 then %> + track_script { + <% @track_script.each do |script| -%> + <%= script %> + <% end %>} + <% end -%> + <% if @auth_type and @auth_pass then %> + authentication { + auth_type <%= @auth_type %> + auth_pass <%= @auth_pass %> + } + <% end -%> + + <% if @notify then -%>notify <%= @notify %><% end %> + <% if @notify_master then -%>notify_master <%= @notify_master %><% end %> + <% if @notify_backup then -%>notify_backup <%= @notify_backup %><% end %> + + <% if @smtp_alert then -%>smtp_alert<% end %> + <% if @nopreempt then -%>nopreempt<% end %> +} diff --git a/keepalived/templates/keepalived_vrrp_script.erb b/keepalived/templates/keepalived_vrrp_script.erb new file mode 100644 index 000000000..b522461fd --- /dev/null +++ b/keepalived/templates/keepalived_vrrp_script.erb @@ -0,0 +1,5 @@ +vrrp_script <%= @name %> { + script "<%= @script_real %>" + interval <%= @interval %> + weight <%= @weight %> +} diff --git a/keepalived/tests/init.pp b/keepalived/tests/init.pp new file mode 100644 index 000000000..e7166d4d4 --- /dev/null +++ b/keepalived/tests/init.pp @@ -0,0 +1,11 @@ +# The baseline for module testing used by Puppet Labs is that each manifest +# should have a corresponding test manifest that declares that class or defined +# type. +# +# Tests are then run by using puppet apply --noop (to check for compilation errors +# and view a log of events) or by fully applying the test in a virtual environment +# (to compare the resulting system state to the desired state). +# +# Learn more about module testing here: http://docs.puppetlabs.com/guides/tests_smoke.html +# +include keepalived diff --git a/keystone/Modulefile b/keystone/Modulefile new file mode 100644 index 000000000..27670233a --- /dev/null +++ b/keystone/Modulefile @@ -0,0 +1,13 @@ +name 'puppetlabs-keystone' +version '4.0.0' +author 'Puppet Labs and StackForge Contributors' +license 'Apache License 2.0' +summary 'Puppet module for OpenStack Keystone' +description 'Installs and configures OpenStack Keystone (Identity).' +project_page 'https://launchpad.net/puppet-keystone' +source 'https://github.com/stackforge/puppet-keystone' + +dependency 'puppetlabs/apache', '>=1.0.0 <2.0.0' +dependency 'puppetlabs/inifile', '>=1.0.0 <2.0.0' +dependency 'puppetlabs/stdlib', '>= 3.2.0' +dependency 'stackforge/openstacklib', '>=5.0.0' diff --git a/keystone/lib/puppet/type/keystone_user_role.rb b/keystone/lib/puppet/type/keystone_user_role.rb index d9f27c41c..60715fb8f 100644 --- a/keystone/lib/puppet/type/keystone_user_role.rb +++ b/keystone/lib/puppet/type/keystone_user_role.rb @@ -23,11 +23,6 @@ end newproperty(:roles, :array_matching => :all) do - def insync?(is) - return false unless is.is_a? Array - # order of roles does not matter - is.sort == self.should.sort - end end newproperty(:id) do diff --git a/keystone/manifests/init.pp b/keystone/manifests/init.pp index 05e95b081..cd3b79722 100644 --- a/keystone/manifests/init.pp +++ b/keystone/manifests/init.pp @@ -27,10 +27,10 @@ # [catalog_template_file] Path to the catalog used if catalog_type equals 'template'. # Defaults to '/etc/keystone/default_catalog.templates' # [token_provider] Format keystone uses for tokens. Optional. -# Defaults to 'keystone.token.providers.uuid.Provider' +# Defaults to 'keystone.token.providers.pki.Provider' # Supports PKI and UUID. # [token_driver] Driver to use for managing tokens. -# Optional. Defaults to 'keystone.token.persistence.backends.sql.Token' +# Optional. Defaults to 'keystone.token.backends.sql.Token' # [token_expiration] Amount of time a token should remain valid (seconds). # Optional. Defaults to 3600 (1 hour). # [token_format] Deprecated: Use token_provider instead. @@ -54,30 +54,22 @@ # [*idle_timeout*] # (optional) Deprecated. Use database_idle_timeout instead. # -# [enable_pki_setup] Enable call to pki_setup to generate the cert for signing pki tokens and -# revocation lists if it doesn't already exist. This generates a cert and key stored in file -# locations based on the signing_certfile and signing_keyfile paramters below. If you are -# providing your own signing cert, make this false. -# [signing_certfile] Location of the cert file for signing pki tokens and revocation lists. -# Optional. Note that if this file already exists (i.e. you are providing your own signing cert), -# the file will not be overwritten, even if enable_pki_setup is set to true. +# [enable_pki_setup] Enable call to pki_setup to generate the cert for signing pki tokens, +# if it doesn't already exist. This generates a cert and key stored in file locations +# based on the signing_certfile and signing_keyfile paramters below. If you are providing +# your own signing cert, make this false. +# [signing_certfile] Location of the cert file for signing pki tokens. Optional. Note that if +# this file already exists (i.e. you are providing your own signing cert), the file will +# not be overwritten, even if enable_pki_setup is set to true. # Default: /etc/keystone/ssl/certs/signing_cert.pem -# [signing_keyfile] Location of the key file for signing pki tokens and revocation lists. Optional. -# Note that if this file already exists (i.e. you are providing your own signing cert), the file -# will not be overwritten, even if enable_pki_setup is set to true. +# [signing_keyfile] Location of the key file for signing pki tokens. Optional. Note that if +# this file already exists (i.e. you are providing your own signing cert), the file will not +# be overwritten, even if enable_pki_setup is set to true. # Default: /etc/keystone/ssl/private/signing_key.pem # [signing_ca_certs] Use this CA certs file along with signing_certfile/signing_keyfile for -# signing pki tokens and revocation lists. Optional. Default: /etc/keystone/ssl/certs/ca.pem +# signing pki tokens. Optional. Default: /etc/keystone/ssl/certs/ca.pem # [signing_ca_key] Use this CA key file along with signing_certfile/signing_keyfile for signing -# pki tokens and revocation lists. Optional. Default: /etc/keystone/ssl/private/cakey.pem -# -# [*signing_cert_subject*] -# (optional) Certificate subject (auto generated certificate) for token signing. -# Defaults to '/C=US/ST=Unset/L=Unset/O=Unset/CN=www.example.com' -# -# [*signing_key_size*] -# (optional) Key size (in bits) for token signing cert (auto generated certificate) -# Defaults to 2048 +# pki tokens. Optional. Default: /etc/keystone/ssl/private/cakey.pem # # [rabbit_host] Location of rabbitmq installation. Optional. Defaults to localhost. # [rabbit_port] Port for rabbitmq instance. Optional. Defaults to 5672. @@ -203,23 +195,6 @@ # custom service provider for changing start/stop/status behavior of service, # and set it here. # -# [*service_name*] -# (optional) Name of the service that will be providing the -# server functionality of keystone. For example, the default -# is just 'keystone', which means keystone will be run as a -# standalone eventlet service, and will able to be managed -# separately by the operating system's service manager. For -# example, you will be able to use -# service openstack-keystone restart -# to restart the service. -# If the value is 'httpd', this means keystone will be a web -# service, and you must use another class to configure that -# web service. For example, after calling class {'keystone'...} -# use class { 'keystone::wsgi::apache'...} to make keystone be -# a web app using apache mod_wsgi. -# Defaults to 'keystone' -# NOTE: validate_service only applies if the value is 'keystone' -# # == Dependencies # None # @@ -230,17 +205,6 @@ # admin_token => 'my_special_token', # } # -# OR -# -# class { 'keystone': -# ... -# service_name => 'httpd', -# ... -# } -# class { 'keystone::wsgi::apache': -# ... -# } -# # == Authors # # Dan Bode dan@puppetlabs.com @@ -268,8 +232,8 @@ $catalog_driver = false, $catalog_template_file = '/etc/keystone/default_catalog.templates', $token_format = false, - $token_provider = 'keystone.token.providers.uuid.Provider', - $token_driver = 'keystone.token.persistence.backends.sql.Token', + $token_provider = 'keystone.token.providers.pki.Provider', + $token_driver = 'keystone.token.backends.sql.Token', $token_expiration = 3600, $public_endpoint = false, $admin_endpoint = false, @@ -289,8 +253,6 @@ $signing_keyfile = '/etc/keystone/ssl/private/signing_key.pem', $signing_ca_certs = '/etc/keystone/ssl/certs/ca.pem', $signing_ca_key = '/etc/keystone/ssl/private/cakey.pem', - $signing_cert_subject = '/C=US/ST=Unset/L=Unset/O=Unset/CN=www.example.com', - $signing_key_size = 2048, $rabbit_host = 'localhost', $rabbit_hosts = false, $rabbit_password = 'guest', @@ -310,12 +272,11 @@ $validate_auth_url = false, $validate_cacert = undef, $service_provider = $::keystone::params::service_provider, - $service_name = 'keystone', # DEPRECATED PARAMETERS $mysql_module = undef, $sql_connection = undef, $idle_timeout = undef, -) inherits keystone::params { +) { if ! $catalog_driver { validate_re($catalog_type, 'template|sql') @@ -347,6 +308,12 @@ warning('Version string /v2.0/ should not be included in keystone::public_endpoint') } + File['/etc/keystone/keystone.conf'] -> Keystone_config<||> ~> Service['keystone'] + Keystone_config<||> ~> Exec<| title == 'keystone-manage db_sync'|> + Keystone_config<||> ~> Exec<| title == 'keystone-manage pki_setup'|> + + include keystone::params + if $rabbit_use_ssl { if !$kombu_ssl_ca_certs { fail('The kombu_ssl_ca_certs parameter is required when rabbit_use_ssl is set to true') @@ -359,10 +326,13 @@ } } - File['/etc/keystone/keystone.conf'] -> Keystone_config<||> ~> Service[$service_name] - Keystone_config<||> ~> Exec<| title == 'keystone-manage db_sync'|> - Keystone_config<||> ~> Exec<| title == 'keystone-manage pki_setup'|> - include ::keystone::params + File { + ensure => present, + owner => 'keystone', + group => 'keystone', + require => Package['keystone'], + notify => Service['keystone'], + } package { 'keystone': ensure => $package_ensure, @@ -385,19 +355,10 @@ file { ['/etc/keystone', '/var/log/keystone', '/var/lib/keystone']: ensure => directory, mode => '0750', - owner => 'keystone', - group => 'keystone', - require => Package['keystone'], - notify => Service[$service_name], } file { '/etc/keystone/keystone.conf': - ensure => present, mode => '0600', - owner => 'keystone', - group => 'keystone', - require => Package['keystone'], - notify => Service[$service_name], } if $bind_host { @@ -522,38 +483,31 @@ # remove the old format in case of an upgrade keystone_config { 'signing/token_format': ensure => absent } - # Set the signing key/cert configuration values. - keystone_config { - 'signing/certfile': value => $signing_certfile; - 'signing/keyfile': value => $signing_keyfile; - 'signing/ca_certs': value => $signing_ca_certs; - 'signing/ca_key': value => $signing_ca_key; - 'signing/cert_subject': value => $signing_cert_subject; - 'signing/key_size': value => $signing_key_size; - } - - # Create cache directory used for signing. - file { $cache_dir: - ensure => directory, - } - - # Only do pki_setup if we were asked to do so. This is needed - # regardless of the token provider since token revocation lists - # are always signed. - if $enable_pki_setup { - exec { 'keystone-manage pki_setup': - path => '/usr/bin', - user => 'keystone', - refreshonly => true, - creates => $signing_keyfile, - notify => Service[$service_name], - subscribe => Package['keystone'], - require => User['keystone'], + if ($token_format == false and $token_provider == 'keystone.token.providers.pki.Provider') or $token_format == 'PKI' { + file { $cache_dir: + ensure => directory, } - } - if ($token_format == false and $token_provider == 'keystone.token.providers.pki.Provider') or $token_format == 'PKI' { - keystone_config { 'token/provider': value => 'keystone.token.providers.pki.Provider' } + keystone_config { + 'token/provider': value => $token_provider; + 'signing/certfile': value => $signing_certfile; + 'signing/keyfile': value => $signing_keyfile; + 'signing/ca_certs': value => $signing_ca_certs; + 'signing/ca_key': value => $signing_ca_key; + } + + # Only do pki_setup if we were asked to do so + if $enable_pki_setup { + exec { 'keystone-manage pki_setup': + path => '/usr/bin', + user => 'keystone', + refreshonly => true, + creates => $signing_keyfile, + notify => Service['keystone'], + subscribe => Package['keystone'], + require => User['keystone'], + } + } } elsif $token_format == 'UUID' { keystone_config { 'token/provider': value => 'keystone.token.providers.uuid.Provider' } } else { @@ -615,43 +569,42 @@ $service_ensure = 'stopped' } - if $service_name == 'keystone' { - if $validate_service { - if $validate_auth_url { - $v_auth_url = $validate_auth_url - } else { - $v_auth_url = $admin_endpoint - } + if $validate_service { - class { 'keystone::service': - ensure => $service_ensure, - service_name => $::keystone::params::service_name, - enable => $enabled, - hasstatus => true, - hasrestart => true, - provider => $service_provider, - validate => true, - admin_endpoint => $v_auth_url, - admin_token => $admin_token, - insecure => $validate_insecure, - cacert => $validate_cacert, - } + if $validate_auth_url { + $v_auth_url = $validate_auth_url } else { - class { 'keystone::service': - ensure => $service_ensure, - service_name => $::keystone::params::service_name, - enable => $enabled, - hasstatus => true, - hasrestart => true, - provider => $service_provider, - validate => false, - } + $v_auth_url = $admin_endpoint + } + + class { 'keystone::service': + ensure => $service_ensure, + service_name => $::keystone::params::service_name, + enable => $enabled, + hasstatus => true, + hasrestart => true, + provider => $service_provider, + validate => true, + admin_endpoint => $v_auth_url, + admin_token => $admin_token, + insecure => $validate_insecure, + cacert => $validate_cacert, + } + } else { + class { 'keystone::service': + ensure => $service_ensure, + service_name => $::keystone::params::service_name, + enable => $enabled, + hasstatus => true, + hasrestart => true, + provider => $service_provider, + validate => false, } } if $enabled { - include ::keystone::db::sync - Class['::keystone::db::sync'] ~> Service[$service_name] + include keystone::db::sync + Class['keystone::db::sync'] ~> Service['keystone'] } # Syslog configuration diff --git a/keystone/manifests/ldap.pp b/keystone/manifests/ldap.pp index 8c3f1b03e..f65a798b0 100644 --- a/keystone/manifests/ldap.pp +++ b/keystone/manifests/ldap.pp @@ -76,6 +76,8 @@ $group_allow_update = undef, $group_allow_delete = undef, $group_additional_attribute_mapping = undef, + $tenant_tree_dn = undef, + $role_tree_dn = undef, $use_tls = undef, $tls_cacertdir = undef, $tls_cacertfile = undef, @@ -84,8 +86,7 @@ $assignment_driver = undef, ) { - $ldap_packages = ['python-ldap', 'python-ldappool'] - package { $ldap_packages: + package { 'python-ldap': ensure => present, } diff --git a/keystone/manifests/logging.pp b/keystone/manifests/logging.pp index dade7df73..aa355c88d 100644 --- a/keystone/manifests/logging.pp +++ b/keystone/manifests/logging.pp @@ -35,13 +35,10 @@ # (optional) Hash of logger (keys) and level (values) pairs. # Defaults to undef. # Example: -# { 'amqp' => 'WARN', 'amqplib' => 'WARN', 'boto' => 'WARN', -# 'qpid' => 'WARN', 'sqlalchemy' => 'WARN', 'suds' => 'INFO', -# 'oslo.messaging' => 'INFO', 'iso8601' => 'WARN', -# 'requests.packages.urllib3.connectionpool' => 'WARN', -# 'urllib3.connectionpool' => 'WARN', -# 'websocket' => 'WARN', 'keystonemiddleware' => 'WARN', -# 'routes.middleware' => 'WARN', stevedore => 'WARN' } +# { 'amqp' => 'WARN', 'amqplib' => 'WARN', 'boto' => 'WARN', +# 'qpid' => 'WARN', 'sqlalchemy' => 'WARN', 'suds' => 'INFO', +# 'iso8601' => 'WARN', +# 'requests.packages.urllib3.connectionpool' => 'WARN' } # # [*publish_errors*] # (optional) Publish error events (boolean value). diff --git a/keystone/manifests/params.pp b/keystone/manifests/params.pp index f3f0f4d26..4b8fe8762 100644 --- a/keystone/manifests/params.pp +++ b/keystone/manifests/params.pp @@ -16,9 +16,6 @@ $keystone_wsgi_script_source = '/usr/share/keystone/wsgi.py' } default: { - # NOTE: Ubuntu does not currently provide the keystone wsgi script in the - # keystone packages. When Ubuntu does provide the script, change this - # to use the correct path (which I'm assuming will be the same as Debian). $service_provider = 'upstart' $keystone_wsgi_script_source = 'puppet:///modules/keystone/httpd/keystone.py' } @@ -30,7 +27,7 @@ $keystone_wsgi_script_path = '/var/www/cgi-bin/keystone' $python_memcache_package_name = 'python-memcached' $service_provider = undef - $keystone_wsgi_script_source = '/usr/share/keystone/keystone.wsgi' + $keystone_wsgi_script_source = 'puppet:///modules/keystone/httpd/keystone.py' } } } diff --git a/keystone/manifests/roles/admin.pp b/keystone/manifests/roles/admin.pp index 4fd5e0970..7e80ca238 100644 --- a/keystone/manifests/roles/admin.pp +++ b/keystone/manifests/roles/admin.pp @@ -12,14 +12,11 @@ # # [email] The email address for the admin. Required. # [password] The admin password. Required. -# [admin_roles] The list of the roles with admin privileges. Optional. Defaults to ['admin']. # [admin_tenant] The name of the tenant to be used for admin privileges. Optional. Defaults to openstack. # [admin] Admin user. Optional. Defaults to admin. # [ignore_default_tenant] Ignore setting the default tenant value when the user is created. Optional. Defaults to false. # [admin_tenant_desc] Optional. Description for admin tenant, defaults to 'admin tenant' # [service_tenant_desc] Optional. Description for admin tenant, defaults to 'Tenant for the openstack services' -# [configure_user] Optional. Should the admin user be created? Defaults to 'true'. -# [configure_user_role] Optional. Should the admin role be configured for the admin user? Defaulst to 'true'. # # == Dependencies # == Examples @@ -36,13 +33,10 @@ $password, $admin = 'admin', $admin_tenant = 'openstack', - $admin_roles = ['admin'], $service_tenant = 'services', $ignore_default_tenant = false, $admin_tenant_desc = 'admin tenant', $service_tenant_desc = 'Tenant for the openstack services', - $configure_user = true, - $configure_user_role = true, ) { keystone_tenant { $service_tenant: @@ -55,26 +49,20 @@ enabled => true, description => $admin_tenant_desc, } + keystone_user { $admin: + ensure => present, + enabled => true, + tenant => $admin_tenant, + email => $email, + password => $password, + ignore_default_tenant => $ignore_default_tenant, + } keystone_role { 'admin': ensure => present, } - - if $configure_user { - keystone_user { $admin: - ensure => present, - enabled => true, - tenant => $admin_tenant, - email => $email, - password => $password, - ignore_default_tenant => $ignore_default_tenant, - } - } - - if $configure_user_role { - keystone_user_role { "${admin}@${admin_tenant}": - ensure => present, - roles => $admin_roles, - } + keystone_user_role { "${admin}@${admin_tenant}": + ensure => present, + roles => 'admin', } } diff --git a/keystone/manifests/wsgi/apache.pp b/keystone/manifests/wsgi/apache.pp index b2a3b10c3..967b9d540 100644 --- a/keystone/manifests/wsgi/apache.pp +++ b/keystone/manifests/wsgi/apache.pp @@ -93,19 +93,16 @@ $ssl_ca = undef, $ssl_crl_path = undef, $ssl_crl = undef, - $ssl_certs_dir = undef, - $threads = $::processorcount, - $priority = '10', + $ssl_certs_dir = undef ) { - include ::keystone::params + include keystone::params include ::apache include ::apache::mod::wsgi - if $ssl { - include ::apache::mod::ssl - } + include keystone::db::sync - Package['keystone'] -> Package['httpd'] + Exec <| title == 'keystone-manage pki_setup' |> ~> Service['httpd'] + Exec <| title == 'keystone-manage db_sync' |> ~> Service['httpd'] Package['keystone'] ~> Service['httpd'] Keystone_config <| |> ~> Service['httpd'] Service['httpd'] -> Keystone_endpoint <| |> @@ -140,8 +137,7 @@ owner => 'keystone', group => 'keystone', mode => '0644', - # source file provided by keystone package - require => [File[$::keystone::params::keystone_wsgi_script_path], Package['keystone']], + require => File[$::keystone::params::keystone_wsgi_script_path], } file { 'keystone_wsgi_main': @@ -151,15 +147,14 @@ owner => 'keystone', group => 'keystone', mode => '0644', - # source file provided by keystone package - require => [File[$::keystone::params::keystone_wsgi_script_path], Package['keystone']], + require => File[$::keystone::params::keystone_wsgi_script_path], } $wsgi_daemon_process_options = { user => 'keystone', group => 'keystone', processes => $workers, - threads => $threads, + threads => '1' } $wsgi_script_aliases_main = hash([$public_path_real,"${::keystone::params::keystone_wsgi_script_path}/main"]) $wsgi_script_aliases_admin = hash([$admin_path_real, "${::keystone::params::keystone_wsgi_script_path}/admin"]) @@ -170,15 +165,13 @@ $wsgi_script_aliases_main_real = $wsgi_script_aliases_main } - ::apache::vhost { 'keystone_wsgi_main': - ensure => 'present', + apache::vhost { 'keystone_wsgi_main': servername => $servername, ip => $bind_host, port => $public_port, docroot => $::keystone::params::keystone_wsgi_script_path, docroot_owner => 'keystone', docroot_group => 'keystone', - priority => $priority, ssl => $ssl, ssl_cert => $ssl_cert, ssl_key => $ssl_key, @@ -187,36 +180,32 @@ ssl_crl_path => $ssl_crl_path, ssl_crl => $ssl_crl, ssl_certs_dir => $ssl_certs_dir, - wsgi_daemon_process => 'keystone_main', + wsgi_daemon_process => 'keystone', wsgi_daemon_process_options => $wsgi_daemon_process_options, - wsgi_process_group => 'keystone_main', + wsgi_process_group => 'keystone', wsgi_script_aliases => $wsgi_script_aliases_main_real, - require => File['keystone_wsgi_main'], + require => [Class['apache::mod::wsgi'], File['keystone_wsgi_main']], } if $public_port != $admin_port { - ::apache::vhost { 'keystone_wsgi_admin': - ensure => 'present', - servername => $servername, - ip => $bind_host, - port => $admin_port, - docroot => $::keystone::params::keystone_wsgi_script_path, - docroot_owner => 'keystone', - docroot_group => 'keystone', - priority => $priority, - ssl => $ssl, - ssl_cert => $ssl_cert, - ssl_key => $ssl_key, - ssl_chain => $ssl_chain, - ssl_ca => $ssl_ca, - ssl_crl_path => $ssl_crl_path, - ssl_crl => $ssl_crl, - ssl_certs_dir => $ssl_certs_dir, - wsgi_daemon_process => 'keystone_admin', - wsgi_daemon_process_options => $wsgi_daemon_process_options, - wsgi_process_group => 'keystone_admin', - wsgi_script_aliases => $wsgi_script_aliases_admin, - require => File['keystone_wsgi_admin'], + apache::vhost { 'keystone_wsgi_admin': + servername => $servername, + ip => $bind_host, + port => $admin_port, + docroot => $::keystone::params::keystone_wsgi_script_path, + docroot_owner => 'keystone', + docroot_group => 'keystone', + ssl => $ssl, + ssl_cert => $ssl_cert, + ssl_key => $ssl_key, + ssl_chain => $ssl_chain, + ssl_ca => $ssl_ca, + ssl_crl_path => $ssl_crl_path, + ssl_crl => $ssl_crl, + ssl_certs_dir => $ssl_certs_dir, + wsgi_process_group => 'keystone', + wsgi_script_aliases => $wsgi_script_aliases_admin, + require => [Class['apache::mod::wsgi'], File['keystone_wsgi_admin']], } } } diff --git a/keystone/metadata.json b/keystone/metadata.json deleted file mode 100644 index 368a28263..000000000 --- a/keystone/metadata.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "stackforge-keystone", - "version": "5.0.0", - "author": "Puppet Labs and StackForge Contributors", - "summary": "Puppet module for OpenStack Keystone", - "license": "Apache License 2.0", - "source": "git://github.com/stackforge/puppet-keystone.git", - "project_page": "https://launchpad.net/puppet-keystone", - "issues_url": "https://bugs.launchpad.net/puppet-keystone", - "requirements": [ - { "name": "pe","version_requirement": "3.x" }, - { "name": "puppet","version_requirement": "3.x" } - ], - "operatingsystem_support": [ - { - "operatingsystem": "Debian", - "operatingsystemrelease": ["7"] - }, - { - "operatingsystem": "Fedora", - "operatingsystemrelease": ["20"] - }, - { - "operatingsystem": "RedHat", - "operatingsystemrelease": ["6.5","7"] - }, - { - "operatingsystem": "Ubuntu", - "operatingsystemrelease": ["12.04","14.04"] - } - ], - "description": "Installs and configures OpenStack Keystone (Identity).", - "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": "stackforge/openstacklib", "version_requirement": ">=5.0.0" } - ] -} diff --git a/keystone/spec/classes/keystone_ldap_spec.rb b/keystone/spec/classes/keystone_ldap_spec.rb index 526710d9b..6d62f56f4 100644 --- a/keystone/spec/classes/keystone_ldap_spec.rb +++ b/keystone/spec/classes/keystone_ldap_spec.rb @@ -76,7 +76,6 @@ } end it { should contain_package('python-ldap') } - it { should contain_package('python-ldappool') } it 'should have basic params' do should contain_keystone_config('ldap/url').with_value('ldap://foo') should contain_keystone_config('ldap/user').with_value('cn=foo,dc=example,dc=com') diff --git a/keystone/spec/classes/keystone_roles_admin_spec.rb b/keystone/spec/classes/keystone_roles_admin_spec.rb index ba7d53296..798dafe2b 100644 --- a/keystone/spec/classes/keystone_roles_admin_spec.rb +++ b/keystone/spec/classes/keystone_roles_admin_spec.rb @@ -31,7 +31,7 @@ )} it { should contain_keystone_role('admin').with_ensure('present') } it { should contain_keystone_user_role('admin@openstack').with( - :roles => ['admin'], + :roles => 'admin', :ensure => 'present' )} @@ -45,7 +45,6 @@ :email => 'foo@baz', :password => 'foo', :admin_tenant => 'admin', - :admin_roles => ['admin', 'heat_stack_owner'], :service_tenant => 'foobar', :ignore_default_tenant => 'true', :admin_tenant_desc => 'admin something else', @@ -72,37 +71,10 @@ :ignore_default_tenant => 'true' )} it { should contain_keystone_user_role('admin@admin').with( - :roles => ['admin', 'heat_stack_owner'], + :roles => 'admin', :ensure => 'present' )} end - describe 'when disabling user configuration' do - before do - let :params do - { - :configure_user => false - } - end - - it { should_not contain_keystone_user('keystone') } - it { should contain_keystone_user_role('keystone@openstack') } - end - end - - describe 'when disabling user and role configuration' do - before do - let :params do - { - :configure_user => false, - :configure_user_role => false - } - end - - it { should_not contain_keystone_user('keystone') } - it { should_not contain_keystone_user_role('keystone@openstack') } - end - end - end diff --git a/keystone/spec/classes/keystone_spec.rb b/keystone/spec/classes/keystone_spec.rb index 675d3a716..5f577c474 100644 --- a/keystone/spec/classes/keystone_spec.rb +++ b/keystone/spec/classes/keystone_spec.rb @@ -2,24 +2,12 @@ describe 'keystone' do - let :global_facts do - { - :processorcount => 42, - :concat_basedir => '/var/lib/puppet/concat', - :fqdn => 'some.host.tld' - } - end - let :facts do - global_facts.merge({ - :osfamily => 'Debian', - :operatingsystem => 'Debian', - :operatingsystemrelease => '7.0' - }) + {:osfamily => 'Debian'} end - default_params = { - 'admin_token' => 'service_token', + let :default_params do + { 'package_ensure' => 'present', 'public_bind_host' => '0.0.0.0', 'admin_bind_host' => '0.0.0.0', @@ -31,8 +19,8 @@ 'debug' => false, 'catalog_type' => 'sql', 'catalog_driver' => false, - 'token_provider' => 'keystone.token.providers.uuid.Provider', - 'token_driver' => 'keystone.token.persistence.backends.sql.Token', + 'token_provider' => 'keystone.token.providers.pki.Provider', + 'token_driver' => 'keystone.token.backends.sql.Token', 'cache_dir' => '/var/cache/keystone', 'enable_ssl' => false, 'ssl_certfile' => '/etc/keystone/ssl/certs/keystone.pem', @@ -52,8 +40,10 @@ 'rabbit_password' => 'guest', 'rabbit_userid' => 'guest', } + end - override_params = { + [{'admin_token' => 'service_token'}, + { 'package_ensure' => 'latest', 'public_bind_host' => '0.0.0.0', 'admin_bind_host' => '0.0.0.0', @@ -86,108 +76,45 @@ 'rabbit_password' => 'openstack', 'rabbit_userid' => 'admin', } + ].each do |param_set| - httpd_params = {'service_name' => 'httpd'}.merge(default_params) - - shared_examples_for 'core keystone examples' do |param_hash| - it { should contain_class('keystone::params') } - - it { should contain_package('keystone').with( - 'ensure' => param_hash['package_ensure'] - ) } - - it { should contain_group('keystone').with( - 'ensure' => 'present', - 'system' => true - ) } - - it { should contain_user('keystone').with( - 'ensure' => 'present', - 'gid' => 'keystone', - 'system' => true - ) } - - it 'should contain the expected directories' do - ['/etc/keystone', '/var/log/keystone', '/var/lib/keystone'].each do |d| - should contain_file(d).with( - 'ensure' => 'directory', - 'owner' => 'keystone', - 'group' => 'keystone', - 'mode' => '0750', - 'require' => 'Package[keystone]' - ) + describe "when #{param_set == {} ? "using default" : "specifying"} class parameters" do + let :param_hash do + default_params.merge(param_set) end - end - - it 'should only synchronize the db if $enabled is true' do - if param_hash['enabled'] - should contain_exec('keystone-manage db_sync').with( - :user => 'keystone', - :refreshonly => true, - :subscribe => ['Package[keystone]', 'Keystone_config[database/connection]'], - :require => 'User[keystone]' - ) - end - end - it 'should contain correct config' do - [ - 'public_bind_host', - 'admin_bind_host', - 'public_port', - 'admin_port', - 'compute_port', - 'verbose', - 'debug' - ].each do |config| - should contain_keystone_config("DEFAULT/#{config}").with_value(param_hash[config]) + let :params do + param_set end - end - - it 'should contain correct admin_token config' do - should contain_keystone_config('DEFAULT/admin_token').with_value(param_hash['admin_token']).with_secret(true) - end - - it 'should contain correct mysql config' do - should contain_keystone_config('database/idle_timeout').with_value(param_hash['database_idle_timeout']) - should contain_keystone_config('database/connection').with_value(param_hash['database_connection']).with_secret(true) - end - - it { should contain_keystone_config('token/provider').with_value( - param_hash['token_provider'] - ) } - - it 'should contain correct token driver' do - should contain_keystone_config('token/driver').with_value(param_hash['token_driver']) - end - it 'should ensure proper setting of admin_endpoint and public_endpoint' do - if param_hash['admin_endpoint'] - should contain_keystone_config('DEFAULT/admin_endpoint').with_value(param_hash['admin_endpoint']) - else - should contain_keystone_config('DEFAULT/admin_endpoint').with_ensure('absent') - end - if param_hash['public_endpoint'] - should contain_keystone_config('DEFAULT/public_endpoint').with_value(param_hash['public_endpoint']) - else - should contain_keystone_config('DEFAULT/public_endpoint').with_ensure('absent') - end - end + it { should contain_class('keystone::params') } - it 'should contain correct rabbit_password' do - should contain_keystone_config('DEFAULT/rabbit_password').with_value(param_hash['rabbit_password']).with_secret(true) - end - end + it { should contain_package('keystone').with( + 'ensure' => param_hash['package_ensure'] + ) } - [default_params, override_params].each do |param_hash| - describe "when #{param_hash == default_params ? "using default" : "specifying"} class parameters for service" do + it { should contain_group('keystone').with( + 'ensure' => 'present', + 'system' => true + ) } + it { should contain_user('keystone').with( + 'ensure' => 'present', + 'gid' => 'keystone', + 'system' => true + ) } - let :params do - param_hash + it 'should contain the expected directories' do + ['/etc/keystone', '/var/log/keystone', '/var/lib/keystone'].each do |d| + should contain_file(d).with( + 'ensure' => 'directory', + 'owner' => 'keystone', + 'group' => 'keystone', + 'mode' => '0750', + 'require' => 'Package[keystone]' + ) + end end - it_configures 'core keystone examples', param_hash - it { should contain_service('keystone').with( 'ensure' => param_hash['enabled'] ? 'running' : 'stopped', 'enable' => param_hash['enabled'], @@ -195,26 +122,65 @@ 'hasrestart' => true ) } - end - end + it 'should only migrate the db if $enabled is true' do + if param_hash['enabled'] + should contain_exec('keystone-manage db_sync').with( + :user => 'keystone', + :refreshonly => true, + :subscribe => ['Package[keystone]', 'Keystone_config[database/connection]'], + :require => 'User[keystone]' + ) + end + end - describe "when using default class parameters for httpd" do - let :params do - httpd_params - end + it 'should contain correct config' do + [ + 'public_bind_host', + 'admin_bind_host', + 'public_port', + 'admin_port', + 'compute_port', + 'verbose', + 'debug' + ].each do |config| + should contain_keystone_config("DEFAULT/#{config}").with_value(param_hash[config]) + end + end - let :pre_condition do - 'include ::apache' - end + it 'should contain correct admin_token config' do + should contain_keystone_config('DEFAULT/admin_token').with_value(param_hash['admin_token']).with_secret(true) + end - it_configures 'core keystone examples', httpd_params + it 'should contain correct mysql config' do + should contain_keystone_config('database/idle_timeout').with_value(param_hash['database_idle_timeout']) + should contain_keystone_config('database/connection').with_value(param_hash['database_connection']).with_secret(true) + end - it do - expect { - should contain_service('keystone') - }.to raise_error(RSpec::Expectations::ExpectationNotMetError, /expected that the catalogue would contain Service\[keystone\]/) - end + it { should contain_keystone_config('token/provider').with_value( + param_hash['token_provider'] + ) } + + it 'should contain correct token driver' do + should contain_keystone_config('token/driver').with_value(param_hash['token_driver']) + end + + it 'should ensure proper setting of admin_endpoint and public_endpoint' do + if param_hash['admin_endpoint'] + should contain_keystone_config('DEFAULT/admin_endpoint').with_value(param_hash['admin_endpoint']) + else + should contain_keystone_config('DEFAULT/admin_endpoint').with_ensure('absent') + end + if param_hash['public_endpoint'] + should contain_keystone_config('DEFAULT/public_endpoint').with_value(param_hash['public_endpoint']) + else + should contain_keystone_config('DEFAULT/public_endpoint').with_ensure('absent') + end + end + it 'should contain correct rabbit_password' do + should contain_keystone_config('DEFAULT/rabbit_password').with_value(param_hash['rabbit_password']).with_secret(true) + end + end end describe 'with deprecated sql_connection parameter' do @@ -244,24 +210,7 @@ 'token_provider' => 'keystone.token.providers.uuid.Provider' } end - it { should contain_exec('keystone-manage pki_setup').with( - :creates => '/etc/keystone/ssl/private/signing_key.pem' - ) } - it { should contain_file('/var/cache/keystone').with_ensure('directory') } - - describe 'when overriding the cache dir' do - before do - params.merge!(:cache_dir => '/var/lib/cache/keystone') - end - it { should contain_file('/var/lib/cache/keystone') } - end - - describe 'when disable pki_setup' do - before do - params.merge!(:enable_pki_setup => false) - end - it { should_not contain_exec('keystone-manage pki_setup') } - end + it { should_not contain_exec('keystone-manage pki_setup') } end describe 'when configuring as PKI' do @@ -291,60 +240,16 @@ end end - describe 'when configuring PKI signing cert paths with UUID and with pki_setup disabled' do - let :params do - { - 'admin_token' => 'service_token', - 'token_provider' => 'keystone.token.providers.uuid.Provider', - 'enable_pki_setup' => false, - 'signing_certfile' => 'signing_certfile', - 'signing_keyfile' => 'signing_keyfile', - 'signing_ca_certs' => 'signing_ca_certs', - 'signing_ca_key' => 'signing_ca_key', - 'signing_cert_subject' => 'signing_cert_subject', - 'signing_key_size' => 2048 - } - end - - it { should_not contain_exec('keystone-manage pki_setup') } - - it 'should contain correct PKI certfile config' do - should contain_keystone_config('signing/certfile').with_value('signing_certfile') - end - - it 'should contain correct PKI keyfile config' do - should contain_keystone_config('signing/keyfile').with_value('signing_keyfile') - end - - it 'should contain correct PKI ca_certs config' do - should contain_keystone_config('signing/ca_certs').with_value('signing_ca_certs') - end - - it 'should contain correct PKI ca_key config' do - should contain_keystone_config('signing/ca_key').with_value('signing_ca_key') - end - - it 'should contain correct PKI cert_subject config' do - should contain_keystone_config('signing/cert_subject').with_value('signing_cert_subject') - end - - it 'should contain correct PKI key_size config' do - should contain_keystone_config('signing/key_size').with_value('2048') - end - end - describe 'when configuring PKI signing cert paths with pki_setup disabled' do let :params do { - 'admin_token' => 'service_token', - 'token_provider' => 'keystone.token.providers.pki.Provider', - 'enable_pki_setup' => false, - 'signing_certfile' => 'signing_certfile', - 'signing_keyfile' => 'signing_keyfile', - 'signing_ca_certs' => 'signing_ca_certs', - 'signing_ca_key' => 'signing_ca_key', - 'signing_cert_subject' => 'signing_cert_subject', - 'signing_key_size' => 2048 + 'admin_token' => 'service_token', + 'token_provider' => 'keystone.token.providers.pki.Provider', + 'enable_pki_setup' => false, + 'signing_certfile' => 'signing_certfile', + 'signing_keyfile' => 'signing_keyfile', + 'signing_ca_certs' => 'signing_ca_certs', + 'signing_ca_key' => 'signing_ca_key' } end @@ -365,14 +270,6 @@ it 'should contain correct PKI ca_key config' do should contain_keystone_config('signing/ca_key').with_value('signing_ca_key') end - - it 'should contain correct PKI cert_subject config' do - should contain_keystone_config('signing/cert_subject').with_value('signing_cert_subject') - end - - it 'should contain correct PKI key_size config' do - should contain_keystone_config('signing/key_size').with_value('2048') - end end describe 'with invalid catalog_type' do @@ -393,49 +290,14 @@ it { should contain_keystone_config('catalog/driver').with_value(params[:catalog_driver]) } end - describe 'when configuring deprecated token_format as UUID with enable_pki_setup' do + describe 'when configuring deprecated token_format as UUID' do let :params do { 'admin_token' => 'service_token', 'token_format' => 'UUID' } end - it { should contain_exec('keystone-manage pki_setup').with( - :creates => '/etc/keystone/ssl/private/signing_key.pem' - ) } - it { should contain_file('/var/cache/keystone').with_ensure('directory') } - describe 'when overriding the cache dir' do - let :params do - { - 'admin_token' => 'service_token', - 'token_provider' => 'keystone.token.providers.pki.Provider', - 'cache_dir' => '/var/lib/cache/keystone' - } - end - it { should contain_file('/var/lib/cache/keystone') } - end - end - - describe 'when configuring deprecated token_format as UUID without enable_pki_setup' do - let :params do - { - 'admin_token' => 'service_token', - 'token_format' => 'UUID', - 'enable_pki_setup' => false - } - end it { should_not contain_exec('keystone-manage pki_setup') } - it { should contain_file('/var/cache/keystone').with_ensure('directory') } - describe 'when overriding the cache dir' do - let :params do - { - 'admin_token' => 'service_token', - 'token_provider' => 'keystone.token.providers.uuid.Provider', - 'cache_dir' => '/var/lib/cache/keystone' - } - end - it { should contain_file('/var/lib/cache/keystone') } - end end describe 'when configuring deprecated token_format as PKI with enable_pki_setup' do @@ -613,6 +475,28 @@ it { should contain_keystone_config('DEFAULT/admin_bind_host').with_value('10.0.0.2') } end + describe 'when configuring as SSL' do + let :params do + { + 'admin_token' => 'service_token', + 'enable_ssl' => true + } + end + it { should contain_exec('keystone-manage pki_setup').with( + :creates => '/etc/keystone/ssl/private/signing_key.pem' + ) } + it { should contain_file('/var/cache/keystone').with_ensure('directory') } + describe 'when overriding the cache dir' do + let :params do + { + 'admin_token' => 'service_token', + 'enable_ssl' => true, + 'cache_dir' => '/var/lib/cache/keystone' + } + end + it { should contain_file('/var/lib/cache/keystone') } + end + end describe 'when enabling SSL' do let :params do { @@ -773,10 +657,7 @@ describe 'setting service_provider' do let :facts do - global_facts.merge({ - :osfamily => 'RedHat', - :operatingsystemrelease => '6.0' - }) + {:osfamily => 'RedHat'} end describe 'with default service_provider' do diff --git a/keystone/spec/classes/keystone_wsgi_apache_spec.rb b/keystone/spec/classes/keystone_wsgi_apache_spec.rb index 5745d6b85..9deaaa038 100644 --- a/keystone/spec/classes/keystone_wsgi_apache_spec.rb +++ b/keystone/spec/classes/keystone_wsgi_apache_spec.rb @@ -11,30 +11,8 @@ end let :pre_condition do - [ - 'class { keystone: admin_token => "dummy", service_name => "httpd", enable_ssl => true }' - ] - end - - # concat::fragment { "${name}-wsgi": - # $filename = regsubst($name, ' ', '_', 'G') - # target => "${priority_real}-${filename}.conf", - # $safe_name = regsubst($name, '[/:\n]', '_', 'GM') - # $safe_target_name = regsubst($target, '[/:\n]', '_', 'GM') - # $concatdir = $concat::setup::concatdir - # $fragdir = "${concatdir}/${safe_target_name}" - # file { "${fragdir}/fragments/${order}_${safe_name}": - def get_concat_name(base_name) -# pp subject.resources - priority = 10 - order = 250 - base_dir = facts[:concat_basedir] - safe_name = base_name.gsub(/[\/:\n]/m, '_') + '-wsgi' - target = "#{priority}-#{base_name}.conf" - safe_target_name = target.gsub(/[\/:\n]/m, '_') - frag_dir = "#{base_dir}/#{safe_target_name}" - full_name = "#{frag_dir}/fragments/#{order}_#{safe_name}" - return full_name + 'include apache + class { keystone: admin_token => "dummy" }' end shared_examples_for 'apache serving keystone with mod_wsgi' do @@ -60,7 +38,7 @@ def get_concat_name(base_name) 'owner' => 'keystone', 'group' => 'keystone', 'mode' => '0644', - 'require' => ["File[#{platform_parameters[:wsgi_script_path]}]", "Package[keystone]"] + 'require' => "File[#{platform_parameters[:wsgi_script_path]}]" )} it { should contain_file('keystone_wsgi_main').with( @@ -70,7 +48,7 @@ def get_concat_name(base_name) 'owner' => 'keystone', 'group' => 'keystone', 'mode' => '0644', - 'require' => ["File[#{platform_parameters[:wsgi_script_path]}]", "Package[keystone]"] + 'require' => "File[#{platform_parameters[:wsgi_script_path]}]" )} it { should contain_apache__vhost('keystone_wsgi_admin').with( @@ -81,10 +59,9 @@ def get_concat_name(base_name) 'docroot_owner' => 'keystone', 'docroot_group' => 'keystone', 'ssl' => 'true', - 'wsgi_daemon_process' => 'keystone_admin', - 'wsgi_process_group' => 'keystone_admin', + 'wsgi_process_group' => 'keystone', 'wsgi_script_aliases' => { '/' => "#{platform_parameters[:wsgi_script_path]}/admin" }, - 'require' => 'File[keystone_wsgi_admin]' + 'require' => ['Class[Apache::Mod::Wsgi]', 'File[keystone_wsgi_admin]'] )} it { should contain_apache__vhost('keystone_wsgi_main').with( @@ -95,18 +72,16 @@ def get_concat_name(base_name) 'docroot_owner' => 'keystone', 'docroot_group' => 'keystone', 'ssl' => 'true', - 'wsgi_daemon_process' => 'keystone_main', - 'wsgi_process_group' => 'keystone_main', + 'wsgi_daemon_process' => 'keystone', + 'wsgi_process_group' => 'keystone', 'wsgi_script_aliases' => { '/' => "#{platform_parameters[:wsgi_script_path]}/main" }, - 'require' => 'File[keystone_wsgi_main]' - )} - it { should contain_file(get_concat_name('keystone_wsgi_main')).with_content( - /^ WSGIDaemonProcess keystone_main group=keystone processes=1 threads=#{facts[:processorcount]} user=keystone$/ + 'require' => ['Class[Apache::Mod::Wsgi]', 'File[keystone_wsgi_main]'] )} - it { should contain_file(get_concat_name('keystone_wsgi_admin')).with_content( - /^ WSGIDaemonProcess keystone_admin group=keystone processes=1 threads=#{facts[:processorcount]} user=keystone$/ - )} - it { should contain_file("#{platform_parameters[:httpd_ports_file]}") } + it "should set keystone wsgi options" do + contain_file('25-keystone_wsgi_main.conf').with_content( + /^ WSGIDaemonProcess keystone group=keystone processes=1 threads=1 user=keystone$/ + ) + end end describe 'when overriding parameters using different ports' do @@ -129,10 +104,9 @@ def get_concat_name(base_name) 'docroot_owner' => 'keystone', 'docroot_group' => 'keystone', 'ssl' => 'false', - 'wsgi_daemon_process' => 'keystone_admin', - 'wsgi_process_group' => 'keystone_admin', + 'wsgi_process_group' => 'keystone', 'wsgi_script_aliases' => { '/' => "#{platform_parameters[:wsgi_script_path]}/admin" }, - 'require' => 'File[keystone_wsgi_admin]' + 'require' => ['Class[Apache::Mod::Wsgi]', 'File[keystone_wsgi_admin]'] )} it { should contain_apache__vhost('keystone_wsgi_main').with( @@ -143,18 +117,16 @@ def get_concat_name(base_name) 'docroot_owner' => 'keystone', 'docroot_group' => 'keystone', 'ssl' => 'false', - 'wsgi_daemon_process' => 'keystone_main', - 'wsgi_process_group' => 'keystone_main', + 'wsgi_daemon_process' => 'keystone', + 'wsgi_process_group' => 'keystone', 'wsgi_script_aliases' => { '/' => "#{platform_parameters[:wsgi_script_path]}/main" }, - 'require' => 'File[keystone_wsgi_main]' + 'require' => ['Class[Apache::Mod::Wsgi]', 'File[keystone_wsgi_main]'] )} - it { should contain_file(get_concat_name('keystone_wsgi_main')).with_content( - /^ WSGIDaemonProcess keystone_main group=keystone processes=#{params[:workers]} threads=#{facts[:processorcount]} user=keystone$/ - )} - it { should contain_file(get_concat_name('keystone_wsgi_admin')).with_content( - /^ WSGIDaemonProcess keystone_admin group=keystone processes=#{params[:workers]} threads=#{facts[:processorcount]} user=keystone$/ - )} - it { should contain_file("#{platform_parameters[:httpd_ports_file]}") } + it "should set keystone wsgi options" do + contain_file('25-keystone_wsgi_main.conf').with_content( + /^ WSGIDaemonProcess keystone group=keystone processes=37 threads=1 user=keystone$/ + ) + end end describe 'when overriding parameters using same port' do @@ -180,22 +152,18 @@ def get_concat_name(base_name) 'docroot_owner' => 'keystone', 'docroot_group' => 'keystone', 'ssl' => 'true', - 'wsgi_daemon_process' => 'keystone_main', - 'wsgi_process_group' => 'keystone_main', + 'wsgi_daemon_process' => 'keystone', + 'wsgi_process_group' => 'keystone', 'wsgi_script_aliases' => { '/main/endpoint' => "#{platform_parameters[:wsgi_script_path]}/main", '/admin/endpoint' => "#{platform_parameters[:wsgi_script_path]}/admin" - }, - 'require' => 'File[keystone_wsgi_main]' - )} - it { should contain_file(get_concat_name('keystone_wsgi_main')).with_content( - /^ WSGIDaemonProcess keystone_main group=keystone processes=#{params[:workers]} threads=#{facts[:processorcount]} user=keystone$/ + }, + 'require' => ['Class[Apache::Mod::Wsgi]', 'File[keystone_wsgi_main]'] )} - it do - expect_file = get_concat_name('keystone_wsgi_admin') - expect { - should contain_file(expect_file) - }.to raise_error(RSpec::Expectations::ExpectationNotMetError, /expected that the catalogue would contain File\[#{expect_file}\]/) + it "should set keystone wsgi options" do + contain_file('25-keystone_wsgi_main.conf').with_content( + /^ WSGIDaemonProcess keystone group=keystone processes=37 threads=1 user=keystone$/ + ) end end @@ -227,9 +195,8 @@ def get_concat_name(base_name) let :platform_parameters do { :httpd_service_name => 'httpd', - :httpd_ports_file => '/etc/httpd/conf/ports.conf', :wsgi_script_path => '/var/www/cgi-bin/keystone', - :wsgi_script_source => '/usr/share/keystone/keystone.wsgi' + :wsgi_script_source => 'puppet:///modules/keystone/httpd/keystone.py' } end @@ -248,7 +215,6 @@ def get_concat_name(base_name) let :platform_parameters do { :httpd_service_name => 'apache2', - :httpd_ports_file => '/etc/apache2/ports.conf', :wsgi_script_path => '/usr/lib/cgi-bin/keystone', :wsgi_script_source => '/usr/share/keystone/wsgi.py' } diff --git a/keystone/spec/unit/type/keystone_user_role_spec.rb b/keystone/spec/unit/type/keystone_user_role_spec.rb deleted file mode 100644 index 82c32688f..000000000 --- a/keystone/spec/unit/type/keystone_user_role_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -require 'spec_helper' -require 'puppet' -require 'puppet/type/keystone_user_role' - -describe Puppet::Type.type(:keystone_user_role) do - - before :each do - @user_roles = Puppet::Type.type(:keystone_user_role).new( - :name => 'foo@bar', - :roles => ['a', 'b'] - ) - - @roles = @user_roles.parameter('roles') - end - - it 'should not be in sync for' do - expect(@roles.insync?(['a', 'b', 'c'])).to be false - expect(@roles.insync?('a')).to be false - expect(@roles.insync?(['a'])).to be false - expect(@roles.insync?(nil)).to be false - end - - it 'should be in sync for' do - expect(@roles.insync?(['a', 'b'])).to be true - expect(@roles.insync?(['b', 'a'])).to be true - end - -end diff --git a/kibana3/.fixtures.yml b/kibana3/.fixtures.yml new file mode 100644 index 000000000..9cfb3a134 --- /dev/null +++ b/kibana3/.fixtures.yml @@ -0,0 +1,19 @@ +fixtures: + repositories: + stdlib: + repo: 'git://github.com/puppetlabs/puppetlabs-stdlib' + ref: '3.2.1' + git: + repo: 'git://github.com/puppetlabs/puppetlabs-git.git' + ref: '0.0.3' + vcsrepo: + repo: 'git://github.com/puppetlabs/puppetlabs-vcsrepo.git' + ref: '0.2.0' + apache: + repo: 'git://github.com/puppetlabs/puppetlabs-apache.git' + ref: '1.0.1' + concat: + repo: 'git://github.com/puppetlabs/puppetlabs-concat.git' + ref: '1.0.0' + symlinks: + kibana3: "#{source_dir}" diff --git a/kibana3/.gitignore b/kibana3/.gitignore new file mode 100644 index 000000000..51e0ece52 --- /dev/null +++ b/kibana3/.gitignore @@ -0,0 +1,7 @@ +spec/fixtures +pkg +.vagrant +.rspec_system +.*.sw? +.bundle +Gemfile.lock diff --git a/kibana3/.ruby-gemset b/kibana3/.ruby-gemset new file mode 100644 index 000000000..cff7f32e6 --- /dev/null +++ b/kibana3/.ruby-gemset @@ -0,0 +1 @@ +kibana3 diff --git a/kibana3/.ruby-version b/kibana3/.ruby-version new file mode 100644 index 000000000..314a6ed8c --- /dev/null +++ b/kibana3/.ruby-version @@ -0,0 +1 @@ +ruby-2.1.1 diff --git a/kibana3/.travis.yml b/kibana3/.travis.yml new file mode 100644 index 000000000..5a99600d2 --- /dev/null +++ b/kibana3/.travis.yml @@ -0,0 +1,38 @@ +--- +notifications: + email: + - alejandro@ideasftw.com +language: ruby +bundler_args: --without development +before_install: rm Gemfile.lock || true +rvm: + - 1.8.7 + - 1.9.3 + - 2.0.0 + - 2.1.0 +script: bundle exec rake test +env: + - PUPPET_VERSION="~> 2.7.0" + - PUPPET_VERSION="~> 3.1.0" + - PUPPET_VERSION="~> 3.2.0" + - PUPPET_VERSION="~> 3.3.0" + - PUPPET_VERSION="~> 3.4.0" + - PUPPET_VERSION="~> 3.5.0" +matrix: + exclude: + - rvm: 1.9.3 + env: PUPPET_VERSION="~> 2.7.0" + - rvm: 2.0.0 + env: PUPPET_VERSION="~> 2.7.0" + - rvm: 2.0.0 + env: PUPPET_VERSION="~> 3.1.0" + - rvm: 2.1.0 + env: PUPPET_VERSION="~> 2.7.0" + - rvm: 2.1.0 + env: PUPPET_VERSION="~> 3.1.0" + - rvm: 2.1.0 + env: PUPPET_VERSION="~> 3.2.0" + - rvm: 2.1.0 + env: PUPPET_VERSION="~> 3.3.0" + - rvm: 2.1.0 + env: PUPPET_VERSION="~> 3.4.0" diff --git a/kibana3/CHANGELOG.md b/kibana3/CHANGELOG.md new file mode 100644 index 000000000..ff5fd1fe4 --- /dev/null +++ b/kibana3/CHANGELOG.md @@ -0,0 +1,13 @@ +## Change Log + +### v0.0.3 (2014-05-07) + - Changed default kibana3 release from tag name to tag commit hash + - Changed default kibana3 version from 3.0.0 to 3.0.1 + - Updated documentation + +### v0.0.2 (2014-03-25) + - Changed doc_root in apache::vhost if managed_ws => true + - Disabled default_vhost id managed_ws => true + +### v0.0.1 (2014-03-23) + - initial commit diff --git a/kibana3/Gemfile b/kibana3/Gemfile new file mode 100644 index 000000000..38b9eb023 --- /dev/null +++ b/kibana3/Gemfile @@ -0,0 +1,21 @@ +source 'https://rubygems.org' + +group :test do + gem "rake" + gem "puppet", ENV['PUPPET_VERSION'] || '~> 3.4.0' + gem "puppet-lint" + gem "rspec-puppet", :git => 'https://github.com/rodjek/rspec-puppet.git' + gem "puppet-syntax" + gem "puppetlabs_spec_helper" +end + +group :development do + gem "travis" + gem "travis-lint" + gem "beaker" + gem "jwt", "~> 0.1.4" + gem "beaker-rspec" + gem "vagrant-wrapper" + gem "puppet-blacksmith" + gem "guard-rake" +end diff --git a/kibana3/LICENSE b/kibana3/LICENSE new file mode 100644 index 000000000..c4cf7b1b2 --- /dev/null +++ b/kibana3/LICENSE @@ -0,0 +1,13 @@ +Copyright 2014 Alejandro Figueroa + +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. diff --git a/kibana3/Modulefile b/kibana3/Modulefile new file mode 100644 index 000000000..b1ddef4d6 --- /dev/null +++ b/kibana3/Modulefile @@ -0,0 +1,12 @@ +name 'thejandroman-kibana3' +version '0.0.3' +source 'https://github.com/thejandroman/kibana3' +author 'thejandroman' +license 'Apache License, Version 2.0' +summary 'Installs and configures kibana3.' +description 'The kibana3 puppet module allows one to setup and configure the kibana3 interface to Logstash and ElasticSearch.' +project_page 'https://github.com/thejandroman/kibana3' +dependency 'puppetlabs/git', '>= 0.0.3' +dependency 'puppetlabs/vcsrepo', '>= 0.2.0' +dependency 'puppetlabs/apache', '>= 1.0.1' +dependency 'puppetlabs/stdlib', '>= 3.2.1' diff --git a/kibana3/README.md b/kibana3/README.md new file mode 100644 index 000000000..054c317e4 --- /dev/null +++ b/kibana3/README.md @@ -0,0 +1,137 @@ +#kibana3 [![Build Status](https://travis-ci.org/thejandroman/kibana3.svg?branch=master)](https://travis-ci.org/thejandroman/kibana3) + +####Table of Contents + +1. [Overview](#overview) +2. [Module Description](#module-description) +3. [Setup](#setup) + * [Examples](#examples) +4. [Usage](#usage) + * [Classes](#classes) + * [kibana3](#class-kibana3) +5. [Limitiations](#limitations) +6. [Contributing](#contributing) +7. [License](#license) + +##Overview +The kibana3 puppet module allows one to setup and configure the [kibana3](http://www.elasticsearch.org/overview/kibana/) interface to Logstash and ElasticSearch. + +##Module Description +This module should cover all configuration options for kibana3 v3.0.0. + +This module checks out the [kibana3 source](https://github.com/elasticsearch/kibana) directly from github and requires git to be installed. By default this module will install git via the [puppetlabs/git](https://github.com/puppetlabs/puppetlabs-git) module. This behavior can be disabled. + +Kibana3 requires a webserver to serve its content. By default this module will install apache via the [puppetlabs/apache](https://github.com/puppetlabs/puppetlabs-apache) module. This behavior can be disabled. + +##Setup +Kibana3 can be installed with a base configuration with `include kibana3` with default configuration options. To disable managing of git or apache see options below. + +This module also provides for a way to uninstall via the `ensure` parameter. If `ensure => 'absent'` is specified and `manage_ws => true` and/or `manage_git => true` is specified, the respective module's packages and configs will also be uninstalled. + +###Examples +To install kibana3 accepting all default options: +```puppet + include kibana3 +``` +To specify a specific elasticsearch host to kibana3: +```puppet + class { + 'kibana3': + config_es_port => '9201', + config_es_protocol => 'https', + config_es_server => 'es.my.domain', + } +``` +To manage webserver configuration outside of the kibana3 module: +```puppet + class { + 'kibana3': + manage_ws => false, + } +``` + +## Usage +###Classes +####Class: `kibana3` +#####`ensure` +**Data Type:** _string_ +**Default:** _present_ +Set to 'absent' to uninstall. Beware, if `manage_git` or `manage_ws` are set to true their respective module's packages and configs will also be uninstalled. + +#####`config_default_route` +**Data Type:** _string_ +**Default:** _/dashboard/file/default.json_ +This is the default landing page when you don't specify a dashboard to load. You can specify files, scripts or saved dashboards here. For example, if you had saved a dashboard called 'WebLogs' to elasticsearch you might +use: `config_default_route => '/dashboard/elasticsearch/WebLogs',` + +#####`config_es_port` +**Data Type:** _string_ +**Default:** _9200_ +The port of the elasticsearch server. Because kibana3 is browser based this must be accessible from the browser loading kibana3. + +#####`config_es_protocol` +**Data Type:** _string_ +**Default:** _http_ +The protocol (http/https) of the elasticsearch server. Because kibana3 is browser based this must be accessible from the browser loading kibana3. + +#####`config_es_server` +**Data Type:** _string_ +**Default:** _"+window.location.hostname+"_ +The FQDN of the elasticsearch server. Because kibana3 is browser based this must be accessible from the browser loading kibana3. + +#####`config_kibana_index` +**Data Type:** _string_ +**Default:** _kibana-int_ +The default ES index to use for storing Kibana specific object such as stored dashboards. + +#####`config_panel_names` +**Data Type:** _array_ +**Default:** _['histogram','map','goal','table','filtering','timepicker','text','hits','column','trends','bettermap','query','terms','stats','sparklines']_ +An array of panel modules available. Panels will only be loaded when they are defined in the dashboard, but this list is used in the "add panel" interface. + +#####`k3_folder_owner` +**Data Type:** _string_ +**Default:** _undef_ +The owner of the kibana3 install located at `k3_install_folder`. If `k3_folder_owner` remains 'undef' it defaults to one of two case: + * if `manage_ws => false` then `k3_folder_owner => 'root'` + * if `manage_ws => true` then `k3_folder_owner => $::apache::params::user` + +#####`k3_install_folder` +**Data Type:** _string_ +**Default:** _/opt/kibana3_ +The folder to install kibana3 into. + +#####`k3_release` +**Data Type:** _string_ +**Default:** _a50a913 (v3.0.1)_ +A tag or branch from the [kibana3](https://github.com/elasticsearch/kibana) repo. Note that you should use the commit hash instead of the tag name (see [issue #5](https://github.com/thejandroman/kibana3/issues/5)) or puppet will overwrite the config.js file. + +#####`manage_git` +**Data Type:** _bool_ +**Default:** _true_ +Should the module manage git. + +#####`manage_ws` +**Data Type:** _bool_ +**Default:** _true_ +Should the module manage the webserver. + +#####`ws_servername` +**Data Type:** _string_ +**Default:** _kibana3_ +Change the default servername for the apache vhost. Only taken into account if `manage_ws => true`. + +#####`ws_port` +**Data Type:** _bool_ +**Default:** _true_ +Change the default port for the webserver to a custom value. Only taken into account if `manage_ws => true`. + +##Limitations + * Tested and built on Ubuntu 12.04. + * Tested with Kibana v3.0.0. + +##Contributing +Pull requests are welcome. Please document and include rspec tests. + +##License +See [LICENSE](https://github.com/thejandroman/kibana3/blob/master/LICENSE) file. diff --git a/kibana3/Rakefile b/kibana3/Rakefile new file mode 100644 index 000000000..8072f2c4d --- /dev/null +++ b/kibana3/Rakefile @@ -0,0 +1,31 @@ +require 'puppetlabs_spec_helper/rake_tasks' +require 'puppet-syntax/tasks/puppet-syntax' +require 'puppet-lint/tasks/puppet-lint' + +begin + require 'puppet_blacksmith/rake_tasks' +rescue LoadError +end + +PuppetLint.configuration.send('disable_class_inherits_from_params_class') +PuppetLint.configuration.fail_on_warnings = true + +exclude_paths = [ + "pkg/**/*", + "vendor/**/*", + "spec/**/*", +] +PuppetLint.configuration.ignore_paths = exclude_paths +PuppetSyntax.exclude_paths = exclude_paths + +desc "Run acceptance tests" +RSpec::Core::RakeTask.new(:acceptance) do |t| + t.pattern = 'spec/acceptance' +end + +desc "Run syntax, lint, and spec tests." +task :test => [ + :syntax, + :lint, + :spec, +] diff --git a/kibana3/manifests/config.pp b/kibana3/manifests/config.pp new file mode 100644 index 000000000..fe7b51a1d --- /dev/null +++ b/kibana3/manifests/config.pp @@ -0,0 +1,21 @@ +# = Private class: kibana3::config +# +# Author: Alejandro Figueroa +class kibana3::config { + if $::kibana3::manage_ws { + file { + "${::kibana3::k3_install_folder}/src/config.js": + ensure => present, + content => template('kibana3/config.js.erb'), + owner => $::kibana3::install::_ws_user, + notify => Class['::Apache::Service'], + } + } else { + file { + "${::kibana3::k3_install_folder}/src/config.js": + ensure => present, + content => template('kibana3/config.js.erb'), + owner => $::kibana3::install::_ws_user, + } + } +} diff --git a/kibana3/manifests/init.pp b/kibana3/manifests/init.pp new file mode 100644 index 000000000..069020ab5 --- /dev/null +++ b/kibana3/manifests/init.pp @@ -0,0 +1,122 @@ +# == Public class: kibana3 +# +# Installs and configures kibana3. +# +# === Parameters +# [*ensure*] +# Set to 'absent' to uninstall. Beware, if $manage_git or $manage_ws are set +# to 'true' their respective modules will also be set to 'absent'. +# +# [*config_default_route*] +# This is the default landing page when you don't specify a dashboard to +# load. You can specify files, scripts or saved dashboards here. For example, +# if you had saved a dashboard called `WebLogs' to elasticsearch you might +# use: +# default_route => '/dashboard/elasticsearch/WebLogs', +# +# [*config_es_port*] +# The port of the elasticsearch server. Because kibana3 is browser based +# this must be accessible from the browser loading kibana3. +# +# [*config_es_protocol*] +# The protocol (http/https) of the elasticsearch server. Because kibana3 is +# browser based this must be accessible from the browser loading kibana3. +# +# [*config_es_server*] +# The FQDN of the elasticsearch server. Because kibana3 is browser based +# this must be accessible from the browser loading kibana3. +# +# [*config_kibana_index*] +# The default ES index to use for storing Kibana specific object such as +# stored dashboards. +# +# [*config_panel_names*] +# An array of panel modules available. Panels will only be loaded when they +# are defined in the dashboard, but this list is used in the "add panel" +# interface. +# +# [*k3_folder_owner*] +# The owner of the kibana3 install located at $k3_install_folder. +# +# [*k3_install_folder*] +# The folder to install kibana3 into. +# +# [*k3_release*] +# A tag or branch from the https://github.com/elasticsearch/kibana repo. Note +# that you should use the commit hash instead of the tag name (see issue #5) +# or puppet will overwrite the config.js file. +# +# [*manage_git*] +# Should the module manage git. +# +# [*manage_ws*] +# Should the module manage the webserver. +# +# [*ws_servername*] +# Specify the virtual host servername . Only taken +# into account if manage_ws => true. +# +# [*ws_port*] +# Change the default port for the webserver to a custom value. Only taken +# into account if manage_ws => true. +# +# === Examples +# +# class { 'kibana3': +# servers => [ 'pool.ntp.org', 'ntp.local.company.com' ], +# } +# +# === Authors +# +# Alejandro Figueroa +# +# === Copyright +# +# Copyright 2014 Alejandro Figueroa, unless otherwise noted. +# +class kibana3 ( + $ensure = $::kibana3::params::ensure, + + $config_default_route = $::kibana3::params::config_default_route, + $config_es_port = $::kibana3::params::config_es_port, + $config_es_protocol = $::kibana3::params::config_es_protocol, + $config_es_server = $::kibana3::params::config_es_server, + $config_kibana_index = $::kibana3::params::config_kibana_index, + $config_panel_names = $::kibana3::params::config_panel_names, + + $k3_folder_owner = $::kibana3::params::k3_folder_owner, + $k3_install_folder = $::kibana3::params::k3_install_folder, + $k3_release = $::kibana3::params::k3_release, + + $manage_git = $::kibana3::params::manage_git, + + $manage_ws = $::kibana3::params::manage_ws, + $ws_servername = $::kibana3::params::ws_servername, + $ws_port = $::kibana3::params::ws_port, + +) inherits kibana3::params { + + validate_string($ensure,$config_default_route,$config_es_port, + $config_es_protocol,$config_es_server,$config_kibana_index, + $k3_folder_owner,$k3_install_folder,$k3_release,$ws_port) + + validate_bool($manage_git,$manage_ws) + + validate_array($config_panel_names) + + case $ensure { + 'present': { + anchor { 'kibana3::begin': } -> + class { 'kibana3::install': } -> + class { 'kibana3::config': } -> + anchor { 'kibana3::end': } + } + 'absent': { + class { 'kibana3::uninstall': } + } + default: { + fail("${ensure} is not supported for ensure. + Allowed values are 'present' and 'absent'.") + } + } +} diff --git a/kibana3/manifests/install.pp b/kibana3/manifests/install.pp new file mode 100644 index 000000000..138917d3d --- /dev/null +++ b/kibana3/manifests/install.pp @@ -0,0 +1,48 @@ +# = Private class: kibana3::install +# +# Author: Alejandro Figueroa +class kibana3::install { + if $::kibana3::manage_git { + require 'git' + } + + if $::kibana3::manage_ws { + include ::apache + } + + if $::kibana3::k3_folder_owner { + $_ws_user = $::kibana3::k3_folder_owner + } elsif $::kibana3::manage_ws { + $_ws_user = $::apache::params::user + } else { + $_ws_user = 'root' + } + + if $::kibana3::manage_ws { + vcsrepo { + $::kibana3::k3_install_folder: + ensure => present, + provider => git, + source => 'https://github.com/elasticsearch/kibana.git', + revision => $::kibana3::k3_release, + owner => $_ws_user, + notify => Class['::Apache::Service'], + } + -> + apache::vhost { + $::kibana3::ws_servername : + port => $::kibana3::ws_port, + docroot => "${::kibana3::k3_install_folder}/src", + docroot_owner => $_ws_user, + } + } else { + vcsrepo { + $::kibana3::k3_install_folder: + ensure => present, + provider => git, + source => 'https://github.com/elasticsearch/kibana.git', + revision => $::kibana3::k3_release, + owner => $_ws_user, + } + } +} diff --git a/kibana3/manifests/params.pp b/kibana3/manifests/params.pp new file mode 100644 index 000000000..2df2e8778 --- /dev/null +++ b/kibana3/manifests/params.pp @@ -0,0 +1,42 @@ +# = Private class: kibana3::params +# +# Author: Alejandro Figueroa +class kibana3::params { + $ensure = 'present' + + $config_default_route = '/dashboard/file/default.json' + $config_es_port = '9200' + $config_es_protocol = 'http' + $config_es_server = '"+window.location.hostname+"' + $config_kibana_index = 'kibana-int' + $config_panel_names = [ + 'histogram', + 'map', + 'goal', + 'table', + 'filtering', + 'timepicker', + 'text', + 'hits', + 'column', + 'trends', + 'bettermap', + 'query', + 'terms', + 'stats', + 'sparklines' + ] + + # If $k3_folder_owner remains 'undef' it defaults to one of two case: + # if $manage_ws = 'false'; $k3_folder_owner = 'root' + # if $manage_ws = 'true'; $k3_folder_owner = $::apache::params::user + $k3_folder_owner = undef + $k3_install_folder = '/opt/kibana3' + $k3_release = 'a50a913' + + $manage_git = true + + $manage_ws = true + $ws_servername = 'kibana3' + $ws_port = '80' +} diff --git a/kibana3/manifests/uninstall.pp b/kibana3/manifests/uninstall.pp new file mode 100644 index 000000000..73d8cf31e --- /dev/null +++ b/kibana3/manifests/uninstall.pp @@ -0,0 +1,34 @@ +# = Private class: kibana3::uninstall +# +# Author: Alejandro Figueroa +class kibana3::uninstall { + if $::kibana3::manage_git { + package { + 'git': + ensure => absent, + } + } + + if $::kibana3::manage_ws { + class { + 'apache': + package_ensure => absent, + } + + apache::vhost { + 'kibana3': + ensure => absent, + docroot => "${::kibana3::k3_install_folder}/src", + } + } + + file { + $::kibana3::k3_install_folder: + ensure => absent, + } + + file { + "${::kibana3::k3_install_folder}/src/config.js": + ensure => absent, + } +} diff --git a/kibana3/metadata.json b/kibana3/metadata.json new file mode 100644 index 000000000..2110dea46 --- /dev/null +++ b/kibana3/metadata.json @@ -0,0 +1,61 @@ +{ + "operatingsystem_support": [ + { + "operatingsystem": "Ubuntu", + "operatingsystemrelease": [ + "12.04" + ] + } + ], + "requirements": [ + { + "name": "puppet", + "version_requirement": "3.x" + } + ], + "name": "thejandroman-kibana3", + "version": "0.0.3", + "source": "https://github.com/thejandroman/kibana3", + "author": "thejandroman", + "license": "Apache License, Version 2.0", + "summary": "Installs and configures kibana3.", + "description": "The kibana3 puppet module allows one to setup and configure the kibana3 interface to Logstash and ElasticSearch.", + "project_page": "https://github.com/thejandroman/kibana3", + "dependencies": [ + { + "name": "puppetlabs/git", + "version_requirement": ">= 0.0.3" + }, + { + "name": "puppetlabs/vcsrepo", + "version_requirement": ">= 0.2.0" + }, + { + "name": "puppetlabs/apache", + "version_requirement": ">= 1.0.1" + }, + { + "name": "puppetlabs/stdlib", + "version_requirement": ">= 3.2.1" + } + ], + "types": [ + + ], + "checksums": { + "Gemfile": "bbaaccead6becc002a74caafda1babe8", + "LICENSE": "b1b1222427797ff2583a958e2f32e9ef", + "Modulefile": "464335b5a25bbf4378d9d04569342b5c", + "README.md": "48525ed0bbaf2620e08cfdb38b8dbbd5", + "Rakefile": "d884be2a35803faeeead87cb61a64647", + "manifests/config.pp": "61ea33cec8d30b5efa320a3e453f7419", + "manifests/init.pp": "fcc1858f8078d12760656cde6f7c1aae", + "manifests/install.pp": "52781d5febae418d77f1fe10acf5cb08", + "manifests/params.pp": "50801b9db7a6eaeb80e90248228f0bed", + "manifests/uninstall.pp": "0f044aefcbf3e97b8eb88feed710433a", + "spec/classes/kibana3_init_spec.rb": "39204446eacdcf9d3f94908f1f857592", + "spec/spec_helper.rb": "3ea886dd135e120afa31e0aab12e85b0", + "templates/config.js.erb": "74fd6df7467defc5fe2493ee4135a0b2", + "tests/init.pp": "5a0f09e3fbda7c532e0b763733bc0094" + } +} diff --git a/kibana3/spec/acceptance/class_spec.rb b/kibana3/spec/acceptance/class_spec.rb new file mode 100644 index 000000000..cb5e8584c --- /dev/null +++ b/kibana3/spec/acceptance/class_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper_acceptance' + +describe 'thejandroman-kibana3 class' do + + context 'default parameters' do + # Using puppet_apply as a helper + it 'should work with no errors' do + pp = <<-EOS + class { 'kibana3': } + EOS + + # Run it twice and test for idempotency + expect(apply_manifest(pp).exit_code).to_not eq(1) + expect(apply_manifest(pp).exit_code).to eq(0) + end + + describe package('thejandroman-kibana3') do + it { should be_installed } + end + + describe service('thejandroman-kibana3') do + it { should be_enabled } + it { should be_running } + end + end +end diff --git a/kibana3/spec/acceptance/nodesets/centos-64-x64.yml b/kibana3/spec/acceptance/nodesets/centos-64-x64.yml new file mode 100644 index 000000000..d19aa6951 --- /dev/null +++ b/kibana3/spec/acceptance/nodesets/centos-64-x64.yml @@ -0,0 +1,11 @@ +HOSTS: + centos-64-x64: + roles: + - master + platform: el-6-x86_64 + box : centos-64-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + log_level: verbose + type: foss diff --git a/kibana3/spec/acceptance/nodesets/default.yml b/kibana3/spec/acceptance/nodesets/default.yml new file mode 100644 index 000000000..b392dab75 --- /dev/null +++ b/kibana3/spec/acceptance/nodesets/default.yml @@ -0,0 +1,12 @@ +HOSTS: + ubuntu-server-12042-x64: + roles: + - master + platform: ubuntu-server-12.04-amd64 + box: ubuntu-server-12042-x64-vbox4210-nocm + box_url: http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-12042-x64-vbox4210-nocm.box + hypervisor: vagrant + +CONFIG: + log_level: verbose + type: foss diff --git a/kibana3/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml b/kibana3/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml new file mode 100644 index 000000000..b392dab75 --- /dev/null +++ b/kibana3/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml @@ -0,0 +1,12 @@ +HOSTS: + ubuntu-server-12042-x64: + roles: + - master + platform: ubuntu-server-12.04-amd64 + box: ubuntu-server-12042-x64-vbox4210-nocm + box_url: http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-12042-x64-vbox4210-nocm.box + hypervisor: vagrant + +CONFIG: + log_level: verbose + type: foss diff --git a/kibana3/spec/classes/config_spec.rb b/kibana3/spec/classes/config_spec.rb new file mode 100644 index 000000000..838fd033c --- /dev/null +++ b/kibana3/spec/classes/config_spec.rb @@ -0,0 +1,136 @@ +require 'spec_helper' + +describe 'kibana3', :type => :class do + ['Debian', 'RedHat'].each do |system| + let :facts do + { + :concat_basedir => '/dne', + :osfamily => system, + :operatingsystemrelease => '12.04', + } + end + + describe "config on #{system}" do + context 'with defaults' do + it { should compile } + it { should contain_file('/opt/kibana3/src/config.js') \ + .with_content(/^\s*elasticsearch: \"http:\/\/\"\+window\.location\.hostname\+\":9200\",$/) \ + .with_content(/^\s*default_route: '\/dashboard\/file\/default\.json',$/) \ + .with_content(/^\s*kibana_index: \"kibana-int\",$/) \ + .with_content(/^\s*panel_names: \[\s*'histogram',\s*'map',\s*'goal',\s*'table',\s*'filtering',\s*'timepicker',\s*'text',\s*'hits',\s*'column',\s*'trends',\s*'bettermap',\s*'query',\s*'terms',\s*'stats',\s*'sparklines',\s*\]$/) \ + .that_notifies('Class[Apache::Service]') + } + end + + context 'without webserver' do + let (:params) {{ :manage_ws => false }} + it { should compile } + it { should contain_file('/opt/kibana3/src/config.js') \ + .with_owner('root') \ + .with_content(/^\s*elasticsearch: \"http:\/\/\"\+window\.location\.hostname\+\":9200\",$/) \ + .with_content(/^\s*default_route: '\/dashboard\/file\/default\.json',$/) \ + .with_content(/^\s*kibana_index: \"kibana-int\",$/) \ + .with_content(/^\s*panel_names: \[\s*'histogram',\s*'map',\s*'goal',\s*'table',\s*'filtering',\s*'timepicker',\s*'text',\s*'hits',\s*'column',\s*'trends',\s*'bettermap',\s*'query',\s*'terms',\s*'stats',\s*'sparklines',\s*\]$/) + } + end + + context 'with non-standard install folder' do + let (:params) {{ :k3_install_folder => '/tmp/kibana3' }} + it { should compile } + it { should contain_file('/tmp/kibana3/src/config.js') \ + .with_content(/^\s*elasticsearch: \"http:\/\/\"\+window\.location\.hostname\+\":9200\",$/) \ + .with_content(/^\s*default_route: '\/dashboard\/file\/default\.json',$/) \ + .with_content(/^\s*kibana_index: \"kibana-int\",$/) \ + .with_content(/^\s*panel_names: \[\s*'histogram',\s*'map',\s*'goal',\s*'table',\s*'filtering',\s*'timepicker',\s*'text',\s*'hits',\s*'column',\s*'trends',\s*'bettermap',\s*'query',\s*'terms',\s*'stats',\s*'sparklines',\s*\]$/) \ + .that_notifies('Class[Apache::Service]') + } + end + + context 'with folder owner' do + let (:params) {{ :k3_folder_owner => 'foo' }} + it { should compile } + it { should contain_file('/opt/kibana3/src/config.js') \ + .with_owner('foo') \ + .with_content(/^\s*elasticsearch: \"http:\/\/\"\+window\.location\.hostname\+\":9200\",$/) \ + .with_content(/^\s*default_route: '\/dashboard\/file\/default\.json',$/) \ + .with_content(/^\s*kibana_index: \"kibana-int\",$/) \ + .with_content(/^\s*panel_names: \[\s*'histogram',\s*'map',\s*'goal',\s*'table',\s*'filtering',\s*'timepicker',\s*'text',\s*'hits',\s*'column',\s*'trends',\s*'bettermap',\s*'query',\s*'terms',\s*'stats',\s*'sparklines',\s*\]$/) \ + .that_notifies('Class[Apache::Service]') + } + end + + context 'with non-standard default route' do + let (:params) {{ :config_default_route => '/dashboard/file/foo.json' }} + it { should compile } + it { should contain_file('/opt/kibana3/src/config.js') \ + .with_content(/^\s*elasticsearch: \"http:\/\/\"\+window\.location\.hostname\+\":9200\",$/) \ + .with_content(/^\s*default_route: '\/dashboard\/file\/foo\.json',$/) \ + .with_content(/^\s*kibana_index: \"kibana-int\",$/) \ + .with_content(/^\s*panel_names: \[\s*'histogram',\s*'map',\s*'goal',\s*'table',\s*'filtering',\s*'timepicker',\s*'text',\s*'hits',\s*'column',\s*'trends',\s*'bettermap',\s*'query',\s*'terms',\s*'stats',\s*'sparklines',\s*\]$/) \ + .that_notifies('Class[Apache::Service]') + } + end + + context 'with non-standard elasticsearch port' do + let (:params) {{ :config_es_port => '8081' }} + it { should compile } + it { should contain_file('/opt/kibana3/src/config.js') \ + .with_content(/^\s*elasticsearch: \"http:\/\/\"\+window\.location\.hostname\+\":8081\",$/) \ + .with_content(/^\s*default_route: '\/dashboard\/file\/default\.json',$/) \ + .with_content(/^\s*kibana_index: \"kibana-int\",$/) \ + .with_content(/^\s*panel_names: \[\s*'histogram',\s*'map',\s*'goal',\s*'table',\s*'filtering',\s*'timepicker',\s*'text',\s*'hits',\s*'column',\s*'trends',\s*'bettermap',\s*'query',\s*'terms',\s*'stats',\s*'sparklines',\s*\]$/) \ + .that_notifies('Class[Apache::Service]') + } + end + + context 'with non-standard elasticsearch protocol' do + let (:params) {{ :config_es_protocol => 'https' }} + it { should compile } + it { should contain_file('/opt/kibana3/src/config.js') \ + .with_content(/^\s*elasticsearch: \"https:\/\/\"\+window\.location\.hostname\+\":9200\",$/) \ + .with_content(/^\s*default_route: '\/dashboard\/file\/default\.json',$/) \ + .with_content(/^\s*kibana_index: \"kibana-int\",$/) \ + .with_content(/^\s*panel_names: \[\s*'histogram',\s*'map',\s*'goal',\s*'table',\s*'filtering',\s*'timepicker',\s*'text',\s*'hits',\s*'column',\s*'trends',\s*'bettermap',\s*'query',\s*'terms',\s*'stats',\s*'sparklines',\s*\]$/) \ + .that_notifies('Class[Apache::Service]') + } + end + + context 'with non-standard elasticsearch server' do + let (:params) {{ :config_es_server => 'localhost' }} + it { should compile } + it { should contain_file('/opt/kibana3/src/config.js') \ + .with_content(/^\s*elasticsearch: \"http:\/\/\localhost:9200\",$/) \ + .with_content(/^\s*default_route: '\/dashboard\/file\/default\.json',$/) \ + .with_content(/^\s*kibana_index: \"kibana-int\",$/) \ + .with_content(/^\s*panel_names: \[\s*'histogram',\s*'map',\s*'goal',\s*'table',\s*'filtering',\s*'timepicker',\s*'text',\s*'hits',\s*'column',\s*'trends',\s*'bettermap',\s*'query',\s*'terms',\s*'stats',\s*'sparklines',\s*\]$/) \ + .that_notifies('Class[Apache::Service]') + } + end + + context 'with non-standard kibana index' do + let (:params) {{ :config_kibana_index => 'kibana-index' }} + it { should compile } + it { should contain_file('/opt/kibana3/src/config.js') \ + .with_content(/^\s*elasticsearch: \"http:\/\/\"\+window\.location\.hostname\+\":9200\",$/) \ + .with_content(/^\s*default_route: '\/dashboard\/file\/default\.json',$/) \ + .with_content(/^\s*kibana_index: \"kibana-index\",$/) \ + .with_content(/^\s*panel_names: \[\s*'histogram',\s*'map',\s*'goal',\s*'table',\s*'filtering',\s*'timepicker',\s*'text',\s*'hits',\s*'column',\s*'trends',\s*'bettermap',\s*'query',\s*'terms',\s*'stats',\s*'sparklines',\s*\]$/) \ + .that_notifies('Class[Apache::Service]') + } + end + + context 'with non-standard panel names' do + let (:params) {{ :config_panel_names => ['test1','test2'] }} + it { should compile } + it { should contain_file('/opt/kibana3/src/config.js') \ + .with_content(/^\s*elasticsearch: \"http:\/\/\"\+window\.location\.hostname\+\":9200\",$/) \ + .with_content(/^\s*default_route: '\/dashboard\/file\/default\.json',$/) \ + .with_content(/^\s*kibana_index: \"kibana-int\",$/) \ + .with_content(/^\s*panel_names: \[\s*'test1',\s*'test2',\s*\]$/) \ + .that_notifies('Class[Apache::Service]') + } + end + end + + end +end diff --git a/kibana3/spec/classes/coverage_spec.rb b/kibana3/spec/classes/coverage_spec.rb new file mode 100644 index 000000000..12513b83c --- /dev/null +++ b/kibana3/spec/classes/coverage_spec.rb @@ -0,0 +1 @@ +at_exit { RSpec::Puppet::Coverage.report! } diff --git a/kibana3/spec/classes/init_spec.rb b/kibana3/spec/classes/init_spec.rb new file mode 100644 index 000000000..cfc747f79 --- /dev/null +++ b/kibana3/spec/classes/init_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' + +describe 'kibana3', :type => :class do + ['Debian', 'RedHat'].each do |system| + let :facts do + { + :concat_basedir => '/dne', + :osfamily => system, + :operatingsystemrelease => '12.04', + } + end + + describe "init on #{system}" do + context 'with defaults' do + it { should compile } + it { should contain_class 'kibana3::params' } + it { should contain_anchor('kibana3::begin') \ + .that_comes_before('Class[kibana3::install]') + } + it { should contain_class('kibana3::install') \ + .that_comes_before('Class[kibana3::config]') + } + it { should contain_class('kibana3::config') \ + .that_comes_before('Anchor[kibana3::end]') + } + it { should contain_anchor 'kibana3::end'} + it { should_not contain_class 'kibana3::uninstall' } + end + + context 'with ensure absent' do + let (:params) {{ :ensure => 'absent' }} + it { should compile } + it { should contain_class 'kibana3::params' } + it { should_not contain_anchor 'kibana3::begin' } + it { should_not contain_class 'kibana3::install' } + it { should_not contain_class 'kibana3::config' } + it { should_not contain_anchor 'kibana3::end' } + it { should contain_class 'kibana3::uninstall' } + end + + context 'with unsupported ensure' do + let (:params) {{ :ensure => 'foobar' }} + it do + expect { + should compile + }.to raise_error + end + end + end + + end +end diff --git a/kibana3/spec/classes/install_spec.rb b/kibana3/spec/classes/install_spec.rb new file mode 100644 index 000000000..a3c7cb71c --- /dev/null +++ b/kibana3/spec/classes/install_spec.rb @@ -0,0 +1,128 @@ +require 'spec_helper' + +describe 'kibana3', :type => :class do + ['Debian', 'RedHat'].each do |system| + let :facts do + { + :concat_basedir => '/dne', + :osfamily => system, + :operatingsystemrelease => '12.04', + } + end + + describe "install on #{system}" do + context 'with defaults' do + it { should compile } + it { should contain_class('git') } + it { should contain_class('apache') } + it { should contain_vcsrepo('/opt/kibana3') \ + .with_revision('a50a913') \ + .that_notifies('Class[Apache::Service]') \ + .that_comes_before('Apache::Vhost[kibana3]') } + it { should contain_apache__vhost('kibana3') \ + .with( + 'port' => '80', + 'docroot' => '/opt/kibana3/src' + ) } + end + + context 'without git' do + let (:params) {{ :manage_git => false }} + it { should compile } + it { should_not contain_class('git') } + it { should contain_class('apache') } + it { should contain_vcsrepo('/opt/kibana3') \ + .with_revision('a50a913') \ + .that_notifies('Class[Apache::Service]') \ + .that_comes_before('Apache::Vhost[kibana3]') } + it { should contain_apache__vhost('kibana3') \ + .with( + 'port' => '80', + 'docroot' => '/opt/kibana3/src' + ) } + end + + context 'without webserver' do + let (:params) {{ :manage_ws => false }} + it { should compile } + it { should contain_class('git') } + it { should_not contain_class('apache') } + it { should contain_vcsrepo('/opt/kibana3') \ + .with( + 'revision' => 'a50a913', + 'owner' => 'root' + ) } + it { should_not contain_apache__vhost('kibana3') } + end + + context 'with folder owner' do + let (:params) {{ :k3_folder_owner => 'foo' }} + it { should compile } + it { should contain_class('git') } + it { should contain_class('apache') } + it { should contain_vcsrepo('/opt/kibana3') \ + .with( + 'revision' => 'a50a913', + 'owner' => 'foo' + ) \ + .that_notifies('Class[Apache::Service]') \ + .that_comes_before('Apache::Vhost[kibana3]') } + it { should contain_apache__vhost('kibana3') \ + .with( + 'port' => '80', + 'docroot' => '/opt/kibana3/src', + 'docroot_owner' => 'foo' + ) } + end + + context 'with non-standard install folder' do + let (:params) {{ :k3_install_folder => '/tmp/kibana3' }} + it { should compile } + it { should contain_class('git') } + it { should contain_class('apache') } + it { should contain_vcsrepo('/tmp/kibana3') \ + .with_revision('a50a913') \ + .that_notifies('Class[Apache::Service]') \ + .that_comes_before('Apache::Vhost[kibana3]') } + it { should contain_apache__vhost('kibana3') \ + .with( + 'port' => '80', + 'docroot' => '/tmp/kibana3/src' + ) } + end + + context 'with non-standard release' do + let (:params) {{ :k3_release => '3a485aa' }} + it { should compile } + it { should contain_class('git') } + it { should contain_class('apache') } + it { should contain_vcsrepo('/opt/kibana3') \ + .with_revision('3a485aa') \ + .that_notifies('Class[Apache::Service]') \ + .that_comes_before('Apache::Vhost[kibana3]') } + it { should contain_apache__vhost('kibana3') \ + .with( + 'port' => '80', + 'docroot' => '/opt/kibana3/src' + ) } + end + + context 'with non-standard port' do + let (:params) {{ :ws_port => '8080' }} + it { should compile } + it { should contain_class('git') } + it { should contain_class('apache') } + it { should contain_vcsrepo('/opt/kibana3') \ + .with_revision('a50a913') \ + .that_notifies('Class[Apache::Service]') \ + .that_comes_before('Apache::Vhost[kibana3]') } + it { should contain_apache__vhost('kibana3') \ + .with( + 'port' => '8080', + 'docroot' => '/opt/kibana3/src' + ) } + end + end + + end +end diff --git a/kibana3/spec/classes/params_spec.rb b/kibana3/spec/classes/params_spec.rb new file mode 100644 index 000000000..b3f757c0e --- /dev/null +++ b/kibana3/spec/classes/params_spec.rb @@ -0,0 +1,8 @@ +require 'spec_helper' + +describe 'kibana3::params', :type => :class do + context 'with defaults' do + it { should compile } + it { should have_resource_count(0) } + end +end diff --git a/kibana3/spec/classes/uninstall_spec.rb b/kibana3/spec/classes/uninstall_spec.rb new file mode 100644 index 000000000..5bb52a397 --- /dev/null +++ b/kibana3/spec/classes/uninstall_spec.rb @@ -0,0 +1,96 @@ +require 'spec_helper' + +describe 'kibana3', :type => :class do + ['Debian', 'RedHat'].each do |system| + let :facts do + { + :concat_basedir => '/dne', + :osfamily => system, + :operatingsystemrelease => '12.04', + } + end + + describe "uninstall on #{system}" do + context 'with defaults' do + let (:params) {{ :ensure => 'absent' }} + it { should compile } + it { should contain_package('git') \ + .with_ensure('absent') + } + it { should contain_class('apache') \ + .with_package_ensure('absent') + } + it { should contain_apache__vhost('kibana3') \ + .with( + 'ensure' => 'absent', + 'docroot' => '/opt/kibana3/src' + ) } + it { should contain_file('/opt/kibana3') \ + .with_ensure('absent') + } + it { should contain_file('/opt/kibana3/src/config.js') \ + .with_ensure('absent') + } + end + + context 'without git' do + let (:params) {{ :ensure => 'absent', :manage_git => false }} + it { should compile } + it { should_not contain_package('git') } + it { should contain_class('apache') \ + .with_package_ensure('absent') + } + it { should contain_apache__vhost('kibana3') \ + .with( + 'ensure' => 'absent', + 'docroot' => '/opt/kibana3/src' + ) } + it { should contain_file('/opt/kibana3') \ + .with_ensure('absent') + } + it { should contain_file('/opt/kibana3/src/config.js') \ + .with_ensure('absent') + } + end + + context 'without webserver' do + let (:params) {{ :ensure => 'absent', :manage_ws => false }} + it { should compile } + it { should contain_package('git') \ + .with_ensure('absent') + } + it { should_not contain_class('apache') } + it { should_not contain_apache__vhost('kibana3') } + it { should contain_file('/opt/kibana3') \ + .with_ensure('absent') + } + it { should contain_file('/opt/kibana3/src/config.js') \ + .with_ensure('absent') + } + end + + context 'with non-standard install folder' do + let (:params) {{ :ensure => 'absent', :k3_install_folder => '/tmp/kibana3' }} + it { should compile } + it { should contain_package('git') \ + .with_ensure('absent') + } + it { should contain_class('apache') \ + .with_package_ensure('absent') + } + it { should contain_apache__vhost('kibana3') \ + .with( + 'ensure' => 'absent', + 'docroot' => '/tmp/kibana3/src' + ) } + it { should contain_file('/tmp/kibana3') \ + .with_ensure('absent') + } + it { should contain_file('/tmp/kibana3/src/config.js') \ + .with_ensure('absent') + } + end + end + + end +end diff --git a/kibana3/spec/spec_helper.rb b/kibana3/spec/spec_helper.rb new file mode 100644 index 000000000..dc7e9f4a0 --- /dev/null +++ b/kibana3/spec/spec_helper.rb @@ -0,0 +1,2 @@ +require 'rubygems' +require 'puppetlabs_spec_helper/module_spec_helper' diff --git a/kibana3/spec/spec_helper_acceptance.rb b/kibana3/spec/spec_helper_acceptance.rb new file mode 100644 index 000000000..614d4a675 --- /dev/null +++ b/kibana3/spec/spec_helper_acceptance.rb @@ -0,0 +1,24 @@ +require 'beaker-rspec/spec_helper' +require 'beaker-rspec/helpers/serverspec' + +hosts.each do |host| + # Install Puppet + install_puppet +end + +RSpec.configure do |c| + # Project root + proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) + + # Readable test descriptions + c.formatter = :documentation + + # Configure all nodes in nodeset + c.before :suite do + # Install module and dependencies + puppet_module_install(:source => proj_root, :module_name => 'thejandroman-kibana3') + hosts.each do |host| + on host, puppet('module', 'install', 'puppetlabs-stdlib'), { :acceptable_exit_codes => [0,1] } + end + end +end diff --git a/kibana3/templates/config.js.erb b/kibana3/templates/config.js.erb new file mode 100644 index 000000000..56b92717b --- /dev/null +++ b/kibana3/templates/config.js.erb @@ -0,0 +1,23 @@ +/** Managed by Puppet +Do not modify by hand +**/ + +define(['settings'], +function (Settings) { + "use strict"; + + return new Settings({ + + elasticsearch: "<%= @config_es_protocol %>://<%= @config_es_server %>:<%= @config_es_port %>", + + default_route: '<%= @config_default_route %>', + + kibana_index: "<%= @config_kibana_index %>", + + panel_names: [ + <%- @config_panel_names.each do |cpn| -%> + '<%= cpn %>', + <%- end -%> + ] + }); +}); diff --git a/kibana3/tests/init.pp b/kibana3/tests/init.pp new file mode 100644 index 000000000..32ca78bfa --- /dev/null +++ b/kibana3/tests/init.pp @@ -0,0 +1,12 @@ +# The baseline for module testing used by Puppet Labs is that each manifest +# should have a corresponding test manifest that declares that class or defined +# type. +# +# Tests are then run by using puppet apply --noop (to check for compilation +# errors and view a log of events) or by fully applying the test in a virtual +# environment (to compare the resulting system state to the desired state). +# +# Learn more about module testing here: +# http://docs.puppetlabs.com/guides/tests_smoke.html +# +include kibana3 diff --git a/kibana3/vagrant/Puppetfile b/kibana3/vagrant/Puppetfile new file mode 100644 index 000000000..4550badf7 --- /dev/null +++ b/kibana3/vagrant/Puppetfile @@ -0,0 +1,10 @@ +forge 'http://forge.puppetlabs.com' + +mod 'puppetlabs/git', '0.0.3' + +mod 'puppetlabs/vcsrepo', '0.2.0' + +mod 'puppetlabs/apache', '1.0.1' +mod 'puppetlabs/concat', '1.0.2' + +mod 'puppetlabs/stdlib', '3.2.1' diff --git a/kibana3/vagrant/Vagrantfile b/kibana3/vagrant/Vagrantfile new file mode 100644 index 000000000..555af0a48 --- /dev/null +++ b/kibana3/vagrant/Vagrantfile @@ -0,0 +1,30 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +VAGRANTFILE_API_VERSION = '2' + +Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| + # CentOS 6.5 + #config.vm.box = 'centos6.5puppet' + #config.vm.box_url = 'http://puppet-vagrant-boxes.puppetlabs.com/centos-65-x64-virtualbox-puppet.box' + + # Ubuntu 13.10 + config.vm.box = 'ubuntu12.04.2puppet' + config.vm.box_url = 'http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-12042-x64-vbox4210.box' + + config.vm.hostname = 'kibana3.thejandroman.local' + + # Configure RAM and CPU(s) + config.vm.provider :virtualbox do |vb| + vb.customize ['modifyvm', :id, '--memory', '512'] + vb.customize ['modifyvm', :id, '--cpus', 1] + vb.customize ['setextradata', :id, 'VBoxInternal2/SharedFoldersEnableSymlinksCreate/v-root', '1'] + end + + config.vm.provision :shell, :path => 'default.sh' + + config.vm.provision :puppet do |puppet| + puppet.module_path = '../..' + #puppet.options = '--verbose --debug' + end +end diff --git a/kibana3/vagrant/default.sh b/kibana3/vagrant/default.sh new file mode 100755 index 000000000..b521a6e95 --- /dev/null +++ b/kibana3/vagrant/default.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# Based on the great work by purple52 +# https://github.com/purple52/librarian-puppet-vagrant + +PUPPET_DIR=/etc/puppet/ +OPERATING_SYSTEM=$(facter operatingsystem) + +redhat () { + if ! rpm -qa git &> /dev/null; then + yum -qy makecache + yum -qy install git + fi +} + +debian () { + if ! dpkg -s git &> /dev/null || + ! dpkg -s ruby-dev &> /dev/null; then + apt-get -qq update + apt-get -qq install git ruby-dev + fi +} + +case $OPERATING_SYSTEM in + 'RedHat' | 'CentOS' ) + redhat + ;; + 'Debian' | 'Ubuntu' ) + debian + ;; +esac + +if [ ! -d "$PUPPET_DIR" ]; then + mkdir -p $PUPPET_DIR +fi +cp /vagrant/Puppetfile $PUPPET_DIR + +if [ "$(gem search -i r10k)" = "false" ]; then + gem install -q r10k --no-ri --no-rdoc +fi +cd $PUPPET_DIR && r10k puppetfile install #--verbose diff --git a/kibana3/vagrant/manifests/default.pp b/kibana3/vagrant/manifests/default.pp new file mode 100644 index 000000000..bbcfec1c7 --- /dev/null +++ b/kibana3/vagrant/manifests/default.pp @@ -0,0 +1 @@ +include kibana3 diff --git a/kmod/.fixtures.yml b/kmod/.fixtures.yml new file mode 100644 index 000000000..f172a1973 --- /dev/null +++ b/kmod/.fixtures.yml @@ -0,0 +1,5 @@ +fixtures: + repositories: + "stdlib": "git://github.com/puppetlabs/puppetlabs-stdlib.git" + symlinks: + "kmod": "#{source_dir}" diff --git a/kmod/.travis.yml b/kmod/.travis.yml new file mode 100644 index 000000000..6d7c0f849 --- /dev/null +++ b/kmod/.travis.yml @@ -0,0 +1,15 @@ +language: ruby +rvm: + - 1.8.7 + - 1.9.3 + - 2.0.0 + - ruby-head +env: + - PUPPET_GEM_VERSION=">= 3.0.0" +matrix: + allow_failures: + - rvm: 2.0.0 + - rvm: ruby-head + include: + - rvm: 1.8.7 + env: PUPPET_GEM_VERSION="~> 2.7" diff --git a/kmod/Gemfile b/kmod/Gemfile new file mode 100644 index 000000000..a500d9ee2 --- /dev/null +++ b/kmod/Gemfile @@ -0,0 +1,13 @@ +source :rubygems + +if ENV.key?('PUPPET_VERSION') + puppetversion = "= #{ENV['PUPPET_VERSION']}" +else + puppetversion = ['>= 2.7'] +end + +gem 'rake' +gem 'puppet-lint' +gem 'rspec-puppet' +gem 'puppet', puppetversion +gem 'puppetlabs_spec_helper', '>= 0.4.0' diff --git a/kmod/LICENSE b/kmod/LICENSE new file mode 100644 index 000000000..94a9ed024 --- /dev/null +++ b/kmod/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/kmod/Modulefile b/kmod/Modulefile new file mode 100644 index 000000000..060522173 --- /dev/null +++ b/kmod/Modulefile @@ -0,0 +1,6 @@ +name 'camptocamp-kmod' +version '0.0.1' +license 'Apache License, Version 2.0' +project_page 'git://github.com/camptocamp/puppet-kmod.git' +description 'Manage Linux kernel modules with Puppet' +summary 'Manage Linux kernel modules with Puppet' diff --git a/kmod/README.md b/kmod/README.md new file mode 100644 index 000000000..014be4764 --- /dev/null +++ b/kmod/README.md @@ -0,0 +1,47 @@ +# Kmod Puppet module + +## Usage + +See inline doc inside: + + * kmod::load + * kmod::alias + * kmod::install + * kmod::blacklist + +## Description + +This module provides definitions to manipulate modprobe.conf (5) stanzas: + + * kmod::alias + * kmod::install + * kmod::blacklist + +It depends on Augeas with the modprobe lens. + +## Contributing + +Please report bugs and feature request using [GitHub issue +tracker](https://github.com/camptocamp/puppet-kmod/issues). + +For pull requests, it is very much appreciated to check your Puppet manifest +with [puppet-lint](https://github.com/camptocamp/puppet-kmod/issues) to follow the recommended Puppet style guidelines from the +[Puppet Labs style guide](http://docs.puppetlabs.com/guides/style_guide.html). + +## License + +Copyright (c) 2013 All rights reserved. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + diff --git a/kmod/Rakefile b/kmod/Rakefile new file mode 100644 index 000000000..cd3d37995 --- /dev/null +++ b/kmod/Rakefile @@ -0,0 +1 @@ +require 'puppetlabs_spec_helper/rake_tasks' diff --git a/kmod/manifests/alias.pp b/kmod/manifests/alias.pp new file mode 100644 index 000000000..ab39bda08 --- /dev/null +++ b/kmod/manifests/alias.pp @@ -0,0 +1,51 @@ +# = Define: kmod::alias +# +# == Example +# +# kmod::alias { 'bond0': +# alias => 'bonding', +# } +# +define kmod::alias( + $source, + $ensure = 'present', + $file = '/etc/modprobe.d/aliases.conf', +) { + + include kmod + + case $ensure { + present: { + $augset = [ + "set alias[. = '${name}'] ${name}", + "set alias[. = '${name}']/modulename ${source}", + ] + $onlyif = "match alias[. = '${name}'] size == 0" + + + augeas { "modprobe alias ${name} ${source}": + incl => $file, + lens => 'Modprobe.lns', + changes => $augset, + onlyif => $onlyif, + require => File[$file], + } + } + + absent: { + kmod::load { $name: + ensure => $ensure, + } + + augeas {"remove modprobe alias ${name}": + incl => $file, + lens => 'Modprobe.lns', + changes => "rm alias[. = '${name}']", + onlyif => "match alias[. = '${name}'] size > 0", + require => File[$file], + } + } + + default: { err ( "unknown ensure value ${ensure}" ) } + } +} diff --git a/kmod/manifests/blacklist.pp b/kmod/manifests/blacklist.pp new file mode 100644 index 000000000..9e7d3d7ac --- /dev/null +++ b/kmod/manifests/blacklist.pp @@ -0,0 +1,27 @@ +/* + +== Definition: kmod::blacklist + +Set a kernel module as blacklisted. + +Parameters: +- *ensure*: present/absent; +- *file*: optionally, set the file where the stanza is written. + +Example usage: + + kmod::blacklist { 'pcspkr': } + +*/ + +define kmod::blacklist( + $ensure=present, + $file='/etc/modprobe.d/blacklist.conf' +) { + kmod::generic {"blacklist ${name}": + ensure => $ensure, + type => 'blacklist', + module => $name, + file => $file, + } +} diff --git a/kmod/manifests/generic.pp b/kmod/manifests/generic.pp new file mode 100644 index 000000000..1bb4d4889 --- /dev/null +++ b/kmod/manifests/generic.pp @@ -0,0 +1,83 @@ +/* + +== Definition: kmod::generic + +Set a kernel module in modprobe.conf (5). + +Parameters: +- *type*: type of modprobe stanza (install/blacklist/etc); +- *module*: module name; +- *ensure*: present/absent; +- *command*: optionally, set the command associated with the kernel module; +- *file*: optionally, set the file where the stanza is written. + +Example usage: + + kmod::generic {'install pcspkr': + type => 'install', + module => 'pcspkr', + } + +*/ + +define kmod::generic( + $type, + $module, + $ensure=present, + $command='', + $file +) { + + include kmod + + case $ensure { + present: { + if $type == 'install' { + kmod::load { $module: + ensure => $ensure, + require => Augeas["${type} module ${module}"], + } + } + + if $command { + # modprobe.conf usage changes in 0.10.0 + if versioncmp($::augeasversion, '0.9.0') < 0 { + $augset = "set ${type}[. = '${module}'] '${module} ${command}'" + $onlyif = "match ${type}[. = '${module} ${command}'] size == 0" + } else { + $augset = [ + "set ${type}[. = '${module}'] ${module}", + "set ${type}[. = '${module}']/command '${command}'", + ] + $onlyif = "match ${type}[. = '${module}'] size == 0" + } + } else { + $augset = "set ${type}[. = '${module}'] ${module}" + } + + augeas {"${type} module ${module}": + incl => $file, + lens => 'Modprobe.lns', + changes => $augset, + onlyif => $onlyif, + require => File[$file], + } + } + + absent: { + kmod::load { $module: + ensure => $ensure, + } + + augeas {"remove module ${module}": + incl => $file, + lens => 'Modprobe.lns', + changes => "rm ${type}[. = '${module}']", + onlyif => "match ${type}[. = '${module}'] size > 0", + require => File[$file], + } + } + + default: { err ( "unknown ensure value ${ensure}" ) } + } +} diff --git a/kmod/manifests/init.pp b/kmod/manifests/init.pp new file mode 100644 index 000000000..6fc22b3e8 --- /dev/null +++ b/kmod/manifests/init.pp @@ -0,0 +1,18 @@ +# +# == Class: kmod +# +# Ensures a couple of mandatory files are present before managing their +# content. +# +# +class kmod { + + file { '/etc/modprobe.d': ensure => directory } + + file { [ + '/etc/modprobe.d/modprobe.conf', + '/etc/modprobe.d/aliases.conf', + '/etc/modprobe.d/blacklist.conf', + ]: ensure => present, + } +} diff --git a/kmod/manifests/install.pp b/kmod/manifests/install.pp new file mode 100644 index 000000000..14652d35f --- /dev/null +++ b/kmod/manifests/install.pp @@ -0,0 +1,30 @@ +/* + +== Definition: kmod::install + +Set a kernel module as installed. + +Parameters: +- *ensure*: present/absent; +- *command*: optionally, set the command associated with the kernel module; +- *file*: optionally, set the file where the stanza is written. + +Example usage: + + kmod::install { 'pcspkr': } + +*/ + +define kmod::install( + $ensure=present, + $command='/bin/true', + $file='/etc/modprobe.d/modprobe.conf' +) { + kmod::generic {"install ${name}": + ensure => $ensure, + type => 'install', + module => $name, + command => $command, + file => $file, + } +} diff --git a/kmod/manifests/load.pp b/kmod/manifests/load.pp new file mode 100644 index 000000000..20943f0ac --- /dev/null +++ b/kmod/manifests/load.pp @@ -0,0 +1,58 @@ +/* + +== Definition: kmod::load + +Manage a kernel module in /etc/modules. + +Parameters: +- *ensure*: present/absent; +- *file*: optionally, set the file where the stanza is written. + +Example usage: + + kmod::load { 'sha256': } + +*/ + +define kmod::load( + $ensure=present, + $file='/etc/modules' +) { + + case $ensure { + present: { + $changes = "clear '${name}'" + + exec { "modprobe ${name}": + unless => "egrep -q '^${name} ' /proc/modules", + } + } + + absent: { + $changes = "rm '${name}'" + + exec { "modprobe -r ${name}": + onlyif => "egrep -q '^${name} ' /proc/modules", + } + } + + default: { err ( "unknown ensure value ${ensure}" ) } + } + + case $::osfamily { + 'Debian': { + augeas {"Manage ${name} in ${file}": + incl => $file, + lens => 'Modules.lns', + changes => $changes, + } + } + 'RedHat': { + file { "/etc/sysconfig/modules/${name}.modules": + ensure => $ensure, + mode => 0755, + content => template('kmod/redhat.modprobe.erb'), + } + } + } +} diff --git a/kmod/spec/classes/kmod_spec.rb b/kmod/spec/classes/kmod_spec.rb new file mode 100644 index 000000000..8487a5c10 --- /dev/null +++ b/kmod/spec/classes/kmod_spec.rb @@ -0,0 +1,9 @@ +require 'spec_helper' + +describe 'kmod', :type => :class do + it { should contain_class('kmod') } + it { should contain_file('/etc/modprobe.d').with({ 'ensure' => 'directory' }) } + ['modprobe.conf','aliases.conf','blacklist.conf'].each do |file| + it { should contain_file("/etc/modprobe.d/#{file}").with({ 'ensure' => 'present' }) } + end +end diff --git a/kmod/spec/defines/kmod_alias_spec.rb b/kmod/spec/defines/kmod_alias_spec.rb new file mode 100644 index 000000000..f6cfda048 --- /dev/null +++ b/kmod/spec/defines/kmod_alias_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe 'kmod::alias', :type => :define do + let(:title) { 'foo' } + let(:default_params) do { :source => 'bar', :file => '/baz' } end + + context 'when ensure is set to present' do + let(:params) do default_params end + it { should include_class('kmod') } + it { should contain_kmod__alias('foo') } + it { should contain_augeas('modprobe alias foo bar').with({ + 'incl' => '/baz', + 'lens' => 'Modprobe.lns', + 'changes' => [ "set alias[. = 'foo'] foo","set alias[. = 'foo']/modulename bar" ], + 'onlyif' => "match alias[. = 'foo'] size == 0", + 'require' => 'File[/baz]' + }) } + end + + context 'when ensure is set to absent' do + let(:params) do + default_params.merge!({ :ensure => 'absent' }) + end + it { should include_class('kmod') } + it { should contain_kmod__alias('foo') } + it { should contain_kmod__load('foo').with({ 'ensure' => 'absent' }) } + it { should contain_augeas('remove modprobe alias foo').with({ + 'incl' => '/baz', + 'lens' => 'Modprobe.lns', + 'changes' => "rm alias[. = 'foo']", + 'onlyif' => "match alias[. = 'foo'] size > 0", + 'require' => 'File[/baz]' + }) } + end +end diff --git a/kmod/spec/defines/kmod_blacklist_spec.rb b/kmod/spec/defines/kmod_blacklist_spec.rb new file mode 100644 index 000000000..b4f889acc --- /dev/null +++ b/kmod/spec/defines/kmod_blacklist_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe 'kmod::blacklist', :type => :define do + let(:title) { 'foo' } + context 'when ensure is set to present' do + let(:params) do { :ensure => 'present', :file => '/bar/baz' } end + it { should contain_kmod__blacklist('foo') } + it { should contain_kmod__generic('blacklist foo').with({ + 'ensure' => 'present', + 'type' => 'blacklist', + 'module' => 'foo', + 'file' => '/bar/baz' + }) } + end + + context 'when ensure is set to absent' do + let(:params) do { :ensure => 'absent', :file => '/bar/baz' } end + it { should contain_kmod__blacklist('foo') } + it { should contain_kmod__generic('blacklist foo').with({ + 'ensure' => 'absent', + 'type' => 'blacklist', + 'module' => 'foo', + 'file' => '/bar/baz' + }) } + end +end \ No newline at end of file diff --git a/kmod/spec/defines/kmod_generic_spec.rb b/kmod/spec/defines/kmod_generic_spec.rb new file mode 100644 index 000000000..f2fac8344 --- /dev/null +++ b/kmod/spec/defines/kmod_generic_spec.rb @@ -0,0 +1,72 @@ +require 'spec_helper' + +describe 'kmod::generic', :type => :define do + let(:title) { 'install foo' } + let(:default_params) do { :type => 'install', :module => 'foo', :file => 'modprobe.conf' } end + let(:params) do default_params end + it { should include_class('kmod') } + + context 'install foo module' do + let(:params) do default_params end + it { should contain_kmod__generic('install foo') } + + context 'with ensure set to present' do + it { should contain_kmod__load('foo').with({ + 'ensure' => 'present', + 'require' => 'Augeas[install module foo]' + })} + + context 'with command set to /bin/true' do + let(:params) do + default_params.merge!({ :command => '/bin/true' }) + end + + context 'with augeasversion < "0.9.0"' do + let(:facts) do { :augeasversion => '0.8.9' } end + it { should contain_augeas('install module foo').with({ + 'incl' => 'modprobe.conf', + 'lens' => 'Modprobe.lns', + 'changes' => "set install[. = 'foo'] 'foo /bin/true'", + 'onlyif' => "match install[. = 'foo /bin/true'] size == 0", + 'require' => 'File[modprobe.conf]' + }) } + end + + context 'with augeasversion >= "0.9.0"' do + let(:facts) do { :augeasversion => '0.9.0' } end + it { should contain_augeas('install module foo').with({ + 'incl' => 'modprobe.conf', + 'lens' => 'Modprobe.lns', + 'changes' => ["set install[. = 'foo'] foo","set install[. = 'foo']/command '/bin/true'"], + 'onlyif' => "match install[. = 'foo'] size == 0", + 'require' => 'File[modprobe.conf]' + }) } + end + end + + context 'without command set' do + it { should contain_augeas('install module foo').with({ + 'incl' => 'modprobe.conf', + 'lens' => 'Modprobe.lns', + 'changes' => "set install[. = 'foo'] foo", + 'onlyif' => nil, + 'require' => 'File[modprobe.conf]' + })} + end + end + + context "with ensure set to absent" do + let(:params) do + default_params.merge!( :ensure => 'absent' ) + end + it { should contain_kmod__load('foo').with({ 'ensure' => 'absent'}) } + it { should contain_augeas('remove module foo').with({ + 'incl' => 'modprobe.conf', + 'lens' => 'Modprobe.lns', + 'changes' => "rm install[. = 'foo']", + 'onlyif' => "match install[. = 'foo'] size > 0", + 'require' => 'File[modprobe.conf]' + })} + end + end +end \ No newline at end of file diff --git a/kmod/spec/defines/kmod_install_spec.rb b/kmod/spec/defines/kmod_install_spec.rb new file mode 100644 index 000000000..c70d32afb --- /dev/null +++ b/kmod/spec/defines/kmod_install_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe 'kmod::install', :type => :define do + context 'install foo module' do + let(:title) { 'foo' } + let(:params) do { :ensure => 'present', :command => '/bin/true', :file => '/etc/modprobe.d/modprobe.conf' } end + it { should contain_kmod__install('foo') } + it { should contain_kmod__generic('install foo').with({ + 'ensure' => 'present', + 'type' => 'install', + 'module' => 'foo', + 'command' => '/bin/true', + 'file' => '/etc/modprobe.d/modprobe.conf' + }) } + end +end + + diff --git a/kmod/spec/defines/kmod_load_spec.rb b/kmod/spec/defines/kmod_load_spec.rb new file mode 100644 index 000000000..b6215d728 --- /dev/null +++ b/kmod/spec/defines/kmod_load_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' + +describe 'kmod::load', :type => :define do + let(:title) { 'foo' } + context 'with ensure set to present' do + let(:params) do { :ensure => 'present', :file => '/foo/bar' } end + it { should contain_kmod__load('foo') } + it { should contain_exec('modprobe foo').with({'unless' => "egrep -q '^foo ' /proc/modules"}) } + + context 'on Debian derivatives' do + let(:facts) do { :osfamily => 'Debian' } end + it { should contain_augeas('Manage foo in /foo/bar').with({ + 'incl' => '/foo/bar', + 'lens' => 'Modules.lns', + 'changes' => "clear 'foo'" + }) } + end + + context 'on redhat derivatives' do + let(:facts) do { :osfamily => 'RedHat' } end + it { should contain_file('/etc/sysconfig/modules/foo.modules').with({ + 'ensure' => 'present', + 'mode' => '0755', + 'content' => /exec \/sbin\/modprobe foo > \/dev\/null 2>&1/ + })} + end + end + + context 'with ensure set to absent' do + let(:params) do { :ensure => 'absent', :file => '/foo/bar' } end + it { should contain_kmod__load('foo') } + it { should contain_exec('modprobe -r foo').with({ 'onlyif' => "egrep -q '^foo ' /proc/modules" }) } + + context 'on Debian derivatives' do + let(:facts) do { :osfamily => 'Debian' } end + it { should contain_augeas('Manage foo in /foo/bar').with({ + 'incl' => '/foo/bar', + 'lens' => 'Modules.lns', + 'changes' => "rm 'foo'" + })} + end + + context 'on redhat derivatives' do + let(:facts) do { :osfamily => 'RedHat' } end + it { should contain_file('/etc/sysconfig/modules/foo.modules').with({ + 'ensure' => 'absent', + 'mode' => '0755', + 'content' => /exec \/sbin\/modprobe foo > \/dev\/null 2>&1/ + })} + end + end +end \ No newline at end of file diff --git a/kmod/spec/spec_helper.rb b/kmod/spec/spec_helper.rb new file mode 100644 index 000000000..2c6f56649 --- /dev/null +++ b/kmod/spec/spec_helper.rb @@ -0,0 +1 @@ +require 'puppetlabs_spec_helper/module_spec_helper' diff --git a/kmod/templates/redhat.modprobe.erb b/kmod/templates/redhat.modprobe.erb new file mode 100644 index 000000000..2322ef5de --- /dev/null +++ b/kmod/templates/redhat.modprobe.erb @@ -0,0 +1,5 @@ +#!/bin/sh + +# file managed by puppet + +exec /sbin/modprobe <%= name %> > /dev/null 2>&1 diff --git a/kwalify/.gitignore b/kwalify/.gitignore new file mode 100644 index 000000000..7ad8fcada --- /dev/null +++ b/kwalify/.gitignore @@ -0,0 +1,2 @@ +pkg/ +coverage/ diff --git a/kwalify/LICENSE b/kwalify/LICENSE new file mode 100644 index 000000000..b604f4f58 --- /dev/null +++ b/kwalify/LICENSE @@ -0,0 +1,15 @@ +Copyright (C) 2011 Puppet Labs Inc + +Puppet Labs can be contacted at: info@puppetlabs.com + +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. diff --git a/kwalify/Modulefile b/kwalify/Modulefile new file mode 100644 index 000000000..a10abc87e --- /dev/null +++ b/kwalify/Modulefile @@ -0,0 +1,8 @@ +name 'puppetlabs-kwalify' +version '0.0.1' +source 'https://github.com/puppetlabs/puppetlabs-kwalify' +author 'Puppetlabs' +license 'Apache License 2.0' +summary 'A set of kwalify related functions for puppet.' +description '' +project_page 'https://github.com/puppetlabs/puppetlabs-kwalify' diff --git a/kwalify/README.markdown b/kwalify/README.markdown new file mode 100644 index 000000000..0914c756c --- /dev/null +++ b/kwalify/README.markdown @@ -0,0 +1,164 @@ +## puppetlabs-kwalify module + +### Overview + +This is the puppetlabs-kwalify module. + +### Disclaimer + +Warning! While this software is written in the best interest of quality it has +not been formally tested by our QA teams. Use at your own risk, but feel free +to enjoy and perhaps improve it while you do. + +Please see the included Apache Software License for more legal details +regarding warranty. + +### Installation + +From github, download the module into your modulepath on your Puppetmaster. If +you are not sure where your module path is try this command: + + puppet --configprint modulepath + +You will also need the kwalify gem. You can do this using your OS, or using the +gem command: + + gem install kwalify + +Depending on the version of Puppet, you may need to restart the puppetmasterd +(or Apache) process before the functions will work. + +## Functions + +### kwalify + +This function allows you to validate Puppet data structures using Kwalify +schemas as documented here: + +http://www.kuwata-lab.com/kwalify/ruby/users-guide.01.html + +To validate, create a schema in Puppet: + + $schema = { + 'type' => 'seq', + 'sequence' => [ + { 'type' => 'str' } + ] + } + +And create some content that you want validated: + + $document = ['a', 'b', 'c'] + +And then use the function to validate: + + kwalify($schema, $document) + +The function will throw an error and list all validation errors if there is a +problem otherwise it succeeds silently. If we break the document on purpose: + + $document = ['a','b',false] + +We actually get the precise place it is broken: + + Failed kwalify schema validation: + [/2] 'false': not a string. at /Users/ken/tmp/kwalify/kwalify1.pp:10 on node kb.local + +The number here is 2 as an array is zero-indexed in this case. + +### get_scope_args + +This function returns a list of arguments passed to the current scope. This +could be a class or defined resource. This allows you to then use the kwalify +function to validate the input for the class or defined resource. + +For example: + + class my_database ( + $db_name = "accounts", + $size = "100 GB", + $replicate = true + ) { + + $args = get_scope_args() + notice(inline_template("<%= args.inspect %>")) + } + + class { "my_database": + replicate => false, + } + +Running this will return: + + notice: Scope(Class[My_database]): {"replicate"=>false, "size"=>"100 GB", "db_name"=>"accounts"} + +Now to achieve validation you can combine this with kwalify as per the +following: + + class my_database ( + $db_name = "accounts", + $size = "100 GB", + $replicate = true + ) { + + $args = get_scope_args() + + $schema = { + 'type' => 'map', + 'mapping' => { + 'db_name' => { + 'type' => 'str', + 'pattern' => '/^\w+$/', + }, + 'size' => { + 'type' => 'str', + 'pattern' => '/^\d+\s(P|T|G|M|K)B$/', + }, + 'replicate' => { + 'type' => 'bool', + }, + } + } + + kwalify($schema, $args) + + } + + class { "my_database": + replicate => false, + } + +This is obviously an example of a good example so running this content should +give you a good build: + + notice: Finished catalog run in 1.23 seconds + +So now lets pass a bad argument. Lets try a 'replicate' attribute which is +invalid: + + class { "my_database": + replicate => "this is not valid!", + } + +Upon running with this invalid content you should get something like: + + Failed kwalify schema validation: + [/replicate] 'this is not valid!': not a boolean. at /Users/ken/tmp/kwalify/test3.pp:26 on node kb.local + +Setting multiple invalid arguments should result in a report which shows all +bad arguments: + + class { "my_database": + replicate => "this is not valid!", + size => "100 YB", + } + +And now the result is: + + Failed kwalify schema validation: + [/replicate] 'this is not valid!': not a boolean. + [/size] '100 YB': not matched to pattern /^\d+\s(G|M|K)B$/. at /Users/ken/tmp/kwalify/test4.pp:26 on node kb.local + +Which is perfect, as now you don't need to keep fixing items and testing. +Aggregate validation reporting such as this means you get all bad cases in one +go. diff --git a/kwalify/Rakefile b/kwalify/Rakefile new file mode 100644 index 000000000..47d8fc3e1 --- /dev/null +++ b/kwalify/Rakefile @@ -0,0 +1,21 @@ +require 'rake' +require 'rspec/core/rake_task' + +task :default => [:test] + +# Create multiple aliases for typos and rvm +task :test => [:spec] +task :tests => [:spec] +task :specs => [:spec] + +desc 'Run RSpec' +RSpec::Core::RakeTask.new(:spec) do |t| + t.pattern = 'spec/{unit}/**/*.rb' + t.rspec_opts = ['--color'] +end + +desc 'Generate code coverage' +RSpec::Core::RakeTask.new(:coverage) do |t| + t.rcov = true + t.rcov_opts = ['--exclude', 'spec'] +end diff --git a/kwalify/lib/puppet/parser/functions/get_scope_args.rb b/kwalify/lib/puppet/parser/functions/get_scope_args.rb new file mode 100644 index 000000000..c47c38dc5 --- /dev/null +++ b/kwalify/lib/puppet/parser/functions/get_scope_args.rb @@ -0,0 +1,26 @@ +module Puppet::Parser::Functions + newfunction(:get_scope_args, :type => :rvalue, :doc => <<-EOS +This function will return all arguments passed to the current scope. This could +be a class or defined resource. + EOS + ) do |arguments| + + if (arguments.size != 0) then + raise(Puppet::ParseError, "validate_resource(): Wrong number of " + + "arguments given #{arguments.size} for 0") + end + + # Grab the current scope, turn it to a hash but do not be recursive + # about it. + classhash = to_hash(recursive=false) + + # Strip bits that do not matter for validation + classhash.delete("name") + classhash.delete("title") + classhash.delete("caller_module_name") + classhash.delete("module_name") + + # Return munged classhash + classhash + end +end diff --git a/kwalify/lib/puppet/parser/functions/kwalify.rb b/kwalify/lib/puppet/parser/functions/kwalify.rb new file mode 100644 index 000000000..49b9aeb56 --- /dev/null +++ b/kwalify/lib/puppet/parser/functions/kwalify.rb @@ -0,0 +1,35 @@ +# +# kwalify.rb +# + +module Puppet::Parser::Functions + newfunction(:kwalify, :type => :statement, :doc => <<-EOS +This function uses kwalify to validate Puppet data structures against Kwalify +schemas. + EOS + ) do |args| + + raise(Puppet::ParseError, "kwalify(): Wrong number of arguments " + + "given (#{args.size} for 2)") if args.size != 2 + + schema = args[0] + document = args[1] + + require 'kwalify' + + validator = Kwalify::Validator.new(schema) + + errors = validator.validate(document) + + if errors && !errors.empty? + error_out = [] + for e in errors + error_out << "[#{e.path}] #{e.message}" + end + raise(Puppet::ParseError, "Failed kwalify schema validation:\n" + error_out.join("\n")) + end + + end +end + +# vim: set ts=2 sw=2 et : diff --git a/kwalify/lib/puppet/parser/functions/validate_resource.rb b/kwalify/lib/puppet/parser/functions/validate_resource.rb new file mode 100644 index 000000000..d71ef3f89 --- /dev/null +++ b/kwalify/lib/puppet/parser/functions/validate_resource.rb @@ -0,0 +1,41 @@ +# +# validate_resource +# + +module Puppet::Parser::Functions + newfunction(:validate_resource, :type => :statement, :doc => <<-EOS +This function when placed at the beginning of a class, will go looking for a +valid kwalify schema by replacing the extension of the file with '.schema'. + +It will then validate the arguments passed to the function using that kwalify +schema. + EOS + ) do |arguments| + + require 'kwalify' + + if (arguments.size != 0) then + raise(Puppet::ParseError, "validate_resource(): Wrong number of arguments "+ + "given #{arguments.size} for 0") + end + + + classhash = to_hash(recursive=false) + sourcepath = source.file + schemapath = sourcepath.gsub(/\.(rb|pp)$/, ".schema") + schema = Kwalify::Yaml.load_file(schemapath) + validator = Kwalify::Validator.new(schema) + errors = validator.validate(classhash) + + if errors && !errors.empty? + error_output = "Resource validation failed:\n" + for e in errors + error_output += "[#{e.path}] #{e.message}\n" + end + raise(Puppet::ParseError, error_output) + end + + end +end + +# vim: set ts=2 sw=2 et : diff --git a/kwalify/spec/lib/puppet_spec/files.rb b/kwalify/spec/lib/puppet_spec/files.rb new file mode 100755 index 000000000..30fb4fc42 --- /dev/null +++ b/kwalify/spec/lib/puppet_spec/files.rb @@ -0,0 +1,53 @@ +require 'fileutils' +require 'tempfile' + +# A support module for testing files. +module PuppetSpec::Files + # This code exists only to support tests that run as root, pretty much. + # Once they have finally been eliminated this can all go... --daniel 2011-04-08 + if Puppet.features.posix? then + def self.in_tmp(path) + path =~ /^\/tmp/ or path =~ /^\/var\/folders/ + end + elsif Puppet.features.microsoft_windows? + def self.in_tmp(path) + tempdir = File.expand_path(File.join(Dir::LOCAL_APPDATA, "Temp")) + path =~ /^#{tempdir}/ + end + else + fail "Help! Can't find in_tmp for this platform" + end + + def self.cleanup + $global_tempfiles ||= [] + while path = $global_tempfiles.pop do + fail "Not deleting tmpfile #{path} outside regular tmpdir" unless in_tmp(path) + + begin + FileUtils.rm_r path, :secure => true + rescue Errno::ENOENT + # nothing to do + end + end + end + + def tmpfile(name) + # Generate a temporary file, just for the name... + source = Tempfile.new(name) + path = source.path + source.close! + + # ...record it for cleanup, + $global_tempfiles ||= [] + $global_tempfiles << File.expand_path(path) + + # ...and bam. + path + end + + def tmpdir(name) + path = tmpfile(name) + FileUtils.mkdir_p(path) + path + end +end diff --git a/kwalify/spec/lib/puppet_spec/fixtures.rb b/kwalify/spec/lib/puppet_spec/fixtures.rb new file mode 100755 index 000000000..7f6bc2a8f --- /dev/null +++ b/kwalify/spec/lib/puppet_spec/fixtures.rb @@ -0,0 +1,28 @@ +module PuppetSpec::Fixtures + def fixtures(*rest) + File.join(PuppetSpec::FIXTURE_DIR, *rest) + end + def my_fixture_dir + callers = caller + while line = callers.shift do + next unless found = line.match(%r{/spec/(.*)_spec\.rb:}) + return fixtures(found[1]) + end + fail "sorry, I couldn't work out your path from the caller stack!" + end + def my_fixture(name) + file = File.join(my_fixture_dir, name) + unless File.readable? file then + fail Puppet::DevError, "fixture '#{name}' for #{my_fixture_dir} is not readable" + end + return file + end + def my_fixtures(glob = '*', flags = 0) + files = Dir.glob(File.join(my_fixture_dir, glob), flags) + unless files.length > 0 then + fail Puppet::DevError, "fixture '#{glob}' for #{my_fixture_dir} had no files!" + end + block_given? and files.each do |file| yield file end + files + end +end diff --git a/kwalify/spec/lib/puppet_spec/matchers.rb b/kwalify/spec/lib/puppet_spec/matchers.rb new file mode 100644 index 000000000..77f580330 --- /dev/null +++ b/kwalify/spec/lib/puppet_spec/matchers.rb @@ -0,0 +1,87 @@ +require 'stringio' + +######################################################################## +# Backward compatibility for Jenkins outdated environment. +module RSpec + module Matchers + module BlockAliases + alias_method :to, :should unless method_defined? :to + alias_method :to_not, :should_not unless method_defined? :to_not + alias_method :not_to, :should_not unless method_defined? :not_to + end + end +end + + +######################################################################## +# Custom matchers... +RSpec::Matchers.define :have_matching_element do |expected| + match do |actual| + actual.any? { |item| item =~ expected } + end +end + + +RSpec::Matchers.define :exit_with do |expected| + actual = nil + match do |block| + begin + block.call + rescue SystemExit => e + actual = e.status + end + actual and actual == expected + end + failure_message_for_should do |block| + "expected exit with code #{expected} but " + + (actual.nil? ? " exit was not called" : "we exited with #{actual} instead") + end + failure_message_for_should_not do |block| + "expected that exit would not be called with #{expected}" + end + description do + "expect exit with #{expected}" + end +end + + +RSpec::Matchers.define :have_printed do |expected| + match do |block| + $stderr = $stdout = StringIO.new + + begin + block.call + ensure + $stdout.rewind + @actual = $stdout.read + + $stdout = STDOUT + $stderr = STDERR + end + + if @actual then + case expected + when String + @actual.include? expected + when Regexp + expected.match @actual + else + raise ArgumentError, "No idea how to match a #{@actual.class.name}" + end + end + end + + failure_message_for_should do |actual| + if actual.nil? then + "expected #{expected.inspect}, but nothing was printed" + else + "expected #{expected.inspect} to be printed; got:\n#{actual}" + end + end + + description do + "expect #{expected.inspect} to be printed" + end + + diffable +end diff --git a/kwalify/spec/lib/puppet_spec/verbose.rb b/kwalify/spec/lib/puppet_spec/verbose.rb new file mode 100755 index 000000000..d9834f2d7 --- /dev/null +++ b/kwalify/spec/lib/puppet_spec/verbose.rb @@ -0,0 +1,9 @@ +# Support code for running stuff with warnings disabled. +module Kernel + def with_verbose_disabled + verbose, $VERBOSE = $VERBOSE, nil + result = yield + $VERBOSE = verbose + return result + end +end diff --git a/kwalify/spec/monkey_patches/alias_should_to_must.rb b/kwalify/spec/monkey_patches/alias_should_to_must.rb new file mode 100755 index 000000000..1a1111799 --- /dev/null +++ b/kwalify/spec/monkey_patches/alias_should_to_must.rb @@ -0,0 +1,8 @@ +require 'rspec' + +class Object + # This is necessary because the RAL has a 'should' + # method. + alias :must :should + alias :must_not :should_not +end diff --git a/kwalify/spec/monkey_patches/publicize_methods.rb b/kwalify/spec/monkey_patches/publicize_methods.rb new file mode 100755 index 000000000..b39e9c002 --- /dev/null +++ b/kwalify/spec/monkey_patches/publicize_methods.rb @@ -0,0 +1,11 @@ +# Some monkey-patching to allow us to test private methods. +class Class + def publicize_methods(*methods) + saved_private_instance_methods = methods.empty? ? self.private_instance_methods : methods + + self.class_eval { public(*saved_private_instance_methods) } + yield + self.class_eval { private(*saved_private_instance_methods) } + end +end + diff --git a/kwalify/spec/spec.opts b/kwalify/spec/spec.opts new file mode 100644 index 000000000..425f0edd3 --- /dev/null +++ b/kwalify/spec/spec.opts @@ -0,0 +1,4 @@ +--format +s +--colour +--backtrace diff --git a/kwalify/spec/spec_helper.rb b/kwalify/spec/spec_helper.rb new file mode 100755 index 000000000..fdc04bc9e --- /dev/null +++ b/kwalify/spec/spec_helper.rb @@ -0,0 +1,78 @@ +dir = File.expand_path(File.dirname(__FILE__)) +$LOAD_PATH.unshift File.join(dir, 'lib') + +p dir + +# Don't want puppet getting the command line arguments for rake or autotest +ARGV.clear + +require 'puppet' +require 'mocha' +gem 'rspec', '>=2.0.0' +require 'rspec/expectations' + +# So everyone else doesn't have to include this base constant. +module PuppetSpec + FIXTURE_DIR = File.join(dir = File.expand_path(File.dirname(__FILE__)), "fixtures") unless defined?(FIXTURE_DIR) +end + +require 'pathname' +require 'tmpdir' + +require 'puppet_spec/verbose' +require 'puppet_spec/files' +require 'puppet_spec/fixtures' +require 'puppet_spec/matchers' +require 'monkey_patches/alias_should_to_must' +require 'monkey_patches/publicize_methods' + +Pathname.glob("#{dir}/shared_behaviours/**/*.rb") do |behaviour| + require behaviour.relative_path_from(Pathname.new(dir)) +end + +RSpec.configure do |config| + include PuppetSpec::Fixtures + + config.mock_with :mocha + + config.before :each do + GC.disable + + # these globals are set by Application + $puppet_application_mode = nil + $puppet_application_name = nil + + # REVISIT: I think this conceals other bad tests, but I don't have time to + # fully diagnose those right now. When you read this, please come tell me + # I suck for letting this float. --daniel 2011-04-21 + Signal.stubs(:trap) + + # Set the confdir and vardir to gibberish so that tests + # have to be correctly mocked. + Puppet[:confdir] = "/dev/null" + Puppet[:vardir] = "/dev/null" + + # Avoid opening ports to the outside world + Puppet.settings[:bindaddress] = "127.0.0.1" + + @logs = [] + Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(@logs)) + + @log_level = Puppet::Util::Log.level + end + + config.after :each do + Puppet.settings.clear + Puppet::Node::Environment.clear + Puppet::Util::Storage.clear + Puppet::Util::ExecutionStub.reset + + PuppetSpec::Files.cleanup + + @logs.clear + Puppet::Util::Log.close_all + Puppet::Util::Log.level = @log_level + + GC.enable + end +end diff --git a/kwalify/spec/unit/puppet/parser/functions/get_scope_args.rb b/kwalify/spec/unit/puppet/parser/functions/get_scope_args.rb new file mode 100755 index 000000000..a9bddd1ad --- /dev/null +++ b/kwalify/spec/unit/puppet/parser/functions/get_scope_args.rb @@ -0,0 +1,29 @@ +#!/usr/bin/env rspec + +require 'spec_helper' + +describe "the get_scope_args function" do + before :all do + Puppet::Parser::Functions.autoloader.loadall + end + + let(:scope) { Puppet::Parser::Scope.new } + let(:ast) { Puppet::Parser::AST } + + it "should exist" do + Puppet::Parser::Functions.function("get_scope_args").should == + "function_get_scope_args" + end + + it "should raise a ParseError if there is any arguments" do + expect { + scope.function_get_scope_args(['a']) + }.should( raise_error(Puppet::ParseError)) + end + + pending "should return a list of arguments when used inside a class" do + # Here we look out for the function and make sure it returns the + # correct values + end + +end diff --git a/kwalify/spec/unit/puppet/parser/functions/kwalify_spec.rb b/kwalify/spec/unit/puppet/parser/functions/kwalify_spec.rb new file mode 100755 index 000000000..abdd52996 --- /dev/null +++ b/kwalify/spec/unit/puppet/parser/functions/kwalify_spec.rb @@ -0,0 +1,62 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +describe "the kwalify function" do + before :all do + Puppet::Parser::Functions.autoloader.loadall + end + + before :each do + @scope = Puppet::Parser::Scope.new + end + + it "should exist" do + Puppet::Parser::Functions.function("kwalify").should == "function_kwalify" + end + + it "should raise a ParseError if there is less than 2 arguments" do + lambda { @scope.function_kwalify([]) }.should( raise_error(Puppet::ParseError)) + end + + it "should validate a simple array schema" do + schema = { + 'type' => 'seq', + 'sequence' => [ + { 'type' => 'str' } + ] + } + document = ['a','b','c'] + @scope.function_kwalify([schema, document]) + end + + it "should not validate a simple array schema when invalid" do + schema = { + 'type' => 'seq', + 'sequence' => [ + { 'type' => 'str' } + ] + } + document = ['a','b',{'a' => 'b'}] + lambda { @scope.function_kwalify([schema, document]) }.should(raise_error(Puppet::ParseError)) + end + + it "should validate a hash schema" do + schema = { + 'type' => 'map', + 'mapping' => { + 'key1' => { + 'type' => 'str', + }, + 'key2' => { + 'type' => 'str', + }, + } + } + document = { + 'key1' => 'b', + 'key2' => 'c', + } + @scope.function_kwalify([schema, document]) + end + +end diff --git a/libvirt/.gitignore b/libvirt/.gitignore new file mode 100644 index 000000000..01d0a0845 --- /dev/null +++ b/libvirt/.gitignore @@ -0,0 +1 @@ +pkg/ diff --git a/libvirt/LICENSE b/libvirt/LICENSE new file mode 100644 index 000000000..47c26bda6 --- /dev/null +++ b/libvirt/LICENSE @@ -0,0 +1,17 @@ +Puppet OpenNebula Module - Puppet module for managing OpenNebula + +Copyright (C) 2011 Puppet Labs Inc + +Puppet Labs can be contacted at: info@puppetlabs.com + +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. diff --git a/libvirt/Modulefile b/libvirt/Modulefile new file mode 100644 index 000000000..76c5f882b --- /dev/null +++ b/libvirt/Modulefile @@ -0,0 +1,8 @@ +name 'puppetlabs-libvirt' +version '0.0.1' +source 'git://github.com/puppetlabs/puppetlabs-libvirt.git' +author 'puppetlabs' +license 'ASL 2.0' +summary 'libvirt Module' +description 'Manages and configures libvirt' +project_page 'https://github.com/puppetlabs/puppetlabs-libvirt' diff --git a/libvirt/README.markdown b/libvirt/README.markdown new file mode 100644 index 000000000..3c341d762 --- /dev/null +++ b/libvirt/README.markdown @@ -0,0 +1,127 @@ +# libvirt Module + +This module manages software to install and configure libvirt from within +Puppet. + +### Overview + +This is the Puppet libvirt module. Here we are providing capability within +Puppet to install and configure libvirt on a host machine. + +It does not take care of the creation of virtual instances - if you wish to +manage instances directly in Puppet use: + +https://github.com/carlasouza/puppet-virt + +### Disclaimer + +Warning! While this software is written in the best interest of quality it has +not been formally tested by our QA teams. Use at your own risk, but feel free +to enjoy and perhaps improve it while you do. + +Please see the included Apache Software License for more legal details +regarding warranty. + +### Requirements + +So this module was predominantly tested on: + +* Puppet 2.7.0rc4 +* Debian Wheezy +* libvirt 0.9.0 + +Other combinations may work, and we are happy to obviously take patches to +support other stacks. + +# Installation + +As with most modules, its best to download this module from the forge: + +http://forge.puppetlabs.com/puppetlabs/libvirt + +If you want the bleeding edge (and potentially broken) version from github, +download the module into your modulepath on your Puppetmaster. If you are not +sure where your module path is try this command: + + puppet --configprint modulepath + +Depending on the version of Puppet, you may need to restart the puppetmasterd +(or Apache) process before the functions will work. + +# Quick Start + +Setup libvirt. + + node "kvm1" { + class { "libvirt": } + } + +Setup and configure libvirt: + + node "kvm1" { + class { libvirt: + libvirtd_config => { + max_clients => { value => 10 }, + }, + qemu_config => { + vnc_listen => { value => "0.0.0.0" }, + }, + } + } + +# Detailed Usage + +## Classes + +### libvirt + +The libvirt class is responsible for installing and setting up libvirt on a +host. + +#### Parameters + +##### libvirtd_config + +This parameter allows you to pass a hash that is passed to the +libvirt::libvirtd_config resource. + +##### qemu_config + +This parameter allows you to pass a hash that is passed to the +libvirt::qemu_config resource. + +#### Examples + +Basic example: + + class { "libvirt": } + +Example with libvirtd_config parameters for configuring your libvirtd.conf +file: + + class { "libvirt": + libvirtd_config => { + max_clients => { value => 5 }, + tcp_port => { value => "16666" }, + } + } + +Example with qemu_config parameters for configuring your qemu.conf file: + + class { "libvirt": + qemu_config => { + vnc_port_listen => { value => "0.0.0.0" }, + } + } + +## Resources + +### libvirt::libvirtd_config + +This resource can be used to configure libvirt by populating items in the +libvirtd.conf file. + +### libvirt::qemu_config + +This resource can be used to configure libvirt by populating items in the +qemu.conf file. \ No newline at end of file diff --git a/libvirt/files/mc-plugins/agent/libvirt.ddl b/libvirt/files/mc-plugins/agent/libvirt.ddl new file mode 100644 index 000000000..89d387ade --- /dev/null +++ b/libvirt/files/mc-plugins/agent/libvirt.ddl @@ -0,0 +1,87 @@ +metadata :name => "SimpleRPC Agent For Libvirt Management", + :description => "Agent To Manage Libvirt", + :author => "Ken Barber", + :license => "ASLv2", + :version => "0.0.1", + :url => "http://github.com/puppetlabs/puppetlabs-libvirt", + :timeout => 180 + +action "domain_list", :description => "List domains" do + display :always + + output :domain_list, + :description => "Hash with small amount of information per domain", + :display_as => "Domain List" + + output :status, + :description => "Status of action", + :display_as => "Status" +end + +action "domain_detail", :description => "List domains" do + display :always + + input :uuid, + :prompt => "UUID of domain", + :description => "UUID of domain", + :type => :string, + :validation => '.', + :optional => false, + :maxlength => 250 + + output :domain_detail, + :description => "Hash with detailed information on a domain", + :display_as => "Domain Detail" + + output :status, + :description => "Status of action", + :display_as => "Status" +end + +action "domain_shutdown", :description => "Shutdown domain" do + display :always + + input :uuid, + :prompt => "UUID of domain", + :description => "UUID of domain", + :type => :string, + :validation => '.', + :optional => false, + :maxlength => 250 + + output :status, + :description => "Status of action", + :display_as => "Status" +end + +action "domain_destroy", :description => "Destroy domain" do + display :always + + input :uuid, + :prompt => "UUID of domain", + :description => "UUID of domain", + :type => :string, + :validation => '.', + :optional => false, + :maxlength => 250 + + output :status, + :description => "Status of action", + :display_as => "Status" +end + +action "domain_create", :description => "Create domain" do + display :always + + input :name, + :prompt => "Name of domain", + :description => "Name of domain", + :type => :string, + :validation => '.', + :optional => false, + :maxlength => 100 + + output :status, + :description => "Status of action", + :display_as => "Status" +end diff --git a/libvirt/files/mc-plugins/agent/libvirt.rb b/libvirt/files/mc-plugins/agent/libvirt.rb new file mode 100644 index 000000000..9f3b78a7d --- /dev/null +++ b/libvirt/files/mc-plugins/agent/libvirt.rb @@ -0,0 +1,231 @@ +require 'libvirt' +require 'xmlsimple' + +module MCollective + module Agent + # An agent that interacts with libvirt. + # + # See http://github.com/puppetlabs/puppetlabs-libvirt/ + # + # Released under the terms of ASL 2.0, same as Puppet + class Libvirt "SimpleRPC Libvirt Agent", + :description => "Agent to interact with libvirt", + :author => "Ken Barber", + :license => "ASLv2", + :version => "0.0.1", + :url => "http://github.com/puppetlabs/puppetlabs-libvirt/", + :timeout => 60 + + # This is a convenience wrapper around opening and closing the connection to + # libvirt. + def libvirt_transaction(uri = "qemu:///system") + conn = ::Libvirt::open(uri) + + yield conn + + conn.close + end + + # This action returns a short list of domains for each hypervisor. + action "domain_list" do + begin + Log.instance.debug("Getting domain_detail for libvirt") + + libvirt_transaction do |conn| + domains = conn.list_domains + + reply["domain_list"] ||= {} + domains.each do |id| + domain = conn.lookup_domain_by_id(id) + reply["domain_list"][domain.uuid] = { + "name" => domain.name, + "id" => domain.id, + "num_vcpus" => domain.info.nr_virt_cpu || 0, + "memory" => domain.info.memory || 0, + "state" => domain.info.state, + } + end + end + + reply["status"] = "ok" + rescue Exception => e + reply.fail "#{e}" + end + end + + # This action returns detailed information gathered from + # libvirt for a particulur domain. + action "domain_detail" do + validate :uuid, String + uuid = request[:uuid] + + begin + Log.instance.debug("Getting domain_detail for libvirt") + + libvirt_transaction do |conn| + domain = conn.lookup_domain_by_uuid(uuid) + + # Grab XML data and turn it into a hash + xml_desc = XmlSimple.xml_in(domain.xml_desc, {}) + + # The interface for information is a bit + # haphazard, so I'm cherrypicking to populate + # this hash. + reply["domain_detail"] = { + "uuid" => uuid, + "cpu_time" => domain.info.cpu_time, + "state" => domain.info.state, + "os_type" => domain.os_type, + "xml_desc" => xml_desc, + } + end + rescue Exception => e + reply.fail "#{e}" + end + end + + action "domain_shutdown" do + validate :uuid, String + uuid = request[:uuid] + + begin + Log.instance.debug("Doing shutdown_domain for libvirt") + + libvirt_transaction do |conn| + domain = conn.lookup_domain_by_uuid(uuid) + domain.shutdown + end + + reply["status"] = ["ok",uuid.to_s] + rescue Exception => e + reply.fail "#{e}" + end + + end + + # Destroy a domain + action "domain_destroy" do + validate :uuid, String + uuid = request[:uuid] + + begin + Log.instance.debug("Doing domain_destroy for libvirt") + + libvirt_transaction do |conn| + domain = conn.lookup_domain_by_uuid(uuid) + domain.destroy + end + + reply["status"] = ["ok",uuid.to_s] + rescue Exception => e + reply.fail "#{e}" + end + + end + + # This action attempts to start a domain that exists. + action "domain_start" do + validate :uuid, String + uuid = request[:uuid] + + begin + Log.instance.debug("Doing domain_start for libvirt") + + libvirt_transaction do |conn| + domain = conn.lookup_domain_by_uuid(uuid) + domain.start + end + + reply["status"] = ["ok", uuid.to_s] + rescue Exception => e + reply.fail "#{e}" + end + + end + + # This action creates a domain. + action "domain_create" do + validate :name, String + name = request[:name] + + # start by creating a disk + # TODO: this is very much inserted just to make this 'work' for now + `qemu-img create -f qcow2 /srv/virt/virtuals/#{name}.img.disk.0 10000000000` + + # TODO: most basic way to create a template obviously. + xml_template = <<-EOS + + #{name} + 256000 + 1 + + hvm + + + + + + + + + destroy + restart + restart + + /usr/bin/kvm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +EOS + + begin + Log.instance.debug("Doing list_nodedevices for libvirt") + + libvirt_transaction do |conn| + domain = conn.define_domain_xml(xml_template) + domain.create + end + + reply["status"] = "ok" + rescue Exception => e + reply.fail "#{e}: #{e.libvirt_message}" + end + + end + + end + end +end + +# vi:tabstop=4:expandtab:ai:filetype=ruby diff --git a/libvirt/files/mc-plugins/application/libvirt.rb b/libvirt/files/mc-plugins/application/libvirt.rb new file mode 100644 index 000000000..73b948c07 --- /dev/null +++ b/libvirt/files/mc-plugins/application/libvirt.rb @@ -0,0 +1,77 @@ +require 'pp' + +class MCollective::Application::Libvirt options) + + action = configuration[:action] + + data = {} + mc.send(action, configuration[:arguments]).each do |resp| + if resp[:statuscode] == 0 + data[resp[:sender]] ||= {} + case action + when "domain_list" + data[resp[:sender]][:domain_list] = resp[:data]["domain_list"] + data[resp[:sender]][:status] = resp[:data]["status"] + when "domain_detail" + data[resp[:sender]][:domain_detail] = resp[:data]["domain_detail"] + data[resp[:sender]][:status] = resp[:data]["status"] + else + data[resp[:sender]][:status] = resp[:data]["status"] + end + else + printf("%-40s error = %s\n", resp[:sender], resp[:statusmsg]) + end + end + + # summarize + case action + when "domain_list" + puts sprintf("%-36s %-4s %-8s %-4s %-4s %-6s %-15s", "UUID", "ID", "NAME", "STATUS", "VCPU", "MEM", "NODE") + data.each do |sender,value| + value[:domain_list].each do |uuid,domain_info| + # Basic conversion to megabytes - but probably should have a nicer way of doing + # this for other unit sizes + memory = (domain_info["memory"].to_i/1024).floor.to_s + "M" + status = domain_info["status"].to_i ? "running" : "stopped" + puts sprintf("%-36.36s %-4.4s %-8.8s %-6.6s %-4.4s %-6.6s %-15.15s", uuid, domain_info["id"], domain_info["name"], status, domain_info["num_vcpus"], memory, sender) + end + end + else + data.each do |sender,data| + puts "#{sender}:" + pp data + end + end + end +end +# vi:tabstop=4:expandtab:ai diff --git a/libvirt/manifests/init.pp b/libvirt/manifests/init.pp new file mode 100644 index 000000000..6bf2bd173 --- /dev/null +++ b/libvirt/manifests/init.pp @@ -0,0 +1,152 @@ +# Install and configure libvirt. +# +# == Parameters +# +# These two parameters are used for configuring various configuration files +# that libvirt uses. +# +# [libvirtd_config] +# *Optional* A hash for creating libvirt::libvirtd_config resources. +# [qemu_config] +# *Optional* A hash for creating libvirt::qemu_config resources. +# +# These parameters are usually worked out automatically and can usually be +# left as they are. +# +# [package] +# *Optional* Package(s) for installing libvirt. +# [version] +# *Optional* Version for the libvirt package. +# [service] +# *Optional* Service(s) for stopping and starting the libvirtd process. +# [user] +# *Optional* User that libvirtd runs as. +# [group] +# *Optional* Group that libvirtd runs as. +# [config_dir] +# *Optional* Path to libvirt configuration. +# [libvirtd_config_file] +# *Optional* Path to the libvirtd configuration file. +# [qemu_config_file] +# *Optional* Path to the qemu configuration file for libvirtd. +# +# == Variables +# +# N/A +# +# == Examples +# +# Default configuration: +# +# class { "libvirt": } +# +# Custom libvirtd configuration: +# +# class { "libvirt": +# libvirtd_config => { +# max_clients => { value => 10 }, +# }, +# qemu_config => { +# vnc_listen => { value => $ipaddress }, +# }, +# } +# +# == Authors +# +# Ken Barber +# +# == Copyright +# +# Copyright 2011 Puppetlabs Inc, unless otherwise noted. +# +class libvirt ( + + $package = $libvirt::params::libvirt_package, + $version = $libvirt::params::libvirt_version, + $service = $libvirt::params::libvirt_service, + $user = $libvirt::params::libvirt_user, + $group = $libvirt::params::libvirt_group, + $libvirtd_config = undef, + $config_dir = $libvirt::params::libvirt_config_dir, + $libvirtd_config_file = $libvirt::params::libvirtd_config_file, + $qemu_config_file = $libvirt::params::qemu_config_file, + $qemu_config = undef + + ) inherits libvirt::params { + + ############################## + # Base packages and service # + ############################## + package { $package: + ensure => $version + } + service { $service: + ensure => running, + enable => true, + hasstatus => true, + hasrestart => true, + } + + ######################## + # libvirtd.conf Config # + ######################## + file { "${config_dir}/libvirtd.d": + ensure => directory, + purge => true, + recurse => true, + require => Package[$package], + notify => Exec["create_libvirtd_conf"], + } + file { "${config_dir}/libvirtd.d/00-header": + content => "# Managed by puppet\n", + require => Package[$package], + notify => Exec["create_libvirtd_conf"], + } + exec { "create_libvirtd_conf": + command => "/bin/cat ${config_dir}/libvirtd.d/* > ${libvirtd_config_file}", + refreshonly => true, + require => [ Package[$package], File["${config_dir}/libvirtd.d"] ], + notify => Service[$service], + } + file { $libvirtd_config_file: + owner => root, + group => root, + mode => "0644", + require => Package[$package], + } + create_resources("libvirt::libvirtd_config", $libvirtd_config) + + # Some minor defaults. These may need to differ per OS in the future. + libvirt::libvirtd_config { ["auth_unix_ro", "auth_unix_rw"]: value => "none" } + libvirt::libvirtd_config { "unix_sock_group": value => $group } + libvirt::libvirtd_config { "unix_sock_rw_perms": value => "0770" } + + #################### + # qemu.conf Config # + #################### + file { "${config_dir}/qemu.d": + ensure => directory, + purge => true, + recurse => true, + require => Package[$package], + notify => Exec["create_qemu_conf"], + } + file { "${config_dir}/qemu.d/00-header": + content => "# Managed by puppet\n", + require => Package[$package], + notify => Exec["create_qemu_conf"], + } + exec { "create_qemu_conf": + command => "/bin/cat ${config_dir}/qemu.d/* > ${qemu_config_file}", + refreshonly => true, + require => [ Package[$package], File["${config_dir}/qemu.d"] ], + } + file { $qemu_config_file: + owner => root, + group => root, + mode => "0644", + require => Package[$package], + } + create_resources("libvirt::qemu_config", $qemu_config) + +} diff --git a/libvirt/manifests/libvirtd_config.pp b/libvirt/manifests/libvirtd_config.pp new file mode 100644 index 000000000..27f63c645 --- /dev/null +++ b/libvirt/manifests/libvirtd_config.pp @@ -0,0 +1,35 @@ +# This resource sets configuration items within libvirtd.conf. +# +# == Parameters +# +# [namevar] +# This is the parameter you wish to change. For example 'unix_sock_group'. +# [value] +# The value you wish to set the parameter to. +# +# == Examples +# +# libvirt::libvirtd_config { "unix_sock_group": +# value => "sasl", +# } +# +# == Authors +# +# Ken Barber +# +# == Copyright +# +# Copyright 2011 Puppetlabs Inc, unless otherwise noted. +# +define libvirt::libvirtd_config( + + $value + + ) { + + file { "/etc/libvirt/libvirtd.d/${name}": + content => template("${module_name}/libvirtd.conf.erb"), + notify => Exec["create_libvirtd_conf"], + } + +} diff --git a/libvirt/manifests/params.pp b/libvirt/manifests/params.pp new file mode 100644 index 000000000..85cf56482 --- /dev/null +++ b/libvirt/manifests/params.pp @@ -0,0 +1,55 @@ +# Libvirt parameter class. Not to be used directly. +# +# == OS Support +# +# * Debian 7.0 (wheezy) +# * Ubuntu +# +# == Variables +# +# This is a list of variables that must be set for each operating system. +# +# [libvirt_package] +# Package(s) for installing libvirt. +# [libvirt_version] +# Version for libvirt package. +# [libvirt_service] +# Service for libvirt. +# [libvirt_user] +# User for libvirt. This is for process and for socket permissions. +# [libvirt_group] +# Group for libvirt. This is for process and for socket permissions. +# [libvirt_config_dir] +# Path to configuration directory for libvirt. +# [libvirtd_config_file] +# Path to libvirtd.conf. +# [qemu_config_file] +# Path to qemu.conf. +# +# == Authors +# +# Ken Barber +# +# == Copyright +# +# Copyright 2011 Puppetlabs Inc, unless otherwise noted. +# +class libvirt::params { + + case $operatingsystem { + 'ubuntu', 'debian': { + $libvirt_package = "libvirt-bin" + $libvirt_version = "installed" + $libvirt_service = "libvirt-bin" + $libvirt_user = "libvirt" + $libvirt_group = "libvirt" + $libvirt_config_dir = "/etc/libvirt" + $libvirtd_config_file = "${libvirt_config_dir}/libvirtd.conf" + $qemu_config_file = "${libvirt_config_dir}/qemu.conf" + } + default: { + fail("Operating system ${operatingsystem} is not supported") + } + } + +} diff --git a/libvirt/manifests/qemu_config.pp b/libvirt/manifests/qemu_config.pp new file mode 100644 index 000000000..7284a5548 --- /dev/null +++ b/libvirt/manifests/qemu_config.pp @@ -0,0 +1,35 @@ +# This resource sets configuration items within qemu.conf. +# +# == Parameters +# +# [namevar] +# This is the parameter you wish to change. For example 'vnc_listen'. +# [value] +# The value you wish to set the parameter to. +# +# == Examples +# +# libvirt::qemu_config { "vnc_listen": +# value => "0.0.0.0", +# } +# +# == Authors +# +# Ken Barber +# +# == Copyright +# +# Copyright 2011 Puppetlabs Inc, unless otherwise noted. +# +define libvirt::qemu_config( + + $value + + ) { + + file { "${libvirt::params::libvirt_config_dir}/qemu.d/${name}": + content => template("${module_name}/qemu.conf.erb"), + notify => Exec["create_qemu_conf"], + } + +} diff --git a/libvirt/templates/libvirtd.conf.erb b/libvirt/templates/libvirtd.conf.erb new file mode 100644 index 000000000..d508e01b8 --- /dev/null +++ b/libvirt/templates/libvirtd.conf.erb @@ -0,0 +1 @@ +<%= name %>=<% if value.kind_of?(Array) %>["<%= value.join("\",\"") %>"]<% elsif value.match(/^[1-9]\d*/) %><%=value%><% else %>"<%= value %>"<% end %><%= "\n" %> \ No newline at end of file diff --git a/libvirt/templates/qemu.conf.erb b/libvirt/templates/qemu.conf.erb new file mode 100644 index 000000000..d508e01b8 --- /dev/null +++ b/libvirt/templates/qemu.conf.erb @@ -0,0 +1 @@ +<%= name %>=<% if value.kind_of?(Array) %>["<%= value.join("\",\"") %>"]<% elsif value.match(/^[1-9]\d*/) %><%=value%><% else %>"<%= value %>"<% end %><%= "\n" %> \ No newline at end of file diff --git a/libvirt/tests/00_basic_include.pp b/libvirt/tests/00_basic_include.pp new file mode 100644 index 000000000..89a9f098a --- /dev/null +++ b/libvirt/tests/00_basic_include.pp @@ -0,0 +1 @@ +class { "libvirt": } diff --git a/logrotate/.gitignore b/logrotate/.gitignore new file mode 100644 index 000000000..b6715f5f7 --- /dev/null +++ b/logrotate/.gitignore @@ -0,0 +1,3 @@ +.bundle/ +vendor/gems/ +.ruby-version diff --git a/logrotate/.travis.yml b/logrotate/.travis.yml new file mode 100644 index 000000000..7813a91bb --- /dev/null +++ b/logrotate/.travis.yml @@ -0,0 +1,16 @@ +script: ./script/cibuild +before_install: + - gem update --system 2.1.11 + - ./script/bootstrap +rvm: 1.8.7 +notifications: + email: + - tim@github.com +env: + - PUPPET_VERSION=2.6.18 + - PUPPET_VERSION=2.7.24 + - PUPPET_VERSION=3.0.2 + - PUPPET_VERSION=3.1.1 + - PUPPET_VERSION=3.2.4 + - PUPPET_VERSION=3.3.2 + - PUPPET_VERSION=3.4.1 diff --git a/logrotate/CONTRIBUTING.md b/logrotate/CONTRIBUTING.md new file mode 100644 index 000000000..1e43e5b18 --- /dev/null +++ b/logrotate/CONTRIBUTING.md @@ -0,0 +1,28 @@ +Thanks for contributing to puppet-logrotate! A couple of notes to help you out: + + * Please don't bump the module version number. + * Make sure your changes have tests. + * Keep your pull requests on topic. Pull requests with a bunch of unrelated + changes won't get merged. Feel free to open seperate pull requests for the + other changes though. + * Yes, I commit vendored gem files into my repositories. No, this is not + a mistake. Please don't "fix" this in your pull request :) + +Before starting, you can prepare your development environment by running: + +``` +script/bootstap +``` + +This will install all the dependencies required to run the test suite. + +Although TravisCI will automatically run the test suite against your branch +when you push, you can (and should) run them locally as you're working. You can +run the test suite by running: + +``` +script/cibuild +``` + +This will run all the rspec-puppet tests followed by puppet-lint to catch any +style issues. diff --git a/logrotate/Gemfile b/logrotate/Gemfile new file mode 100644 index 000000000..386b6a884 --- /dev/null +++ b/logrotate/Gemfile @@ -0,0 +1,7 @@ +source 'https://rubygems.org' + +puppetversion = ENV.key?('PUPPET_VERSION') ? "= #{ENV['PUPPET_VERSION']}" : ['>= 3.2'] + +gem 'puppet-lint' +gem 'rspec-puppet' +gem 'puppet', puppetversion diff --git a/logrotate/Gemfile.lock b/logrotate/Gemfile.lock new file mode 100644 index 000000000..2cbbedc04 --- /dev/null +++ b/logrotate/Gemfile.lock @@ -0,0 +1,36 @@ +GEM + remote: https://rubygems.org/ + specs: + diff-lcs (1.2.5) + facter (1.7.1) + hiera (1.2.1) + json_pure + json_pure (1.8.0) + metaclass (0.0.1) + mocha (0.14.0) + metaclass (~> 0.0.1) + puppet (3.2.1) + facter (~> 1.6) + hiera (~> 1.0) + rgen (~> 0.6) + puppet-lint (0.3.2) + rake (10.0.4) + rgen (0.6.2) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.7) + rspec-expectations (2.14.4) + diff-lcs (>= 1.1.3, < 2.0) + rspec-mocks (2.14.4) + rspec-puppet (1.0.1) + rspec + +PLATFORMS + ruby + +DEPENDENCIES + puppet (>= 3.2) + puppet-lint + rspec-puppet diff --git a/logrotate/LICENSE b/logrotate/LICENSE new file mode 100644 index 000000000..186510b6d --- /dev/null +++ b/logrotate/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2012 Tim Sharpe + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/logrotate/Modulefile b/logrotate/Modulefile new file mode 100644 index 000000000..08240960d --- /dev/null +++ b/logrotate/Modulefile @@ -0,0 +1,7 @@ +name 'rodjek-logrotate' +version '1.1.1' +source 'https://github.com/rodjek/puppet-logrotate' +license 'MIT' +summary 'Logrotate module' +description 'Manage logrotate on your servers with Puppet' +project_page 'https://github.com/rodjek/puppet-logrotate' diff --git a/logrotate/README.md b/logrotate/README.md new file mode 100644 index 000000000..1f83fa4dc --- /dev/null +++ b/logrotate/README.md @@ -0,0 +1,139 @@ +# Logrotate module for Puppet + +[![Build Status](https://secure.travis-ci.org/rodjek/puppet-logrotate.png)](http://travis-ci.org/rodjek/puppet-logrotate) + +A more Puppety way of managing logrotate configs. Where possible, as many of +the configuration options have remained the same with a couple of notable +exceptions: + + * Booleans are now used instead of the ``/`no` pattern. + e.g. `copy` == `copy => true`, `nocopy` == `copy => false`. + * `create` and it's three optional arguments have been split into seperate + parameters documented below. + * Instead of 'daily', 'weekly', 'monthly' or 'yearly', there is a + `rotate_every` parameter (see documentation below). + +## logrotate::rule + +The only thing you'll need to deal with, this type configures a logrotate rule. +Using this type will automatically include a private class that will install +and configure logrotate for you. + +``` +namevar - The String name of the rule. +path - The path String to the logfile(s) to be rotated. +ensure - The desired state of the logrotate rule as a String. Valid + values are 'absent' and 'present' (default: 'present'). +compress - A Boolean value specifying whether the rotated logs should + be compressed (optional). +compresscmd - The command String that should be executed to compress the + rotated logs (optional). +compressext - The extention String to be appended to the rotated log files + after they have been compressed (optional). +compressoptions - A String of command line options to be passed to the + compression program specified in `compresscmd` (optional). +copy - A Boolean specifying whether logrotate should just take a + copy of the log file and not touch the original (optional). +copytruncate - A Boolean specifying whether logrotate should truncate the + original log file after taking a copy (optional). +create - A Boolean specifying whether logrotate should create a new + log file immediately after rotation (optional). +create_mode - An octal mode String logrotate should apply to the newly + created log file if create => true (optional). +create_owner - A username String that logrotate should set the owner of the + newly created log file to if create => true (optional). +create_group - A String group name that logrotate should apply to the newly + created log file if create => true (optional). +dateext - A Boolean specifying whether rotated log files should be + archived by adding a date extension rather just a number + (optional). +dateformat - The format String to be used for `dateext` (optional). + Valid specifiers are '%Y', '%m', '%d' and '%s'. +delaycompress - A Boolean specifying whether compression of the rotated + log file should be delayed until the next logrotate run + (optional). +extension - Log files with this extension String are allowed to keep it + after rotation (optional). +ifempty - A Boolean specifying whether the log file should be rotated + even if it is empty (optional). +mail - The email address String that logs that are about to be + rotated out of existence are emailed to (optional). +mailfirst - A Boolean that when used with `mail` has logrotate email the + just rotated file rather than the about to expire file + (optional). +maillast - A Boolean that when used with `mail` has logrotate email the + about to expire file rather than the just rotated file + (optional). +maxage - The Integer maximum number of days that a rotated log file + can stay on the system (optional). +minsize - The String minimum size a log file must be to be rotated, + but not before the scheduled rotation time (optional). + The default units are bytes, append k, M or G for kilobytes, + megabytes and gigabytes respectively. +missingok - A Boolean specifying whether logrotate should ignore missing + log files or issue an error (optional). +olddir - A String path to a directory that rotated logs should be + moved to (optional). +postrotate - A command String or an Array of Strings that should be + executed by /bin/sh after the log file is rotated (optional). +prerotate - A command String or an Array of Strings that should be + executed by /bin/sh before the log file is rotated and only + if it will be rotated (optional). +firstaction - A command String or an Array of Strings that should be + executed by /bin/sh once before all log files that match the + wildcard pattern are rotated (optional). +lastaction - A command String or an Array of Strings that should be + executed by /bin/sh once after all the log files that match + the wildcard pattern are rotated (optional). +rotate - The Integer number of rotated log files to keep on disk + (optional). +rotate_every - How often the log files should be rotated as a String. + Valid values are 'hour', 'day', 'week', 'month' and 'year' + (optional). Please note, older versions of logrotate do not + support yearly log rotation. +size - The String size a log file has to reach before it will be + rotated (optional). The default units are bytes, append k, + M or G for kilobytes, megabytes or gigabytes respectively. +sharedscripts - A Boolean specifying whether logrotate should run the + postrotate and prerotate scripts for each matching file or + just once (optional). +shred - A Boolean specifying whether logs should be deleted with + shred instead of unlink (optional). +shredcycles - The Integer number of times shred should overwrite log files + before unlinking them (optional). +start - The Integer number to be used as the base for the extensions + appended to the rotated log files (optional). +su - A Boolean specifying whether logrotate should rotate under + the specific su_owner and su_group instead of the default. + First available in logrotate 3.8.0. (optional) +su_owner - A username String that logrotate should use to rotate a + log file set instead of using the default if + su => true (optional). +su_group - A String group name that logrotate should use to rotate a + log file set instead of using the default if + su => true (optional). +uncompresscmd - The String command to be used to uncompress log files + (optional). +``` + +Further details about these options can be found by reading `man 8 logrotate`. + +### Examples + +``` +logrotate::rule { 'messages': + path => '/var/log/messages', + rotate => 5, + rotate_every => 'week', + postrotate => '/usr/bin/killall -HUP syslogd', +} + +logrotate::rule { 'apache': + path => '/var/log/httpd/*.log', + rotate => 5, + mail => 'test@example.com', + size => '100k', + sharedscripts => true, + postrotate => '/etc/init.d/httpd restart', +} +``` diff --git a/logrotate/files/etc/cron.daily/logrotate b/logrotate/files/etc/cron.daily/logrotate new file mode 100644 index 000000000..f48c56a77 --- /dev/null +++ b/logrotate/files/etc/cron.daily/logrotate @@ -0,0 +1,11 @@ +#!/bin/sh +# THIS FILE IS AUTOMATICALLY DISTRIBUTED BY PUPPET. ANY CHANGES WILL BE +# OVERWRITTEN. + +OUTPUT=$(/usr/sbin/logrotate /etc/logrotate.conf 2>&1) +EXITVALUE=$? +if [ $EXITVALUE != 0 ]; then + /usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]" + echo "${OUTPUT}" +fi +exit $EXITVALUE diff --git a/logrotate/files/etc/cron.hourly/logrotate b/logrotate/files/etc/cron.hourly/logrotate new file mode 100644 index 000000000..53a179c08 --- /dev/null +++ b/logrotate/files/etc/cron.hourly/logrotate @@ -0,0 +1,11 @@ +#!/bin/sh +# THIS FILE IS AUTOMATICALLY DISTRIBUTED BY PUPPET. ANY CHANGES WILL BE +# OVERWRITTEN. + +OUTPUT=$(/usr/sbin/logrotate /etc/logrotate.d/hourly 2>&1) +EXITVALUE=$? +if [ $EXITVALUE != 0 ]; then + /usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]" + echo "${OUTPUT}" +fi +exit $EXITVALUE diff --git a/logrotate/files/etc/logrotate.conf b/logrotate/files/etc/logrotate.conf new file mode 100644 index 000000000..2cdc6f619 --- /dev/null +++ b/logrotate/files/etc/logrotate.conf @@ -0,0 +1,15 @@ +# THIS FILE IS AUTOMATICALLY DISTRIBUTED BY PUPPET. ANY CHANGES WILL BE +# OVERWRITTEN. + +# Default values +# rotate log files weekly +weekly + +# keep 4 weeks worth of backlogs +rotate 4 + +# create new (empty) log files after rotating old ones +create + +# packages drop log rotation information into this directory +include /etc/logrotate.d diff --git a/logrotate/manifests/base.pp b/logrotate/manifests/base.pp new file mode 100644 index 000000000..706a0d438 --- /dev/null +++ b/logrotate/manifests/base.pp @@ -0,0 +1,43 @@ +# Internal: Install logrotate and configure it to read from /etc/logrotate.d +# +# Examples +# +# include logrotate::base +class logrotate::base { + package { 'logrotate': + ensure => latest, + } + + File { + owner => 'root', + group => 'root', + require => Package['logrotate'], + } + + file { + '/etc/logrotate.conf': + ensure => file, + mode => '0444', + source => 'puppet:///modules/logrotate/etc/logrotate.conf'; + '/etc/logrotate.d': + ensure => directory, + mode => '0755'; + '/etc/cron.daily/logrotate': + ensure => file, + mode => '0555', + source => 'puppet:///modules/logrotate/etc/cron.daily/logrotate'; + } + + case $::osfamily { + 'Debian': { + include logrotate::defaults::debian + } + 'RedHat': { + include logrotate::defaults::redhat + } + 'SuSE': { + include logrotate::defaults::suse + } + default: { } + } +} diff --git a/logrotate/manifests/defaults/debian.pp b/logrotate/manifests/defaults/debian.pp new file mode 100644 index 000000000..b8eab866c --- /dev/null +++ b/logrotate/manifests/defaults/debian.pp @@ -0,0 +1,24 @@ +# Internal: Manage the default debian logrotate rules. +# +# Examples +# +# include logrotate::defaults::debian +class logrotate::defaults::debian { + Logrotate::Rule { + missingok => true, + rotate_every => 'month', + create => true, + create_owner => 'root', + create_group => 'utmp', + rotate => '1', + } + + logrotate::rule { + 'wtmp': + path => '/var/log/wtmp', + create_mode => '0664'; + 'btmp': + path => '/var/log/btmp', + create_mode => '0660'; + } +} diff --git a/logrotate/manifests/defaults/redhat.pp b/logrotate/manifests/defaults/redhat.pp new file mode 100644 index 000000000..9f9a0ef11 --- /dev/null +++ b/logrotate/manifests/defaults/redhat.pp @@ -0,0 +1,27 @@ +# Internal: Manage the default redhat logrotate rules. +# +# Examples +# +# include logrotate::defaults::redhat +class logrotate::defaults::redhat { + Logrotate::Rule { + missingok => true, + rotate_every => 'month', + create => true, + create_owner => 'root', + create_group => 'utmp', + rotate => '1', + } + + logrotate::rule { + 'wtmp': + path => '/var/log/wtmp', + create_mode => '0664', + missingok => false, + minsize => '1M'; + 'btmp': + path => '/var/log/btmp', + create_mode => '0660', + minsize => '1M'; + } +} diff --git a/logrotate/manifests/defaults/suse.pp b/logrotate/manifests/defaults/suse.pp new file mode 100644 index 000000000..79300bb17 --- /dev/null +++ b/logrotate/manifests/defaults/suse.pp @@ -0,0 +1,28 @@ +# Internal: Manage the default suse logrotate rules. +# +# Examples +# +# include logrotate::defaults::suse +class logrotate::defaults::suse { + Logrotate::Rule { + missingok => true, + rotate_every => 'month', + create => true, + create_owner => 'root', + create_group => 'utmp', + rotate => '99', + maxage => '365', + size => '400k' + } + + logrotate::rule { + 'wtmp': + path => '/var/log/wtmp', + create_mode => '0664', + missingok => false; + 'btmp': + path => '/var/log/btmp', + create_mode => '0600', + create_group => 'root'; + } +} diff --git a/logrotate/manifests/hourly.pp b/logrotate/manifests/hourly.pp new file mode 100644 index 000000000..1dc2b7662 --- /dev/null +++ b/logrotate/manifests/hourly.pp @@ -0,0 +1,45 @@ +# Internal: Configure a host for hourly logrotate jobs. +# +# ensure - The desired state of hourly logrotate support. Valid values are +# 'absent' and 'present' (default: 'present'). +# +# Examples +# +# # Set up hourly logrotate jobs +# include logrotate::hourly +# +# # Remove hourly logrotate job support +# class { 'logrotate::hourly': +# ensure => absent, +# } +class logrotate::hourly($ensure='present') { + case $ensure { + 'absent': { + $dir_ensure = $ensure + } + 'present': { + $dir_ensure = 'directory' + } + default: { + fail("Class[Logrotate::Hourly]: Invalid ensure value '${ensure}'") + } + } + + file { + '/etc/logrotate.d/hourly': + ensure => $dir_ensure, + owner => 'root', + group => 'root', + mode => '0755'; + '/etc/cron.hourly/logrotate': + ensure => $ensure, + owner => 'root', + group => 'root', + mode => '0555', + source => 'puppet:///modules/logrotate/etc/cron.hourly/logrotate', + require => [ + File['/etc/logrotate.d/hourly'], + Package['logrotate'], + ]; + } +} diff --git a/logrotate/manifests/init.pp b/logrotate/manifests/init.pp new file mode 100644 index 000000000..e69de29bb diff --git a/logrotate/manifests/rule.pp b/logrotate/manifests/rule.pp new file mode 100644 index 000000000..22b520197 --- /dev/null +++ b/logrotate/manifests/rule.pp @@ -0,0 +1,432 @@ +# Public: Configure logrotate to rotate a logfile. +# +# namevar - The String name of the rule. +# path - The path String to the logfile(s) to be rotated. +# ensure - The desired state of the logrotate rule as a String. Valid +# values are 'absent' and 'present' (default: 'present'). +# compress - A Boolean value specifying whether the rotated logs should +# be compressed (optional). +# compresscmd - The command String that should be executed to compress the +# rotated logs (optional). +# compressext - The extention String to be appended to the rotated log files +# after they have been compressed (optional). +# compressoptions - A String of command line options to be passed to the +# compression program specified in `compresscmd` (optional). +# copy - A Boolean specifying whether logrotate should just take a +# copy of the log file and not touch the original (optional). +# copytruncate - A Boolean specifying whether logrotate should truncate the +# original log file after taking a copy (optional). +# create - A Boolean specifying whether logrotate should create a new +# log file immediately after rotation (optional). +# create_mode - An octal mode String logrotate should apply to the newly +# created log file if create => true (optional). +# create_owner - A username String that logrotate should set the owner of the +# newly created log file to if create => true (optional). +# create_group - A String group name that logrotate should apply to the newly +# created log file if create => true (optional). +# dateext - A Boolean specifying whether rotated log files should be +# archived by adding a date extension rather just a number +# (optional). +# dateformat - The format String to be used for `dateext` (optional). +# Valid specifiers are '%Y', '%m', '%d' and '%s'. +# delaycompress - A Boolean specifying whether compression of the rotated +# log file should be delayed until the next logrotate run +# (optional). +# extension - Log files with this extension String are allowed to keep it +# after rotation (optional). +# ifempty - A Boolean specifying whether the log file should be rotated +# even if it is empty (optional). +# mail - The email address String that logs that are about to be +# rotated out of existence are emailed to (optional). +# mailfirst - A Boolean that when used with `mail` has logrotate email the +# just rotated file rather than the about to expire file +# (optional). +# maillast - A Boolean that when used with `mail` has logrotate email the +# about to expire file rather than the just rotated file +# (optional). +# maxage - The Integer maximum number of days that a rotated log file +# can stay on the system (optional). +# minsize - The String minimum size a log file must be to be rotated, +# but not before the scheduled rotation time (optional). +# The default units are bytes, append k, M or G for kilobytes, +# megabytes and gigabytes respectively. +# missingok - A Boolean specifying whether logrotate should ignore missing +# log files or issue an error (optional). +# olddir - A String path to a directory that rotated logs should be +# moved to (optional). +# postrotate - A command String or an Array of Strings that should be +# executed by /bin/sh after the log file is rotated +# optional). +# prerotate - A command String or an Array of Strings that should be +# executed by /bin/sh before the log file is rotated and +# only if it will be rotated (optional). +# firstaction - A command String or an Array of Strings that should be +# executed by /bin/sh once before all log files that match +# the wildcard pattern are rotated (optional). +# lastaction - A command String or an Array of Strings that should be +# executed by /bin/sh once after all the log files that match +# the wildcard pattern are rotated (optional). +# rotate - The Integer number of rotated log files to keep on disk +# (optional). +# rotate_every - How often the log files should be rotated as a String. +# Valid values are 'day', 'week', 'month' and 'year' +# (optional). +# size - The String size a log file has to reach before it will be +# rotated (optional). The default units are bytes, append k, +# M or G for kilobytes, megabytes or gigabytes respectively. +# sharedscripts - A Boolean specifying whether logrotate should run the +# postrotate and prerotate scripts for each matching file or +# just once (optional). +# shred - A Boolean specifying whether logs should be deleted with +# shred instead of unlink (optional). +# shredcycles - The Integer number of times shred should overwrite log files +# before unlinking them (optional). +# start - The Integer number to be used as the base for the extensions +# appended to the rotated log files (optional). +# su - A Boolean specifying whether logrotate should rotate under +# the specific su_owner and su_group instead of the default. +# First available in logrotate 3.8.0. (optional) +# su_owner - A username String that logrotate should use to rotate a +# log file set instead of using the default if +# su => true (optional). +# su_group - A String group name that logrotate should use to rotate a +# log file set instead of using the default if +# su => true (optional). +# uncompresscmd - The String command to be used to uncompress log files +# (optional). +# +# Examples +# +# # Rotate /var/log/syslog daily and keep 7 days worth of compressed logs. +# logrotate::rule { 'messages': +# path => '/var/log/messages', +# copytruncate => true, +# missingok => true, +# rotate_every => 'day', +# rotate => 7, +# compress => true, +# ifempty => true, +# } +# +# # Rotate /var/log/nginx/access_log weekly and keep 3 weeks of logs +# logrotate::rule { 'nginx_access_log': +# path => '/var/log/nginx/access_log', +# missingok => true, +# rotate_every => 'week', +# rotate => 3, +# postrotate => '/etc/init.d/nginx restart', +# } +define logrotate::rule( + $path = 'undef', + $ensure = 'present', + $compress = 'undef', + $compresscmd = 'undef', + $compressext = 'undef', + $compressoptions = 'undef', + $copy = 'undef', + $copytruncate = 'undef', + $create = 'undef', + $create_mode = 'undef', + $create_owner = 'undef', + $create_group = 'undef', + $dateext = 'undef', + $dateformat = 'undef', + $delaycompress = 'undef', + $extension = 'undef', + $ifempty = 'undef', + $mail = 'undef', + $mailfirst = 'undef', + $maillast = 'undef', + $maxage = 'undef', + $minsize = 'undef', + $missingok = 'undef', + $olddir = 'undef', + $postrotate = 'undef', + $prerotate = 'undef', + $firstaction = 'undef', + $lastaction = 'undef', + $rotate = 'undef', + $rotate_every = 'undef', + $size = 'undef', + $sharedscripts = 'undef', + $shred = 'undef', + $shredcycles = 'undef', + $start = 'undef', + $su = 'undef', + $su_owner = 'undef', + $su_group = 'undef', + $uncompresscmd = 'undef' + ) { + + ############################################################################# + # SANITY CHECK VALUES + + if $name !~ /^[a-zA-Z0-9\._-]+$/ { + fail("Logrotate::Rule[${name}]: namevar must be alphanumeric") + } + + case $ensure { + 'present': { + if $path == 'undef' { + fail("Logrotate::Rule[${name}]: path not specified") + } + } + 'absent': {} + default: { + fail("Logrotate::Rule[${name}]: invalid ensure value") + } + } + + case $compress { + 'undef': {} + true: { $sane_compress = 'compress' } + false: { $sane_compress = 'nocompress' } + default: { + fail("Logrotate::Rule[${name}]: compress must be a boolean") + } + } + + case $copy { + 'undef': {} + true: { $sane_copy = 'copy' } + false: { $sane_copy = 'nocopy' } + default: { + fail("Logrotate::Rule[${name}]: copy must be a boolean") + } + } + + case $copytruncate { + 'undef': {} + true: { $sane_copytruncate = 'copytruncate' } + false: { $sane_copytruncate = 'nocopytruncate' } + default: { + fail("Logrotate::Rule[${name}]: copytruncate must be a boolean") + } + } + + case $create { + 'undef': {} + true: { $sane_create = 'create' } + false: { $sane_create = 'nocreate' } + default: { + fail("Logrotate::Rule[${name}]: create must be a boolean") + } + } + + case $delaycompress { + 'undef': {} + true: { $sane_delaycompress = 'delaycompress' } + false: { $sane_delaycompress = 'nodelaycompress' } + default: { + fail("Logrotate::Rule[${name}]: delaycompress must be a boolean") + } + } + + case $dateext { + 'undef': {} + true: { $sane_dateext = 'dateext' } + false: { $sane_dateext = 'nodateext' } + default: { + fail("Logrotate::Rule[${name}]: dateext must be a boolean") + } + } + + case $mail { + 'undef': {} + false: { $sane_mail = 'nomail' } + default: { + $sane_mail = "mail ${mail}" + } + } + + case $missingok { + 'undef': {} + true: { $sane_missingok = 'missingok' } + false: { $sane_missingok = 'nomissingok' } + default: { + fail("Logrotate::Rule[${name}]: missingok must be a boolean") + } + } + + case $olddir { + 'undef': {} + false: { $sane_olddir = 'noolddir' } + default: { + $sane_olddir = "olddir ${olddir}" + } + } + + case $sharedscripts { + 'undef': {} + true: { $sane_sharedscripts = 'sharedscripts' } + false: { $sane_sharedscripts = 'nosharedscripts' } + default: { + fail("Logrotate::Rule[${name}]: sharedscripts must be a boolean") + } + } + + case $shred { + 'undef': {} + true: { $sane_shred = 'shred' } + false: { $sane_shred = 'noshred' } + default: { + fail("Logrotate::Rule[${name}]: shred must be a boolean") + } + } + + case $ifempty { + 'undef': {} + true: { $sane_ifempty = 'ifempty' } + false: { $sane_ifempty = 'notifempty' } + default: { + fail("Logrotate::Rule[${name}]: ifempty must be a boolean") + } + } + + case $rotate_every { + 'undef': {} + 'hour', 'hourly': {} + 'day': { $sane_rotate_every = 'daily' } + 'week': { $sane_rotate_every = 'weekly' } + 'month': { $sane_rotate_every = 'monthly' } + 'year': { $sane_rotate_every = 'yearly' } + 'daily', 'weekly','monthly','yearly': { $sane_rotate_every = $rotate_every } + default: { + fail("Logrotate::Rule[${name}]: invalid rotate_every value") + } + } + + case $maxage { + 'undef': {} + /^\d+$/: {} + default: { + fail("Logrotate::Rule[${name}]: maxage must be an integer") + } + } + + case $minsize { + 'undef': {} + /^\d+[kMG]?$/: {} + default: { + fail("Logrotate::Rule[${name}]: minsize must match /\\d+[kMG]?/") + } + } + + case $rotate { + 'undef': {} + /^\d+$/: {} + default: { + fail("Logrotate::Rule[${name}]: rotate must be an integer") + } + } + + case $size { + 'undef': {} + /^\d+[kMG]?$/: {} + default: { + fail("Logrotate::Rule[${name}]: size must match /\\d+[kMG]?/") + } + } + + case $shredcycles { + 'undef': {} + /^\d+$/: {} + default: { + fail("Logrotate::Rule[${name}]: shredcycles must be an integer") + } + } + + case $start { + 'undef': {} + /^\d+$/: {} + default: { + fail("Logrotate::Rule[${name}]: start must be an integer") + } + } + + case $su { + 'undef',false: {} + true: { $sane_su = 'su' } + default: { + fail("Logrotate::Rule[${name}]: su must be a boolean") + } + } + + case $mailfirst { + 'undef',false: {} + true: { + if $maillast == true { + fail("Logrotate::Rule[${name}]: Can't set both mailfirst and maillast") + } + + $sane_mailfirst = 'mailfirst' + } + default: { + fail("Logrotate::Rule[${name}]: mailfirst must be a boolean") + } + } + + case $maillast { + 'undef',false: {} + true: { + $sane_maillast = 'maillast' + } + default: { + fail("Logrotate::Rule[${name}]: maillast must be a boolean") + } + } + + if ($create_group != 'undef') and ($create_owner == 'undef') { + fail("Logrotate::Rule[${name}]: create_group requires create_owner") + } + + if ($create_owner != 'undef') and ($create_mode == 'undef') { + fail("Logrotate::Rule[${name}]: create_owner requires create_mode") + } + + if ($create_mode != 'undef') and ($create != true) { + fail("Logrotate::Rule[${name}]: create_mode requires create") + } + + # su requires at least su_owner + if ($su == true) and ($su_owner == 'undef') { + fail("Logrotate::Rule[${name}]: su requires su_owner and optional su_group") + } + + # su should be set to true if su_owner exists + if ($su_owner != 'undef') and ($su != true) { + fail("Logrotate::Rule[${name}]: su_owner requires su") + } + + ############################################################################# + # + + include logrotate::base + + case $rotate_every { + 'hour', 'hourly': { + include logrotate::hourly + $rule_path = "/etc/logrotate.d/hourly/${name}" + + file { "/etc/logrotate.d/${name}": + ensure => absent, + } + } + default: { + $rule_path = "/etc/logrotate.d/${name}" + + file { "/etc/logrotate.d/hourly/${name}": + ensure => absent, + } + } + } + + file { $rule_path: + ensure => $ensure, + owner => 'root', + group => 'root', + mode => '0444', + content => template('logrotate/etc/logrotate.d/rule.erb'), + require => Class['logrotate::base'], + } +} diff --git a/logrotate/script/bootstrap b/logrotate/script/bootstrap new file mode 100755 index 000000000..c9421072b --- /dev/null +++ b/logrotate/script/bootstrap @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +bundle install --path vendor/gems diff --git a/logrotate/script/cibuild b/logrotate/script/cibuild new file mode 100755 index 000000000..d1c8c0669 --- /dev/null +++ b/logrotate/script/cibuild @@ -0,0 +1,9 @@ +#!/usr/bin/env sh + +set -e + +[ -z "${PUPPET_VERSION}" ] && script/bootstrap + +bundle exec rspec + +bundle exec puppet-lint --with-filename --fail-on-warnings manifests diff --git a/logrotate/script/package b/logrotate/script/package new file mode 100755 index 000000000..eb3ee2ba1 --- /dev/null +++ b/logrotate/script/package @@ -0,0 +1,5 @@ +#!/bin/sh + +rm -rf pkg vendor Gemfile Gemfile.lock spec/fixtures +puppet module build +git checkout -- . diff --git a/logrotate/spec/classes/base_spec.rb b/logrotate/spec/classes/base_spec.rb new file mode 100644 index 000000000..933b481d8 --- /dev/null +++ b/logrotate/spec/classes/base_spec.rb @@ -0,0 +1,59 @@ +require 'spec_helper' + +describe 'logrotate::base' do + it do + should contain_package('logrotate').with_ensure('latest') + + should contain_file('/etc/logrotate.conf').with({ + 'ensure' => 'file', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0444', + 'source' => 'puppet:///modules/logrotate/etc/logrotate.conf', + 'require' => 'Package[logrotate]', + }) + + should contain_file('/etc/logrotate.d').with({ + 'ensure' => 'directory', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0755', + 'require' => 'Package[logrotate]', + }) + + should contain_file('/etc/cron.daily/logrotate').with({ + 'ensure' => 'file', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0555', + 'source' => 'puppet:///modules/logrotate/etc/cron.daily/logrotate', + 'require' => 'Package[logrotate]', + }) + end + + context 'on Debian' do + let(:facts) { {:osfamily => 'Debian'} } + + it { should contain_class('logrotate::defaults::debian') } + end + + context 'on RedHat' do + let(:facts) { {:osfamily => 'RedHat'} } + + it { should contain_class('logrotate::defaults::redhat') } + end + + context 'on SuSE' do + let(:facts) { {:osfamily => 'SuSE'} } + + it { should contain_class('logrotate::defaults::suse') } + end + + context 'on Gentoo' do + let(:facts) { {:operatingsystem => 'Gentoo'} } + + it { should_not contain_class('logrotate::defaults::debian') } + it { should_not contain_class('logrotate::defaults::redhat') } + it { should_not contain_class('logrotate::defaults::suse') } + end +end diff --git a/logrotate/spec/classes/defaults_debian_spec.rb b/logrotate/spec/classes/defaults_debian_spec.rb new file mode 100644 index 000000000..22be3b119 --- /dev/null +++ b/logrotate/spec/classes/defaults_debian_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe 'logrotate::defaults::debian' do + it do + should contain_logrotate__rule('wtmp').with({ + 'rotate_every' => 'month', + 'rotate' => '1', + 'create' => true, + 'create_mode' => '0664', + 'create_owner' => 'root', + 'create_group' => 'utmp', + 'missingok' => true, + }) + + should contain_logrotate__rule('btmp').with({ + 'rotate_every' => 'month', + 'rotate' => '1', + 'create' => true, + 'create_mode' => '0660', + 'create_owner' => 'root', + 'create_group' => 'utmp', + 'missingok' => true, + }) + end +end diff --git a/logrotate/spec/classes/hourly_spec.rb b/logrotate/spec/classes/hourly_spec.rb new file mode 100644 index 000000000..213f7ac8f --- /dev/null +++ b/logrotate/spec/classes/hourly_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +describe 'logrotate::hourly' do + context 'with default values' do + it do + should contain_file('/etc/logrotate.d/hourly').with({ + 'ensure' => 'directory', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0755', + }) + end + + it do + should contain_file('/etc/cron.hourly/logrotate').with({ + 'ensure' => 'present', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0555', + 'source' => 'puppet:///modules/logrotate/etc/cron.hourly/logrotate', + 'require' => [ + 'File[/etc/logrotate.d/hourly]', + 'Package[logrotate]', + ], + }) + end + end + + context 'with ensure => absent' do + let(:params) { {:ensure => 'absent'} } + + it { should contain_file('/etc/logrotate.d/hourly').with_ensure('absent') } + it { should contain_file('/etc/cron.hourly/logrotate').with_ensure('absent') } + end + + context 'with ensure => foo' do + let(:params) { {:ensure => 'foo'} } + + it do + expect { + should contain_file('/etc/logrotate.d/hourly') + }.to raise_error(Puppet::Error, /Invalid ensure value 'foo'/) + end + end +end diff --git a/logrotate/spec/defines/rule_spec.rb b/logrotate/spec/defines/rule_spec.rb new file mode 100644 index 000000000..4caa8ffaa --- /dev/null +++ b/logrotate/spec/defines/rule_spec.rb @@ -0,0 +1,1167 @@ +require 'spec_helper' + +describe 'logrotate::rule' do + context 'with an alphanumeric title' do + let(:title) { 'test' } + + context 'and ensure => absent' do + let(:params) { {:ensure => 'absent'} } + + it do + should contain_file('/etc/logrotate.d/test').with_ensure('absent') + end + end + + let(:params) { {:path => '/var/log/foo.log'} } + it do + should contain_class('logrotate::base') + should contain_file('/etc/logrotate.d/test').with({ + 'owner' => 'root', + 'group' => 'root', + 'ensure' => 'present', + 'mode' => '0444', + }).with_content(%r{^/var/log/foo\.log \{\n\}\n}) + end + + context 'with an array path' do + let (:params) { {:path => ['/var/log/foo1.log','/var/log/foo2.log']} } + it do + should contain_file('/etc/logrotate.d/test').with_content( + %r{/var/log/foo1\.log /var/log/foo2\.log \{\n\}\n} + ) + end + end + + ########################################################################### + # COMPRESS + context 'and compress => true' do + let(:params) { + {:path => '/var/log/foo.log', :compress => true} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ compress$/) + end + end + + context 'and compress => false' do + let(:params) { + {:path => '/var/log/foo.log', :compress => false} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ nocompress$/) + end + end + + context 'and compress => foo' do + let(:params) { + {:path => '/var/log/foo.log', :compress => 'foo'} + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /compress must be a boolean/) + end + end + + ########################################################################### + # COMPRESSCMD + context 'and compresscmd => bzip2' do + let(:params) { + {:path => '/var/log/foo.log', :compresscmd => 'bzip2'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ compresscmd bzip2$/) + end + end + + ########################################################################### + # COMPRESSEXT + context 'and compressext => .bz2' do + let(:params) { + {:path => '/var/log/foo.log', :compressext => '.bz2'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ compressext .bz2$/) + end + end + + ########################################################################### + # COMPRESSOPTIONS + context 'and compressoptions => -9' do + let(:params) { + {:path => '/var/log/foo.log', :compressoptions => '-9'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ compressoptions -9$/) + end + end + + ########################################################################### + # COPY + context 'and copy => true' do + let(:params) { + {:path => '/var/log/foo.log', :copy => true} + } + + it do + should contain_file('/etc/logrotate.d/test').with_content(/^ copy$/) + end + end + + context 'and copy => false' do + let(:params) { + {:path => '/var/log/foo.log', :copy => false} + } + + it do + should contain_file('/etc/logrotate.d/test').with_content(/^ nocopy$/) + end + end + + context 'and copy => foo' do + let(:params) { + {:path => '/var/log/foo.log', :copy => 'foo'} + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /copy must be a boolean/) + end + end + + ########################################################################### + # COPYTRUNCATE + context 'and copytruncate => true' do + let(:params) { + {:path => '/var/log/foo.log', :copytruncate => true} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ copytruncate$/) + end + end + + context 'and copytruncate => false' do + let(:params) { + {:path => '/var/log/foo.log', :copytruncate => false} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ nocopytruncate$/) + end + end + + context 'and copytruncate => foo' do + let(:params) { + {:path => '/var/log/foo.log', :copytruncate => 'foo'} + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /copytruncate must be a boolean/) + end + end + + ########################################################################### + # CREATE / CREATE_MODE / CREATE_OWNER / CREATE_GROUP + context 'and create => true' do + let(:params) { + {:path => '/var/log/foo.log', :create => true} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ create$/) + end + + context 'and create_mode => 0777' do + let(:params) { + { + :path => '/var/log/foo.log', + :create => true, + :create_mode => '0777', + } + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ create 0777$/) + end + + context 'and create_owner => www-data' do + let(:params) { + { + :path => '/var/log/foo.log', + :create => true, + :create_mode => '0777', + :create_owner => 'www-data', + } + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ create 0777 www-data/) + end + + context 'and create_group => admin' do + let(:params) { + { + :path => '/var/log/foo.log', + :create => true, + :create_mode => '0777', + :create_owner => 'www-data', + :create_group => 'admin', + } + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ create 0777 www-data admin$/) + end + end + end + + context 'and create_group => admin' do + let(:params) { + { + :path => '/var/log/foo.log', + :create => true, + :create_mode => '0777', + :create_group => 'admin', + } + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /create_group requires create_owner/) + end + end + end + + context 'and create_owner => www-data' do + let(:params) { + { + :path => '/var/log/foo.log', + :create => true, + :create_owner => 'www-data', + } + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /create_owner requires create_mode/) + end + end + end + + context 'and create => false' do + let(:params) { + {:path => '/var/log/foo.log', :create => false} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ nocreate$/) + end + + context 'and create_mode => 0777' do + let(:params) { + { + :path => '/var/log/foo.log', + :create => false, + :create_mode => '0777', + } + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /create_mode requires create/) + end + end + end + + context 'and create => foo' do + let(:params) { + {:path => '/var/log/foo.log', :create => 'foo'} + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /create must be a boolean/) + end + end + + ########################################################################### + # DATEEXT + context 'and dateext => true' do + let(:params) { + {:path => '/var/log/foo.log', :dateext => true} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ dateext$/) + end + end + + context 'and dateext => false' do + let(:params) { + {:path => '/var/log/foo.log', :dateext => false} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ nodateext$/) + end + end + + context 'and dateext => foo' do + let(:params) { + {:path => '/var/log/foo.log', :dateext => 'foo'} + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /dateext must be a boolean/) + end + end + + ########################################################################### + # DATEFORMAT + context 'and dateformat => -%Y%m%d' do + let(:params) { + {:path => '/var/log/foo.log', :dateformat => '-%Y%m%d'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ dateformat -%Y%m%d$/) + end + end + + ########################################################################### + # DELAYCOMPRESS + context 'and delaycompress => true' do + let(:params) { + {:path => '/var/log/foo.log', :delaycompress => true} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ delaycompress$/) + end + end + + context 'and delaycompress => false' do + let(:params) { + {:path => '/var/log/foo.log', :delaycompress => false} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ nodelaycompress$/) + end + end + + context 'and delaycompress => foo' do + let(:params) { + {:path => '/var/log/foo.log', :delaycompress => 'foo'} + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /delaycompress must be a boolean/) + end + end + + ########################################################################### + # EXTENSION + context 'and extension => foo' do + let(:params) { + {:path => '/var/log/foo.log', :extension => '.foo'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ extension \.foo$/) + end + end + + ########################################################################### + # IFEMPTY + context 'and ifempty => true' do + let(:params) { + {:path => '/var/log/foo.log', :ifempty => true} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ ifempty$/) + end + end + + context 'and ifempty => false' do + let(:params) { + {:path => '/var/log/foo.log', :ifempty => false} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ notifempty$/) + end + end + + context 'and ifempty => foo' do + let(:params) { + {:path => '/var/log/foo.log', :ifempty => 'foo'} + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /ifempty must be a boolean/) + end + end + + ########################################################################### + # MAIL / MAILFIRST / MAILLAST + context 'and mail => test.example.com' do + let(:params) { + {:path => '/var/log/foo.log', :mail => 'test@example.com'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ mail test@example.com$/) + end + + context 'and mailfirst => true' do + let(:params) { + { + :path => '/var/log/foo.log', + :mail => 'test@example.com', + :mailfirst => true, + } + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ mailfirst$/) + end + + context 'and maillast => true' do + let(:params) { + { + :path => '/var/log/foo.log', + :mail => 'test@example.com', + :mailfirst => true, + :maillast => true, + } + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /set both mailfirst and maillast/) + end + end + end + + context 'and maillast => true' do + let(:params) { + { + :path => '/var/log/foo.log', + :mail => 'test@example.com', + :maillast => true, + } + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ maillast$/) + end + end + end + + context 'and mail => false' do + let(:params) { + {:path => '/var/log/foo.log', :mail => false} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ nomail$/) + end + end + + ########################################################################### + # MAXAGE + context 'and maxage => 3' do + let(:params) { + {:path => '/var/log/foo.log', :maxage => 3} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ maxage 3$/) + end + end + + context 'and maxage => foo' do + let(:params) { + {:path => '/var/log/foo.log', :maxage => 'foo'} + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /maxage must be an integer/) + end + end + + ########################################################################### + # MINSIZE + context 'and minsize => 100' do + let(:params) { + {:path => '/var/log/foo.log', :minsize => 100} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ minsize 100$/) + end + end + + context 'and minsize => 100k' do + let(:params) { + {:path => '/var/log/foo.log', :minsize => '100k'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ minsize 100k$/) + end + end + + context 'and minsize => 100M' do + let(:params) { + {:path => '/var/log/foo.log', :minsize => '100M'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ minsize 100M$/) + end + end + + context 'and minsize => 100G' do + let(:params) { + {:path => '/var/log/foo.log', :minsize => '100G'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ minsize 100G$/) + end + end + + context 'and minsize => foo' do + let(:params) { + {:path => '/var/log/foo.log', :minsize => 'foo'} + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /minsize must match/) + end + end + + ########################################################################### + # MISSINGOK + context 'and missingok => true' do + let(:params) { + {:path => '/var/log/foo.log', :missingok => true} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ missingok$/) + end + end + + context 'and missingok => false' do + let(:params) { + {:path => '/var/log/foo.log', :missingok => false} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ nomissingok$/) + end + end + + context 'and missingok => foo' do + let(:params) { + {:path => '/var/log/foo.log', :missingok => 'foo'} + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /missingok must be a boolean/) + end + end + + ########################################################################### + # OLDDIR + context 'and olddir => /var/log/old' do + let(:params) { + {:path => '/var/log/foo.log', :olddir => '/var/log/old'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ olddir \/var\/log\/old$/) + end + end + + context 'and olddir => false' do + let(:params) { + {:path => '/var/log/foo.log', :olddir => false} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ noolddir$/) + end + end + + ########################################################################### + # POSTROTATE + context 'and postrotate => /bin/true' do + let(:params) { + {:path => '/var/log/foo.log', :postrotate => '/bin/true'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/postrotate\n \/bin\/true\n endscript/) + end + end + + context "and postrotate => ['/bin/true', '/bin/false']" do + let(:params) { + {:path => '/var/log/foo.log', :postrotate => ['/bin/true', '/bin/false']} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/postrotate\n \/bin\/true\n \/bin\/false\n endscript/) + end + end + + ########################################################################### + # PREROTATE + context 'and prerotate => /bin/true' do + let(:params) { + {:path => '/var/log/foo.log', :prerotate => '/bin/true'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/prerotate\n \/bin\/true\n endscript/) + end + end + + context "and prerotate => ['/bin/true', '/bin/false']" do + let(:params) { + {:path => '/var/log/foo.log', :prerotate => ['/bin/true', '/bin/false']} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/prerotate\n \/bin\/true\n \/bin\/false\n endscript/) + end + end + + ########################################################################### + # FIRSTACTION + context 'and firstaction => /bin/true' do + let(:params) { + {:path => '/var/log/foo.log', :firstaction => '/bin/true'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/firstaction\n \/bin\/true\n endscript/) + end + end + + context "and firstaction => ['/bin/true', '/bin/false']" do + let(:params) { + {:path => '/var/log/foo.log', :firstaction => ['/bin/true', '/bin/false']} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/firstaction\n \/bin\/true\n \/bin\/false\n endscript/) + end + end + + ########################################################################### + # LASTACTION + context 'and lastaction => /bin/true' do + let(:params) { + {:path => '/var/log/foo.log', :lastaction => '/bin/true'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/lastaction\n \/bin\/true\n endscript/) + end + end + + context "and lastaction => ['/bin/true', '/bin/false']" do + let(:params) { + {:path => '/var/log/foo.log', :lastaction => ['/bin/true', '/bin/false']} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/lastaction\n \/bin\/true\n \/bin\/false\n endscript/) + end + end + + ########################################################################### + # ROTATE + context 'and rotate => 3' do + let(:params) { + {:path => '/var/log/foo.log', :rotate => 3} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ rotate 3$/) + end + end + + context 'and rotate => foo' do + let(:params) { + {:path => '/var/log/foo.log', :rotate => 'foo'} + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /rotate must be an integer/) + end + end + + ########################################################################### + # ROTATE_EVERY + context 'and rotate_every => hour' do + let(:params) { + {:path => '/var/log/foo.log', :rotate_every => 'hour'} + } + + it { should contain_class('logrotate::hourly') } + it { should contain_file('/etc/logrotate.d/hourly/test') } + it { should contain_file('/etc/logrotate.d/test').with_ensure('absent') } + end + + context 'and rotate_every => day' do + let(:params) { + {:path => '/var/log/foo.log', :rotate_every => 'day'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ daily$/) + end + + it do + should contain_file('/etc/logrotate.d/hourly/test') \ + .with_ensure('absent') + end + end + + context 'and rotate_every => week' do + let(:params) { + {:path => '/var/log/foo.log', :rotate_every => 'week'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ weekly$/) + end + + it do + should contain_file('/etc/logrotate.d/hourly/test') \ + .with_ensure('absent') + end + end + + context 'and rotate_every => month' do + let(:params) { + {:path => '/var/log/foo.log', :rotate_every => 'month'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ monthly$/) + end + + it do + should contain_file('/etc/logrotate.d/hourly/test') \ + .with_ensure('absent') + end + end + + context 'and rotate_every => year' do + let(:params) { + {:path => '/var/log/foo.log', :rotate_every => 'year'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ yearly$/) + end + + it do + should contain_file('/etc/logrotate.d/hourly/test') \ + .with_ensure('absent') + end + end + + context 'and rotate_every => foo' do + let(:params) { + {:path => '/var/log/foo.log', :rotate_every => 'foo'} + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /invalid rotate_every value/) + end + end + + ########################################################################### + # SIZE + context 'and size => 100' do + let(:params) { + {:path => '/var/log/foo.log', :size => 100} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ size 100$/) + end + end + + context 'and size => 100k' do + let(:params) { + {:path => '/var/log/foo.log', :size => '100k'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ size 100k$/) + end + end + + context 'and size => 100M' do + let(:params) { + {:path => '/var/log/foo.log', :size => '100M'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ size 100M$/) + end + end + + context 'and size => 100G' do + let(:params) { + {:path => '/var/log/foo.log', :size => '100G'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ size 100G$/) + end + end + + context 'and size => foo' do + let(:params) { + {:path => '/var/log/foo.log', :size => 'foo'} + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /size must match/) + end + end + + ########################################################################### + # SHAREDSCRIPTS + context 'and sharedscripts => true' do + let(:params) { + {:path => '/var/log/foo.log', :sharedscripts => true} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ sharedscripts$/) + end + end + + context 'and sharedscripts => false' do + let(:params) { + {:path => '/var/log/foo.log', :sharedscripts => false} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ nosharedscripts$/) + end + end + + context 'and sharedscripts => foo' do + let(:params) { + {:path => '/var/log/foo.log', :sharedscripts => 'foo'} + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /sharedscripts must be a boolean/) + end + end + + ########################################################################### + # SHRED / SHREDCYCLES + context 'and shred => true' do + let(:params) { + {:path => '/var/log/foo.log', :shred => true} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ shred$/) + end + + context 'and shredcycles => 3' do + let(:params) { + {:path => '/var/log/foo.log', :shred => true, :shredcycles => 3} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ shredcycles 3$/) + end + end + + context 'and shredcycles => foo' do + let(:params) { + {:path => '/var/log/foo.log', :shred => true, :shredcycles => 'foo'} + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /shredcycles must be an integer/) + end + end + end + + context 'and shred => false' do + let(:params) { + {:path => '/var/log/foo.log', :shred => false} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ noshred$/) + end + end + + context 'and shred => foo' do + let(:params) { + {:path => '/var/log/foo.log', :shred => 'foo'} + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /shred must be a boolean/) + end + end + + ########################################################################### + # START + context 'and start => 0' do + let(:params) { + {:path => '/var/log/foo.log', :start => 0} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ start 0$/) + end + end + + context 'and start => foo' do + let(:params) { + {:path => '/var/log/foo.log', :start => 'foo'} + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /start must be an integer/) + end + end + + ########################################################################### + # SU / SU_OWNER / SU_GROUP + context 'and su => true' do + let(:params) { + {:path => '/var/log/foo.log', :su => true} + } + + context 'and su_owner => www-data' do + let(:params) { + { + :path => '/var/log/foo.log', + :su => true, + :su_owner => 'www-data', + } + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ su www-data/) + end + + context 'and su_group => admin' do + let(:params) { + { + :path => '/var/log/foo.log', + :su => true, + :su_owner => 'www-data', + :su_group => 'admin', + } + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ su www-data admin$/) + end + end + end + + context 'and missing su_owner' do + let(:params) { + { + :path => '/var/log/foo.log', + :su => true, + } + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /su requires su_owner/) + end + end + end + + context 'and su => false' do + let(:params) { + {:path => '/var/log/foo.log', :su => false} + } + + it do + should_not contain_file('/etc/logrotate.d/test') \ + .with_content(/^ su\s/) + end + + context 'and su_owner => wwww-data' do + let(:params) { + { + :path => '/var/log/foo.log', + :su => false, + :su_owner => 'www-data', + } + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /su_owner requires su/) + end + end + end + + context 'and su => foo' do + let(:params) { + {:path => '/var/log/foo.log', :su => 'foo'} + } + + it do + expect { + should contain_file('/etc/logrotate.d/test') + }.to raise_error(Puppet::Error, /su must be a boolean/) + end + end + + ########################################################################### + # UNCOMPRESSCMD + context 'and uncompresscmd => bunzip2' do + let(:params) { + {:path => '/var/log/foo.log', :uncompresscmd => 'bunzip2'} + } + + it do + should contain_file('/etc/logrotate.d/test') \ + .with_content(/^ uncompresscmd bunzip2$/) + end + end + end + + context 'with a non-alphanumeric title' do + let(:title) { 'foo bar' } + let(:params) { + {:path => '/var/log/foo.log'} + } + + it do + expect { + should contain_file('/etc/logrotate.d/foo bar') + }.to raise_error(Puppet::Error, /namevar must be alphanumeric/) + end + end +end diff --git a/logrotate/spec/fixtures/manifests/site.pp b/logrotate/spec/fixtures/manifests/site.pp new file mode 100644 index 000000000..e69de29bb diff --git a/logrotate/spec/fixtures/modules/logrotate/files b/logrotate/spec/fixtures/modules/logrotate/files new file mode 120000 index 000000000..84dafe8de --- /dev/null +++ b/logrotate/spec/fixtures/modules/logrotate/files @@ -0,0 +1 @@ +../../../../files \ No newline at end of file diff --git a/logrotate/spec/fixtures/modules/logrotate/manifests b/logrotate/spec/fixtures/modules/logrotate/manifests new file mode 120000 index 000000000..373b99206 --- /dev/null +++ b/logrotate/spec/fixtures/modules/logrotate/manifests @@ -0,0 +1 @@ +../../../../manifests \ No newline at end of file diff --git a/logrotate/spec/fixtures/modules/logrotate/templates b/logrotate/spec/fixtures/modules/logrotate/templates new file mode 120000 index 000000000..f8a06d1d1 --- /dev/null +++ b/logrotate/spec/fixtures/modules/logrotate/templates @@ -0,0 +1 @@ +../../../../templates \ No newline at end of file diff --git a/logrotate/spec/spec_helper.rb b/logrotate/spec/spec_helper.rb new file mode 100644 index 000000000..8c3a6cb51 --- /dev/null +++ b/logrotate/spec/spec_helper.rb @@ -0,0 +1,6 @@ +require 'rspec-puppet' + +RSpec.configure do |c| + c.module_path = File.expand_path(File.join(__FILE__, '..', 'fixtures', 'modules')) + c.manifest_dir = File.expand_path(File.join(__FILE__, '..', 'fixtures', 'manifests')) +end diff --git a/logrotate/templates/etc/logrotate.d/rule.erb b/logrotate/templates/etc/logrotate.d/rule.erb new file mode 100644 index 000000000..67ca0148a --- /dev/null +++ b/logrotate/templates/etc/logrotate.d/rule.erb @@ -0,0 +1,84 @@ +# THIS FILE IS AUTOMATICALLY DISTRIBUTED BY PUPPET. ANY CHANGES WILL BE +# OVERWRITTEN. + +<% + opts = [] + + if @path.kind_of?(Array) + rpath = @path.join(' ') + else + rpath = @path + end + + if scope.to_hash.has_key?('sane_create') + if @sane_create == 'create' + opts << [@sane_create, @create_mode, @create_owner, @create_group].reject { |r| + r == 'undef' + }.join(' ') + else + opts << @sane_create + end + end + + if scope.to_hash.has_key?('sane_su') + if @sane_su == 'su' + opts << [@sane_su, @su_owner, @su_group].reject { |r| + r == 'undef' + }.join(' ') + end + end + + [ + 'compress', 'copy', 'copytruncate', 'delaycompress', 'dateext', + 'mail', 'missingok', 'olddir', 'sharedscripts', 'ifempty', 'maillast', + 'mailfirst', 'shred', 'rotate_every' + ].each do |bool| + opts << scope.to_hash["sane_#{bool}"] if scope.to_hash.has_key?("sane_#{bool}") + end + + [ + 'compresscmd', 'compressext', 'compressoptions', 'dateformat', 'extension', + 'maxage', 'minsize', 'rotate', 'size', 'shredcycles', 'start', + 'uncompresscmd' + ].each do |key| + value = scope.to_hash[key] + opts << "#{key} #{value}" if value != 'undef' + end +-%> +<%= rpath %> { +<% opts.sort_by{|key,value| key}.each do |opt| -%> + <%= opt %> +<% end -%> +<% if @postrotate != 'undef' -%> + postrotate + <%- @postrotate = [@postrotate] unless @postrotate.is_a?(Array) -%> + <%- @postrotate.each do |val| -%> + <%= val %> + <%- end -%> + endscript +<% end -%> +<% if @prerotate != 'undef' -%> + prerotate + <%- @prerotate = [@prerotate] unless @prerotate.is_a?(Array) -%> + <%- @prerotate.each do |val| -%> + <%= val %> + <%- end -%> + endscript +<% end -%> +<% if @firstaction != 'undef' -%> + firstaction + <%- @firstaction = [@firstaction] unless @firstaction.is_a?(Array) -%> + <%- @firstaction.each do |val| -%> + <%= val %> + <%- end -%> + endscript +<% end -%> +<% if @lastaction != 'undef' -%> + lastaction + <%- @lastaction = [@lastaction] unless @lastaction.is_a?(Array) -%> + <%- @lastaction.each do |val| -%> + <%= val %> + <%- end -%> + endscript +<% end -%> +} diff --git a/logrotate/vendor/cache/diff-lcs-1.2.5.gem b/logrotate/vendor/cache/diff-lcs-1.2.5.gem new file mode 100644 index 000000000..e4436ccc5 Binary files /dev/null and b/logrotate/vendor/cache/diff-lcs-1.2.5.gem differ diff --git a/logrotate/vendor/cache/facter-1.7.1.gem b/logrotate/vendor/cache/facter-1.7.1.gem new file mode 100644 index 000000000..de5126192 Binary files /dev/null and b/logrotate/vendor/cache/facter-1.7.1.gem differ diff --git a/logrotate/vendor/cache/hiera-1.2.1.gem b/logrotate/vendor/cache/hiera-1.2.1.gem new file mode 100644 index 000000000..cad36e172 Binary files /dev/null and b/logrotate/vendor/cache/hiera-1.2.1.gem differ diff --git a/logrotate/vendor/cache/json_pure-1.8.0.gem b/logrotate/vendor/cache/json_pure-1.8.0.gem new file mode 100644 index 000000000..6486fa0df Binary files /dev/null and b/logrotate/vendor/cache/json_pure-1.8.0.gem differ diff --git a/logrotate/vendor/cache/metaclass-0.0.1.gem b/logrotate/vendor/cache/metaclass-0.0.1.gem new file mode 100644 index 000000000..0054609ea Binary files /dev/null and b/logrotate/vendor/cache/metaclass-0.0.1.gem differ diff --git a/logrotate/vendor/cache/mocha-0.14.0.gem b/logrotate/vendor/cache/mocha-0.14.0.gem new file mode 100644 index 000000000..08f06c47f Binary files /dev/null and b/logrotate/vendor/cache/mocha-0.14.0.gem differ diff --git a/logrotate/vendor/cache/puppet-3.2.1.gem b/logrotate/vendor/cache/puppet-3.2.1.gem new file mode 100644 index 000000000..853170bf1 Binary files /dev/null and b/logrotate/vendor/cache/puppet-3.2.1.gem differ diff --git a/logrotate/vendor/cache/puppet-lint-0.3.2.gem b/logrotate/vendor/cache/puppet-lint-0.3.2.gem new file mode 100644 index 000000000..e87018399 Binary files /dev/null and b/logrotate/vendor/cache/puppet-lint-0.3.2.gem differ diff --git a/logrotate/vendor/cache/rake-10.0.4.gem b/logrotate/vendor/cache/rake-10.0.4.gem new file mode 100644 index 000000000..773869c2e Binary files /dev/null and b/logrotate/vendor/cache/rake-10.0.4.gem differ diff --git a/logrotate/vendor/cache/rgen-0.6.2.gem b/logrotate/vendor/cache/rgen-0.6.2.gem new file mode 100644 index 000000000..8306d99ce Binary files /dev/null and b/logrotate/vendor/cache/rgen-0.6.2.gem differ diff --git a/logrotate/vendor/cache/rspec-2.14.1.gem b/logrotate/vendor/cache/rspec-2.14.1.gem new file mode 100644 index 000000000..ea2c04a53 Binary files /dev/null and b/logrotate/vendor/cache/rspec-2.14.1.gem differ diff --git a/logrotate/vendor/cache/rspec-core-2.14.7.gem b/logrotate/vendor/cache/rspec-core-2.14.7.gem new file mode 100644 index 000000000..5ebd87780 Binary files /dev/null and b/logrotate/vendor/cache/rspec-core-2.14.7.gem differ diff --git a/logrotate/vendor/cache/rspec-expectations-2.14.4.gem b/logrotate/vendor/cache/rspec-expectations-2.14.4.gem new file mode 100644 index 000000000..887b58b46 Binary files /dev/null and b/logrotate/vendor/cache/rspec-expectations-2.14.4.gem differ diff --git a/logrotate/vendor/cache/rspec-mocks-2.14.4.gem b/logrotate/vendor/cache/rspec-mocks-2.14.4.gem new file mode 100644 index 000000000..be3c6fd95 Binary files /dev/null and b/logrotate/vendor/cache/rspec-mocks-2.14.4.gem differ diff --git a/logrotate/vendor/cache/rspec-puppet-1.0.1.gem b/logrotate/vendor/cache/rspec-puppet-1.0.1.gem new file mode 100644 index 000000000..122a14b85 Binary files /dev/null and b/logrotate/vendor/cache/rspec-puppet-1.0.1.gem differ diff --git a/memcached/.gemfile b/memcached/.gemfile index 9aad840c0..e9e12704e 100644 --- a/memcached/.gemfile +++ b/memcached/.gemfile @@ -1,5 +1,14 @@ -source :rubygems +source 'https://rubygems.org' -puppetversion = ENV.key?('PUPPET_VERSION') ? "= #{ENV['PUPPET_VERSION']}" : ['>= 2.7'] -gem 'puppet', puppetversion -gem 'puppetlabs_spec_helper', '>= 0.1.0' +group :development, :test do + gem 'rake', :require => false + gem 'puppet-lint', :require => false + gem 'rspec-puppet', :require => false + gem 'puppetlabs_spec_helper', :require => false +end + +if puppetversion = ENV['PUPPET_GEM_VERSION'] + gem 'puppet', puppetversion, :require => false +else + gem 'puppet', :require => false +end diff --git a/memcached/.gitignore b/memcached/.gitignore index 37e8aed56..1bccb81ed 100644 --- a/memcached/.gitignore +++ b/memcached/.gitignore @@ -1,3 +1,4 @@ pkg/ *.swp -/metadata.json \ No newline at end of file +/metadata.json +.forge-releng/ diff --git a/memcached/.travis.yml b/memcached/.travis.yml index 37e322a76..f98750476 100644 --- a/memcached/.travis.yml +++ b/memcached/.travis.yml @@ -1,17 +1,37 @@ -language: ruby -rvm: - - 1.8.7 -before_script: -after_script: -script: "rake spec" +--- branches: only: - master +language: ruby +bundler_args: --without development +script: bundle exec rake spec SPEC_OPTS='--format documentation' +after_success: + - git clone -q git://github.com/puppetlabs/ghpublisher.git .forge-releng + - .forge-releng/publish +rvm: + - 1.8.7 + - 1.9.3 env: - - PUPPET_VERSION=2.7.13 - - PUPPET_VERSION=2.7.6 - - PUPPET_VERSION=2.6.9 - - PUPPET_VERSION=3.0.0 + matrix: + - PUPPET_GEM_VERSION="~> 2.7.0" + - PUPPET_GEM_VERSION="~> 3.0.0" + - PUPPET_GEM_VERSION="~> 3.1.0" + - PUPPET_GEM_VERSION="~> 3.2.0" + - PUPPET_GEM_VERSION="~> 3.3.0" + global: + - PUBLISHER_LOGIN=saz + - secure: |- + KHycFEf0ALVjITczYG0pcfk912muQkbJiGzKa5yyC8C9ppDW+dTYgDQu8AO1KXFHzds + NUASY2XNjrJNv27w7A2eMp88qU1ID1s8CWALph4fuxGcM/HoPw9q8sldJ9/sHGlY9Ye + DEeIvgt9qkwKtG/kb7dN7la42nv5fffWE95OU= +matrix: + include: + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 3.2.0" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 3.3.0" + - rvm: 1.8.7 + env: PUPPET_GEM_VERSION="~> 2.6.0" notifications: email: false gemfile: .gemfile diff --git a/memcached/Modulefile b/memcached/Modulefile index 3f339a9c6..9039e169e 100644 --- a/memcached/Modulefile +++ b/memcached/Modulefile @@ -1,5 +1,5 @@ name 'saz-memcached' -version '2.0.4' +version '2.2.4' source 'git://github.com/saz/puppet-memcached.git' author 'saz' license 'Apache License, Version 2.0' diff --git a/memcached/README.md b/memcached/README.md index 01bded1b5..dceda1bf4 100644 --- a/memcached/README.md +++ b/memcached/README.md @@ -1,9 +1,10 @@ -# puppet-memcached - -[![Build Status](https://secure.travis-ci.org/saz/puppet-memcached.png)](http://travis-ci.org/saz/puppet-memcached) +# puppet-memcached [![Build Status](https://secure.travis-ci.org/saz/puppet-memcached.png)](http://travis-ci.org/saz/puppet-memcached) Manage memcached via Puppet +## Show some love +If you find this module useful, send some bitcoins to 1Na3YFUmdxKxJLiuRXQYJU2kiNqA3KY2j9 + ## How to use ### Use roughly 90% of memory @@ -30,10 +31,17 @@ Manage memcached via Puppet ### Other class parameters +* $package_ensure = 'present' * $logfile = '/var/log/memcached.log' +* $max_memory = false +* $item_size = false +* $lock_memory = false (WARNING: good if used intelligently, google for -k key) * $listen_ip = '0.0.0.0' * $tcp_port = 11211 * $udp_port = 11211 * $user = '' (OS specific setting, see params.pp) * $max_connections = 8192 -* $lock_memory = false (WARNING: good if used intelligently, google for -k key) +* $verbosity = undef +* $unix_socket = undef +* $install_dev = false (TRUE if 'libmemcached-dev' package should be installed) +* $processorcount = $::processorcount diff --git a/memcached/manifests/init.pp b/memcached/manifests/init.pp index 8d1ca1540..6aac02bd7 100644 --- a/memcached/manifests/init.pp +++ b/memcached/manifests/init.pp @@ -2,6 +2,7 @@ $package_ensure = 'present', $logfile = '/var/log/memcached.log', $max_memory = false, + $item_size = false, $lock_memory = false, $listen_ip = '0.0.0.0', $tcp_port = 11211, @@ -9,13 +10,28 @@ $user = $::memcached::params::user, $max_connections = '8192', $verbosity = undef, - $unix_socket = undef + $unix_socket = undef, + $install_dev = false, + $processorcount = $::processorcount ) inherits memcached::params { + if $package_ensure == 'absent' { + $service_ensure = 'stopped' + } else { + $service_ensure = 'running' + } + package { $memcached::params::package_name: ensure => $package_ensure, } + if $install_dev { + package { $memcached::params::dev_package_name: + ensure => $package_ensure, + require => Package[$memcached::params::package_name] + } + } + file { $memcached::params::config_file: owner => 'root', group => 'root', @@ -25,10 +41,10 @@ } service { $memcached::params::service_name: - ensure => running, + ensure => $service_ensure, enable => true, hasrestart => true, - hasstatus => false, + hasstatus => $memcached::params::service_hasstatus, subscribe => File[$memcached::params::config_file], } } diff --git a/memcached/manifests/params.pp b/memcached/manifests/params.pp index 9c1ca4710..ee77e1e1e 100644 --- a/memcached/manifests/params.pp +++ b/memcached/manifests/params.pp @@ -1,18 +1,22 @@ class memcached::params { case $::osfamily { 'Debian': { - $package_name = 'memcached' - $service_name = 'memcached' - $config_file = '/etc/memcached.conf' - $config_tmpl = "$module_name/memcached.conf.erb" - $user = 'nobody' + $package_name = 'memcached' + $service_name = 'memcached' + $service_hasstatus = false + $dev_package_name = 'libmemcached-dev' + $config_file = '/etc/memcached.conf' + $config_tmpl = "${module_name}/memcached.conf.erb" + $user = 'nobody' } 'RedHat': { - $package_name = 'memcached' - $service_name = 'memcached' - $config_file = '/etc/sysconfig/memcached' - $config_tmpl = "$module_name/memcached_sysconfig.erb" - $user = 'memcached' + $package_name = 'memcached' + $service_name = 'memcached' + $service_hasstatus = true + $dev_package_name = 'libmemcached-devel' + $config_file = '/etc/sysconfig/memcached' + $config_tmpl = "${module_name}/memcached_sysconfig.erb" + $user = 'memcached' } default: { fail("Unsupported platform: ${::osfamily}") diff --git a/memcached/spec/classes/memcached_spec.rb b/memcached/spec/classes/memcached_spec.rb index d39701d16..a56888f96 100644 --- a/memcached/spec/classes/memcached_spec.rb +++ b/memcached/spec/classes/memcached_spec.rb @@ -6,12 +6,15 @@ :package_ensure => 'present', :logfile => '/var/log/memcached.log', :max_memory => false, + :item_size => false, :lock_memory => false, :listen_ip => '0.0.0.0', :tcp_port => '11211', :udp_port => '11211', :user => 'nobody', - :max_connections => '8192' + :max_connections => '8192', + :install_dev => false, + :processorcount => 1 } end @@ -20,13 +23,15 @@ :package_ensure => 'latest', :logfile => '/var/log/memcached.log', :max_memory => '2', + :item_size => false, :lock_memory => true, :listen_ip => '127.0.0.1', :tcp_port => '11212', :udp_port => '11213', :user => 'somebdy', :max_connections => '8193', - :verbosity => 'vvv' + :verbosity => 'vvv', + :processorcount => 3 }, { :package_ensure => 'present', @@ -38,7 +43,9 @@ :udp_port => '11213', :user => 'somebdy', :max_connections => '8193', - :verbosity => 'vvv' + :verbosity => 'vvv', + :install_dev => true, + :processorcount => 1 } ].each do |param_set| describe "when #{param_set == {} ? "using default" : "specifying"} class parameters" do @@ -63,16 +70,22 @@ describe "on supported osfamily: #{osfamily}" do - it { should contain_class('memcached::params') } + it { should contain_class("memcached::params") } - it { should contain_package('memcached').with_ensure(param_hash[:package_ensure]) } + it { should contain_package("memcached").with_ensure(param_hash[:package_ensure]) } - it { should contain_file('/etc/memcached.conf').with( + it { + if param_hash[:install_dev] + should contain_package("libmemcached-dev").with_ensure(param_hash[:package_ensure]) + end + } + + it { should contain_file("/etc/memcached.conf").with( 'owner' => 'root', 'group' => 'root' )} - it { should contain_service('memcached').with( + it { should contain_service("memcached").with( 'ensure' => 'running', 'enable' => true, 'hasrestart' => true, @@ -93,7 +106,7 @@ "-U #{param_hash[:udp_port]}", "-u #{param_hash[:user]}", "-c #{param_hash[:max_connections]}", - "-t #{facts[:processorcount]}" + "-t #{param_hash[:processorcount]}" ] if(param_hash[:max_memory]) if(param_hash[:max_memory].end_with?('%')) diff --git a/memcached/templates/memcached.conf.erb b/memcached/templates/memcached.conf.erb index 03344d5da..9b48ce9e4 100644 --- a/memcached/templates/memcached.conf.erb +++ b/memcached/templates/memcached.conf.erb @@ -7,16 +7,16 @@ -P /var/run/memcached.pid # Log memcached's output -logfile <%= logfile -%> +logfile <%= @logfile -%> <% if @verbosity -%> # Verbosity --<%= verbosity %> +-<%= @verbosity %> <% end -%> # Use MB memory max to use for object storage. <% Puppet::Parser::Functions.function('memcached_max_memory') -%> --m <%= scope.function_memcached_max_memory([max_memory]) %> +-m <%= scope.function_memcached_max_memory([@max_memory]) %> <% if @lock_memory -%> # Lock down all paged memory. There is a limit on how much memory you may lock. @@ -25,23 +25,29 @@ logfile <%= logfile -%> <% if @unix_socket -%> # UNIX socket path to listen on --s <%= unix_socket %> +-s <%= @unix_socket %> <% else -%> # IP to listen on --l <%= listen_ip %> +-l <%= @listen_ip %> # TCP port to listen on --p <%= tcp_port %> +-p <%= @tcp_port %> # UDP port to listen on --U <%= udp_port %> +-U <%= @udp_port %> <% end -%> # Run daemon as user --u <%= user %> +-u <%= @user %> # Limit the number of simultaneous incoming connections. --c <%= max_connections %> +-c <%= @max_connections %> # Number of threads to use to process incoming requests. --t <%= processorcount %> +-t <%= @processorcount %> + +<% if @item_size -%> +# Override the default size of each slab page +-I <%= @item_size %> +<% end -%> + diff --git a/memcached/templates/memcached_sysconfig.erb b/memcached/templates/memcached_sysconfig.erb index 3c980309b..9f20c5895 100644 --- a/memcached/templates/memcached_sysconfig.erb +++ b/memcached/templates/memcached_sysconfig.erb @@ -1,21 +1,24 @@ -PORT="<%= tcp_port %>" -USER="<%= user %>" -MAXCONN="<%= max_connections %>" +PORT="<%= @tcp_port %>" +USER="<%= @user %>" +MAXCONN="<%= @max_connections %>" <% Puppet::Parser::Functions.function('memcached_max_memory') -%> -CACHESIZE="<%= scope.function_memcached_max_memory([max_memory]) %>" +CACHESIZE="<%= scope.function_memcached_max_memory([@max_memory]) %>" OPTIONS="<% result = [] if @verbosity - result << '-' + verbosity + result << '-' + @verbosity end if @lock_memory result << '-k' end if @listen_ip - result << '-l ' + listen_ip + result << '-l ' + @listen_ip end if @udp_port - result << '-U ' + udp_port + result << '-U ' + @udp_port end -result << '-t ' + processorcount +if @item_size + result << '-I ' + @item_size +end +result << '-t ' + @processorcount -%><%= result.join(' ') -%>" diff --git a/module-data/Modulefile b/module-data/Modulefile deleted file mode 100644 index 5c05ad258..000000000 --- a/module-data/Modulefile +++ /dev/null @@ -1,6 +0,0 @@ -name 'ripienaar-module_data' -version '0.0.3' -description 'A hiera backend to allow the use of data while writing sharable modules' -project_page 'https://github.com/ripienaar/puppet-module-data' -license 'ASL 2.0' -author 'R.I.Pienaar ' diff --git a/module-data/README.md b/module-data/README.md deleted file mode 100644 index d1409ecb9..000000000 --- a/module-data/README.md +++ /dev/null @@ -1,61 +0,0 @@ -What? -===== - -While hiera does a decent job of separating code and data for users -it is quite difficult for module authors to use hiera to create reusable -modules. This is because the puppet backend is optional and even when -it is loaded the module author cannot influence the hierarchy. - -With this commit we add a new module_data backend that loads data from -within a module and allow the module author to set a hierarchy for this -data. - -The goal of this backend is to allow module authors to specify true -module default data in their modules but still allow users to override -the data using the standard method - especially useful with the puppet 3 -hiera integration. - -This backend is always loaded as the least important tier in the -hierarchy - unless a user choose to put it somewhere specific, but this -backend will always be enabled. - -Given a module layout: - - your_module - ├── data - │ ├── hiera.yaml - │ └── osfamily - │ ├── Debian.yaml - │ └── RedHat.yaml - └── manifests - └── init.pp - -The hiera.yaml is optional in this example it would be: - - --- - :hierarchy: - - osfamily/%{::osfamily} - - common - -But when no hiera.yaml exist in the module, the default would be: - - --- - :hierarchy: - - common - -The data directory is then a standard Hiera data store. - -Status? -------- - -This is but a first stab at turning my old pull request for ticket 16856 -into a standalone module that any > 3.0.0 Puppet user can depend on to -get this essential feature. - -Some more testing is needed, sanity checking for support versions etc so -consider this a early feedback-saught release - -Contact? --------- - -R.I.Pienaar / rip@devco.net / @ripienaar / http://devco.net diff --git a/module-data/lib/hiera/backend/module_data_backend.rb b/module-data/lib/hiera/backend/module_data_backend.rb deleted file mode 100644 index 5e8ff3a37..000000000 --- a/module-data/lib/hiera/backend/module_data_backend.rb +++ /dev/null @@ -1,91 +0,0 @@ -class Hiera - module Backend - class Module_data_backend - def initialize(cache=nil) - require 'yaml' - require 'hiera/filecache' - - Hiera.debug("Hiera Module Data backend starting") - - @cache = cache || Filecache.new - end - - def load_module_config(module_name, environment) - default_config = {:hierarchy => ["common"]} - - mod = Puppet::Module.find(module_name, environment) - - return default_config unless mod - - path = mod.path - module_config = File.join(path, "data", "hiera.yaml") - config = {} - - if File.exist?(module_config) - Hiera.debug("Reading config from %s file" % module_config) - config = load_data(module_config) - end - - config["path"] = path - - default_config.merge(config) - end - - def load_data(path) - return {} unless File.exist?(path) - - @cache.read(path, Hash, {}) do |data| - YAML.load(data) - end - end - - def lookup(key, scope, order_override, resolution_type) - answer = nil - - Hiera.debug("Looking up %s in Module Data backend" % key) - - unless scope["module_name"] - Hiera.debug("Skipping Module Data backend as this does not look like a module") - return answer - end - - config = load_module_config(scope["module_name"], scope["environment"]) - - unless config["path"] - Hiera.debug("Could not find a path to the module '%s' in environment '%s'" % [scope["module_name"], scope["environment"]]) - return answer - end - - config[:hierarchy].each do |source| - source = File.join(config["path"], "data", "%s.yaml" % Backend.parse_string(source, scope)) - - Hiera.debug("Looking for data in source %s" % source) - data = load_data(source) - - raise("Data loaded from %s should be a hash but got %s" % [source, data.class]) unless data.is_a?(Hash) - - next if data.empty? - next unless data.include?(key) - - found = data[key] - - case resolution_type - when :array - raise("Hiera type mismatch: expected Array or String and got %s" % found.class) unless [Array, String].include?(found.class) - answer ||= [] - answer << Backend.parse_answer(found, scope) - - when :hash - raise("Hiera type mismatch: expected Hash and got %s" % found.class) unless found.is_a?(Hash) - answer = Backend.parse_answer(found, scope).merge(answer || {}) - else - answer = Backend.parse_answer(found, scope) - break - end - end - - return answer - end - end - end -end diff --git a/module-data/lib/puppet/indirector/data_binding/hiera.rb b/module-data/lib/puppet/indirector/data_binding/hiera.rb deleted file mode 100644 index df8c8ba6b..000000000 --- a/module-data/lib/puppet/indirector/data_binding/hiera.rb +++ /dev/null @@ -1,75 +0,0 @@ -require "hiera" -require "hiera/config" -require "hiera/scope" - -begin - require 'puppet/indirector/hiera' -rescue LoadError => e - begin - require "puppet/indirector/code" - rescue LoadError => e - $stderr.puts "Couldn't require either of puppet/indirector/{hiera,code}!" - end -end - - -class Hiera::Config - class << self - alias :old_load :load unless respond_to?(:old_load) - - def load(source) - old_load(source) - - @config[:backends] << "module_data" unless @config[:backends].include?("module_data") - - @config - end - end -end - -class Puppet::DataBinding::Hiera < Puppet::Indirector::Code - desc "Retrieve data using Hiera." - - def initialize(*args) - if ! Puppet.features.hiera? - raise "Hiera terminus not supported without hiera library" - end - super - end - - if defined?(::Psych::SyntaxError) - DataBindingExceptions = [::StandardError, ::Psych::SyntaxError] - else - DataBindingExceptions = [::StandardError] - end - - def find(request) - hiera.lookup(request.key, nil, Hiera::Scope.new(request.options[:variables]), nil, nil) - rescue *DataBindingExceptions => detail - raise Puppet::DataBinding::LookupError.new(detail.message, detail) - end - - private - - def self.hiera_config - hiera_config = Puppet.settings[:hiera_config] - config = {} - - if File.exist?(hiera_config) - config = Hiera::Config.load(hiera_config) - else - Puppet.warning "Config file #{hiera_config} not found, using Hiera defaults" - end - - config[:logger] = 'puppet' - config - end - - def self.hiera - @hiera ||= Hiera.new(:config => hiera_config) - end - - def hiera - self.class.hiera - end -end diff --git a/mongodb/.gitignore b/mongodb/.gitignore index 9bbbbed4b..794d108d5 100644 --- a/mongodb/.gitignore +++ b/mongodb/.gitignore @@ -7,4 +7,3 @@ tmp/ pkg/ spec/fixtures/manifests .rspec_system/ -.vagrant/ \ No newline at end of file diff --git a/mongodb/CHANGELOG b/mongodb/CHANGELOG index 92a220f48..164b35e02 100644 --- a/mongodb/CHANGELOG +++ b/mongodb/CHANGELOG @@ -1,25 +1,3 @@ -##2014-05-27 - Release 0.8.0 - -This feature features a rewritten mongodb_replset{} provider, includes several -important bugfixes, ruby 1.8 support, and two new features. - -####Features -- Rewritten mongodb_replset{}, featuring puppet resource support, prefetching, -and flushing. -- Add Ruby 1.8 compatibility. -- Adds `syslog`, allowing you to configure mongodb to send all logging to the hosts syslog. -- Add mongodb::replset, a wrapper class for hiera users. -- Improved testing! - -####Bugfixes -- Fixes the package names to work since 10gen renamed them again. -- Fix provider name in the README. -- Disallow `nojournal` and `journal` to be set at the same time. -- Changed - to = for versioned install on Ubuntu. - -####Known Bugs -* No known bugs - 2014-1-29 - Version 0.7.0 Summary: diff --git a/mongodb/Gemfile b/mongodb/Gemfile index d0c14e046..3bdc54725 100644 --- a/mongodb/Gemfile +++ b/mongodb/Gemfile @@ -1,16 +1,13 @@ -source ENV['GEM_SOURCE'] || 'https://rubygems.org' +source 'https://rubygems.org' group :test, :development do gem 'rspec-puppet', :require => false gem 'rake', :require => false gem 'puppetlabs_spec_helper', :require => false + gem 'rspec-system', :require => false + gem 'rspec-system-puppet', :require => false + gem 'rspec-system-serverspec', :require => false gem 'serverspec', :require => false - gem 'puppet-lint', :require => false - gem 'pry', :require => false - gem 'simplecov', :require => false - gem 'beaker', :require => false - gem 'beaker-rspec', :require => false - gem 'vagrant-wrapper', :require => false end if puppetversion = ENV['PUPPET_VERSION'] diff --git a/mongodb/Modulefile b/mongodb/Modulefile index e2eb82771..cbe568b90 100644 --- a/mongodb/Modulefile +++ b/mongodb/Modulefile @@ -1,5 +1,5 @@ name 'puppetlabs-mongodb' -version '0.8.0' +version '0.7.0' source 'git@github.com:puppetlabs/puppetlabs-mongodb.git' author 'puppetlabs' license 'Apache License Version 2.0' diff --git a/mongodb/README.md b/mongodb/README.md index 9462cbd49..3134e3893 100644 --- a/mongodb/README.md +++ b/mongodb/README.md @@ -184,7 +184,7 @@ For more details about configuration parameters consult the [MongoDB Configuration File Options](http://docs.mongodb.org/manual/reference/configuration-options/). #####`ensure` -Used to ensure that the package is installed and the service is running, or that the package is absent/purged and the service is stopped. Valid values are true/false/present/absent/purged. +enable or disable the service #####`config` Path of the config file. If not specified, the module will use the default @@ -351,11 +351,6 @@ replication configuration. Default: False *Note*: deprecated – use replica se Specify extra configuration file parameters (i.e. textSearchEnabled=true). Default: None -#####`syslog` -Sends all logging output to the host’s syslog system rather than to standard -output or a log file. Default: None -*Important*: You cannot use syslog with logpath. - #####`slave` Set to true to configure the current instance to act as slave instance in a replication configuration. Default: false @@ -452,7 +447,7 @@ Array of 'host:port' of the replicaset members. It currently only adds members without options. -## Limitations +## Limitation This module has been tested on: @@ -465,8 +460,6 @@ This module has been tested on: For a full list of tested operating systems please have a look at the [.nodeset.xml](https://github.com/puppetlabs/puppetlabs-mongodb/blob/master/.nodeset.yml) definition. -This module should support `service_ensure` separate from the `ensure` value on `Class[mongodb::server]` but it does not yet. - ## Development Puppet Labs modules on the Puppet Forge are open projects, and community diff --git a/mongodb/Rakefile b/mongodb/Rakefile index cd3d37995..bb60173e5 100644 --- a/mongodb/Rakefile +++ b/mongodb/Rakefile @@ -1 +1,2 @@ require 'puppetlabs_spec_helper/rake_tasks' +require 'rspec-system/rake_task' diff --git a/mongodb/lib/puppet/provider/mongodb_conn_validator/tcp_port.rb b/mongodb/lib/puppet/provider/mongodb_conn_validator/tcp_port.rb deleted file mode 100644 index f913696b7..000000000 --- a/mongodb/lib/puppet/provider/mongodb_conn_validator/tcp_port.rb +++ /dev/null @@ -1,53 +0,0 @@ -$LOAD_PATH.unshift(File.join(File.dirname(__FILE__),"..","..","..")) -require 'puppet/util/mongodb_validator' - -# This file contains a provider for the resource type `mongodb_conn_validator`, -# which validates the mongodb connection by attempting an https connection. - -Puppet::Type.type(:mongodb_conn_validator).provide(:tcp_port) do - desc "A provider for the resource type `mongodb_conn_validator`, - which validates the mongodb connection by attempting an https - connection to the mongodb server. Uses the puppet SSL certificate - setup from the local puppet environment to authenticate." - - def exists? - start_time = Time.now - timeout = resource[:timeout] - - success = validator.attempt_connection - - while success == false && ((Time.now - start_time) < timeout) - # It can take several seconds for the mongodb server to start up; - # especially on the first install. Therefore, our first connection attempt - # may fail. Here we have somewhat arbitrarily chosen to retry every 4 - # seconds until the configurable timeout has expired. - Puppet.debug("Failed to connect to mongodb; sleeping 4 seconds before retry") - sleep 4 - success = validator.attempt_connection - end - - if success - Puppet.debug("Connected to mongodb in #{Time.now - start_time} seconds.") - else - Puppet.notice("Failed to connect to mongodb within timeout window of #{timeout} seconds; giving up.") - end - - success - end - - def create - # If `#create` is called, that means that `#exists?` returned false, which - # means that the connection could not be established... so we need to - # cause a failure here. - raise Puppet::Error, "Unable to connect to mongodb server! (#{@validator.mongodb_server}:#{@validator.mongodb_port})" - end - - private - - # @api private - def validator - @validator ||= Puppet::Util::MongodbValidator.new(resource[:server], resource[:port]) - end - -end - diff --git a/mongodb/lib/puppet/provider/mongodb_database/mongodb.rb b/mongodb/lib/puppet/provider/mongodb_database/mongodb.rb index 2de8420cc..3ca5f5043 100644 --- a/mongodb/lib/puppet/provider/mongodb_database/mongodb.rb +++ b/mongodb/lib/puppet/provider/mongodb_database/mongodb.rb @@ -9,14 +9,10 @@ def block_until_mongodb(tries = 10) begin mongo('--quiet', '--eval', 'db.getMongo()') - rescue => e + rescue debug('MongoDB server not ready, retrying') sleep 2 - if (tries -= 1) > 0 - retry - else - raise e - end + retry unless (tries -= 1) <= 0 end end @@ -30,7 +26,7 @@ def destroy def exists? block_until_mongodb(@resource[:tries]) - mongo("--quiet", "--eval", 'db.getMongo().getDBNames()').chomp.split(",").include?(@resource[:name]) + mongo("--quiet", "--eval", 'db.getMongo().getDBNames()').split(",").include?(@resource[:name]) end end diff --git a/mongodb/lib/puppet/provider/mongodb_replset/mongo.rb b/mongodb/lib/puppet/provider/mongodb_replset/mongo.rb index d77afe303..3663ca22d 100644 --- a/mongodb/lib/puppet/provider/mongodb_replset/mongo.rb +++ b/mongodb/lib/puppet/provider/mongodb_replset/mongo.rb @@ -2,212 +2,95 @@ # Author: François Charlier # +require 'json' + Puppet::Type.type(:mongodb_replset).provide(:mongo) do desc "Manage hosts members for a replicaset." - confine :true => - begin - require 'json' - true - rescue LoadError - false - end - commands :mongo => 'mongo' - mk_resource_methods - - def initialize(resource={}) - super(resource) - @property_flush = {} - end - - def members=(hosts) - @property_flush[:members] = hosts - end - - def self.instances - instance = get_replset_properties - if instance - # There can only be one replset per node - [new(instance)] - else - [] - end - end - - def self.prefetch(resources) - instances.each do |prov| - if resource = resources[prov.name] - resource.provider = prov - end - end - end - - def exists? - @property_hash[:ensure] == :present - end - def create - @property_flush[:ensure] = :present - @property_flush[:members] = resource.should(:members) - end - - def destroy - @property_flush[:ensure] = :absent - end - - def flush - set_members - @property_hash = self.class.get_replset_properties - end - - private - - def db_ismaster(host) - mongo_command("db.isMaster()", host) - end - - def rs_initiate(conf, master) - return mongo_command("rs.initiate(#{conf})", master) - end - - def rs_status(host) - mongo_command("rs.status()", host) - end - - def rs_add(host, master) - mongo_command("rs.add(\"#{host}\")", master) - end - - def rs_remove(host, master) - mongo_command("rs.remove(\"#{host}\")", master) - end - - def master_host(hosts) - hosts.each do |host| - status = db_ismaster(host) - if status.has_key?('primary') - return status['primary'] - end + alive_members = members_present + hostsconf = alive_members.each_with_index.map do |host, id| + "{ _id: #{id}, host: \"#{host}\" }" + end.join(',') + conf = "{ _id: \"#{@resource[:name]}\", members: [ #{hostsconf} ] }" + output = self.rs_initiate(conf, alive_members[0]) + if output['ok'] == 0 + raise Puppet::Error, "rs.initiate() failed for replicaset #{@resource[:name]}: #{output['errmsg']}" end - false end - def self.get_replset_properties - output = mongo_command('rs.conf()') - if output['members'] - members = output['members'].collect do |val| - val['host'] - end - props = { - :name => output['_id'], - :ensure => :present, - :members => members, - :provider => :mongo, - } - else - props = nil - end - Puppet.debug("MongoDB replset properties: #{props.inspect}") - props + def destroy end - def alive_members(hosts) - hosts.select do |host| + def exists? + failcount = 0 + is_configured = false + @resource[:members].each do |host| begin - Puppet.debug "Checking replicaset member #{host} ..." - status = rs_status(host) + debug "Checking replicaset member #{host} ..." + status = self.rs_status(host) if status.has_key?('errmsg') and status['errmsg'] == 'not running with --replSet' - raise Puppet::Error, "Can't configure replicaset #{self.name}, host #{host} is not supposed to be part of a replicaset." + raise Puppet::Error, "Can't configure replicaset #{@resource[:name]}, host #{host} is not supposed to be part of a replicaset." end if status.has_key?('set') - if status['set'] != self.name - raise Puppet::Error, "Can't configure replicaset #{self.name}, host #{host} is already part of another replicaset." + if status['set'] != @resource[:name] + raise Puppet::Error, "Can't configure replicaset #{@resource[:name]}, host #{host} is already part of another replicaset." end - - # This node is alive and supposed to be a member of our set - Puppet.debug "Host #{self.name} is available for replset #{status['set']}" - true - elsif status.has_key?('info') - Puppet.debug "Host #{self.name} is alive but unconfigured: #{status['info']}" - true + is_configured = true end rescue Puppet::ExecutionFailure - Puppet.warning "Can't connect to replicaset member #{host}." - - false + debug "Can't connect to replicaset member #{host}." + failcount += 1 end end - end - def set_members - if @property_flush[:ensure] == :absent - # TODO: I don't know how to remove a node from a replset; unimplemented - #Puppet.debug "Removing all members from replset #{self.name}" - #@property_hash[:members].collect do |member| - # rs_remove(member, master_host(@property_hash[:members])) - #end - return + if failcount == @resource[:members].length + raise Puppet::Error, "Can't connect to any member of replicaset #{@resource[:name]}." end + return is_configured + end - if ! @property_flush[:members].empty? - # Find the alive members so we don't try to add dead members to the replset - alive_hosts = alive_members(@property_flush[:members]) - dead_hosts = @property_flush[:members] - alive_hosts - raise Puppet::Error, "Can't connect to any member of replicaset #{self.name}." if alive_hosts.empty? - Puppet.debug "Alive members: #{alive_hosts.inspect}" - Puppet.debug "Dead members: #{dead_hosts.inspect}" unless dead_hosts.empty? + def members + if master = self.master_host() + self.db_ismaster(master)['hosts'] else - alive_hosts = [] + raise Puppet::Error, "Can't find master host for replicaset #{@resource[:name]}." end + end - if @property_flush[:ensure] == :present and @property_hash[:ensure] != :present - Puppet.debug "Initializing the replset #{self.name}" - - # Create a replset configuration - hostconf = alive_hosts.each_with_index.map do |host,id| - "{ _id: #{id}, host: \"#{host}\" }" - end.join(',') - conf = "{ _id: \"#{self.name}\", members: [ #{hostconf} ] }" - - # Set replset members with the first host as the master - output = rs_initiate(conf, alive_hosts[0]) - if output['ok'] == 0 - raise Puppet::Error, "rs.initiate() failed for replicaset #{self.name}: #{output['errmsg']}" + def members=(hosts) + if master = master_host() + current = self.db_ismaster(master)['hosts'] + newhosts = hosts - current + newhosts.each do |host| + #TODO: check output (['ok'] == 0 should be sufficient) + self.rs_add(host, master) end else - # Add members to an existing replset - if master = master_host(alive_hosts) - current_hosts = db_ismaster(master)['hosts'] - newhosts = alive_hosts - current_hosts - newhosts.each do |host| - output = rs_add(host, master) - if output['ok'] == 0 - raise Puppet::Error, "rs.add() failed to add host to replicaset #{self.name}: #{output['errmsg']}" - end - end - else - raise Puppet::Error, "Can't find master host for replicaset #{self.name}." - end + raise Puppet::Error, "Can't find master host for replicaset #{@resource[:name]}." end end - def mongo_command(command, host, retries=4) - self.class.mongo_command(command,host,retries) + def members_present + @resource[:members].select do |host| + begin + self.mongo('--host', host, '--quiet', '--eval', 'db.version()') + true + rescue Puppet::ExecutionFailure + false + end + end end - def self.mongo_command(command, host=nil, retries=4) + def mongo_command(command, host, retries=4) # Allow waiting for mongod to become ready # Wait for 2 seconds initially and double the delay at each retry wait = 2 begin - args = Array.new - args << '--quiet' - args << ['--host',host] if host - args << ['--eval',"printjson(#{command})"] - output = mongo(args.flatten) + output = self.mongo('--quiet', '--host', host, '--eval', "printjson(#{command})") rescue Puppet::ExecutionFailure => e if e =~ /Error: couldn't connect to server/ and wait <= 2**max_wait info("Waiting #{wait} seconds for mongod to become available") @@ -222,11 +105,34 @@ def self.mongo_command(command, host=nil, retries=4) # Dirty hack to remove JavaScript objects output.gsub!(/ISODate\((.+?)\)/, '\1 ') output.gsub!(/Timestamp\((.+?)\)/, '[\1]') + JSON.parse(output) + end + + def master_host + @resource[:members].each do |host| + status = self.db_ismaster(host) + if status.has_key?('primary') + return status['primary'] + end + end + false + end + + def db_ismaster(host) + self.mongo_command("db.isMaster()", host) + end - #Hack to avoid non-json empty sets - output = "{}" if output == "null\n" + def rs_initiate(conf, host) + return self.mongo_command("rs.initiate(#{conf})", @resource[:members][0]) - JSON.parse(output) + end + + def rs_status(host) + self.mongo_command("rs.status()", host) + end + + def rs_add(host, master) + self.mongo_command("rs.add(\"#{host}\")", master) end end diff --git a/mongodb/lib/puppet/type/mongodb_conn_validator.rb b/mongodb/lib/puppet/type/mongodb_conn_validator.rb deleted file mode 100644 index c67043eae..000000000 --- a/mongodb/lib/puppet/type/mongodb_conn_validator.rb +++ /dev/null @@ -1,43 +0,0 @@ -Puppet::Type.newtype(:mongodb_conn_validator) do - - @doc = "Verify that a connection can be successfully established between a node - and the mongodb server. Its primary use is as a precondition to - prevent configuration changes from being applied if the mongodb - server cannot be reached, but it could potentially be used for other - purposes such as monitoring." - - ensurable do - defaultvalues - defaultto :present - end - - newparam(:name, :namevar => true) do - desc 'An arbitrary name used as the identity of the resource.' - end - - newparam(:server) do - desc 'An array containing DNS names or IP addresses of the server where mongodb should be running.' - munge do |value| - Array(value).first - end - end - - newparam(:port) do - desc 'The port that the mongodb server should be listening on.' - end - - newparam(:timeout) do - desc 'The max number of seconds that the validator should wait before giving up and deciding that puppetdb is not running; defaults to 60 seconds.' - defaultto 60 - - validate do |value| - # This will raise an error if the string is not convertible to an integer - Integer(value) - end - - munge do |value| - Integer(value) - end - end - -end diff --git a/mongodb/lib/puppet/type/mongodb_database.rb b/mongodb/lib/puppet/type/mongodb_database.rb index 8be5f2e0a..0343b9d97 100644 --- a/mongodb/lib/puppet/type/mongodb_database.rb +++ b/mongodb/lib/puppet/type/mongodb_database.rb @@ -17,11 +17,4 @@ end end - autorequire(:package) do - 'mongodb_client' - end - - autorequire(:service) do - 'mongodb' - end end diff --git a/mongodb/lib/puppet/type/mongodb_replset.rb b/mongodb/lib/puppet/type/mongodb_replset.rb index f186657c0..8115ef034 100644 --- a/mongodb/lib/puppet/type/mongodb_replset.rb +++ b/mongodb/lib/puppet/type/mongodb_replset.rb @@ -26,7 +26,7 @@ def insync?(is) end autorequire(:package) do - 'mongodb_client' + 'mongodb' end autorequire(:service) do diff --git a/mongodb/lib/puppet/type/mongodb_user.rb b/mongodb/lib/puppet/type/mongodb_user.rb index 9cd7b1038..c5259063d 100644 --- a/mongodb/lib/puppet/type/mongodb_user.rb +++ b/mongodb/lib/puppet/type/mongodb_user.rb @@ -53,11 +53,4 @@ def is_to_s(value) newvalue(/^\w+$/) end - autorequire(:package) do - 'mongodb_client' - end - - autorequire(:service) do - 'mongodb' - end end diff --git a/mongodb/lib/puppet/util/mongodb_validator.rb b/mongodb/lib/puppet/util/mongodb_validator.rb deleted file mode 100644 index 86ae740ad..000000000 --- a/mongodb/lib/puppet/util/mongodb_validator.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'socket' -require 'timeout' - - -module Puppet - module Util - class MongodbValidator - attr_reader :mongodb_server - attr_reader :mongodb_port - - def initialize(mongodb_server, mongodb_port) - @mongodb_server = mongodb_server - @mongodb_port = mongodb_port - end - - # Utility method; attempts to make an https connection to the mongodb server. - # This is abstracted out into a method so that it can be called multiple times - # for retry attempts. - # - # @return true if the connection is successful, false otherwise. - def attempt_connection - Timeout::timeout(Puppet[:configtimeout]) do - begin - TCPSocket.new(@mongodb_server, @mongodb_port).close - true - rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH => e - Puppet.debug "Unable to connect to mongodb server (#{@mongodb_server}:#{@mongodb_port}): #{e.message}" - false - end - end - rescue Timeout::Error - false - end - end - end -end - diff --git a/mongodb/manifests/client.pp b/mongodb/manifests/client.pp index bf557d690..de1b339c7 100644 --- a/mongodb/manifests/client.pp +++ b/mongodb/manifests/client.pp @@ -12,5 +12,15 @@ $ensure = $mongodb::params::ensure_client, $package_name = $mongodb::params::client_package_name, ) inherits mongodb::params { - class { 'mongodb::client::install': } + case $::osfamily { + 'RedHat', 'Linux': { + class { 'mongodb::client::install': } + } + 'Debian': { + warning ('Debian client is included by default with server. Please use ::mongodb::server to install the mongo client for Debian family systems.') + } + default: { + # no action taken, failure happens in params.pp + } + } } diff --git a/mongodb/manifests/client/install.pp b/mongodb/manifests/client/install.pp index 51ed31474..6da26003b 100644 --- a/mongodb/manifests/client/install.pp +++ b/mongodb/manifests/client/install.pp @@ -18,11 +18,9 @@ } } - if $package_name { - package { 'mongodb_client': - ensure => $my_package_ensure, - name => $package_name, - tag => 'mongodb', - } + package { 'mongodb_client': + ensure => $my_package_ensure, + name => $package_name, + tag => 'mongodb', } } diff --git a/mongodb/manifests/globals.pp b/mongodb/manifests/globals.pp index 0eebcd59b..fb04df694 100644 --- a/mongodb/manifests/globals.pp +++ b/mongodb/manifests/globals.pp @@ -5,8 +5,6 @@ $server_package_name = undef, $client_package_name = undef, - $service_enable = undef, - $service_ensure = undef, $service_name = undef, $service_provider = undef, $service_status = undef, diff --git a/mongodb/manifests/params.pp b/mongodb/manifests/params.pp index 50abeffbc..7f05b2b7d 100644 --- a/mongodb/manifests/params.pp +++ b/mongodb/manifests/params.pp @@ -1,8 +1,6 @@ # PRIVATE CLASS: do not use directly class mongodb::params inherits mongodb::globals { $ensure = true - $service_enable = pick($service_enable, true) - $service_ensure = pick($service_ensure, 'running') $service_status = $service_status $ensure_client = true @@ -14,16 +12,16 @@ $user = pick($user, 'mongod') $group = pick($group, 'mongod') if $::mongodb::globals::version { - $server_package_name = pick("$::mongodb::globals::server_package_name-${::mongodb::globals::version}", "mongodb-org-server-${::mongodb::globals::version}") - $client_package_name = pick("$::mongodb::globals::client_package_name-${::mongodb::globals::version}", "mongodb-org-shell-${::mongodb::globals::version}") + $server_package_name = "mongo-10gen-server-${::mongodb::globals::version}" + $client_package_name = "mongo-10gen-${::mongodb::globals::version}" } else { - $server_package_name = pick($::mongodb::globals::server_package_name, 'mongodb-org-server') - $client_package_name = pick($::mongodb::globals::client_package_name, 'mongodb-org-shell') + $server_package_name = 'mongo-10gen-server' + $client_package_name = 'mongo-10gen' } - $service_name = pick($::mongodb::globals::service_name, 'mongod') + $service_name = pick($service_name, 'mongod') $config = '/etc/mongod.conf' - $dbpath = '/var/lib/mongodb' - $logpath = '/var/log/mongodb/mongod.log' + $dbpath = '/var/lib/mongo' + $logpath = '/var/log/mongo/mongod.log' $pidfilepath = '/var/run/mongodb/mongod.pid' $bind_ip = pick($bind_ip, ['127.0.0.1']) $fork = true @@ -50,15 +48,12 @@ $user = pick($user, 'mongodb') $group = pick($group, 'mongodb') if $::mongodb::globals::version { - $server_package_name = pick("${::mongodb::globals::server_package_name}=${::mongodb::globals::version}", "mongodb-org-server-${::mongodb::globals::version}") - $client_package_name = pick("${::mongodb::globals::client_package_name}=${::mongodb::globals::version}", "mongodb-org-shell-${::mongodb::globals::version}") - } - else { - $server_package_name = pick($::mongodb::globals::server_package_name, 'mongodb-org-server') - $client_package_name = pick($::mongodb::globals::client_package_name, 'mongodb-org-shell') + $server_package_name = "mongodb-10gen-${::mongodb::globals::version}" + } else { + $server_package_name = 'mongodb-10gen' } - $service_name = pick($::mongodb::globals::service_name, 'mongod') - $config = '/etc/mongod.conf' + $service_name = 'mongodb' + $config = '/etc/mongodb.conf' $dbpath = '/var/lib/mongodb' $logpath = '/var/log/mongodb/mongodb.log' $bind_ip = ['127.0.0.1'] @@ -70,7 +65,7 @@ $user = pick($user, 'mongodb') $group = pick($group, 'mongodb') $server_package_name = pick($server_package_name, 'mongodb-server') - $client_package_name = $client_package_name + $client_package_name = pick($client_package_name, 'mongodb') $service_name = pick($service_name, 'mongodb') $config = '/etc/mongodb.conf' $dbpath = '/var/lib/mongodb' diff --git a/mongodb/manifests/replset.pp b/mongodb/manifests/replset.pp deleted file mode 100644 index ce4a02555..000000000 --- a/mongodb/manifests/replset.pp +++ /dev/null @@ -1,10 +0,0 @@ -# Wrapper class useful for hiera based deployments - -class mongodb::replset( - $sets = undef -) { - - if $sets { - create_resources(mongodb_replset, $sets) - } -} diff --git a/mongodb/manifests/repo/apt.pp b/mongodb/manifests/repo/apt.pp index 4f14632d4..e8f4b812f 100644 --- a/mongodb/manifests/repo/apt.pp +++ b/mongodb/manifests/repo/apt.pp @@ -11,7 +11,7 @@ release => 'dist', repos => '10gen', key => '9ECBEC467F0CEB10', - key_server => 'hkp://keyserver.ubuntu.com:80', + key_server => 'keyserver.ubuntu.com', include_src => false, } diff --git a/mongodb/manifests/server.pp b/mongodb/manifests/server.pp index 90e785e65..20eda1daa 100644 --- a/mongodb/manifests/server.pp +++ b/mongodb/manifests/server.pp @@ -11,8 +11,6 @@ $service_provider = $mongodb::params::service_provider, $service_name = $mongodb::params::service_name, - $service_enable = $mongodb::params::service_enable, - $service_ensure = $mongodb::params::service_ensure, $service_status = $mongodb::params::service_status, $package_ensure = $ensure, @@ -53,7 +51,6 @@ $slowms = undef, $keyfile = undef, $set_parameter = undef, - $syslog = undef, # Deprecated parameters $master = undef, diff --git a/mongodb/manifests/server/config.pp b/mongodb/manifests/server/config.pp index 2056c14d5..7b303deaf 100644 --- a/mongodb/manifests/server/config.pp +++ b/mongodb/manifests/server/config.pp @@ -45,15 +45,12 @@ $directoryperdb = $mongodb::server::directoryperdb $profile = $mongodb::server::profile $set_parameter = $mongodb::server::set_parameter - $syslog = $mongodb::server::syslog File { owner => $user, group => $group, } - if ($logpath and $syslog) { fail('You cannot use syslog with logpath')} - if ($ensure == 'present' or $ensure == true) { # Exists for future compatibility and clarity. diff --git a/mongodb/manifests/server/install.pp b/mongodb/manifests/server/install.pp index 46b0e749b..d1ad33624 100644 --- a/mongodb/manifests/server/install.pp +++ b/mongodb/manifests/server/install.pp @@ -9,14 +9,10 @@ $file_ensure = 'directory' } false: { - $my_package_ensure = 'absent' + $my_package_ensure = 'purged' $file_ensure = 'absent' } 'absent': { - $my_package_ensure = 'absent' - $file_ensure = 'absent' - } - 'purged': { $my_package_ensure = 'purged' $file_ensure = 'absent' } diff --git a/mongodb/manifests/server/service.pp b/mongodb/manifests/server/service.pp index 3a714705e..97a32a4ee 100644 --- a/mongodb/manifests/server/service.pp +++ b/mongodb/manifests/server/service.pp @@ -1,34 +1,22 @@ # PRIVATE CLASS: do not call directly class mongodb::server::service { - $ensure = $mongodb::server::service_ensure - $service_enable = $mongodb::server::service_enable + $ensure = $mongodb::server::ensure $service_name = $mongodb::server::service_name $service_provider = $mongodb::server::service_provider $service_status = $mongodb::server::service_status - $bind_ip = $mongodb::server::bind_ip - $port = $mongodb::server::port $service_ensure = $ensure ? { present => true, absent => false, - purged => false, default => $ensure } service { 'mongodb': ensure => $service_ensure, name => $service_name, - enable => $service_enable, + enable => $service_ensure, provider => $service_provider, hasstatus => true, status => $service_status, } - if $service_ensure { - mongodb_conn_validator { "mongodb": - server => $bind_ip, - port => $port, - timeout => '240', - require => Service['mongodb'], - } - } } diff --git a/mongodb/spec/acceptance/nodesets/centos-6-vcloud.yml b/mongodb/spec/acceptance/nodesets/centos-6-vcloud.yml deleted file mode 100644 index ae19ee77c..000000000 --- a/mongodb/spec/acceptance/nodesets/centos-6-vcloud.yml +++ /dev/null @@ -1,21 +0,0 @@ -HOSTS: - 'master': - roles: - - master - platform: el-6-x86_64 - hypervisor: vcloud - template: centos-6-x86_64 - 'slave': - roles: - - slave - platform: el-6-x86_64 - hypervisor: vcloud - template: centos-6-x86_64 -CONFIG: - type: foss - ssh: - keys: "~/.ssh/id_rsa-acceptance" - datastore: instance0 - folder: Delivery/Quality Assurance/Enterprise/Dynamic - resourcepool: delivery/Quality Assurance/Enterprise/Dynamic - pooling_api: http://vcloud.delivery.puppetlabs.net/ diff --git a/mongodb/spec/acceptance/nodesets/debian-607-x64.yml b/mongodb/spec/acceptance/nodesets/debian-607-x64.yml deleted file mode 100644 index 4668f5f9d..000000000 --- a/mongodb/spec/acceptance/nodesets/debian-607-x64.yml +++ /dev/null @@ -1,10 +0,0 @@ -HOSTS: - debian-607-x64: - roles: - - master - platform: debian-6-amd64 - box : debian-607-x64-vbox4210-nocm - box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-607-x64-vbox4210-nocm.box - hypervisor : vagrant -CONFIG: - type: git \ No newline at end of file diff --git a/mongodb/spec/acceptance/nodesets/fedora-18-x64.yml b/mongodb/spec/acceptance/nodesets/fedora-18-x64.yml deleted file mode 100644 index 136164983..000000000 --- a/mongodb/spec/acceptance/nodesets/fedora-18-x64.yml +++ /dev/null @@ -1,10 +0,0 @@ -HOSTS: - fedora-18-x64: - roles: - - master - platform: fedora-18-x86_64 - box : fedora-18-x64-vbox4210-nocm - box_url : http://puppet-vagrant-boxes.puppetlabs.com/fedora-18-x64-vbox4210-nocm.box - hypervisor : vagrant -CONFIG: - type: foss diff --git a/mongodb/spec/acceptance/nodesets/multi-centos-6-vcloud.yml b/mongodb/spec/acceptance/nodesets/multi-centos-6-vcloud.yml deleted file mode 100644 index ae19ee77c..000000000 --- a/mongodb/spec/acceptance/nodesets/multi-centos-6-vcloud.yml +++ /dev/null @@ -1,21 +0,0 @@ -HOSTS: - 'master': - roles: - - master - platform: el-6-x86_64 - hypervisor: vcloud - template: centos-6-x86_64 - 'slave': - roles: - - slave - platform: el-6-x86_64 - hypervisor: vcloud - template: centos-6-x86_64 -CONFIG: - type: foss - ssh: - keys: "~/.ssh/id_rsa-acceptance" - datastore: instance0 - folder: Delivery/Quality Assurance/Enterprise/Dynamic - resourcepool: delivery/Quality Assurance/Enterprise/Dynamic - pooling_api: http://vcloud.delivery.puppetlabs.net/ diff --git a/mongodb/spec/acceptance/nodesets/multi-centos-64-x64.yml b/mongodb/spec/acceptance/nodesets/multi-centos-64-x64.yml deleted file mode 100644 index 5bcd4779d..000000000 --- a/mongodb/spec/acceptance/nodesets/multi-centos-64-x64.yml +++ /dev/null @@ -1,17 +0,0 @@ -HOSTS: - 'master': - roles: - - master - platform: el-6-x86_64 - hypervisor : vagrant - box : centos-64-x64-vbox4210-nocm - box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box - 'slave': - roles: - - master - platform: el-6-x86_64 - hypervisor : vagrant - box : centos-64-x64-vbox4210-nocm - box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box -CONFIG: - type: foss diff --git a/mongodb/spec/acceptance/nodesets/sles-11-x64.yml b/mongodb/spec/acceptance/nodesets/sles-11-x64.yml deleted file mode 100644 index 41abe2135..000000000 --- a/mongodb/spec/acceptance/nodesets/sles-11-x64.yml +++ /dev/null @@ -1,10 +0,0 @@ -HOSTS: - sles-11-x64.local: - roles: - - master - platform: sles-11-x64 - box : sles-11sp1-x64-vbox4210-nocm - box_url : http://puppet-vagrant-boxes.puppetlabs.com/sles-11sp1-x64-vbox4210-nocm.box - hypervisor : vagrant -CONFIG: - type: foss diff --git a/mongodb/spec/acceptance/replset_spec.rb b/mongodb/spec/acceptance/replset_spec.rb deleted file mode 100644 index 8be5a0f46..000000000 --- a/mongodb/spec/acceptance/replset_spec.rb +++ /dev/null @@ -1,69 +0,0 @@ -require 'spec_helper_acceptance' - -if hosts.length > 1 - describe 'mongodb_replset resource' do - after :all do - # Have to drop the DB to disable replsets for further testing - on hosts, %{mongo local --verbose --eval 'db.dropDatabase()'} - - pp = <<-EOS - class { 'mongodb::globals': } - -> class { 'mongodb::server': - ensure => purged, - } - if $::osfamily == 'RedHat' { - class { 'mongodb::client': } - } - EOS - - apply_manifest_on(hosts.reverse, pp, :catch_failures => true) - end - - it 'configures mongo on both nodes' do - pp = <<-EOS - class { 'mongodb::globals': } - -> class { 'mongodb::server': - bind_ip => '0.0.0.0', - replset => 'test', - } - if $::osfamily == 'RedHat' { - class { 'mongodb::client': } - } - EOS - - apply_manifest_on(hosts.reverse, pp, :catch_failures => true) - apply_manifest_on(hosts.reverse, pp, :catch_changes => true) - end - - it 'sets up the replset with puppet' do - pp = <<-EOS - mongodb_replset { 'test': - members => [#{hosts.collect{|x|"'#{x}:27017'"}.join(',')}], - } - EOS - apply_manifest_on(hosts_as('master'), pp, :catch_failures => true) - on(hosts_as('master'), 'mongo --quiet --eval "printjson(rs.conf())"') do |r| - expect(r.stdout).to match /#{hosts[0]}:27017/ - expect(r.stdout).to match /#{hosts[1]}:27017/ - end - end - - it 'inserts data on the master' do - sleep(30) - on hosts_as('master'), %{mongo --verbose --eval 'db.test.save({name:"test1",value:"some value"})'} - end - - it 'checks the data on the master' do - on hosts_as('master'), %{mongo --verbose --eval 'printjson(db.test.findOne({name:"test1"}))'} do |r| - expect(r.stdout).to match /some value/ - end - end - - it 'checks the data on the slave' do - sleep(10) - on hosts_as('slave'), %{mongo --verbose --eval 'rs.slaveOk(); printjson(db.test.findOne({name:"test1"}))'} do |r| - expect(r.stdout).to match /some value/ - end - end - end -end diff --git a/mongodb/spec/acceptance/server_spec.rb b/mongodb/spec/acceptance/server_spec.rb deleted file mode 100644 index a313b9885..000000000 --- a/mongodb/spec/acceptance/server_spec.rb +++ /dev/null @@ -1,111 +0,0 @@ -require 'spec_helper_acceptance' - -describe 'mongodb::server class' do - - shared_examples 'normal tests' do |tengen| - if tengen - case fact('osfamily') - when 'RedHat' - package_name = 'mongodb-org-server' - service_name = 'mongod' - config_file = '/etc/mongod.conf' - when 'Debian' - package_name = 'mongodb-org-server' - service_name = 'mongod' - config_file = '/etc/mongod.conf' - end - else - case fact('osfamily') - when 'RedHat' - package_name = 'mongodb-server' - service_name = 'mongod' - config_file = '/etc/mongodb.conf' - when 'Debian' - package_name = 'mongodb-server' - service_name = 'mongodb' - config_file = '/etc/mongodb.conf' - end - end - - client_name = 'mongo --version' - - context "default parameters with 10gen => #{tengen}" do - after :all do - if tengen - puts "XXX uninstalls mongodb because changing the port with tengen doesn't work because they have a crappy init script" - pp = <<-EOS - class {'mongodb::globals': manage_package_repo => #{tengen}, } - -> class { 'mongodb::server': ensure => absent, } - -> class { 'mongodb::client': ensure => absent, } - EOS - apply_manifest(pp, :catch_failures => true) - end - end - - it 'should work with no errors' do - pp = <<-EOS - class { 'mongodb::globals': manage_package_repo => #{tengen}, } - -> class { 'mongodb::server': } - -> class { 'mongodb::client': } - EOS - - apply_manifest(pp, :catch_failures => true) - apply_manifest(pp, :catch_changes => true) - end - - describe package(package_name) do - it { should be_installed } - end - - describe file(config_file) do - it { should be_file } - end - - describe service(service_name) do - it { should be_enabled } - it { should be_running } - end - - describe port(27017) do - it { should be_listening } - end - - describe command(client_name) do - it do - should return_exit_status 0 - end - end - end - - context "test using custom port and 10gen => #{tengen}" do - it 'change port to 27018' do - pp = <<-EOS - class { 'mongodb::globals': manage_package_repo => #{tengen}, } - -> class { 'mongodb::server': port => 27018, } - -> class { 'mongodb::client': } - EOS - - apply_manifest(pp, :catch_failures => true) - apply_manifest(pp, :catch_changes => true) - end - - describe port(27018) do - it { should be_listening } - end - end - - describe "uninstalling with 10gen => #{tengen}" do - it 'uninstalls mongodb' do - pp = <<-EOS - class {'mongodb::globals': manage_package_repo => #{tengen}, } - -> class { 'mongodb::server': ensure => absent, } - -> class { 'mongodb::client': ensure => absent, } - EOS - apply_manifest(pp, :catch_failures => true) - end - end - end - - it_behaves_like 'normal tests', false - it_behaves_like 'normal tests', true -end diff --git a/mongodb/spec/classes/repo_spec.rb b/mongodb/spec/classes/repo_spec.rb index aa051e915..81899baf4 100644 --- a/mongodb/spec/classes/repo_spec.rb +++ b/mongodb/spec/classes/repo_spec.rb @@ -7,7 +7,6 @@ { :osfamily => 'Debian', :operatingsystem => 'Debian', - :lsbdistid => 'Debian', } end @@ -29,4 +28,4 @@ } end -end +end \ No newline at end of file diff --git a/mongodb/spec/classes/server_config_spec.rb b/mongodb/spec/classes/server_config_spec.rb index db05b88e3..14b142e24 100644 --- a/mongodb/spec/classes/server_config_spec.rb +++ b/mongodb/spec/classes/server_config_spec.rb @@ -94,23 +94,4 @@ end end - describe 'when specifying syslog value' do - context 'it should be set to true' do - let(:pre_condition) { ["class mongodb::server { $config = '/etc/mongod.conf' $dbpath = '/var/lib/mongo' $ensure = present $syslog = true }", "include mongodb::server"]} - - it { - should contain_file('/etc/mongod.conf').with_content(/^syslog = true/) - } - end - - context 'if logpath is also set an error should be raised' do - let(:pre_condition) { ["class mongodb::server { $config = '/etc/mongod.conf' $dbpath = '/var/lib/mongo' $ensure = present $syslog = true $logpath ='/var/log/mongo/mongod.log' }", "include mongodb::server"]} - - it { - expect { should contain_file('/etc/mongod.conf') }.to raise_error(Puppet::Error, /You cannot use syslog with logpath/) - } - end - - end - end diff --git a/mongodb/spec/spec_helper_acceptance.rb b/mongodb/spec/spec_helper_acceptance.rb deleted file mode 100755 index 5ea038b34..000000000 --- a/mongodb/spec/spec_helper_acceptance.rb +++ /dev/null @@ -1,41 +0,0 @@ -#! /usr/bin/env ruby -S rspec -require 'beaker-rspec' - -UNSUPPORTED_PLATFORMS = [] - -unless ENV['RS_PROVISION'] == 'no' or ENV['BEAKER_provision'] == 'no' - if hosts.first.is_pe? - install_pe - on hosts, 'mkdir -p /etc/puppetlabs/facter/facts.d' - else - install_puppet - on hosts, 'mkdir -p /etc/facter/facts.d' - on hosts, '/bin/touch /etc/puppet/hiera.yaml' - end - hosts.each do |host| - on host, "mkdir -p #{host['distmoduledir']}" - end -end - -RSpec.configure do |c| - # Project root - proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) - - # Readable test descriptions - c.formatter = :documentation - - # Configure all nodes in nodeset - c.before :suite do - hosts.each do |host| - copy_module_to(host, :source => proj_root, :module_name => 'mongodb') - end - on hosts, 'puppet module install puppetlabs-stdlib' - on hosts, 'puppet module install puppetlabs-apt' - case fact('osfamily') - when 'RedHat' - on hosts, 'puppet module install stahnma-epel' - apply_manifest_on hosts, 'include epel' - on hosts, 'service iptables stop' - end - end -end diff --git a/mongodb/spec/system/server_10gen_spec.rb b/mongodb/spec/system/server_10gen_spec.rb index a9a6b86d3..de2bec167 100644 --- a/mongodb/spec/system/server_10gen_spec.rb +++ b/mongodb/spec/system/server_10gen_spec.rb @@ -13,12 +13,23 @@ config_file = '/etc/mongodb.conf' end + client_name = 'mongo --version' + context 'default parameters' do it 'should work with no errors' do - pp = <<-EOS - class { 'mongodb::globals': manage_package_repo => true }-> - class { 'mongodb::server': } - EOS + case node.facts['osfamily'] + when 'RedHat' + pp = <<-EOS + class { 'mongodb::globals': manage_package_repo => true }-> + class { 'mongodb::server': }-> + class { 'mongodb::client': } + EOS + when 'Debian' + pp = <<-EOS + class { 'mongodb::globals': manage_package_repo => true }-> + class { 'mongodb::server': } + EOS + end puppet_apply(pp) do |r| r.exit_code.should == 2 @@ -42,13 +53,19 @@ class { 'mongodb::server': } describe port(27017) do it do + sleep(20) should be_listening end end + + describe command(client_name) do + it do + should return_exit_status 0 + end + end end context 'test using custom port' do - it 'change port to 27018' do pp = <<-EOS class { 'mongodb::globals': manage_package_repo => true }-> @@ -57,19 +74,36 @@ class { 'mongodb::server': port => 27018 } puppet_apply(pp) do |r| r.exit_code.should == 2 - r.refresh - r.exit_code.should == 0 end end describe port(27018) do + sleep(20) it { should be_listening } end end + context 'test shutdown of custom port' do + it 'shut down service on port 27018' do + pp = <<-EOS + class {'mongodb::globals': manage_package_repo => true}-> + class {'mongodb::server': port => 27018, ensure => false} + EOS + + puppet_apply(pp) do |r| + r.exit_code.should == 2 + end + end + + describe port(27018) do + it { should_not be_listening} + end + + end + describe 'cleanup' do it 'uninstalls mongodb' do - puppet_apply("class {'mongodb::globals': manage_package_repo => true }-> class { 'mongodb::server': ensure => false }") do |r| + puppet_apply("class {'mongodb::globals': manage_package_repo => true }-> class { 'mongodb::server': ensure => false }-> class { 'mongodb::client': ensure => false}") do |r| r.exit_code.should_not == 1 end end diff --git a/mongodb/spec/system/server_distro_spec.rb b/mongodb/spec/system/server_distro_spec.rb index 6ed97d088..c0e0619bf 100644 --- a/mongodb/spec/system/server_distro_spec.rb +++ b/mongodb/spec/system/server_distro_spec.rb @@ -1,4 +1,3 @@ -begin require 'spec_helper_system' describe 'mongodb::server:' do @@ -14,11 +13,21 @@ config_file = '/etc/mongodb.conf' end + client_name = 'mongo --version' + context 'default parameters' do it 'should work with no errors' do - pp = <<-EOS - class { 'mongodb::server': } - EOS + case node.facts['osfamily'] + when 'RedHat' + pp = <<-EOS + class { 'mongodb::server': } + class { 'mongodb::client': } + EOS + when 'Debian' + pp = <<-EOS + class { 'mongodb::server': } + EOS + end puppet_apply(pp) do |r| r.exit_code.should == 2 @@ -46,6 +55,12 @@ class { 'mongodb::server': } should be_listening end end + + describe command(client_name) do + it do + should return_exit_status 0 + end + end end context 'test using custom port' do @@ -56,16 +71,38 @@ class { 'mongodb::server': port => 27018 } puppet_apply(pp) do |r| r.exit_code.should == 2 - r.refresh - r.exit_code.should == 0 end end describe port(27018) do + sleep(20) it { should be_listening } end + end + + context 'test shutdown of custom port' do + it 'shut down service on port 27018' do + pp = <<-EOS + class {'mongodb::globals': manage_package_repo => true}-> + class {'mongodb::server': port => 27018, ensure => false} + EOS + + puppet_apply(pp) do |r| + r.exit_code.should == 2 + end + end + + describe port(27018) do + it { should_not be_listening} + end end -end + describe 'cleanup' do + it 'uninstalls mongodb' do + puppet_apply("class {'mongodb::globals': manage_package_repo => true }-> class { 'mongodb::server': ensure => false }-> class { 'mongodb::client': ensure => false}") do |r| + r.exit_code.should_not == 1 + end + end + end end diff --git a/mongodb/spec/unit/puppet/provider/mongodb_database/mongodb_spec.rb b/mongodb/spec/unit/puppet/provider/mongodb_database/mongodb_spec.rb index 91cbe8c5f..4376ada54 100644 --- a/mongodb/spec/unit/puppet/provider/mongodb_database/mongodb_spec.rb +++ b/mongodb/spec/unit/puppet/provider/mongodb_database/mongodb_spec.rb @@ -28,7 +28,7 @@ describe 'exists?' do it 'checks if database exists' do provider.expects(:mongo).at_least(2).returns("db1,new_database,db2") - provider.exists?.should eql true + provider.exists?.should be_true end end diff --git a/mongodb/spec/unit/puppet/provider/mongodb_replset/mongodb_spec.rb b/mongodb/spec/unit/puppet/provider/mongodb_replset/mongodb_spec.rb index d0c2d128e..76fe98fc8 100644 --- a/mongodb/spec/unit/puppet/provider/mongodb_replset/mongodb_spec.rb +++ b/mongodb/spec/unit/puppet/provider/mongodb_replset/mongodb_spec.rb @@ -17,25 +17,22 @@ } )} - let(:resources) { { 'rs_test' => resource } } - let(:provider) { described_class.new(resource) } + let(:provider) { resource.provider } - describe '#create' do + describe 'create' do it 'should create a replicaset' do - provider.class.stubs(:get_replset_properties) - provider.stubs(:alive_members).returns(valid_members) + provider.stubs(:members_present).returns(valid_members) provider.expects('rs_initiate').with("{ _id: \"rs_test\", members: [ { _id: 0, host: \"mongo1:27017\" },{ _id: 1, host: \"mongo2:27017\" },{ _id: 2, host: \"mongo3:27017\" } ] }", "mongo1:27017").returns( { "info" => "Config now saved locally. Should come online in about a minute.", "ok" => 1 } ) provider.create - provider.flush end end - describe '#exists?' do - describe 'when the replicaset does not exist' do + describe 'exists?' do + describe 'when the replicaset is not created' do it 'returns false' do - provider.class.stubs(:mongo).returns(< "rs_test" }) - provider.expects('rs_add').times(2).returns({ 'ok' => 1 }) - provider.members=(valid_members) - provider.flush + provider.members.should =~ valid_members end it 'raises an error when the master host is not available' do - provider.stubs(:rs_status).returns({ "set" => "rs_test" }) - provider.stubs(:db_ismaster).returns({ "primary" => false }) - provider.members=(valid_members) - expect { provider.flush }.to raise_error(Puppet::Error, "Can't find master host for replicaset #{resource[:name]}.") + provider.stubs(:master_host).returns(nil) + expect { provider.members }.to raise_error(Puppet::Error, "Can't find master host for replicaset #{resource[:name]}.") end - it 'raises an error when at least one member is not running with --replSet' do - provider.stubs(:rs_status).returns({ "ok" => 0, "errmsg" => "not running with --replSet" }) - provider.members=(valid_members) - expect { provider.flush }.to raise_error(Puppet::Error, /is not supposed to be part of a replicaset\.$/) - end + end - it 'raises an error when at least one member is configured with another replicaset name' do - provider.stubs(:rs_status).returns({ "set" => "rs_another" }) + describe 'members=' do + it 'adds missing members to an existing replicaset' do + provider.stubs(:mongo).returns(< #where to log logpath=<%= @logpath %> -<% if @logappend -%> logappend=<%= @logappend %> -<% end -%> -<% end -%> <% if @bind_ip -%> # Set this option to configure the mongod or mongos process to bind to and # listen for connections from applications on this address. # You may concatenate a list of comma separated values to bind mongod to multiple IP addresses. -bind_ip = <%= Array(@bind_ip).join(',') %> +bind_ip = <%= @bind_ip.join(',') %> <% end -%> <% if @fork -%> # fork and run in background @@ -22,13 +17,13 @@ port = <%= @port %> dbpath=<%= @dbpath %> <% if @pidfilepath -%> # location of pidfile -pidfilepath=<%= @pidfilepath %> +pidfilepath = <%= @pidfilepath %> <% end -%> -<% if @nojournal and not @journal -%> +<% if @nojournal -%> # Disables write-ahead journaling nojournal = <%= @nojournal %> <% end -%> -<% if @journal and not @nojournal -%> +<% if @journal -%> # Enables journaling journal = <%= @journal %> <% end -%> @@ -37,10 +32,10 @@ journal = <%= @journal %> cpu = <%= @cpu %> <% end -%> # Turn on/off security. Off is currently the default -<% if @noauth and not @auth -%> +<% if @noauth -%> noauth=<%= @noauth %> <% end -%> -<% if @auth and not @noauth -%> +<% if @auth -%> auth=<%= @auth %> <% end -%> <% if @verbose -%> @@ -161,9 +156,9 @@ slowms = <%= @slowms %> # Specify the path to a key file to store authentication information. keyFile = <%= @keyfile %> <% end -%> +<% if @directoryperdb -%> +directoryperdb = <%= @directoryperdb %> +<% end -%> <% if @set_parameter -%> setParameter = <%= @set_parameter %> <% end -%> -<% if @syslog -%> -syslog = <%= @syslog %> -<% end -%> diff --git a/mysql/.gitignore b/mysql/.gitignore index b5b7a00d6..b20556999 100644 --- a/mysql/.gitignore +++ b/mysql/.gitignore @@ -1,7 +1,7 @@ +*.swp pkg/ Gemfile.lock -vendor/ -spec/fixtures/ -.vagrant/ -.bundle/ -coverage/ +.rspec_system +spec/fixtures +/coverage/ +.vagrant diff --git a/mysql/.sync.yml b/mysql/.sync.yml deleted file mode 100644 index d35f21205..000000000 --- a/mysql/.sync.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -Gemfile: - optional: - - gem: mime-types - version: '<2.0' -spec/spec_helper.rb: - unmanaged: true diff --git a/mysql/.travis.yml b/mysql/.travis.yml index a40ae502e..42aea5918 100644 --- a/mysql/.travis.yml +++ b/mysql/.travis.yml @@ -1,17 +1,34 @@ --- +branches: + only: + - master language: ruby bundler_args: --without development -script: "bundle exec rake validate && bundle exec rake lint && bundle exec rake spec SPEC_OPTS='--format documentation'" +script: "bundle exec rake spec SPEC_OPTS='--format documentation'" +after_success: + - git clone -q git://github.com/puppetlabs/ghpublisher.git .forge-release + - .forge-release/publish +rvm: + - 1.8.7 + - 1.9.3 + - 2.0.0 +env: + matrix: + - PUPPET_GEM_VERSION="~> 2.7.0" + - PUPPET_GEM_VERSION="~> 3.3.0" + - PUPPET_GEM_VERSION="~> 3.4.0" + global: + - PUBLISHER_LOGIN=puppetlabs + - secure: |- + Hc9OPm/kRTmjXSP3TbLir/y6Yy1LqmZS8zrqxdTbpo3Z04EYv1uKhaFDpECl + 0a6bJRUWpLWIuDco08fHMeCTWoFGzE97EDelhHKSYiTNllzYKWPHy7ki/al6 + wjz0gLtiDfmktHQOHatBy6EKLFjoyjGoE4cUUta4Ixq4tMBNzEA= matrix: fast_finish: true - include: - - rvm: 1.8.7 - env: PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.6.0" - - rvm: 1.8.7 - env: PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.7.0" - - rvm: 1.9.3 - env: PUPPET_GEM_VERSION="~> 3.0" - - rvm: 2.0.0 - env: PUPPET_GEM_VERSION="~> 3.0" + exclude: + - rvm: 1.9.3 + env: PUPPET_GEM_VERSION="~> 2.7.0" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 2.7.0" notifications: email: false diff --git a/mysql/CHANGELOG.md b/mysql/CHANGELOG.md index 5f004f98b..8349998f3 100644 --- a/mysql/CHANGELOG.md +++ b/mysql/CHANGELOG.md @@ -1,30 +1,3 @@ -##2014-07-15 - Supported Release 2.3.1 -###Summary - -This release merely updates metadata.json so the module can be uninstalled and -upgraded via the puppet module command. - -##2014-05-14 - Supported Release 2.3.0 - -This release primarily adds support for RHEL7 and Ubuntu 14.04 but it -also adds a couple of new parameters to allow for further customization, -as well as ensuring backups can backup stored procedures properly. - -####Features -Added `execpath` to allow a custom executable path for non-standard mysql installations. -Added `dbname` to mysql::db and use ensure_resource to create the resource. -Added support for RHEL7 and Fedora Rawhide. -Added support for Ubuntu 14.04. -Create a warning for if you disable SSL. -Ensure the error logfile is owned by MySQL. -Disable ssl on FreeBSD. -Add PROCESS privilege for backups. - -####Bugfixes - -####Known Bugs -* No known bugs - ##2014-03-04 - Supported Release 2.2.3 ###Summary diff --git a/mysql/Gemfile b/mysql/Gemfile index 31c883e91..39687be03 100644 --- a/mysql/Gemfile +++ b/mysql/Gemfile @@ -1,19 +1,19 @@ -source ENV['GEM_SOURCE'] || "https://rubygems.org" +source ENV['GEM_SOURCE'] || 'https://rubygems.org' group :development, :test do + gem 'mime-types', '<2.0', :require => false gem 'rake', :require => false gem 'rspec-puppet', :require => false gem 'puppetlabs_spec_helper', :require => false + gem 'rspec-system', :require => false + gem 'rspec-system-puppet', :require => false + gem 'rspec-system-serverspec', :require => false gem 'serverspec', :require => false gem 'puppet-lint', :require => false + gem 'pry', :require => false + gem 'simplecov', :require => false + gem 'beaker', :require => false gem 'beaker-rspec', :require => false - gem 'puppet_facts', :require => false -end - -if facterversion = ENV['FACTER_GEM_VERSION'] - gem 'facter', facterversion, :require => false -else - gem 'facter', :require => false end if puppetversion = ENV['PUPPET_GEM_VERSION'] diff --git a/mysql/Modulefile b/mysql/Modulefile new file mode 100644 index 000000000..e89be88ec --- /dev/null +++ b/mysql/Modulefile @@ -0,0 +1,9 @@ +name 'puppetlabs-mysql' +version '2.2.3' +source 'git://github.com/puppetlabs/puppetlabs-mysql.git' +author 'Puppet Labs' +license 'Apache 2.0' +summary 'Mysql module' +description 'Mysql module' +project_page 'http://github.com/puppetlabs/puppetlabs-mysql' +dependency 'puppetlabs/stdlib', '>= 3.2.0' diff --git a/mysql/README.md b/mysql/README.md index d7fd5355b..03b6bbd36 100644 --- a/mysql/README.md +++ b/mysql/README.md @@ -4,9 +4,9 @@ 1. [Overview](#overview) 2. [Module Description - What the module does and why it is useful](#module-description) -3. [Backwards compatibility information](#backwards-compatibility) 3. [Setup - The basics of getting started with mysql](#setup) * [What mysql affects](#what-mysql-affects) + * [Setup requirements](#setup-requirements) * [Beginning with mysql](#beginning-with-mysql) 4. [Usage - Configuration options and additional functionality](#usage) 5. [Reference - An under-the-hood peek at what the module is doing and how](#reference) @@ -53,10 +53,9 @@ password or `/etc/my.cnf` settings, then you must also pass in an override hash: ```puppet class { '::mysql::server': root_password => 'strongpassword', - override_options => $override_options + override_options => { 'mysqld' => { 'max_connections' => '1024' } } } ``` -(see 'Overrides' below for examples of the hash structure for `$override_options`) ##Usage @@ -71,7 +70,7 @@ The hash structure for overrides in `mysql::server` is as follows: ```puppet $override_options = { 'section' => { - 'item' => 'thing', + 'item' => 'thing', } } ``` @@ -105,7 +104,7 @@ replicate-do-db = base2 ###Custom configuration To add custom MySQL configuration, drop additional files into -`includedir`. Dropping files into `includedir` allows you to override settings or add additional ones, which is helpful if you choose not to use `override_options` in `mysql::server`. The `includedir` location is by default set to /etc/mysql/conf.d. +`/etc/mysql/conf.d/`. Dropping files into conf.d allows you to override settings or add additional ones, which is helpful if you choose not to use `override_options` in `mysql::server`. The conf.d location is hardcoded into the my.cnf template file. ##Reference @@ -128,7 +127,6 @@ To add custom MySQL configuration, drop additional files into * `mysql::server::providers`: Creates users, grants, and databases. * `mysql::bindings::java`: Installs Java bindings. * `mysql::bindings::perl`: Installs Perl bindings. -* `mysql::bindings::php`: Installs PHP bindings. * `mysql::bindings::python`: Installs Python bindings. * `mysql::bindings::ruby`: Installs Ruby bindings. * `mysql::client::install`: Installs MySQL client. @@ -175,12 +173,9 @@ The location of the MySQL configuration file. Whether the MySQL configuration file should be managed. -#####`includedir` -The location of !includedir for custom configuration overrides. - #####`purge_conf_dir` -Whether the `includedir` directory should be purged. +Whether the conf.d directory should be purged. #####`restart` @@ -223,8 +218,8 @@ The provider to use to manage the service. Optional hash of users to create, which are passed to [mysql_user](#mysql_user). -``` -users => { +```puppet +$users = { 'someuser@localhost' => { ensure => 'present', max_connections_per_hour => '0', @@ -240,8 +235,8 @@ users => { Optional hash of grants, which are passed to [mysql_grant](#mysql_grant). -``` -grants => { +```puppet +$grants = { 'someuser@localhost/somedb.*' => { ensure => 'present', options => ['GRANT'], @@ -256,8 +251,8 @@ grants => { Optional hash of databases to create, which are passed to [mysql_database](#mysql_database). -``` -databases => { +```puppet +$databases = { 'somedb' => { ensure => 'present', charset => 'utf8', @@ -318,10 +313,6 @@ Whether a separate file be used per database. Allows you to remove the backup scripts. Can be 'present' or 'absent'. -#####`execpath` - -Allows you to set a custom PATH should your mysql installation be non-standard places. Defaults to `/usr/bin:/usr/sbin:/bin:/sbin` - #####`time` An array of two elements to set the backup time. Allows ['23', '5'] or ['3', '45'] for HH:MM times. @@ -443,42 +434,6 @@ Creates a database with a user and assigns some privileges. } ``` -Or using a different resource name with exported resources, - -```puppet - @@mysql::db { "mydb_${fqdn}": - user => 'myuser', - password => 'mypass', - dbname => 'mydb', - host => ${fqdn}, - grant => ['SELECT', 'UPDATE'], - tag => $domain, - } -``` - -Then collect it on the remote DB server. - -```puppet - Mysql::Db <<| tag == $domain |>> -``` - -If you set the sql param to a file when creating a database, -the file gets imported into the new database. - -For large sql files you should raise the $import_timeout parameter, -set by default to 300 seconds - -```puppet - mysql::db { 'mydb': - user => 'myuser', - password => 'mypass', - host => 'localhost', - grant => ['SELECT', 'UPDATE'], - sql => '/path/to/sqlfile', - import_timeout => 900, - } -``` - ###Providers ####mysql_database @@ -528,16 +483,6 @@ mysql_grant { 'root@localhost/*.*': } ``` -It is possible to specify privileges down to the column level: -```puppet -mysql_grant { 'root@localhost/mysql.user': - ensure => 'present', - privileges => ['SELECT (Host, User)'], - table => 'mysql.user', - user => 'root@localhost', -} -``` - ##Limitations This module has been tested on: diff --git a/mysql/Rakefile b/mysql/Rakefile index 5868545f2..bb60173e5 100644 --- a/mysql/Rakefile +++ b/mysql/Rakefile @@ -1,10 +1,2 @@ require 'puppetlabs_spec_helper/rake_tasks' -require 'puppet-lint/tasks/puppet-lint' - -PuppetLint.configuration.fail_on_warnings -PuppetLint.configuration.send('disable_80chars') -PuppetLint.configuration.send('disable_class_inherits_from_params_class') -PuppetLint.configuration.send('disable_class_parameter_defaults') -PuppetLint.configuration.send('disable_documentation') -PuppetLint.configuration.send('disable_single_quote_string_with_variables') -PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "pkg/**/*.pp"] +require 'rspec-system/rake_task' diff --git a/mysql/files/mysqltuner.pl b/mysql/files/mysqltuner.pl index ecf2cb16c..46cdb7f42 100644 --- a/mysql/files/mysqltuner.pl +++ b/mysql/files/mysqltuner.pl @@ -1,10 +1,10 @@ #!/usr/bin/perl -w -# mysqltuner.pl - Version 1.3.0 +# mysqltuner.pl - Version 1.2.0 # High Performance MySQL Tuning Script -# Copyright (C) 2006-2014 Major Hayden - major@mhtx.net +# Copyright (C) 2006-2011 Major Hayden - major@mhtx.net # # For the latest updates, please visit http://mysqltuner.com/ -# Git repository available at http://github.com/major/MySQLTuner-perl +# Git repository available at http://github.com/rackerhacker/MySQLTuner-perl # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -41,7 +41,7 @@ use Getopt::Long; # Set up a few variables for use in the script -my $tunerversion = "1.3.0"; +my $tunerversion = "1.2.0"; my (@adjvars, @generalrec); # Set defaults @@ -76,7 +76,6 @@ 'pass=s', 'skipsize', 'checkversion', - 'mysqladmin=s', 'help', ); @@ -101,7 +100,6 @@ sub usage { " --port Port to use for connection (default: 3306)\n". " --user Username to use for authentication\n". " --pass Password to use for authentication\n". - " --mysqladmin Path to a custom mysqladmin executable\n". "\n". " Performance and Reporting Options\n". " --skipsize Don't enumerate tables and their types/sizes (default: on)\n". @@ -221,14 +219,14 @@ sub os_setup { } elsif ($os =~ /Darwin/) { $physical_memory = `sysctl -n hw.memsize` or memerror; $swap_memory = `sysctl -n vm.swapusage | awk '{print \$3}' | sed 's/\..*\$//'` or memerror; - } elsif ($os =~ /NetBSD|OpenBSD|FreeBSD/) { + } elsif ($os =~ /NetBSD|OpenBSD/) { $physical_memory = `sysctl -n hw.physmem` or memerror; if ($physical_memory < 0) { $physical_memory = `sysctl -n hw.physmem64` or memerror; } $swap_memory = `swapctl -l | grep '^/' | awk '{ s+= \$2 } END { print s }'` or memerror; } elsif ($os =~ /BSD/) { - $physical_memory = `sysctl -n hw.realmem` or memerror; + $physical_memory = `sysctl -n hw.realmem`; $swap_memory = `swapinfo | grep '^/' | awk '{ s+= \$2 } END { print s }'`; } elsif ($os =~ /SunOS/) { $physical_memory = `/usr/sbin/prtconf | grep Memory | cut -f 3 -d ' '` or memerror; @@ -251,22 +249,12 @@ sub os_setup { sub mysql_setup { $doremote = 0; $remotestring = ''; - my $mysqladmincmd; - if ($opt{mysqladmin}) { - $mysqladmincmd = $opt{mysqladmin}; - } else { - $mysqladmincmd = `which mysqladmin`; - } - chomp($mysqladmincmd); - if (! -e $mysqladmincmd && $opt{mysqladmin}) { - badprint "Unable to find the mysqladmin command you specified: ".$mysqladmincmd."\n"; - exit; - } elsif (! -e $mysqladmincmd) { - badprint "Couldn't find mysqladmin in your \$PATH. Is MySQL installed?\n"; + my $command = `which mysqladmin`; + chomp($command); + if (! -e $command) { + badprint "Unable to find mysqladmin in your \$PATH. Is MySQL installed?\n"; exit; } - - # Are we being asked to connect via a socket? if ($opt{socket} ne 0) { $remotestring = " -S $opt{socket}"; @@ -287,7 +275,7 @@ sub mysql_setup { # Did we already get a username and password passed on the command line? if ($opt{user} ne 0 and $opt{pass} ne 0) { $mysqllogin = "-u $opt{user} -p'$opt{pass}'".$remotestring; - my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; + my $loginstatus = `mysqladmin ping $mysqllogin 2>&1`; if ($loginstatus =~ /mysqld is alive/) { goodprint "Logged in using credentials passed on the command line\n"; return 1; @@ -296,52 +284,18 @@ sub mysql_setup { exit 0; } } - my $svcprop = `which svcprop 2>/dev/null`; - if (substr($svcprop, 0, 1) =~ "/") { - # We are on solaris - (my $mysql_login = `svcprop -p quickbackup/username svc:/network/mysql-quickbackup:default`) =~ s/\s+$//; - (my $mysql_pass = `svcprop -p quickbackup/password svc:/network/mysql-quickbackup:default`) =~ s/\s+$//; - if ( substr($mysql_login, 0, 7) ne "svcprop" ) { - # mysql-quickbackup is installed - $mysqllogin = "-u $mysql_login -p$mysql_pass"; - my $loginstatus = `mysqladmin $mysqllogin ping 2>&1`; - if ($loginstatus =~ /mysqld is alive/) { - goodprint "Logged in using credentials from mysql-quickbackup.\n"; - return 1; - } else { - badprint "Attempted to use login credentials from mysql-quickbackup, but they failed.\n"; - exit 0; - } - } - } elsif ( -r "/etc/psa/.psa.shadow" and $doremote == 0 ) { + if ( -r "/etc/psa/.psa.shadow" and $doremote == 0 ) { # It's a Plesk box, use the available credentials $mysqllogin = "-u admin -p`cat /etc/psa/.psa.shadow`"; - my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; - unless ($loginstatus =~ /mysqld is alive/) { - badprint "Attempted to use login credentials from Plesk, but they failed.\n"; - exit 0; - } - } elsif ( -r "/usr/local/directadmin/conf/mysql.conf" and $doremote == 0 ){ - # It's a DirectAdmin box, use the available credentials - my $mysqluser=`cat /usr/local/directadmin/conf/mysql.conf | egrep '^user=.*'`; - my $mysqlpass=`cat /usr/local/directadmin/conf/mysql.conf | egrep '^passwd=.*'`; - - $mysqluser =~ s/user=//; - $mysqluser =~ s/[\r\n]//; - $mysqlpass =~ s/passwd=//; - $mysqlpass =~ s/[\r\n]//; - - $mysqllogin = "-u $mysqluser -p$mysqlpass"; - my $loginstatus = `mysqladmin ping $mysqllogin 2>&1`; unless ($loginstatus =~ /mysqld is alive/) { - badprint "Attempted to use login credentials from DirectAdmin, but they failed.\n"; + badprint "Attempted to use login credentials from Plesk, but they failed.\n"; exit 0; } } elsif ( -r "/etc/mysql/debian.cnf" and $doremote == 0 ){ # We have a debian maintenance account, use it - $mysqllogin = "--defaults-file=/etc/mysql/debian.cnf"; - my $loginstatus = `$mysqladmincmd $mysqllogin ping 2>&1`; + $mysqllogin = "--defaults-extra-file=/etc/mysql/debian.cnf"; + my $loginstatus = `mysqladmin $mysqllogin ping 2>&1`; if ($loginstatus =~ /mysqld is alive/) { goodprint "Logged in using credentials from debian maintenance account.\n"; return 1; @@ -351,7 +305,7 @@ sub mysql_setup { } } else { # It's not Plesk or debian, we should try a login - my $loginstatus = `$mysqladmincmd $remotestring ping 2>&1`; + my $loginstatus = `mysqladmin $remotestring ping 2>&1`; if ($loginstatus =~ /mysqld is alive/) { # Login went just fine $mysqllogin = " $remotestring "; @@ -378,7 +332,7 @@ sub mysql_setup { $mysqllogin .= " -p'$password'"; } $mysqllogin .= $remotestring; - my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; + my $loginstatus = `mysqladmin ping $mysqllogin 2>&1`; if ($loginstatus =~ /mysqld is alive/) { print STDERR "\n"; if (! length($password)) { @@ -462,10 +416,35 @@ sub get_replication_status { } } +# Checks for updates to MySQLTuner +sub validate_tuner_version { + print "\n-------- General Statistics --------------------------------------------------\n"; + if ($opt{checkversion} eq 0) { + infoprint "Skipped version check for MySQLTuner script\n"; + return; + } + my $update; + my $url = "http://mysqltuner.com/versioncheck.php?v=$tunerversion"; + if (-e "/usr/bin/curl") { + $update = `/usr/bin/curl --connect-timeout 5 '$url' 2>$devnull`; + chomp($update); + } elsif (-e "/usr/bin/wget") { + $update = `/usr/bin/wget -e timestamping=off -T 5 -O - '$url' 2>$devnull`; + chomp($update); + } + if ($update eq 1) { + badprint "There is a new version of MySQLTuner available\n"; + } elsif ($update eq 0) { + goodprint "You have the latest version of MySQLTuner\n"; + } else { + infoprint "Unable to check for the latest MySQLTuner version\n"; + } +} + # Checks for supported or EOL'ed MySQL versions my ($mysqlvermajor,$mysqlverminor); sub validate_mysql_version { - ($mysqlvermajor,$mysqlverminor) = $myvar{'version'} =~ /(\d+)\.(\d+)/; + ($mysqlvermajor,$mysqlverminor) = $myvar{'version'} =~ /(\d)\.(\d)/; if (!mysql_version_ge(5)) { badprint "Your MySQL version ".$myvar{'version'}." is EOL software! Upgrade soon!\n"; } elsif (mysql_version_ge(6)) { @@ -494,20 +473,6 @@ sub check_architecture { } elsif (`uname` =~ /AIX/ && `bootinfo -K` =~ /64/) { $arch = 64; goodprint "Operating on 64-bit architecture\n"; - } elsif (`uname` =~ /NetBSD|OpenBSD/ && `sysctl -b hw.machine` =~ /64/) { - $arch = 64; - goodprint "Operating on 64-bit architecture\n"; - } elsif (`uname` =~ /FreeBSD/ && `sysctl -b hw.machine_arch` =~ /64/) { - $arch = 64; - goodprint "Operating on 64-bit architecture\n"; - } elsif (`uname` =~ /Darwin/ && `uname -m` =~ /Power Macintosh/) { - # Darwin box.local 9.8.0 Darwin Kernel Version 9.8.0: Wed Jul 15 16:57:01 PDT 2009; root:xnu1228.15.4~1/RELEASE_PPC Power Macintosh - $arch = 64; - goodprint "Operating on 64-bit architecture\n"; - } elsif (`uname` =~ /Darwin/ && `uname -m` =~ /x86_64/) { - # Darwin gibas.local 12.3.0 Darwin Kernel Version 12.3.0: Sun Jan 6 22:37:10 PST 2013; root:xnu-2050.22.13~1/RELEASE_X86_64 x86_64 - $arch = 64; - goodprint "Operating on 64-bit architecture\n"; } else { $arch = 32; if ($physical_memory > 2147483648) { @@ -529,21 +494,12 @@ sub check_storage_engines { print "\n-------- Storage Engine Statistics -------------------------------------------\n"; infoprint "Status: "; my $engines; - if (mysql_version_ge(5)) { - my @engineresults = `mysql $mysqllogin -Bse "SELECT ENGINE,SUPPORT FROM information_schema.ENGINES WHERE ENGINE NOT IN ('performance_schema','MyISAM','MERGE','MEMORY') ORDER BY ENGINE ASC"`; - foreach my $line (@engineresults) { - my ($engine,$engineenabled); - ($engine,$engineenabled) = $line =~ /([a-zA-Z_]*)\s+([a-zA-Z]+)/; - $engines .= ($engineenabled eq "YES" || $engineenabled eq "DEFAULT") ? greenwrap "+".$engine." " : redwrap "-".$engine." "; - } - } else { - $engines .= (defined $myvar{'have_archive'} && $myvar{'have_archive'} eq "YES")? greenwrap "+Archive " : redwrap "-Archive " ; - $engines .= (defined $myvar{'have_bdb'} && $myvar{'have_bdb'} eq "YES")? greenwrap "+BDB " : redwrap "-BDB " ; - $engines .= (defined $myvar{'have_federated_engine'} && $myvar{'have_federated_engine'} eq "YES")? greenwrap "+Federated " : redwrap "-Federated " ; - $engines .= (defined $myvar{'have_innodb'} && $myvar{'have_innodb'} eq "YES")? greenwrap "+InnoDB " : redwrap "-InnoDB " ; - $engines .= (defined $myvar{'have_isam'} && $myvar{'have_isam'} eq "YES")? greenwrap "+ISAM " : redwrap "-ISAM " ; - $engines .= (defined $myvar{'have_ndbcluster'} && $myvar{'have_ndbcluster'} eq "YES")? greenwrap "+NDBCluster " : redwrap "-NDBCluster " ; - } + $engines .= (defined $myvar{'have_archive'} && $myvar{'have_archive'} eq "YES")? greenwrap "+Archive " : redwrap "-Archive " ; + $engines .= (defined $myvar{'have_bdb'} && $myvar{'have_bdb'} eq "YES")? greenwrap "+BDB " : redwrap "-BDB " ; + $engines .= (defined $myvar{'have_federated_engine'} && $myvar{'have_federated_engine'} eq "YES")? greenwrap "+Federated " : redwrap "-Federated " ; + $engines .= (defined $myvar{'have_innodb'} && $myvar{'have_innodb'} eq "YES")? greenwrap "+InnoDB " : redwrap "-InnoDB " ; + $engines .= (defined $myvar{'have_isam'} && $myvar{'have_isam'} eq "YES")? greenwrap "+ISAM " : redwrap "-ISAM " ; + $engines .= (defined $myvar{'have_ndbcluster'} && $myvar{'have_ndbcluster'} eq "YES")? greenwrap "+NDBCluster " : redwrap "-NDBCluster " ; print "$engines\n"; if (mysql_version_ge(5)) { # MySQL 5 servers can have table sizes calculated quickly from information schema @@ -649,10 +605,8 @@ sub calculations { $mycalc{'pct_connections_used'} = ($mycalc{'pct_connections_used'} > 100) ? 100 : $mycalc{'pct_connections_used'} ; # Key buffers - if (mysql_version_ge(4, 1) && $myvar{'key_buffer_size'} > 0) { + if (mysql_version_ge(4, 1)) { $mycalc{'pct_key_buffer_used'} = sprintf("%.1f",(1 - (($mystat{'Key_blocks_unused'} * $myvar{'key_cache_block_size'}) / $myvar{'key_buffer_size'})) * 100); - } else { - $mycalc{'pct_key_buffer_used'} = 0; } if ($mystat{'Key_read_requests'} > 0) { $mycalc{'pct_keys_from_mem'} = sprintf("%.1f",(100 - (($mystat{'Key_reads'} / $mystat{'Key_read_requests'}) * 100))); @@ -826,10 +780,7 @@ sub mysql_stats { } elsif ($myvar{'query_cache_size'} < 1) { badprint "Query cache is disabled\n"; push(@adjvars,"query_cache_size (>= 8M)"); - } elsif ($myvar{'query_cache_type'} eq "OFF") { - badprint "Query cache is disabled\n"; - push(@adjvars,"query_cache_type (=1)"); - } elsif ($mystat{'Com_select'} == 0) { + } elsif ($mystat{'Com_select'} == 0) { badprint "Query cache cannot be analyzed - no SELECT statements executed\n"; } else { if ($mycalc{'query_cache_efficiency'} < 20) { @@ -908,18 +859,15 @@ sub mysql_stats { } # Table cache - my $table_cache_var = ""; if ($mystat{'Open_tables'} > 0) { if ($mycalc{'table_cache_hit_rate'} < 20) { badprint "Table cache hit rate: $mycalc{'table_cache_hit_rate'}% (".hr_num($mystat{'Open_tables'})." open / ".hr_num($mystat{'Opened_tables'})." opened)\n"; if (mysql_version_ge(5, 1)) { - $table_cache_var = "table_open_cache"; + push(@adjvars,"table_cache (> ".$myvar{'table_open_cache'}.")"); } else { - $table_cache_var = "table_cache"; + push(@adjvars,"table_cache (> ".$myvar{'table_cache'}.")"); } - push(@adjvars,$table_cache_var." (> ".$myvar{'table_open_cache'}.")"); - push(@generalrec,"Increase ".$table_cache_var." gradually to avoid file descriptor limits"); - push(@generalrec,"Read this before increasing ".$table_cache_var." over 64: http://bit.ly/1mi7c4C"); + push(@generalrec,"Increase table_cache gradually to avoid file descriptor limits"); } else { goodprint "Table cache hit rate: $mycalc{'table_cache_hit_rate'}% (".hr_num($mystat{'Open_tables'})." open / ".hr_num($mystat{'Opened_tables'})." opened)\n"; } @@ -961,17 +909,11 @@ sub mysql_stats { # InnoDB if (defined $myvar{'have_innodb'} && $myvar{'have_innodb'} eq "YES" && defined $enginestats{'InnoDB'}) { if ($myvar{'innodb_buffer_pool_size'} > $enginestats{'InnoDB'}) { - goodprint "InnoDB buffer pool / data size: ".hr_bytes($myvar{'innodb_buffer_pool_size'})."/".hr_bytes($enginestats{'InnoDB'})."\n"; + goodprint "InnoDB data size / buffer pool: ".hr_bytes($enginestats{'InnoDB'})."/".hr_bytes($myvar{'innodb_buffer_pool_size'})."\n"; } else { - badprint "InnoDB buffer pool / data size: ".hr_bytes($myvar{'innodb_buffer_pool_size'})."/".hr_bytes($enginestats{'InnoDB'})."\n"; + badprint "InnoDB data size / buffer pool: ".hr_bytes($enginestats{'InnoDB'})."/".hr_bytes($myvar{'innodb_buffer_pool_size'})."\n"; push(@adjvars,"innodb_buffer_pool_size (>= ".hr_bytes_rnd($enginestats{'InnoDB'}).")"); } - if (defined $mystat{'Innodb_log_waits'} && $mystat{'Innodb_log_waits'} > 0) { - badprint "InnoDB log waits: ".$mystat{'Innodb_log_waits'}; - push(@adjvars,"innodb_log_buffer_size (>= ".hr_bytes_rnd($myvar{'innodb_log_buffer_size'}).")"); - } else { - goodprint "InnoDB log waits: ".$mystat{'Innodb_log_waits'}; - } } } @@ -1005,6 +947,7 @@ sub make_recommendations { mysql_setup; # Gotta login first os_setup; # Set up some OS variables get_all_vars; # Toss variables/status into hashes +validate_tuner_version; # Check current MySQLTuner version validate_mysql_version; # Check current MySQL version check_architecture; # Suggest 64-bit upgrade check_storage_engines; # Show enabled storage engines diff --git a/mysql/lib/puppet/provider/database/mysql.rb b/mysql/lib/puppet/provider/database/mysql.rb new file mode 100644 index 000000000..ace742967 --- /dev/null +++ b/mysql/lib/puppet/provider/database/mysql.rb @@ -0,0 +1,41 @@ +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'mysql')) +Puppet::Type.type(:database).provide(:mysql, :parent => Puppet::Provider::Mysql) do + desc 'Manages MySQL database.' + + defaultfor :kernel => 'Linux' + + optional_commands :mysql => 'mysql' + optional_commands :mysqladmin => 'mysqladmin' + + def self.instances + mysql([defaults_file, '-NBe', 'show databases'].compact).split("\n").collect do |name| + new(:name => name) + end + end + + def create + mysql([defaults_file, '-NBe', "create database `#{@resource[:name]}` character set #{resource[:charset]}"].compact) + end + + def destroy + mysqladmin([defaults_file, '-f', 'drop', @resource[:name]].compact) + end + + def charset + mysql([defaults_file, '-NBe', "show create database `#{resource[:name]}`"].compact).match(/.*?(\S+)\s(?:COLLATE.*)?\*\//)[1] + end + + def charset=(value) + mysql([defaults_file, '-NBe', "alter database `#{resource[:name]}` CHARACTER SET #{value}"].compact) + end + + def exists? + begin + mysql([defaults_file, '-NBe', 'show databases'].compact).match(/^#{@resource[:name]}$/) + rescue => e + debug(e.message) + return nil + end + end + +end diff --git a/mysql/lib/puppet/provider/database_grant/mysql.rb b/mysql/lib/puppet/provider/database_grant/mysql.rb new file mode 100644 index 000000000..eabc649c3 --- /dev/null +++ b/mysql/lib/puppet/provider/database_grant/mysql.rb @@ -0,0 +1,199 @@ +# A grant is either global or per-db. This can be distinguished by the syntax +# of the name: +# user@host => global +# user@host/db => per-db + +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'mysql')) +Puppet::Type.type(:database_grant).provide(:mysql, :parent => Puppet::Provider::Mysql) do + + desc 'Uses mysql as database.' + + defaultfor :kernel => 'Linux' + + optional_commands :mysql => 'mysql' + optional_commands :mysqladmin => 'mysqladmin' + + def self.prefetch(resources) + @user_privs = query_user_privs + @db_privs = query_db_privs + end + + def self.user_privs + @user_privs || query_user_privs + end + + def self.db_privs + @db_privs || query_db_privs + end + + def user_privs + self.class.user_privs + end + + def db_privs + self.class.db_privs + end + + def self.query_user_privs + results = mysql([defaults_file, 'mysql', '-Be', 'describe user'].compact) + column_names = results.split(/\n/).map { |l| l.chomp.split(/\t/)[0] } + @user_privs = column_names.delete_if { |e| !( e =~/_priv$/) } + end + + def self.query_db_privs + results = mysql([defaults_file, 'mysql', '-Be', 'describe db'].compact) + column_names = results.split(/\n/).map { |l| l.chomp.split(/\t/)[0] } + @db_privs = column_names.delete_if { |e| !(e =~/_priv$/) } + end + + def mysql_flush + mysqladmin([defaults_file, 'flush-privileges'].compact) + end + + # this parses the + def split_name(string) + matches = /^([^@]*)@([^\/]*)(\/(.*))?$/.match(string).captures.compact + case matches.length + when 2 + { + :type => :user, + :user => matches[0], + :host => matches[1] + } + when 4 + { + :type => :db, + :user => matches[0], + :host => matches[1], + :db => matches[3] + } + end + end + + def create_row + unless @resource.should(:privileges).empty? + name = split_name(@resource[:name]) + case name[:type] + when :user + mysql([defaults_file, 'mysql', '-e', "INSERT INTO user (host, user) VALUES ('%s', '%s')" % [ + name[:host], name[:user], + ]].compact) + when :db + mysql([defaults_file, 'mysql', '-e', "INSERT INTO db (host, user, db) VALUES ('%s', '%s', '%s')" % [ + name[:host], name[:user], name[:db], + ]].compact) + end + mysql_flush + end + end + + def destroy + mysql([defaults_file, 'mysql', '-e', "REVOKE ALL ON '%s'.* FROM '%s@%s'" % [ @resource[:privileges], @resource[:database], @resource[:name], @resource[:host] ]].compact) + end + + def row_exists? + name = split_name(@resource[:name]) + fields = [:user, :host] + if name[:type] == :db + fields << :db + end + not mysql([defaults_file, 'mysql', '-NBe', "SELECT '1' FROM %s WHERE %s" % [ name[:type], fields.map do |f| "%s='%s'" % [f, name[f]] end.join(' AND ')]].compact).empty? + end + + def all_privs_set? + all_privs = case split_name(@resource[:name])[:type] + when :user + user_privs + when :db + db_privs + end + all_privs = all_privs.collect do |p| p.downcase end.sort.join('|') + privs = privileges.collect do |p| p.downcase end.sort.join('|') + + all_privs == privs + end + + def privileges + name = split_name(@resource[:name]) + privs = '' + + case name[:type] + when :user + privs = mysql([defaults_file, 'mysql', '-Be', "select * from mysql.user where user='%s' and host='%s'" % [ name[:user], name[:host] ]].compact) + when :db + privs = mysql([defaults_file, 'mysql', '-Be', "select * from mysql.db where user='%s' and host='%s' and db='%s'" % [ name[:user], name[:host], name[:db] ]].compact) + end + + if privs.match(/^$/) + privs = [] # no result, no privs + else + # returns a line with field names and a line with values, each tab-separated + privs = privs.split(/\n/).map! do |l| l.chomp.split(/\t/) end + # transpose the lines, so we have key/value pairs + privs = privs[0].zip(privs[1]) + privs = privs.select do |p| p[0].match(/_priv$/) and p[1] == 'Y' end + end + + privs.collect do |p| p[0] end + end + + def privileges=(privs) + unless row_exists? + create_row + end + + # puts "Setting privs: ", privs.join(", ") + name = split_name(@resource[:name]) + stmt = '' + where = '' + all_privs = [] + case name[:type] + when :user + stmt = 'update user set ' + where = " where user='%s' and host='%s'" % [ name[:user], name[:host] ] + all_privs = user_privs + when :db + stmt = 'update db set ' + where = " where user='%s' and host='%s' and db='%s'" % [ name[:user], name[:host], name[:db] ] + all_privs = db_privs + end + + if privs[0].downcase == 'all' + privs = all_privs + end + + # Downcase the requested priviliges for case-insensitive selection + # we don't map! here because the all_privs object has to remain in + # the same case the DB gave it to us in + privs = privs.map { |p| p.downcase } + + # puts "stmt:", stmt + set = all_privs.collect do |p| "%s = '%s'" % [p, privs.include?(p.downcase) ? 'Y' : 'N'] end.join(', ') + # puts "set:", set + stmt = stmt << set << where + + validate_privs privs, all_privs + mysql([defaults_file, 'mysql', '-Be', stmt].compact) + mysql_flush + end + + def validate_privs(set_privs, all_privs) + all_privs = all_privs.collect { |p| p.downcase } + set_privs = set_privs.collect { |p| p.downcase } + invalid_privs = Array.new + hints = Array.new + # Test each of the user provided privs to see if they exist in all_privs + set_privs.each do |priv| + invalid_privs << priv unless all_privs.include?(priv) + hints << "#{priv}_priv" if all_privs.include?("#{priv}_priv") + end + unless invalid_privs.empty? + # Print a decently helpful and gramatically correct error message + hints = "Did you mean '#{hints.join(',')}'?" unless hints.empty? + p = invalid_privs.size > 1 ? ['s', 'are not valid'] : ['', 'is not valid'] + detail = ["The privilege#{p[0]} '#{invalid_privs.join(',')}' #{p[1]}."] + fail [detail, hints].join(' ') + end + end + +end diff --git a/mysql/lib/puppet/provider/database_user/mysql.rb b/mysql/lib/puppet/provider/database_user/mysql.rb new file mode 100644 index 000000000..71e76d5c3 --- /dev/null +++ b/mysql/lib/puppet/provider/database_user/mysql.rb @@ -0,0 +1,65 @@ +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'mysql')) +Puppet::Type.type(:database_user).provide(:mysql, :parent => Puppet::Provider::Mysql) do + + desc 'manage users for a mysql database.' + + defaultfor :kernel => 'Linux' + + commands :mysql => 'mysql' + commands :mysqladmin => 'mysqladmin' + + def self.instances + users = mysql([defaults_file, 'mysql', '-BNe' "select concat(User, '@',Host) as User from mysql.user"].compact).split("\n") + users.select{ |user| user =~ /.+@/ }.collect do |name| + new(:name => name) + end + end + + def create + merged_name = self.class.cmd_user(@resource[:name]) + password_hash = @resource.value(:password_hash) + max_user_connections = @resource.value(:max_user_connections) || 0 + + mysql([defaults_file, 'mysql', '-e', "grant usage on *.* to #{merged_name} identified by PASSWORD + '#{password_hash}' with max_user_connections #{max_user_connections}"].compact) + + exists? ? (return true) : (return false) + end + + def destroy + merged_name = self.class.cmd_user(@resource[:name]) + mysql([defaults_file, 'mysql', '-e', "drop user #{merged_name}"].compact) + + exists? ? (return false) : (return true) + end + + def password_hash + mysql([defaults_file, 'mysql', '-NBe', "select password from mysql.user where CONCAT(user, '@', host) = '#{@resource[:name]}'"].compact).chomp + end + + def password_hash=(string) + mysql([defaults_file, 'mysql', '-e', "SET PASSWORD FOR #{self.class.cmd_user(@resource[:name])} = '#{string}'"].compact) + + password_hash == string ? (return true) : (return false) + end + + def max_user_connections + mysql([defaults_file, "mysql", "-NBe", "select max_user_connections from mysql.user where CONCAT(user, '@', host) = '#{@resource[:name]}'"].compact).chomp + end + + def max_user_connections=(int) + mysql([defaults_file, "mysql", "-e", "grant usage on *.* to %s with max_user_connections #{int}" % [ self.class.cmd_user(@resource[:name])] ].compact).chomp + + max_user_connections == int ? (return true) : (return false) + end + + def exists? + not mysql([defaults_file, 'mysql', '-NBe', "select '1' from mysql.user where CONCAT(user, '@', host) = '%s'" % @resource.value(:name)].compact).empty? + end + + def flush + @property_hash.clear + mysqladmin([defaults_file, 'flush-privileges'].compact) + end + +end diff --git a/mysql/lib/puppet/provider/mysql_database/mysql.rb b/mysql/lib/puppet/provider/mysql_database/mysql.rb index 4587c1882..ae9ab3930 100644 --- a/mysql/lib/puppet/provider/mysql_database/mysql.rb +++ b/mysql/lib/puppet/provider/mysql_database/mysql.rb @@ -31,7 +31,7 @@ def self.prefetch(resources) end def create - mysql([defaults_file, '-NBe', "create database if not exists `#{@resource[:name]}` character set `#{@resource[:charset]}` collate `#{@resource[:collate]}`"].compact) + mysql([defaults_file, '-NBe', "create database if not exists `#{@resource[:name]}` character set #{@resource[:charset]} collate #{@resource[:collate]}"].compact) @property_hash[:ensure] = :present @property_hash[:charset] = @resource[:charset] @@ -41,7 +41,7 @@ def create end def destroy - mysql([defaults_file, '-NBe', "drop database if exists `#{@resource[:name]}`"].compact) + mysql([defaults_file, '-NBe', "drop database `#{@resource[:name]}`"].compact) @property_hash.clear exists? ? (return false) : (return true) diff --git a/mysql/lib/puppet/provider/mysql_grant/mysql.rb b/mysql/lib/puppet/provider/mysql_grant/mysql.rb index 32211e36a..7dabf4620 100644 --- a/mysql/lib/puppet/provider/mysql_grant/mysql.rb +++ b/mysql/lib/puppet/provider/mysql_grant/mysql.rb @@ -8,45 +8,21 @@ def self.instances users.select{ |user| user =~ /.+@/ }.collect do |user| user_string = self.cmd_user(user) query = "SHOW GRANTS FOR #{user_string};" - begin - grants = mysql([defaults_file, "-NBe", query].compact) - rescue Puppet::ExecutionFailure => e - # Silently ignore users with no grants. Can happen e.g. if user is - # defined with fqdn and server is run with skip-name-resolve. Example: - # Default root user created by mysql_install_db on a host with fqdn - # of myhost.mydomain.my: root@myhost.mydomain.my, when MySQL is started - # with --skip-name-resolve. - if e.inspect =~ /There is no such grant defined for user/ - next - else - raise Puppet::Error, "#mysql had an error -> #{e.inspect}" - end - end + grants = mysql([defaults_file, "-NBe", query].compact) # Once we have the list of grants generate entries for each. grants.each_line do |grant| # Match the munges we do in the type. munged_grant = grant.delete("'").delete("`") # Matching: GRANT (SELECT, UPDATE) PRIVILEGES ON (*.*) TO ('root')@('127.0.0.1') (WITH GRANT OPTION) - if match = munged_grant.match(/^GRANT\s(.+)\sON\s(.+)\sTO\s(.*)@(.*?)(\s.*)?$/) + if match = munged_grant.match(/^GRANT\s(.+)\sON\s(.+)\sTO\s(.*)@(.*?)(\s.*)$/) privileges, table, user, host, rest = match.captures - # split on ',' if it is not a non-'('-containing string followed by a - # closing parenthesis ')'-char - e.g. only split comma separated elements not in - # parentheses - stripped_privileges = privileges.strip.split(/\s*,\s*(?![^(]*\))/).map do |priv| - # split and sort the column_privileges in the parentheses and rejoin - if priv.include?('(') - type, col=priv.strip.split(/\s+|\b/,2) - type.upcase + " (" + col.slice(1...-1).strip.split(/\s*,\s*/).sort.join(', ') + ")" - else - # Once we split privileges up on the , we need to make sure we - # shortern ALL PRIVILEGES to just all. - priv == 'ALL PRIVILEGES' ? 'ALL' : priv.strip - end + # Once we split privileges up on the , we need to make sure we + # shortern ALL PRIVILEGES to just all. + stripped_privileges = privileges.split(',').map do |priv| + priv == 'ALL PRIVILEGES' ? 'ALL' : priv.lstrip.rstrip end # Same here, but to remove OPTION leaving just GRANT. options = ['GRANT'] if rest.match(/WITH\sGRANT\sOPTION/) - # fix double backslash that MySQL prints, so resources match - table.gsub!("\\\\", "\\") # We need to return an array of instances so capture these instances << new( :name => "#{user}@#{host}/#{table}", @@ -98,15 +74,13 @@ def revoke(user, table) user_string = self.class.cmd_user(user) table_string = self.class.cmd_table(table) + query = "REVOKE ALL ON #{table_string} FROM #{user_string}" + mysql([defaults_file, '-e', query].compact) # revoke grant option needs to be a extra query, because # "REVOKE ALL PRIVILEGES, GRANT OPTION [..]" is only valid mysql syntax # if no ON clause is used. - # It hast to be executed before "REVOKE ALL [..]" since a GRANT has to - # exist to be executed successfully query = "REVOKE GRANT OPTION ON #{table_string} FROM #{user_string}" mysql([defaults_file, '-e', query].compact) - query = "REVOKE ALL ON #{table_string} FROM #{user_string}" - mysql([defaults_file, '-e', query].compact) end def destroy diff --git a/mysql/lib/puppet/type/database.rb b/mysql/lib/puppet/type/database.rb new file mode 100644 index 000000000..b02fb1099 --- /dev/null +++ b/mysql/lib/puppet/type/database.rb @@ -0,0 +1,21 @@ +# This has to be a separate type to enable collecting +Puppet::Type.newtype(:database) do + @doc = 'Manage databases.' + + ensurable + + newparam(:name, :namevar=>true) do + desc 'The name of the database.' + validate do |value| + Puppet.warning("database has been deprecated in favor of mysql_database.") + true + end + end + + newproperty(:charset) do + desc 'The characterset to use for a database' + defaultto :utf8 + newvalue(/^\S+$/) + end + +end diff --git a/mysql/lib/puppet/type/database_grant.rb b/mysql/lib/puppet/type/database_grant.rb new file mode 100644 index 000000000..7fdad8231 --- /dev/null +++ b/mysql/lib/puppet/type/database_grant.rb @@ -0,0 +1,79 @@ +# This has to be a separate type to enable collecting +Puppet::Type.newtype(:database_grant) do + @doc = "Manage a database user's rights." + #ensurable + + autorequire :database do + # puts "Starting db autoreq for %s" % self[:name] + reqs = [] + matches = self[:name].match(/^([^@]+)@([^\/]+)\/(.+)$/) + unless matches.nil? + reqs << matches[3] + end + # puts "Autoreq: '%s'" % reqs.join(" ") + reqs + end + + autorequire :database_user do + # puts "Starting user autoreq for %s" % self[:name] + reqs = [] + matches = self[:name].match(/^([^@]+)@([^\/]+).*$/) + unless matches.nil? + reqs << '%s@%s' % [ matches[1], matches[2] ] + end + # puts "Autoreq: '%s'" % reqs.join(" ") + reqs + end + + newparam(:name, :namevar=>true) do + desc 'The primary key: either user@host for global privilges or user@host/database for database specific privileges' + validate do |value| + Puppet.warning("database_grant has been deprecated in favor of mysql_grant.") + true + end + end + + newproperty(:privileges, :array_matching => :all) do + desc 'The privileges the user should have. The possible values are implementation dependent.' + + def should_to_s(newvalue = @should) + if newvalue + unless newvalue.is_a?(Array) + newvalue = [ newvalue ] + end + newvalue.collect do |v| v.downcase end.sort.join ', ' + else + nil + end + end + + def is_to_s(currentvalue = @is) + if currentvalue + unless currentvalue.is_a?(Array) + currentvalue = [ currentvalue ] + end + currentvalue.collect do |v| v.downcase end.sort.join ', ' + else + nil + end + end + + # use the sorted outputs for comparison + def insync?(is) + if defined? @should and @should + case self.should_to_s + when 'all' + self.provider.all_privs_set? + when self.is_to_s(is) + true + else + false + end + else + true + end + end + end + +end + diff --git a/mysql/lib/puppet/type/database_user.rb b/mysql/lib/puppet/type/database_user.rb new file mode 100644 index 000000000..9a9d5fbc7 --- /dev/null +++ b/mysql/lib/puppet/type/database_user.rb @@ -0,0 +1,31 @@ +# This has to be a separate type to enable collecting +Puppet::Type.newtype(:database_user) do + @doc = 'Manage a database user. This includes management of users password as well as privileges' + + ensurable + + newparam(:name, :namevar=>true) do + desc "The name of the user. This uses the 'username@hostname' or username@hostname." + validate do |value| + Puppet.warning("database has been deprecated in favor of mysql_user.") + # https://dev.mysql.com/doc/refman/5.1/en/account-names.html + # Regex should problably be more like this: /^[`'"]?[^`'"]*[`'"]?@[`'"]?[\w%\.]+[`'"]?$/ + raise(ArgumentError, "Invalid database user #{value}") unless value =~ /[\w-]*@[\w%\.:]+/ + username = value.split('@')[0] + if username.size > 16 + raise ArgumentError, 'MySQL usernames are limited to a maximum of 16 characters' + end + end + end + + newproperty(:password_hash) do + desc 'The password hash of the user. Use mysql_password() for creating such a hash.' + newvalue(/\w+/) + end + + newproperty(:max_user_connections) do + desc "Max concurrent connections for the user. 0 means no (or global) limit." + newvalue(/\d+/) + end + +end diff --git a/mysql/lib/puppet/type/mysql_database.rb b/mysql/lib/puppet/type/mysql_database.rb index 5df43218f..3e8518c96 100644 --- a/mysql/lib/puppet/type/mysql_database.rb +++ b/mysql/lib/puppet/type/mysql_database.rb @@ -3,8 +3,6 @@ ensurable - autorequire(:file) { '/root/.my.cnf' } - newparam(:name, :namevar => true) do desc 'The name of the MySQL database to manage.' end diff --git a/mysql/lib/puppet/type/mysql_grant.rb b/mysql/lib/puppet/type/mysql_grant.rb index 8262e1e53..be8dfbc34 100644 --- a/mysql/lib/puppet/type/mysql_grant.rb +++ b/mysql/lib/puppet/type/mysql_grant.rb @@ -17,22 +17,13 @@ def initialize(*args) # Sort the privileges array in order to ensure the comparision in the provider # self.instances method match. Otherwise this causes it to keep resetting the # privileges. - self[:privileges] = Array(self[:privileges]).map{ |priv| - # split and sort the column_privileges in the parentheses and rejoin - if priv.include?('(') - type, col=priv.strip.split(/\s+|\b/,2) - type.upcase + " (" + col.slice(1...-1).strip.split(/\s*,\s*/).sort.join(', ') + ")" - else - priv.strip.upcase - end - }.uniq.reject{|k| k == 'GRANT' or k == 'GRANT OPTION'}.sort! + self[:privileges] = Array(self[:privileges]).map(&:upcase).uniq.reject{|k| k == 'GRANT' or k == 'GRANT OPTION'}.sort! end validate do fail('privileges parameter is required.') if self[:ensure] == :present and self[:privileges].nil? fail('table parameter is required.') if self[:ensure] == :present and self[:table].nil? fail('user parameter is required.') if self[:ensure] == :present and self[:user].nil? - fail('name must match user and table parameters') if self[:name] != "#{self[:user]}/#{self[:table]}" end newparam(:name, :namevar => true) do @@ -45,6 +36,10 @@ def initialize(*args) newproperty(:privileges, :array_matching => :all) do desc 'Privileges for user' + + munge do |value| + value.upcase + end end newproperty(:table) do diff --git a/mysql/lib/puppet/type/mysql_user.rb b/mysql/lib/puppet/type/mysql_user.rb index 759eb52c6..38564a948 100644 --- a/mysql/lib/puppet/type/mysql_user.rb +++ b/mysql/lib/puppet/type/mysql_user.rb @@ -4,8 +4,6 @@ ensurable - autorequire(:file) { '/root/.my.cnf' } - newparam(:name, :namevar => true) do desc "The name of the user. This uses the 'username@hostname' or username@hostname." validate do |value| @@ -17,11 +15,6 @@ raise ArgumentError, 'MySQL usernames are limited to a maximum of 16 characters' end end - - munge do |value| - user_part, host_part = value.split('@') - "#{user_part}@#{host_part.downcase}" - end end newproperty(:password_hash) do diff --git a/mysql/manifests/backup.pp b/mysql/manifests/backup.pp new file mode 100644 index 000000000..680a5744d --- /dev/null +++ b/mysql/manifests/backup.pp @@ -0,0 +1,31 @@ +# Deprecated class +class mysql::backup ( + $backupuser, + $backuppassword, + $backupdir, + $backupcompress = true, + $backuprotate = 30, + $delete_before_dump = false, + $backupdatabases = [], + $file_per_database = false, + $ensure = 'present', + $time = ['23', '5'], +) { + + crit("This class has been deprecated and callers should directly call + mysql::server::backup now.") + + class { 'mysql::server::backup': + ensure => $ensure, + backupuser => $backupuser, + backuppassword => $backuppassword, + backupdir => $backupdir, + backupcompress => $backupcompress, + backuprotate => $backuprotate, + delete_before_dump => $delete_before_dump, + backupdatabases => $backupdatabases, + file_per_database => $file_per_database, + time => $time, + } + +} diff --git a/mysql/manifests/bindings.pp b/mysql/manifests/bindings.pp index 40aa51d74..88c490339 100644 --- a/mysql/manifests/bindings.pp +++ b/mysql/manifests/bindings.pp @@ -6,51 +6,28 @@ $php_enable = false, $python_enable = false, $ruby_enable = false, - $client_dev = false, - $daemon_dev = false, # Settings for the various classes. - $java_package_ensure = $mysql::params::java_package_ensure, - $java_package_name = $mysql::params::java_package_name, - $java_package_provider = $mysql::params::java_package_provider, - $perl_package_ensure = $mysql::params::perl_package_ensure, - $perl_package_name = $mysql::params::perl_package_name, - $perl_package_provider = $mysql::params::perl_package_provider, - $php_package_ensure = $mysql::params::php_package_ensure, - $php_package_name = $mysql::params::php_package_name, - $php_package_provider = $mysql::params::php_package_provider, - $python_package_ensure = $mysql::params::python_package_ensure, - $python_package_name = $mysql::params::python_package_name, - $python_package_provider = $mysql::params::python_package_provider, - $ruby_package_ensure = $mysql::params::ruby_package_ensure, - $ruby_package_name = $mysql::params::ruby_package_name, - $ruby_package_provider = $mysql::params::ruby_package_provider, - $client_dev_package_ensure = $mysql::params::client_dev_package_ensure, - $client_dev_package_name = $mysql::params::client_dev_package_name, - $client_dev_package_provider = $mysql::params::client_dev_package_provider, - $daemon_dev_package_ensure = $mysql::params::daemon_dev_package_ensure, - $daemon_dev_package_name = $mysql::params::daemon_dev_package_name, - $daemon_dev_package_provider = $mysql::params::daemon_dev_package_provider + $java_package_ensure = $mysql::params::java_package_ensure, + $java_package_name = $mysql::params::java_package_name, + $java_package_provider = $mysql::params::java_package_provider, + $perl_package_ensure = $mysql::params::perl_package_ensure, + $perl_package_name = $mysql::params::perl_package_name, + $perl_package_provider = $mysql::params::perl_package_provider, + $php_package_ensure = $mysql::params::php_package_ensure, + $php_package_name = $mysql::params::php_package_name, + $php_package_provider = $mysql::params::php_package_provider, + $python_package_ensure = $mysql::params::python_package_ensure, + $python_package_name = $mysql::params::python_package_name, + $python_package_provider = $mysql::params::python_package_provider, + $ruby_package_ensure = $mysql::params::ruby_package_ensure, + $ruby_package_name = $mysql::params::ruby_package_name, + $ruby_package_provider = $mysql::params::ruby_package_provider ) inherits mysql::params { - case $::osfamily { - 'Archlinux': { - if $java_enable { fatal("::mysql::bindings::java cannot be managed by puppet on ${::osfamily} as it is not in official repositories. Please disable java mysql binding.") } - if $perl_enable { include '::mysql::bindings::perl' } - if $php_enable { warn("::mysql::bindings::php does not need to be managed by puppet on ${::osfamily} as it is included in mysql package by default.") } - if $python_enable { include '::mysql::bindings::python' } - if $ruby_enable { fatal("::mysql::bindings::ruby cannot be managed by puppet on ${::osfamily} as it is not in official repositories. Please disable ruby mysql binding.") } - } - - default: { - if $java_enable { include '::mysql::bindings::java' } - if $perl_enable { include '::mysql::bindings::perl' } - if $php_enable { include '::mysql::bindings::php' } - if $python_enable { include '::mysql::bindings::python' } - if $ruby_enable { include '::mysql::bindings::ruby' } - } - } - - if $client_dev { include '::mysql::bindings::client_dev' } - if $daemon_dev { include '::mysql::bindings::daemon_dev' } + if $java_enable { include '::mysql::bindings::java' } + if $perl_enable { include '::mysql::bindings::perl' } + if $php_enable { include '::mysql::bindings::php' } + if $python_enable { include '::mysql::bindings::python' } + if $ruby_enable { include '::mysql::bindings::ruby' } } diff --git a/mysql/manifests/bindings/client_dev.pp b/mysql/manifests/bindings/client_dev.pp deleted file mode 100644 index f8275039a..000000000 --- a/mysql/manifests/bindings/client_dev.pp +++ /dev/null @@ -1,14 +0,0 @@ -# Private class -class mysql::bindings::client_dev { - - if $mysql::bindings::client_dev_package_name { - package { 'mysql-client_dev': - ensure => $mysql::bindings::client_dev_package_ensure, - name => $mysql::bindings::client_dev_package_name, - provider => $mysql::bindings::client_dev_package_provider, - } - } else { - warning("No MySQL client development package configured for ${::operatingsystem}.") - } - -} \ No newline at end of file diff --git a/mysql/manifests/bindings/daemon_dev.pp b/mysql/manifests/bindings/daemon_dev.pp deleted file mode 100644 index fe2db53ee..000000000 --- a/mysql/manifests/bindings/daemon_dev.pp +++ /dev/null @@ -1,14 +0,0 @@ -# Private class -class mysql::bindings::daemon_dev { - - if $mysql::bindings::daemon_dev_package_name { - package { 'mysql-daemon_dev': - ensure => $mysql::bindings::daemon_dev_package_ensure, - name => $mysql::bindings::daemon_dev_package_name, - provider => $mysql::bindings::daemon_dev_package_provider, - } - } else { - warning("No MySQL daemon development package configured for ${::operatingsystem}.") - } - -} \ No newline at end of file diff --git a/mysql/manifests/db.pp b/mysql/manifests/db.pp index 29181eee9..d406a07d5 100644 --- a/mysql/manifests/db.pp +++ b/mysql/manifests/db.pp @@ -2,31 +2,29 @@ define mysql::db ( $user, $password, - $dbname = $name, $charset = 'utf8', $collate = 'utf8_general_ci', $host = 'localhost', $grant = 'ALL', - $sql = undef, + $sql = '', $enforce_sql = false, - $ensure = 'present', - $import_timeout = 300, + $ensure = 'present' ) { #input validation validate_re($ensure, '^(present|absent)$', "${ensure} is not supported for ensure. Allowed values are 'present' and 'absent'.") - $table = "${dbname}.*" + $table = "${name}.*" include '::mysql::client' - $db_resource = { + mysql_database { $name: ensure => $ensure, charset => $charset, collate => $collate, provider => 'mysql', require => [ Class['mysql::server'], Class['mysql::client'] ], + before => Mysql_user["${user}@${host}"], } - ensure_resource('mysql_database', $dbname, $db_resource) $user_resource = { ensure => $ensure, @@ -42,20 +40,19 @@ provider => 'mysql', user => "${user}@${host}", table => $table, - require => [Mysql_database[$dbname], Mysql_user["${user}@${host}"], Class['mysql::server'] ], + require => [ Mysql_user["${user}@${host}"], Class['mysql::server'] ], } $refresh = ! $enforce_sql if $sql { - exec{ "${dbname}-import": - command => "/usr/bin/mysql ${dbname} < ${sql}", + exec{ "${name}-import": + command => "/usr/bin/mysql ${name} < ${sql}", logoutput => true, environment => "HOME=${::root_home}", refreshonly => $refresh, require => Mysql_grant["${user}@${host}/${table}"], - subscribe => Mysql_database[$dbname], - timeout => $import_timeout, + subscribe => Mysql_database[$name], } } } diff --git a/mysql/manifests/init.pp b/mysql/manifests/init.pp new file mode 100644 index 000000000..eba5c2063 --- /dev/null +++ b/mysql/manifests/init.pp @@ -0,0 +1,100 @@ +# +class mysql( + $basedir = '', + $bind_address = '', + $client_package_ensure = '', + $client_package_name = '', + $config_file = '', + $config_template = '', + $datadir = '', + $default_engine = '', + $etc_root_password = '', + $log_error = '', + $manage_config_file = '', + $manage_service = '', + $max_allowed_packet = '', + $max_connections = '', + $old_root_password = '', + $package_ensure = '', + $php_package_name = '', + $pidfile = '', + $port = '', + $purge_conf_dir = '', + $restart = '', + $root_group = '', + $root_password = '', + $server_package_name = '', + $service_name = '', + $service_provider = '', + $socket = '', + $ssl = '', + $ssl_ca = '', + $ssl_cert = '', + $ssl_key = '', + $tmpdir = '', + $attempt_compatibility_mode = false, +) { + + if $attempt_compatibility_mode { + notify { "An attempt has been made below to automatically apply your custom + settings to mysql::server. Please verify this works in a safe test + environment.": } + + $override_options = { + 'client' => { + 'port' => $port, + 'socket' => $socket + }, + 'mysqld_safe' => { + 'log_error' => $log_error, + 'socket' => $socket, + }, + 'mysqld' => { + 'basedir' => $basedir, + 'bind_address' => $bind_address, + 'datadir' => $datadir, + 'log_error' => $log_error, + 'max_allowed_packet' => $max_allowed_packet, + 'max_connections' => $max_connections, + 'pid_file' => $pidfile, + 'port' => $port, + 'socket' => $socket, + 'ssl-ca' => $ssl_ca, + 'ssl-cert' => $ssl_cert, + 'ssl-key' => $ssl_key, + 'tmpdir' => $tmpdir, + }, + 'mysqldump' => { + 'max_allowed_packet' => $max_allowed_packet, + }, + 'config_file' => $config_file, + 'etc_root_password' => $etc_root_password, + 'manage_config_file' => $manage_config_file, + 'old_root_password' => $old_root_password, + 'purge_conf_dir' => $purge_conf_dir, + 'restart' => $restart, + 'root_group' => $root_group, + 'root_password' => $root_password, + 'service_name' => $service_name, + 'ssl' => $ssl + } + $filtered_options = mysql_strip_hash($override_options) + validate_hash($filtered_options) + notify { $filtered_options: } + class { 'mysql::server': + override_options => $filtered_options, + } + + } else { + fail("ERROR: This class has been deprecated and the functionality moved + into mysql::server. If you run mysql::server without correctly calling + mysql:: server with the new override_options hash syntax you will revert + your MySQL to the stock settings. Do not proceed without removing this + class and using mysql::server correctly. + + If you are brave you may set attempt_compatibility_mode in this class which + attempts to automap the previous settings to appropriate calls to + mysql::server") + } + +} diff --git a/mysql/manifests/params.pp b/mysql/manifests/params.pp index e46ab98ef..67dd785b0 100644 --- a/mysql/manifests/params.pp +++ b/mysql/manifests/params.pp @@ -9,82 +9,47 @@ $server_package_ensure = 'present' $server_service_manage = true $server_service_enabled = true - $client_package_ensure = 'present' # mysql::bindings - $bindings_enable = false - $java_package_ensure = 'present' - $java_package_provider = undef - $perl_package_ensure = 'present' - $perl_package_provider = undef - $php_package_ensure = 'present' - $php_package_provider = undef - $python_package_ensure = 'present' - $python_package_provider = undef - $ruby_package_ensure = 'present' - $ruby_package_provider = undef - $client_dev_package_ensure = 'present' - $client_dev_package_provider = undef - $daemon_dev_package_ensure = 'present' - $daemon_dev_package_provider = undef + $bindings_enable = false + $java_package_ensure = 'present' + $java_package_provider = undef + $perl_package_ensure = 'present' + $perl_package_provider = undef + $php_package_ensure = 'present' + $php_package_provider = undef + $python_package_ensure = 'present' + $python_package_provider = undef + $ruby_package_ensure = 'present' + $ruby_package_provider = undef case $::osfamily { 'RedHat': { - case $::operatingsystem { - 'Fedora': { - if is_integer($::operatingsystemrelease) and $::operatingsystemrelease >= 19 or $::operatingsystemrelease == 'Rawhide' { - $provider = 'mariadb' - } else { - $provider = 'mysql' - } - } - /^(RedHat|CentOS|Scientific|OracleLinux)$/: { - if $::operatingsystemmajrelease >= 7 { - $provider = 'mariadb' - } else { - $provider = 'mysql' - } - } - default: { - $provider = 'mysql' - } - } - - if $provider == 'mariadb' { + if $::operatingsystem == 'Fedora' and (is_integer($::operatingsystemrelease) and $::operatingsystemrelease >= 19 or $::operatingsystemrelease == "Rawhide") { $client_package_name = 'mariadb' $server_package_name = 'mariadb-server' - $server_service_name = 'mariadb' - $log_error = '/var/log/mariadb/mariadb.log' - $config_file = '/etc/my.cnf.d/server.cnf' - # mariadb package by default has !includedir set in my.cnf to /etc/my.cnf.d - $includedir = undef - $pidfile = '/var/run/mariadb/mariadb.pid' } else { $client_package_name = 'mysql' $server_package_name = 'mysql-server' - $server_service_name = 'mysqld' - $log_error = '/var/log/mysqld.log' - $config_file = '/etc/my.cnf' - $includedir = '/etc/my.cnf.d' - $pidfile = '/var/run/mysqld/mysqld.pid' } - - $basedir = '/usr' - $datadir = '/var/lib/mysql' - $root_group = 'root' - $socket = '/var/lib/mysql/mysql.sock' - $ssl_ca = '/etc/mysql/cacert.pem' - $ssl_cert = '/etc/mysql/server-cert.pem' - $ssl_key = '/etc/mysql/server-key.pem' - $tmpdir = '/tmp' + $basedir = '/usr' + $config_file = '/etc/my.cnf' + $datadir = '/var/lib/mysql' + $log_error = '/var/log/mysqld.log' + $pidfile = '/var/run/mysqld/mysqld.pid' + $root_group = 'root' + $server_service_name = 'mysqld' + $socket = '/var/lib/mysql/mysql.sock' + $ssl_ca = '/etc/mysql/cacert.pem' + $ssl_cert = '/etc/mysql/server-cert.pem' + $ssl_key = '/etc/mysql/server-key.pem' + $tmpdir = '/tmp' # mysql::bindings - $java_package_name = 'mysql-connector-java' - $perl_package_name = 'perl-DBD-MySQL' - $php_package_name = 'php-mysql' - $python_package_name = 'MySQL-python' - $ruby_package_name = 'ruby-mysql' - $client_dev_package_name = undef - $daemon_dev_package_name = 'mysql-devel' + $java_package_name = 'mysql-connector-java' + $perl_package_name = 'perl-DBD-MySQL' + $php_package_name = 'php-mysql' + $python_package_name = 'MySQL-python' + $ruby_package_name = 'ruby-mysql' } 'Suse': { @@ -98,7 +63,6 @@ } $basedir = '/usr' $config_file = '/etc/my.cnf' - $includedir = '/etc/my.cnf.d' $datadir = '/var/lib/mysql' $log_error = $::operatingsystem ? { /OpenSuSE/ => '/var/log/mysql/mysqld.log', @@ -127,84 +91,30 @@ /OpenSuSE/ => 'rubygem-mysql', /(SLES|SLED)/ => 'ruby-mysql', } - $client_dev_package_name = 'libmysqlclient-devel' - $daemon_dev_package_name = 'mysql-devel' } 'Debian': { - $client_package_name = 'mysql-client' - $server_package_name = 'mysql-server' + $client_package_name = 'mysql-client' + $server_package_name = 'mysql-server' - $basedir = '/usr' - $config_file = '/etc/mysql/my.cnf' - $includedir = '/etc/mysql/conf.d' - $datadir = '/var/lib/mysql' - $log_error = '/var/log/mysql/error.log' - $pidfile = '/var/run/mysqld/mysqld.pid' - $root_group = 'root' - $server_service_name = 'mysql' - $socket = '/var/run/mysqld/mysqld.sock' - $ssl_ca = '/etc/mysql/cacert.pem' - $ssl_cert = '/etc/mysql/server-cert.pem' - $ssl_key = '/etc/mysql/server-key.pem' - $tmpdir = '/tmp' - # mysql::bindings - $java_package_name = 'libmysql-java' - $perl_package_name = 'libdbd-mysql-perl' - $php_package_name = 'php5-mysql' - $python_package_name = 'python-mysqldb' - $ruby_package_name = $::lsbdistcodename ? { - 'trusty' => 'ruby-mysql', - default => 'libmysql-ruby', - } - $client_dev_package_name = 'libmysqlclient-dev' - $daemon_dev_package_name = 'libmysqld-dev' - } - - 'Archlinux': { - $client_package_name = 'mariadb-clients' - $server_package_name = 'mariadb' $basedir = '/usr' $config_file = '/etc/mysql/my.cnf' $datadir = '/var/lib/mysql' - $log_error = '/var/log/mysqld.log' + $log_error = '/var/log/mysql/error.log' $pidfile = '/var/run/mysqld/mysqld.pid' $root_group = 'root' - $server_service_name = 'mysqld' - $socket = '/var/lib/mysql/mysql.sock' - $ssl_ca = '/etc/mysql/cacert.pem' - $ssl_cert = '/etc/mysql/server-cert.pem' - $ssl_key = '/etc/mysql/server-key.pem' - $tmpdir = '/tmp' - # mysql::bindings - $java_package_name = 'mysql-connector-java' - $perl_package_name = 'perl-dbd-mysql' - $php_package_name = undef - $python_package_name = 'mysql-python' - $ruby_package_name = 'mysql-ruby' - } - - 'Gentoo': { - $client_package_name = 'virtual/mysql' - $server_package_name = 'virtual/mysql' - $basedir = '/usr' - $config_file = '/etc/mysql/my.cnf' - $datadir = '/var/lib/mysql' - $log_error = '/var/log/mysql/mysqld.err' - $pidfile = '/run/mysqld/mysqld.pid' - $root_group = 'root' $server_service_name = 'mysql' - $socket = '/run/mysqld/mysqld.sock' + $socket = '/var/run/mysqld/mysqld.sock' $ssl_ca = '/etc/mysql/cacert.pem' $ssl_cert = '/etc/mysql/server-cert.pem' $ssl_key = '/etc/mysql/server-key.pem' $tmpdir = '/tmp' # mysql::bindings - $java_package_name = 'dev-java/jdbc-mysql' - $perl_package_name = 'dev-perl/DBD-mysql' - $php_package_name = undef - $python_package_name = 'dev-python/mysql-python' - $ruby_package_name = 'dev-ruby/mysql-ruby' + $java_package_name = 'libmysql-java' + $perl_package_name = 'libdbd-mysql-perl' + $php_package_name = 'php5-mysql' + $python_package_name = 'python-mysqldb' + $ruby_package_name = 'libmysql-ruby' } 'FreeBSD': { @@ -212,7 +122,6 @@ $server_package_name = 'databases/mysql55-server' $basedir = '/usr/local' $config_file = '/var/db/mysql/my.cnf' - $includedir = '/var/db/mysql/my.cnf.d' $datadir = '/var/db/mysql' $log_error = "/var/db/mysql/${::hostname}.err" $pidfile = '/var/db/mysql/mysql.pid' @@ -229,9 +138,6 @@ $php_package_name = 'php5-mysql' $python_package_name = 'databases/py-MySQLdb' $ruby_package_name = 'databases/ruby-mysql' - # The libraries installed by these packages are included in client and server packages, no installation required. - $client_dev_package_name = undef - $daemon_dev_package_name = undef } default: { @@ -241,7 +147,6 @@ $server_package_name = 'mysql-server' $basedir = '/usr' $config_file = '/etc/my.cnf' - $includedir = '/etc/my.cnf.d' $datadir = '/var/lib/mysql' $log_error = '/var/log/mysqld.log' $pidfile = '/var/run/mysqld/mysqld.pid' @@ -258,13 +163,10 @@ $php_package_name = 'php-mysql' $python_package_name = 'MySQL-python' $ruby_package_name = 'ruby-mysql' - # The libraries installed by these packages are included in client and server packages, no installation required. - $client_dev_package_name = undef - $daemon_dev_package_name = undef } default: { - fail("Unsupported platform: puppetlabs-${module_name} currently doesn't support ${::osfamily} or ${::operatingsystem}") + fail("Unsupported osfamily: ${::osfamily} operatingsystem: ${::operatingsystem}, module ${module_name} only support osfamily RedHat, Debian, and FreeBSD, or operatingsystem Amazon") } } } @@ -310,7 +212,6 @@ 'ssl-ca' => $mysql::params::ssl_ca, 'ssl-cert' => $mysql::params::ssl_cert, 'ssl-key' => $mysql::params::ssl_key, - 'ssl-disable' => false, 'thread_cache_size' => '8', 'thread_stack' => '256K', 'tmpdir' => $mysql::params::tmpdir, @@ -326,8 +227,4 @@ }, } - ## Additional graceful failures - if $::osfamily == 'RedHat' and $::operatingsystemmajrelease == '4' { - fail("Unsupported platform: puppetlabs-${module_name} only supports RedHat 5.0 and beyond") - } } diff --git a/mysql/manifests/server.pp b/mysql/manifests/server.pp index 2bd354053..ae8b208a1 100644 --- a/mysql/manifests/server.pp +++ b/mysql/manifests/server.pp @@ -1,7 +1,6 @@ # Class: mysql::server: See README.md for documentation. class mysql::server ( $config_file = $mysql::params::config_file, - $includedir = $mysql::params::includedir, $manage_config_file = $mysql::params::manage_config_file, $old_root_password = $mysql::params::old_root_password, $override_options = {}, diff --git a/mysql/manifests/server/backup.pp b/mysql/manifests/server/backup.pp index 6e9ecdee9..a33b2b03b 100644 --- a/mysql/manifests/server/backup.pp +++ b/mysql/manifests/server/backup.pp @@ -15,7 +15,6 @@ $ensure = 'present', $time = ['23', '5'], $postscript = false, - $execpath = '/usr/bin:/usr/sbin:/bin:/sbin', ) { mysql_user { "${backupuser}@localhost": @@ -26,10 +25,10 @@ } mysql_grant { "${backupuser}@localhost/*.*": - ensure => $ensure, + ensure => present, user => "${backupuser}@localhost", table => '*.*', - privileges => [ 'SELECT', 'RELOAD', 'LOCK TABLES', 'SHOW VIEW', 'PROCESS' ], + privileges => [ 'SELECT', 'RELOAD', 'LOCK TABLES', 'SHOW VIEW' ], require => Mysql_user["${backupuser}@localhost"], } diff --git a/mysql/manifests/server/config.pp b/mysql/manifests/server/config.pp index 6a7e8ea8f..c51f22ff9 100644 --- a/mysql/manifests/server/config.pp +++ b/mysql/manifests/server/config.pp @@ -2,7 +2,6 @@ class mysql::server::config { $options = $mysql::server::options - $includedir = $mysql::server::includedir File { owner => 'root', @@ -10,26 +9,22 @@ mode => '0400', } - if $includedir and $includedir != '' { - file { "$mysql::server::includedir": - ensure => directory, - mode => '0755', - recurse => $mysql::server::purge_conf_dir, - purge => $mysql::server::purge_conf_dir, - } + file { '/etc/mysql': + ensure => directory, + mode => '0755', + } + + file { '/etc/mysql/conf.d': + ensure => directory, + mode => '0755', + recurse => $mysql::server::purge_conf_dir, + purge => $mysql::server::purge_conf_dir, } if $mysql::server::manage_config_file { - file { 'mysql-config-file': - path => $mysql::server::config_file, + file { $mysql::server::config_file: content => template('mysql/my.cnf.erb'), mode => '0644', } } - - if $options['mysqld']['ssl-disable'] { - notify {'ssl-disable': - message =>'Disabling SSL is evil! You should never ever do this except if you are forced to use a mysql version compiled without SSL support' - } - } } diff --git a/mysql/manifests/server/install.pp b/mysql/manifests/server/install.pp index f7736d586..dcb04cb12 100644 --- a/mysql/manifests/server/install.pp +++ b/mysql/manifests/server/install.pp @@ -6,24 +6,4 @@ name => $mysql::server::package_name, } - # Build the initial databases. - if $mysql::server::override_options['mysqld'] and $mysql::server::override_options['mysqld']['datadir'] { - $mysqluser = $mysql::server::options['mysqld']['user'] - $datadir = $mysql::server::override_options['mysqld']['datadir'] - - exec { 'mysql_install_db': - command => "mysql_install_db --datadir=${datadir} --user=${mysqluser}", - creates => "${datadir}/mysql", - logoutput => on_failure, - path => '/bin:/sbin:/usr/bin:/usr/sbin', - require => Package['mysql-server'], - } - - if $mysql::server::restart { - Exec['mysql_install_db'] { - notify => Class['mysql::server::service'], - } - } - } - } diff --git a/mysql/manifests/server/service.pp b/mysql/manifests/server/service.pp index 95618fb8d..c60e03662 100644 --- a/mysql/manifests/server/service.pp +++ b/mysql/manifests/server/service.pp @@ -1,25 +1,19 @@ # class mysql::server::service { - if $mysql::server::real_service_manage { - if $mysql::server::real_service_enabled { - $service_ensure = 'running' - } else { - $service_ensure = 'stopped' - } - } - - file { $mysql::params::log_error: - ensure => present, - owner => 'mysql', - group => 'mysql', + if $mysql::server::real_service_enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' } - service { 'mysqld': - ensure => $service_ensure, - name => $mysql::server::service_name, - enable => $mysql::server::real_service_enabled, - provider => $mysql::server::service_provider, + if $mysql::server::real_service_manage { + service { 'mysqld': + ensure => $service_ensure, + name => $mysql::server::service_name, + enable => $mysql::server::real_service_enabled, + provider => $mysql::server::service_provider, + } } } diff --git a/mysql/metadata.json b/mysql/metadata.json index f00ff1818..e1a8c7905 100644 --- a/mysql/metadata.json +++ b/mysql/metadata.json @@ -1,49 +1,36 @@ { - "name": "puppetlabs-mysql", - "version": "3.0.0", - "author": "Puppet Labs", - "summary": "Mysql module", - "license": "Apache 2.0", - "source": "git://github.com/puppetlabs/puppetlabs-mysql.git", - "project_page": "http://github.com/puppetlabs/puppetlabs-mysql", - "issues_url": "https://github.com/puppetlabs/puppetlabs-mysql/issues", "operatingsystem_support": [ { "operatingsystem": "RedHat", "operatingsystemrelease": [ "5", - "6", - "7" + "6" ] }, { "operatingsystem": "CentOS", "operatingsystemrelease": [ "5", - "6", - "7" + "6" ] }, { "operatingsystem": "OracleLinux", "operatingsystemrelease": [ "5", - "6", - "7" + "6" ] }, { "operatingsystem": "Scientific", "operatingsystemrelease": [ "5", - "6", - "7" + "6" ] }, { "operatingsystem": "SLES", "operatingsystemrelease": [ - "10 SP4", "11 SP1" ] }, @@ -58,22 +45,28 @@ "operatingsystem": "Ubuntu", "operatingsystemrelease": [ "10.04", - "12.04", - "14.04" + "12.04" ] } ], "requirements": [ { "name": "pe", - "version_requirement": "3.x" + "version_requirement": "3.2.x" }, { "name": "puppet", "version_requirement": "3.x" } ], + "name": "puppetlabs-mysql", + "version": "2.2.3", + "source": "git://github.com/puppetlabs/puppetlabs-mysql.git", + "author": "Puppet Labs", + "license": "Apache 2.0", + "summary": "Mysql module", "description": "Mysql module", + "project_page": "http://github.com/puppetlabs/puppetlabs-mysql", "dependencies": [ { "name": "puppetlabs/stdlib", diff --git a/mysql/spec/acceptance/mysql_account_delete_spec.rb b/mysql/spec/acceptance/mysql_account_delete_spec.rb new file mode 100644 index 000000000..07c99a183 --- /dev/null +++ b/mysql/spec/acceptance/mysql_account_delete_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper_acceptance' + +describe 'mysql::server::account_security class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do + describe 'running puppet code' do + it 'should work with no errors' do + pp = <<-EOS + class { 'mysql::server': remove_default_accounts => true } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + describe 'accounts' do + it 'should delete accounts' do + shell("mysql -e 'show grants for root@127.0.0.1;'", :acceptable_exit_codes => 1) + end + + it 'should delete databases' do + shell("mysql -e 'show databases;' |grep test", :acceptable_exit_codes => 1) + end + end + end +end diff --git a/mysql/spec/acceptance/mysql_backup_spec.rb b/mysql/spec/acceptance/mysql_backup_spec.rb index 7f6e322ab..131520661 100644 --- a/mysql/spec/acceptance/mysql_backup_spec.rb +++ b/mysql/spec/acceptance/mysql_backup_spec.rb @@ -1,14 +1,11 @@ require 'spec_helper_acceptance' -describe 'mysql::server::backup class' do +describe 'mysql::server::backup class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do context 'should work with no errors' do it 'when configuring mysql backups' do pp = <<-EOS class { 'mysql::server': root_password => 'password' } - mysql::db { [ - 'backup1', - 'backup2' - ]: + mysql::db { 'backup1': user => 'backup', password => 'secret', } @@ -24,12 +21,15 @@ class { 'mysql::server::backup': 'cp -r /tmp/backups /var/tmp/mysqlbackups', 'touch /var/tmp/mysqlbackups.done', ], - execpath => '/usr/bin:/usr/sbin:/bin:/sbin:/opt/zimbra/bin', } EOS - apply_manifest(pp, :catch_failures => true) - apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_failures => true) do |r| + expect(r.stderr).to eq("") + end + apply_manifest(pp, :catch_failures => true) do |r| + expect(r.stderr).to eq("") + end end end @@ -61,72 +61,4 @@ class { 'mysql::server::backup': end end end - - context 'with one file per database' do - context 'should work with no errors' do - it 'when configuring mysql backups' do - pp = <<-EOS - class { 'mysql::server': root_password => 'password' } - mysql::db { [ - 'backup1', - 'backup2' - ]: - user => 'backup', - password => 'secret', - } - - class { 'mysql::server::backup': - backupuser => 'myuser', - backuppassword => 'mypassword', - backupdir => '/tmp/backups', - backupcompress => true, - file_per_database => true, - postscript => [ - 'rm -rf /var/tmp/mysqlbackups', - 'rm -f /var/tmp/mysqlbackups.done', - 'cp -r /tmp/backups /var/tmp/mysqlbackups', - 'touch /var/tmp/mysqlbackups.done', - ], - execpath => '/usr/bin:/usr/sbin:/bin:/sbin:/opt/zimbra/bin', - } - EOS - - apply_manifest(pp, :catch_failures => true) - apply_manifest(pp, :catch_failures => true) - end - end - - describe 'mysqlbackup.sh' do - it 'should run mysqlbackup.sh with no errors without root credentials' do - shell("HOME=/tmp/dontreadrootcredentials /usr/local/sbin/mysqlbackup.sh") do |r| - expect(r.stderr).to eq("") - end - end - - it 'should create one file per database' do - ['backup1', 'backup2'].each do |database| - shell("ls -l /tmp/backups/mysql_backup_#{database}_*-*.sql.bz2 | wc -l") do |r| - expect(r.stdout).to match(/1/) - expect(r.exit_code).to be_zero - end - end - end - - context 'should create one file per database per run' do - it 'executes mysqlbackup.sh a second time' do - shell('sleep 1') - shell('HOME=/tmp/dontreadrootcredentials /usr/local/sbin/mysqlbackup.sh') - end - - it 'has one file per database per run' do - ['backup1', 'backup2'].each do |database| - shell("ls -l /tmp/backups/mysql_backup_#{database}_*-*.sql.bz2 | wc -l") do |r| - expect(r.stdout).to match(/2/) - expect(r.exit_code).to be_zero - end - end - end - end - end - end end diff --git a/mysql/spec/acceptance/mysql_bindings_spec.rb b/mysql/spec/acceptance/mysql_bindings_spec.rb new file mode 100644 index 000000000..b7e25d63d --- /dev/null +++ b/mysql/spec/acceptance/mysql_bindings_spec.rb @@ -0,0 +1,117 @@ +require 'spec_helper_acceptance' + +osfamily = fact('osfamily') +operatingsystem = fact('operatingsystem') + +case osfamily +when 'RedHat' + java_package = 'mysql-connector-java' + perl_package = 'perl-DBD-MySQL' + php_package = 'php-mysql' + python_package = 'MySQL-python' + ruby_package = 'ruby-mysql' +when 'Suse' + java_package = 'mysql-connector-java' + perl_package = 'perl-DBD-mysql' + php_package = 'apache2-mod_php53' + python_package = 'python-mysql' + case operatingsystem + when /OpenSuSE/ + ruby_package = 'rubygem-mysql' + when /(SLES|SLED)/ + ruby_package = 'ruby-mysql' + end +when 'Debian' + java_package = 'libmysql-java' + perl_package = 'libdbd-mysql-perl' + php_package = 'php5-mysql' + python_package = 'python-mysqldb' + ruby_package = 'libmysql-ruby' +when 'FreeBSD' + java_package = 'databases/mysql-connector-java' + perl_package = 'p5-DBD-mysql' + php_package = 'php5-mysql' + python_package = 'databases/py-MySQLdb' + ruby_package = 'ruby-mysql' +else + case operatingsystem + when 'Amazon' + java_package = 'mysql-connector-java' + perl_package = 'perl-DBD-MySQL' + php_package = 'php5-mysql' + python_package = 'MySQL-python' + ruby_package = 'ruby-mysql' + end +end + +describe 'mysql::bindings class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do + + describe 'running puppet code' do + it 'should work with no errors' do + pp = <<-EOS + class { 'mysql::bindings': } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + end + + describe 'all parameters' do + it 'should work with no errors' do + pp = <<-EOS + class { 'mysql::bindings': + java_enable => true, + perl_enable => true, + php_enable => true, + python_enable => true, + ruby_enable => true, + java_package_ensure => present, + perl_package_ensure => present, + php_package_ensure => present, + python_package_ensure => present, + ruby_package_ensure => present, + java_package_name => #{java_package}, + perl_package_name => #{perl_package}, + php_package_name => #{php_package}, + python_package_name => #{python_package}, + ruby_package_name => #{ruby_package}, + java_package_provider => undef, + perl_package_provider => undef, + php_package_provider => undef, + python_package_provider => undef, + ruby_package_provider => undef, + } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + describe package(java_package) do + it { should be_installed } + end + + describe package(perl_package) do + it { should be_installed } + end + + # This package is not available out of the box and adding in other repos + # is a bit much for the scope of this test. + unless fact('osfamily') == 'RedHat' + describe package(php_package) do + it { should be_installed } + end + end + + describe package(python_package) do + it { should be_installed } + end + + describe package(ruby_package) do + it { should be_installed } + end + end +end diff --git a/mysql/spec/acceptance/mysql_db_spec.rb b/mysql/spec/acceptance/mysql_db_spec.rb index 88ded3aec..1e8df8a1f 100644 --- a/mysql/spec/acceptance/mysql_db_spec.rb +++ b/mysql/spec/acceptance/mysql_db_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper_acceptance' -describe 'mysql::db define' do +describe 'mysql::db define', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do describe 'creating a database' do # Using puppet_apply as a helper it 'should work with no errors' do @@ -46,26 +46,4 @@ class { 'mysql::server': override_options => { 'root_password' => 'password' } } expect(shell("mysql -e 'show tables;' spec2|grep table1").exit_code).to be_zero end end - - describe 'creating a database with dbname parameter' do - # Using puppet_apply as a helper - it 'should work with no errors' do - pp = <<-EOS - class { 'mysql::server': override_options => { 'root_password' => 'password' } } - mysql::db { 'spec1': - user => 'root1', - password => 'password', - dbname => 'realdb', - } - EOS - - # Run it twice and test for idempotency - apply_manifest(pp, :catch_failures => true) - apply_manifest(pp, :catch_changes => true) - end - - it 'should have the database named realdb' do - expect(shell("mysql -e 'show databases;'|grep realdb").exit_code).to be_zero - end - end end diff --git a/mysql/spec/acceptance/mysql_server_config_spec.rb b/mysql/spec/acceptance/mysql_server_config_spec.rb new file mode 100644 index 000000000..d327dd29a --- /dev/null +++ b/mysql/spec/acceptance/mysql_server_config_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper_acceptance' + +describe 'config location', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do + it 'creates the file elsewhere' do + pp = <<-EOS + class { 'mysql::server': + config_file => '/etc/testmy.cnf', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file('/etc/testmy.cnf') do + it { should be_file } + end +end + +describe 'manage_config_file', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do + it 'wont reset the location of my.cnf' do + pp = <<-EOS + class { 'mysql::server': + config_file => '/etc/my.cnf', + manage_config_file => false, + service_manage => false, + } + EOS + # Make sure this doesn't exist so we can test if puppet + # readded it. It may not exist in the first place on + # some platforms. + shell('rm /etc/my.cnf', :acceptable_exit_codes => [0,1,2]) + apply_manifest(pp, :catch_failures => true) + end + + describe file('/etc/my.cnf') do + it { should_not be_file } + end +end + +describe 'resets', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do + it 'cleans up' do + pp = <<-EOS + class { 'mysql::server': } + EOS + apply_manifest(pp, :catch_failures => true) + shell('rm /etc/testmy.cnf') + end +end diff --git a/mysql/spec/acceptance/mysql_server_monitor_spec.rb b/mysql/spec/acceptance/mysql_server_monitor_spec.rb new file mode 100644 index 000000000..9c66334e5 --- /dev/null +++ b/mysql/spec/acceptance/mysql_server_monitor_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper_acceptance' + +describe 'mysql::server::monitor class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do + it 'should work with no errors' do + pp = <<-EOS + class { 'mysql::server': root_password => 'password' } + + class { 'mysql::server::monitor': + mysql_monitor_username => 'monitoruser', + mysql_monitor_password => 'monitorpass', + mysql_monitor_hostname => 'localhost', + } + EOS + + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + it 'should run mysqladmin ping with no errors' do + expect(shell("mysqladmin -u monitoruser -pmonitorpass -h localhost ping").stdout).to match(/mysqld is alive/) + end +end diff --git a/mysql/spec/acceptance/mysql_server_root_password_spec.rb b/mysql/spec/acceptance/mysql_server_root_password_spec.rb new file mode 100644 index 000000000..f6035f0b3 --- /dev/null +++ b/mysql/spec/acceptance/mysql_server_root_password_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper_acceptance' + +describe 'mysql::server::root_password class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do + + describe 'reset' do + it 'shuts down mysql' do + pp = <<-EOS + class { 'mysql::server': service_enabled => false } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'deletes the /root/.my.cnf password' do + shell('rm -rf /root/.my.cnf') + end + + it 'deletes all databases' do + case fact('osfamily') + when 'RedHat', 'Suse' + shell('rm -rf `grep datadir /etc/my.cnf | cut -d" " -f 3`/*') + when 'Debian' + shell('rm -rf `grep datadir /etc/mysql/my.cnf | cut -d" " -f 3`/*') + shell('mysql_install_db') + end + end + + it 'starts up mysql' do + pp = <<-EOS + class { 'mysql::server': service_enabled => true } + EOS + + puppet_apply(pp, :catch_failures => true) + end + end + + describe 'when unset' do + it 'should work' do + pp = <<-EOS + class { 'mysql::server': root_password => 'test' } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + end + + describe 'when set' do + it 'should work' do + pp = <<-EOS + class { 'mysql::server': root_password => 'new', old_root_password => 'test' } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + end +end + +# Debian relies on a debian-sys-maint@ account to do almost everything. +# Without recreating this account we can't even stop the service in future +# tests. +if fact('osfamily') == 'Debian' + describe 'readd debian user' do + it 'readds the user' do + shell("MYSQL_PASSWORD=`head -5 /etc/mysql/debian.cnf | grep password | cut -d' ' -f 3`; mysql -NBe \"GRANT ALL PRIVILEGES ON *.* to 'debian-sys-maint'@'localhost' IDENTIFIED BY '${MYSQL_PASSWORD}' WITH GRANT OPTION;\"") + end + end +end diff --git a/mysql/spec/acceptance/mysql_server_spec.rb b/mysql/spec/acceptance/mysql_server_spec.rb index 06646cbbe..938368dbf 100644 --- a/mysql/spec/acceptance/mysql_server_spec.rb +++ b/mysql/spec/acceptance/mysql_server_spec.rb @@ -1,55 +1,281 @@ require 'spec_helper_acceptance' -describe 'mysql class' do +describe 'mysql class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do + case fact('osfamily') + when 'RedHat' + package_name = 'mysql-server' + service_name = 'mysqld' + service_provider = 'undef' + mycnf = '/etc/my.cnf' + when 'Suse' + case fact('operatingsystem') + when 'OpenSuSE' + package_name = 'mysql-community-server' + service_name = 'mysql' + service_provider = 'undef' + mycnf = '/etc/my.cnf' + when 'SLES' + package_name = 'mysql' + service_name = 'mysql' + service_provider = 'undef' + mycnf = '/etc/my.cnf' + end + when 'Debian' + package_name = 'mysql-server' + service_name = 'mysql' + service_provider = 'undef' + mycnf = '/etc/mysql/my.cnf' + when 'Ubuntu' + package_name = 'mysql-server' + service_name = 'mysql' + service_provider = 'upstart' + mycnf = '/etc/mysql/my.cnf' + end describe 'running puppet code' do # Using puppet_apply as a helper it 'should work with no errors' do - tmpdir = default.tmpdir('mysql') - pp = <<-EOS - class { 'mysql::server': - config_file => '#{tmpdir}/my.cnf', - includedir => '#{tmpdir}/include', - manage_config_file => 'true', - override_options => { 'mysqld' => { 'key_buffer_size' => '32M' }}, - package_ensure => 'present', - purge_conf_dir => 'true', - remove_default_accounts => 'true', - restart => 'true', - root_group => 'root', - root_password => 'test', - service_enabled => 'true', - service_manage => 'true', - users => { - 'someuser@localhost' => { - ensure => 'present', - max_connections_per_hour => '0', - max_queries_per_hour => '0', - max_updates_per_hour => '0', - max_user_connections => '0', - password_hash => '*F3A2A51A9B0F2BE2468926B4132313728C250DBF', - }}, - grants => { - 'someuser@localhost/somedb.*' => { - ensure => 'present', - options => ['GRANT'], - privileges => ['SELECT', 'INSERT', 'UPDATE', 'DELETE'], - table => 'somedb.*', - user => 'someuser@localhost', - }, - }, - databases => { - 'somedb' => { - ensure => 'present', - charset => 'utf8', - }, - } + pp = <<-EOS + class { 'mysql::server': } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + describe package(package_name) do + it { should be_installed } + end + + describe service(service_name) do + it { should be_running } + it { should be_enabled } + end + end + + describe 'mycnf' do + it 'should contain sensible values' do + pp = <<-EOS + class { 'mysql::server': } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file(mycnf) do + it { should contain 'key_buffer_size = 16M' } + it { should contain 'max_binlog_size = 100M' } + it { should contain 'query_cache_size = 16M' } + end + end + + describe 'my.cnf changes' do + it 'sets values' do + pp = <<-EOS + class { 'mysql::server': + override_options => { 'mysqld' => + { 'key_buffer' => '32M', + 'max_binlog_size' => '200M', + 'query_cache_size' => '32M', + } + } } EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file(mycnf) do + it { should contain 'key_buffer = 32M' } + it { should contain 'max_binlog_size = 200M' } + it { should contain 'query_cache_size = 32M' } + end + end + describe 'my.cnf should contain multiple instances of the same option' do + it 'sets multiple values' do + pp = <<-EOS + class { 'mysql::server': + override_options => { 'mysqld' => + { 'replicate-do-db' => ['base1', 'base2', 'base3'], } + } + } + EOS apply_manifest(pp, :catch_failures => true) - apply_manifest(pp, :catch_changes => true) + end + + describe file(mycnf) do + it { should contain 'replicate-do-db = base1' } + it { should contain 'replicate-do-db = base2' } + it { should contain 'replicate-do-db = base3' } end end -end + describe 'package_ensure => absent' do + it 'uninstalls mysql' do + pp = <<-EOS + class { 'mysql::server': + service_enabled => false, + package_ensure => absent, + package_name => #{package_name} + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe package(package_name) do + it { should_not be_installed } + end + end + + describe 'package_ensure => present' do + it 'installs mysql' do + pp = <<-EOS + class { 'mysql::server': + package_ensure => present, + package_name => #{package_name} + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe package(package_name) do + it { should be_installed } + end + end + + describe 'purge_conf_dir' do + + it 'purges the conf dir' do + pp = <<-EOS + class { 'mysql::server': + purge_conf_dir => true + } + EOS + shell('touch /etc/mysql/conf.d/test.conf') + apply_manifest(pp, :catch_failures => true) + end + + describe file('/etc/mysql/conf.d/test.conf') do + it { should_not be_file } + end + end + + describe 'restart' do + it 'restart => true' do + pp = <<-EOS + class { 'mysql::server': + restart => true, + override_options => { 'mysqldump' => { 'default-character-set' => 'UTF-8' } } + } + EOS + + apply_manifest(pp, :catch_failures => true) do |r| + expect(r.exit_code).to eq(2) + expect(r.stdout).to match(/Scheduling refresh/) + end + end + it 'restart => false' do + pp = <<-EOS + class { 'mysql::server': + restart => false, + override_options => { 'mysqldump' => { 'default-character-set' => 'UTF-16' } } + } + EOS + + apply_manifest(pp, :catch_failures => true) do |r| + expect(r.exit_code).to eq(2) + expect(r.stdout).to_not match(/Scheduling refresh/) + end + end + end + + describe 'root_group' do + it 'creates a group' do + pp = "group { 'test': ensure => present }" + apply_manifest(pp, :catch_failures => true) + end + + it 'sets the group of files' do + pp = <<-EOS + class { 'mysql::server': + root_group => 'test', + config_file => '/tmp/mysql_group_test', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file('/tmp/mysql_group_test') do + it { should be_grouped_into 'test' } + end + end + + describe 'service parameters' do + it 'calls all parameters' do + pp = <<-EOS + class { 'mysql::server': + service_enabled => true, + service_manage => true, + service_name => #{service_name}, + service_provider => #{service_provider} + } + EOS + apply_manifest(pp, :catch_failures => true) + end + end + + describe 'users, grants, and databases' do + it 'are created' do + pp = <<-EOS + class { 'mysql::server': + users => { + 'zerg1@localhost' => { + ensure => 'present', + max_connections_per_hour => '0', + max_queries_per_hour => '0', + max_updates_per_hour => '0', + max_user_connections => '0', + password_hash => '*F3A2A51A9B0F2BE2468926B4132313728C250DBF', + } + }, + grants => { + 'zerg1@localhost/zergdb.*' => { + ensure => 'present', + options => ['GRANT'], + privileges => ['SELECT', 'INSERT', 'UPDATE', 'DELETE'], + table => 'zergdb.*', + user => 'zerg1@localhost', + } + }, + databases => { + 'zergdb' => { + ensure => 'present', + charset => 'utf8', + } + }, + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + it 'has a user' do + shell("mysql -NBe \"select '1' from mysql.user where CONCAT(user, '@', host) = 'zerg1@localhost'\"") do |r| + expect(r.stdout).to match(/^1$/) + expect(r.stderr).to be_empty + end + end + it 'has a grant' do + shell("mysql -NBe \"SHOW GRANTS FOR zerg1@localhost\"") do |r| + expect(r.stdout).to match(/GRANT SELECT, INSERT, UPDATE, DELETE ON `zergdb`.* TO 'zerg1'@'localhost' WITH GRANT OPTION/) + expect(r.stderr).to be_empty + end + end + it 'has a database' do + shell("mysql -NBe \"SHOW DATABASES LIKE 'zergdb'\"") do |r| + expect(r.stdout).to match(/zergdb/) + expect(r.stderr).to be_empty + end + end + end + +end diff --git a/mysql/spec/acceptance/nodesets/default.yml b/mysql/spec/acceptance/nodesets/default.yml index 6505c6ded..05540ed8c 100644 --- a/mysql/spec/acceptance/nodesets/default.yml +++ b/mysql/spec/acceptance/nodesets/default.yml @@ -2,7 +2,6 @@ HOSTS: centos-64-x64: roles: - master - - default platform: el-6-x86_64 box : centos-64-x64-vbox4210-nocm box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box diff --git a/mysql/spec/acceptance/types/mysql_database_spec.rb b/mysql/spec/acceptance/types/mysql_database_spec.rb index b19026ddb..c06420991 100644 --- a/mysql/spec/acceptance/types/mysql_database_spec.rb +++ b/mysql/spec/acceptance/types/mysql_database_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper_acceptance' -describe 'mysql_database' do +describe 'mysql_database', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do describe 'setup' do it 'should work with no errors' do pp = <<-EOS diff --git a/mysql/spec/acceptance/types/mysql_grant_spec.rb b/mysql/spec/acceptance/types/mysql_grant_spec.rb index e3474c533..df8bea4f3 100644 --- a/mysql/spec/acceptance/types/mysql_grant_spec.rb +++ b/mysql/spec/acceptance/types/mysql_grant_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper_acceptance' -describe 'mysql_grant' do +describe 'mysql_grant', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do describe 'setup' do it 'setup mysql::server' do @@ -70,21 +70,6 @@ class { 'mysql::server': } end end - describe 'adding privileges with invalid name' do - it 'should fail' do - pp = <<-EOS - mysql_grant { 'test': - ensure => 'present', - table => 'test.*', - user => 'test2@tester', - privileges => ['SELECT', 'UPDATE'], - } - EOS - - expect(apply_manifest(pp, :expect_failures => true).stderr).to match(/name must match user and table parameters/) - end - end - describe 'adding option' do it 'should work without errors' do pp = <<-EOS @@ -320,75 +305,4 @@ class { 'mysql::server': } end end end - - describe 'grants with skip-name-resolve specified' do - it 'setup mysql::server' do - pp = <<-EOS - class { 'mysql::server': - override_options => { - 'mysqld' => {'skip-name-resolve' => true} - }, - restart => true, - } - EOS - - apply_manifest(pp, :catch_failures => true) - end - - it 'should apply' do - pp = <<-EOS - mysql_grant { 'test@fqdn.com/test.*': - ensure => 'present', - table => 'test.*', - user => 'test@fqdn.com', - privileges => 'ALL', - } - mysql_grant { 'test@192.168.5.7/test.*': - ensure => 'present', - table => 'test.*', - user => 'test@192.168.5.7', - privileges => 'ALL', - } - EOS - - apply_manifest(pp, :catch_failures => true) - end - - it 'should fail with fqdn' do - expect(shell("mysql -NBe \"SHOW GRANTS FOR test@fqdn.com\"", { :acceptable_exit_codes => 1}).stderr).to match(/There is no such grant defined for user 'test' on host 'fqdn.com'/) - end - it 'finds ipv4' do - shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'192.168.5.7'\"") do |r| - expect(r.stdout).to match(/GRANT ALL PRIVILEGES ON `test`.* TO 'test'@'192.168.5.7'/) - expect(r.stderr).to be_empty - end - end - - it 'should fail to execute while applying' do - pp = <<-EOS - mysql_grant { 'test@fqdn.com/test.*': - ensure => 'present', - table => 'test.*', - user => 'test@fqdn.com', - privileges => 'ALL', - } - EOS - - mysql_cmd = shell('which mysql').stdout.chomp - shell("mv #{mysql_cmd} #{mysql_cmd}.bak") - expect(apply_manifest(pp, :expect_failures => true).stderr).to match(/Command mysql is missing/) - shell("mv #{mysql_cmd}.bak #{mysql_cmd}") - end - - it 'reset mysql::server config' do - pp = <<-EOS - class { 'mysql::server': - restart => true, - } - EOS - - apply_manifest(pp, :catch_failures => true) - end - end - end diff --git a/mysql/spec/acceptance/types/mysql_user_spec.rb b/mysql/spec/acceptance/types/mysql_user_spec.rb index a0fa4f78b..65b403f19 100644 --- a/mysql/spec/acceptance/types/mysql_user_spec.rb +++ b/mysql/spec/acceptance/types/mysql_user_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper_acceptance' -describe 'mysql_user' do +describe 'mysql_user', :unless => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do describe 'setup' do it 'should work with no errors' do pp = <<-EOS @@ -11,44 +11,21 @@ class { 'mysql::server': } end end - context 'using ashp@localhost' do - describe 'adding user' do - it 'should work without errors' do - pp = <<-EOS - mysql_user { 'ashp@localhost': - password_hash => '6f8c114b58f2ce9e', - } - EOS - - apply_manifest(pp, :catch_failures => true) - end + describe 'adding user' do + it 'should work without errors' do + pp = <<-EOS + mysql_user { 'ashp@localhost': + password_hash => '6f8c114b58f2ce9e', + } + EOS - it 'should find the user' do - shell("mysql -NBe \"select '1' from mysql.user where CONCAT(user, '@', host) = 'ashp@localhost'\"") do |r| - expect(r.stdout).to match(/^1$/) - expect(r.stderr).to be_empty - end - end + apply_manifest(pp, :catch_failures => true) end - end - - context 'using ashp@LocalHost' do - describe 'adding user' do - it 'should work without errors' do - pp = <<-EOS - mysql_user { 'ashp@LocalHost': - password_hash => '6f8c114b58f2ce9e', - } - EOS - - apply_manifest(pp, :catch_failures => true) - end - it 'should find the user' do - shell("mysql -NBe \"select '1' from mysql.user where CONCAT(user, '@', host) = 'ashp@localhost'\"") do |r| - expect(r.stdout).to match(/^1$/) - expect(r.stderr).to be_empty - end + it 'should find the user' do + shell("mysql -NBe \"select '1' from mysql.user where CONCAT(user, '@', host) = 'ashp@localhost'\"") do |r| + expect(r.stdout).to match(/^1$/) + expect(r.stderr).to be_empty end end end diff --git a/mysql/spec/acceptance/unsupported_spec.rb b/mysql/spec/acceptance/unsupported_spec.rb new file mode 100644 index 000000000..449f35a63 --- /dev/null +++ b/mysql/spec/acceptance/unsupported_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper_acceptance' + +describe 'unsupported distributions and OSes', :if => UNSUPPORTED_PLATFORMS.include?(fact('operatingsystem')) do + it 'should fail' do + pp = <<-EOS + class { 'mysql::server': } + EOS + expect(apply_manifest(pp, :expect_failures => true).stderr).to match(/unsupported osfamily/i) + end +end diff --git a/mysql/spec/classes/graceful_failures_spec.rb b/mysql/spec/classes/graceful_failures_spec.rb deleted file mode 100644 index 7f0781bcc..000000000 --- a/mysql/spec/classes/graceful_failures_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' - -describe 'mysql::server' do - on_pe_unsupported_platforms.each do |pe_version,pe_platforms| - pe_platforms.each do |pe_platform,facts| - describe "on #{pe_version} #{pe_platform}" do - let(:facts) { facts } - - context 'should gracefully fail' do - it { expect { is_expected.to compile}.to raise_error(Puppet::Error, /Unsupported platform:/) } - end - end - end - end -end diff --git a/mysql/spec/classes/mycnf_template_spec.rb b/mysql/spec/classes/mycnf_template_spec.rb deleted file mode 100644 index c0607fb0a..000000000 --- a/mysql/spec/classes/mycnf_template_spec.rb +++ /dev/null @@ -1,78 +0,0 @@ -require 'spec_helper' - -describe 'mysql::server' do - context 'my.cnf template' do - on_pe_supported_platforms(PLATFORMS).each do |pe_version,pe_platforms| - pe_platforms.each do |pe_platform,facts| - describe "on #{pe_version} #{pe_platform}" do - let(:facts) { facts } - - context 'normal entry' do - let(:params) {{ :override_options => { 'mysqld' => { 'socket' => '/var/lib/mysql/mysql.sock' } } }} - it do - is_expected.to contain_file('mysql-config-file').with({ - :mode => '0644', - }).with_content(/socket = \/var\/lib\/mysql\/mysql.sock/) - end - end - - describe 'array entry' do - let(:params) {{ :override_options => { 'mysqld' => { 'replicate-do-db' => ['base1', 'base2'], } }}} - it do - is_expected.to contain_file('mysql-config-file').with_content( - /.*replicate-do-db = base1\nreplicate-do-db = base2.*/ - ) - end - end - - describe 'ssl set to true' do - let(:params) {{ :override_options => { 'mysqld' => { 'ssl' => true }}}} - it { is_expected.to contain_file('mysql-config-file').with_content(/ssl/) } - it { is_expected.to contain_file('mysql-config-file').without_content(/ssl = true/) } - end - - describe 'ssl set to false' do - let(:params) {{ :override_options => { 'mysqld' => { 'ssl' => false }}}} - it { is_expected.to contain_file('mysql-config-file').with_content(/ssl = false/) } - end - - # ssl-disable (and ssl) are special cased within mysql. - describe 'possibility of disabling ssl completely' do - let(:params) {{ :override_options => { 'mysqld' => { 'ssl' => true, 'ssl-disable' => true }}}} - it { is_expected.to contain_file('mysql-config-file').without_content(/ssl = true/) } - end - - describe 'a non ssl option set to true' do - let(:params) {{ :override_options => { 'mysqld' => { 'test' => true }}}} - it { is_expected.to contain_file('mysql-config-file').with_content(/^test$/) } - it { is_expected.to contain_file('mysql-config-file').without_content(/test = true/) } - end - - context 'with includedir' do - let(:params) {{ :includedir => '/etc/my.cnf.d' }} - it 'makes the directory' do - is_expected.to contain_file('/etc/my.cnf.d').with({ - :ensure => :directory, - :mode => '0755', - }) - end - - it { is_expected.to contain_file('mysql-config-file').with_content(/!includedir/) } - end - - context 'without includedir' do - let(:params) {{ :includedir => '' }} - it 'shouldnt contain the directory' do - is_expected.not_to contain_file('mysql-config-file').with({ - :ensure => :directory, - :mode => '0755', - }) - end - - it { is_expected.to contain_file('mysql-config-file').without_content(/!includedir/) } - end - end - end - end - end -end diff --git a/mysql/spec/classes/mysql_bindings_spec.rb b/mysql/spec/classes/mysql_bindings_spec.rb index 805817956..8ec4428bb 100644 --- a/mysql/spec/classes/mysql_bindings_spec.rb +++ b/mysql/spec/classes/mysql_bindings_spec.rb @@ -1,30 +1,58 @@ require 'spec_helper' describe 'mysql::bindings' do - on_pe_supported_platforms(PLATFORMS).each do |pe_version,pe_platforms| - pe_platforms.each do |pe_platform,facts| - describe "on #{pe_version} #{pe_platform}" do - let(:facts) { facts } - - let(:params) {{ - 'java_enable' => true, - 'perl_enable' => true, - 'php_enable' => true, - 'python_enable' => true, - 'ruby_enable' => true, - 'client_dev' => true, - 'daemon_dev' => true, - 'client_dev_package_name' => 'libmysqlclient-devel', - 'daemon_dev_package_name' => 'mysql-devel', - }} - - it { is_expected.to contain_package('mysql-connector-java') } - it { is_expected.to contain_package('perl_mysql') } - it { is_expected.to contain_package('python-mysqldb') } - it { is_expected.to contain_package('ruby_mysql') } - it { is_expected.to contain_package('mysql-client_dev') } - it { is_expected.to contain_package('mysql-daemon_dev') } - end + let(:params) {{ + 'java_enable' => true, + 'perl_enable' => true, + 'php_enable' => true, + 'python_enable' => true, + 'ruby_enable' => true, + }} + + shared_examples 'bindings' do |osfamily, operatingsystem, java_name, perl_name, php_name, python_name, ruby_name| + let :facts do + { :osfamily => osfamily, :operatingsystem => operatingsystem, :root_home => '/root'} end + it { should contain_package('mysql-connector-java').with( + :name => java_name, + :ensure => 'present' + )} + it { should contain_package('perl_mysql').with( + :name => perl_name, + :ensure => 'present' + )} + it { should contain_package('python-mysqldb').with( + :name => python_name, + :ensure => 'present' + )} + it { should contain_package('ruby_mysql').with( + :name => ruby_name, + :ensure => 'present' + )} + end + + context 'Debian' do + it_behaves_like 'bindings', 'Debian', 'Debian', 'libmysql-java', 'libdbd-mysql-perl', 'php5-mysql', 'python-mysqldb', 'libmysql-ruby' + it_behaves_like 'bindings', 'Debian', 'Ubuntu', 'libmysql-java', 'libdbd-mysql-perl', 'php5-mysql', 'python-mysqldb', 'libmysql-ruby' + end + + context 'freebsd' do + it_behaves_like 'bindings', 'FreeBSD', 'FreeBSD', 'databases/mysql-connector-java', 'p5-DBD-mysql', 'databases/php5-mysql', 'databases/py-MySQLdb', 'databases/ruby-mysql' + end + + context 'redhat' do + it_behaves_like 'bindings', 'RedHat', 'RedHat', 'mysql-connector-java', 'perl-DBD-MySQL', 'php-mysql', 'MySQL-python', 'ruby-mysql' + it_behaves_like 'bindings', 'RedHat', 'OpenSuSE', 'mysql-connector-java', 'perl-DBD-MySQL', 'php-mysql', 'MySQL-python', 'ruby-mysql' end + + describe 'on any other os' do + let :facts do + {:osfamily => 'foo', :root_home => '/root'} + end + + it 'should fail' do + expect { subject }.to raise_error(/Unsupported osfamily: foo/) + end + end + end diff --git a/mysql/spec/classes/mysql_client_spec.rb b/mysql/spec/classes/mysql_client_spec.rb index 7f67f49cd..d7386d17a 100644 --- a/mysql/spec/classes/mysql_client_spec.rb +++ b/mysql/spec/classes/mysql_client_spec.rb @@ -1,23 +1,16 @@ -require 'spec_helper' - describe 'mysql::client' do - on_pe_supported_platforms(PLATFORMS).each do |pe_version,pe_platforms| - pe_platforms.each do |pe_platform,facts| - describe "on #{pe_version} #{pe_platform}" do - let(:facts) { facts } + let(:facts) {{ :osfamily => 'RedHat' }} - context 'with defaults' do - it { is_expected.not_to contain_class('mysql::bindings') } - it { is_expected.to contain_package('mysql_client') } - end + context 'with defaults' do + it { should_not contain_class('mysql::bindings') } + it { should contain_package('mysql_client') } + end - context 'with bindings enabled' do - let(:params) {{ :bindings_enable => true }} + context 'with bindings enabled' do + let(:params) {{ :bindings_enable => true }} - it { is_expected.to contain_class('mysql::bindings') } - it { is_expected.to contain_package('mysql_client') } - end - end - end + it { should contain_class('mysql::bindings') } + it { should contain_package('mysql_client') } end + end diff --git a/mysql/spec/classes/mysql_server_account_security_spec.rb b/mysql/spec/classes/mysql_server_account_security_spec.rb index 9bd65082f..1d0e7506c 100644 --- a/mysql/spec/classes/mysql_server_account_security_spec.rb +++ b/mysql/spec/classes/mysql_server_account_security_spec.rb @@ -1,36 +1,41 @@ require 'spec_helper' describe 'mysql::server::account_security' do - on_pe_supported_platforms(PLATFORMS).each do |pe_version,pe_platforms| - pe_platforms.each do |pe_platform,facts| - describe "on #{pe_version} #{pe_platform}" do - let(:facts) { facts.merge({:fqdn => 'myhost.mydomain', :hostname => 'myhost'}) } - [ 'root@myhost.mydomain', - 'root@127.0.0.1', - 'root@::1', - '@myhost.mydomain', - '@localhost', - '@%', - ].each do |user| - it 'removes Mysql_User[#{user}]' do - is_expected.to contain_mysql_user(user).with_ensure('absent') - end - end + let :facts do { + :fqdn => 'myhost.mydomain', + :hostname => 'myhost', + :root_home => '/root' + } + end - # When the hostname doesn't match the fqdn we also remove these. - # We don't need to test the inverse as when they match they are - # covered by the above list. - [ 'root@myhost', '@myhost' ].each do |user| - it 'removes Mysql_User[#{user}]' do - is_expected.to contain_mysql_user(user).with_ensure('absent') - end - end + it 'should remove Mysql_User[root@myhost.mydomain]' do + should contain_mysql_user('root@myhost.mydomain').with_ensure('absent') + end + it 'should remove Mysql_User[root@myhost]' do + should contain_mysql_user('root@myhost').with_ensure('absent') + end + it 'should remove Mysql_User[root@127.0.0.1]' do + should contain_mysql_user('root@127.0.0.1').with_ensure('absent') + end + it 'should remove Mysql_User[root@::1]' do + should contain_mysql_user('root@::1').with_ensure('absent') + end + it 'should remove Mysql_User[@myhost.mydomain]' do + should contain_mysql_user('@myhost.mydomain').with_ensure('absent') + end + it 'should remove Mysql_User[@myhost]' do + should contain_mysql_user('@myhost').with_ensure('absent') + end + it 'should remove Mysql_User[@localhost]' do + should contain_mysql_user('@localhost').with_ensure('absent') + end + it 'should remove Mysql_User[@%]' do + should contain_mysql_user('@%').with_ensure('absent') + end - it 'should remove Mysql_database[test]' do - is_expected.to contain_mysql_database('test').with_ensure('absent') - end - end - end + it 'should remove Mysql_database[test]' do + should contain_mysql_database('test').with_ensure('absent') end + end diff --git a/mysql/spec/classes/mysql_server_backup_spec.rb b/mysql/spec/classes/mysql_server_backup_spec.rb index c7e2583c2..c46f95bd7 100644 --- a/mysql/spec/classes/mysql_server_backup_spec.rb +++ b/mysql/spec/classes/mysql_server_backup_spec.rb @@ -1,189 +1,183 @@ require 'spec_helper' describe 'mysql::server::backup' do - on_pe_supported_platforms(PLATFORMS).each do |pe_version,pe_platforms| - pe_platforms.each do |pe_platform,facts| - describe "on #{pe_version} #{pe_platform}" do - let(:facts) { facts } - let(:default_params) { - { 'backupuser' => 'testuser', + let(:default_params) { + { 'backupuser' => 'testuser', 'backuppassword' => 'testpass', 'backupdir' => '/tmp', 'backuprotate' => '25', 'delete_before_dump' => true, - 'execpath' => '/usr/bin:/usr/sbin:/bin:/sbin:/opt/zimbra/bin', - } } + } + context 'standard conditions' do + let(:params) { default_params } - context 'standard conditions' do - let(:params) { default_params } + it { should contain_mysql_user('testuser@localhost').with( + :require => 'Class[Mysql::Server::Root_password]' + )} - # Cannot use that_requires here, doesn't work on classes. - it { is_expected.to contain_mysql_user('testuser@localhost').with( - :require => 'Class[Mysql::Server::Root_password]') } + it { should contain_mysql_grant('testuser@localhost/*.*').with( + :privileges => ["SELECT", "RELOAD", "LOCK TABLES", "SHOW VIEW"] + )} - it { is_expected.to contain_mysql_grant('testuser@localhost/*.*').with( - :privileges => ['SELECT', 'RELOAD', 'LOCK TABLES', 'SHOW VIEW', 'PROCESS'] - ).that_requires('Mysql_user[testuser@localhost]') } - - it { is_expected.to contain_cron('mysql-backup').with( + it { should contain_cron('mysql-backup').with( :command => '/usr/local/sbin/mysqlbackup.sh', :ensure => 'present' - )} + )} - it { is_expected.to contain_file('mysqlbackup.sh').with( + it { should contain_file('mysqlbackup.sh').with( :path => '/usr/local/sbin/mysqlbackup.sh', :ensure => 'present' - ) } + ) } - it { is_expected.to contain_file('mysqlbackupdir').with( + it { should contain_file('mysqlbackupdir').with( :path => '/tmp', :ensure => 'directory' - )} - - it 'should have compression by default' do - is_expected.to contain_file('mysqlbackup.sh').with( - :content => /bzcat -zc/ - ) - end - it 'should skip backing up events table by default' do - is_expected.to contain_file('mysqlbackup.sh').with( - :content => /EVENTS="--ignore-table=mysql.event"/ - ) - end - - it 'should have 25 days of rotation' do - # MySQL counts from 0 - is_expected.to contain_file('mysqlbackup.sh').with_content(/.*ROTATE=24.*/) - end - - it 'should have a standard PATH' do - is_expected.to contain_file('mysqlbackup.sh').with_content(%r{PATH=/usr/bin:/usr/sbin:/bin:/sbin:/opt/zimbra/bin}) - end + )} + + it 'should have compression by default' do + verify_contents(subject, 'mysqlbackup.sh', [ + ' --all-databases | bzcat -zc > ${DIR}/${PREFIX}`date +%Y%m%d-%H%M%S`.sql.bz2', + ]) + end + it 'should skip backing up events table by default' do + verify_contents(subject, 'mysqlbackup.sh', [ + 'EVENTS="--ignore-table=mysql.event"', + ]) end - context 'custom ownership and mode for backupdir' do - let(:params) do + it 'should have 25 days of rotation' do + # MySQL counts from 0 I guess. + should contain_file('mysqlbackup.sh').with_content(/.*ROTATE=24.*/) + end + end + + context 'custom ownership and mode for backupdir' do + let(:params) do { :backupdirmode => '0750', - :backupdirowner => 'testuser', - :backupdirgroup => 'testgrp', + :backupdirowner => 'testuser', + :backupdirgroup => 'testgrp', }.merge(default_params) - end + end - it { is_expected.to contain_file('mysqlbackupdir').with( + it { should contain_file('mysqlbackupdir').with( :path => '/tmp', :ensure => 'directory', :mode => '0750', :owner => 'testuser', :group => 'testgrp' - ) } - end + ) } + end - context 'with compression disabled' do - let(:params) do + context 'with compression disabled' do + let(:params) do { :backupcompress => false }.merge(default_params) - end + end - it { is_expected.to contain_file('mysqlbackup.sh').with( + it { should contain_file('mysqlbackup.sh').with( :path => '/usr/local/sbin/mysqlbackup.sh', :ensure => 'present' - ) } + ) } - it 'should be able to disable compression' do - is_expected.to contain_file('mysqlbackup.sh').without_content( - /.*bzcat -zc.*/ - ) - end + it 'should be able to disable compression' do + verify_contents(subject, 'mysqlbackup.sh', [ + ' --all-databases > ${DIR}/${PREFIX}`date +%Y%m%d-%H%M%S`.sql', + ]) end + end - context 'with mysql.events backedup' do - let(:params) do + context 'with mysql.events backedup' do + let(:params) do { :ignore_events => false }.merge(default_params) - end + end - it { is_expected.to contain_file('mysqlbackup.sh').with( + it { should contain_file('mysqlbackup.sh').with( :path => '/usr/local/sbin/mysqlbackup.sh', :ensure => 'present' - ) } + ) } - it 'should be able to backup events table' do - is_expected.to contain_file('mysqlbackup.sh').with_content( - /EVENTS="--events"/ - ) - end + it 'should be able to backup events table' do + verify_contents(subject, 'mysqlbackup.sh', [ + 'EVENTS="--events"', + ]) end + end - context 'with database list specified' do - let(:params) do + + context 'with database list specified' do + let(:params) do { :backupdatabases => ['mysql'] }.merge(default_params) - end + end - it { is_expected.to contain_file('mysqlbackup.sh').with( + it { should contain_file('mysqlbackup.sh').with( :path => '/usr/local/sbin/mysqlbackup.sh', :ensure => 'present' - ) - } - - it 'should have a backup file for each database' do - is_expected.to contain_file('mysqlbackup.sh').with_content( - /mysql | bzcat -zc \${DIR}\\\${PREFIX}mysql_`date'/ - ) - end - end + ) } + + it 'should have a backup file for each database' do + content = subject.resource('file','mysqlbackup.sh').send(:parameters)[:content] + content.should match(' mysql | bzcat -zc \${DIR}\\\${PREFIX}mysql_`date') + # verify_contents(subject, 'mysqlbackup.sh', [ + # ' mysql | bzcat -zc ${DIR}/${PREFIX}mysql_`date +%Y%m%d-%H%M%S`.sql', + # ]) + end + end - context 'with file per database' do - let(:params) do + context 'with file per database' do + let(:params) do default_params.merge({ :file_per_database => true }) - end + end - it 'should loop through backup all databases' do - is_expected.to contain_file('mysqlbackup.sh').with_content(/.*SHOW DATABASES.*/) - end + it 'should loop through backup all databases' do + verify_contents(subject, 'mysqlbackup.sh', [ + 'mysql -s -r -N -e \'SHOW DATABASES\' | while read dbname', + 'do', + ' mysqldump -u${USER} -p${PASS} --opt --flush-logs --single-transaction \\', + ' ${EVENTS} \\', + ' ${dbname} | bzcat -zc > ${DIR}/${PREFIX}${dbname}_`date +%Y%m%d-%H%M%S`.sql.bz2', + 'done', + ]) + end - context 'with compression disabled' do + context 'with compression disabled' do let(:params) do - default_params.merge({ :file_per_database => true, :backupcompress => false }) + default_params.merge({ :file_per_database => true, :backupcompress => false }) end it 'should loop through backup all databases without compression' do - is_expected.to contain_file('mysqlbackup.sh').with_content( - /.*SHOW DATABASES.*/ - ) - is_expected.to contain_file('mysqlbackup.sh').without_content( - /.*bzcat -zc.*/ - ) + verify_contents(subject, 'mysqlbackup.sh', [ + ' ${dbname} > ${DIR}/${PREFIX}${dbname}_`date +%Y%m%d-%H%M%S`.sql', + ]) end - end end - - context 'with postscript' do - let(:params) do - default_params.merge({ :postscript => 'rsync -a /tmp backup01.local-lan:' }) - end - - it 'should be add postscript' do - is_expected.to contain_file('mysqlbackup.sh').with_content( - /rsync -a \/tmp backup01.local-lan:/ - ) - end - end - - context 'with postscripts' do - let(:params) do - default_params.merge({ :postscript => [ - 'rsync -a /tmp backup01.local-lan:', - 'rsync -a /tmp backup02.local-lan:', - ]}) - end - - it 'should be add postscript' do - is_expected.to contain_file('mysqlbackup.sh').with_content( - /.*rsync -a \/tmp backup01.local-lan:\n\nrsync -a \/tmp backup02.local-lan:.*/ - ) - end - end - end end - end + + context 'with postscript' do + let(:params) do + default_params.merge({ :postscript => 'rsync -a /tmp backup01.local-lan:' }) + end + + it 'should be add postscript' do + verify_contents(subject, 'mysqlbackup.sh', [ + 'rsync -a /tmp backup01.local-lan:', + ]) + end + end + + context 'with postscripts' do + let(:params) do + default_params.merge({ :postscript => [ + 'rsync -a /tmp backup01.local-lan:', + 'rsync -a /tmp backup02.local-lan:', + ]}) + end + + it 'should be add postscript' do + verify_contents(subject, 'mysqlbackup.sh', [ + 'rsync -a /tmp backup01.local-lan:', + 'rsync -a /tmp backup02.local-lan:', + ]) + end + end end diff --git a/mysql/spec/classes/mysql_server_monitor_spec.rb b/mysql/spec/classes/mysql_server_monitor_spec.rb index d9eea001d..8df9530f0 100644 --- a/mysql/spec/classes/mysql_server_monitor_spec.rb +++ b/mysql/spec/classes/mysql_server_monitor_spec.rb @@ -1,35 +1,31 @@ require 'spec_helper' describe 'mysql::server::monitor' do - on_pe_supported_platforms(PLATFORMS).each do |pe_version,pe_platforms| - pe_platforms.each do |pe_platform,facts| - describe "on #{pe_version} #{pe_platform}" do - let(:facts) { facts } - let :pre_condition do - "include 'mysql::server'" - end + let :facts do + { :osfamily => 'Debian', :root_home => '/root' } + end + let :pre_condition do + "include 'mysql::server'" + end - let :default_params do - { - :mysql_monitor_username => 'monitoruser', - :mysql_monitor_password => 'monitorpass', - :mysql_monitor_hostname => 'monitorhost', - } - end + let :default_params do + { + :mysql_monitor_username => 'monitoruser', + :mysql_monitor_password => 'monitorpass', + :mysql_monitor_hostname => 'monitorhost', + } + end - let :params do - default_params - end + let :params do + default_params + end - it { is_expected.to contain_mysql_user('monitoruser@monitorhost')} + it { should contain_mysql_user('monitoruser@monitorhost')} - it { is_expected.to contain_mysql_grant('monitoruser@monitorhost/*.*').with( - :ensure => 'present', - :user => 'monitoruser@monitorhost', - :table => '*.*', - :privileges => ["PROCESS", "SUPER"], - :require => 'Mysql_user[monitoruser@monitorhost]' - )} - end - end - end + it { should contain_mysql_grant('monitoruser@monitorhost/*.*').with( + :ensure => 'present', + :user => 'monitoruser@monitorhost', + :table => '*.*', + :privileges => ["PROCESS", "SUPER"], + :require => 'Mysql_user[monitoruser@monitorhost]' + )} end diff --git a/mysql/spec/classes/mysql_server_mysqltuner_spec.rb b/mysql/spec/classes/mysql_server_mysqltuner_spec.rb index 51d2a7aa0..7e9499a65 100644 --- a/mysql/spec/classes/mysql_server_mysqltuner_spec.rb +++ b/mysql/spec/classes/mysql_server_mysqltuner_spec.rb @@ -1,13 +1,5 @@ -require 'spec_helper' - describe 'mysql::server::mysqltuner' do - on_pe_supported_platforms(PLATFORMS).each do |pe_version,pe_platforms| - pe_platforms.each do |pe_platform,facts| - describe "on #{pe_version} #{pe_platform}" do - let(:facts) { facts } - it { is_expected.to contain_file('/usr/local/bin/mysqltuner') } - end - end - end + it { should contain_file('/usr/local/bin/mysqltuner') } + end diff --git a/mysql/spec/classes/mysql_server_spec.rb b/mysql/spec/classes/mysql_server_spec.rb index 21efa1170..c14b70f7c 100644 --- a/mysql/spec/classes/mysql_server_spec.rb +++ b/mysql/spec/classes/mysql_server_spec.rb @@ -1,136 +1,193 @@ require 'spec_helper' - describe 'mysql::server' do - on_pe_supported_platforms(PLATFORMS).each do |pe_version,pe_platforms| - pe_platforms.each do |pe_platform,facts| - describe "on #{pe_version} #{pe_platform}" do - let(:facts) { facts } - - context 'with defaults' do - it { is_expected.to contain_class('mysql::server::install') } - it { is_expected.to contain_class('mysql::server::config') } - it { is_expected.to contain_class('mysql::server::service') } - it { is_expected.to contain_class('mysql::server::root_password') } - it { is_expected.to contain_class('mysql::server::providers') } - end - - context 'with remove_default_accounts set' do - let(:params) {{ :remove_default_accounts => true }} - it { is_expected.to contain_class('mysql::server::account_security') } - end - - context 'mysql::server::install' do - it 'contains the package' do - is_expected.to contain_package('mysql-server').with({ - :ensure => :present, - }) - end - context 'with datadir overridden' do - let(:params) {{ :override_options => { 'mysqld' => { 'datadir' => '/tmp' }} }} - it { is_expected.to contain_exec('mysql_install_db') } - end - end - - context 'mysql::server::service' do - context 'with defaults' do - it { is_expected.to contain_service('mysqld') } - end - - context 'service_enabled set to false' do - let(:params) {{ :service_enabled => false }} - - it do - is_expected.to contain_service('mysqld').with({ - :ensure => :stopped - }) - end - end - end - - context 'mysql::server::root_password' do - describe 'when defaults' do - it { is_expected.not_to contain_mysql_user('root@localhost') } - it { is_expected.not_to contain_file('/root/.my.cnf') } - end - describe 'when set' do - let(:params) {{:root_password => 'SET' }} - it { is_expected.to contain_mysql_user('root@localhost') } - it { is_expected.to contain_file('/root/.my.cnf') } - end - end - - context 'mysql::server::providers' do - describe 'with users' do - let(:params) {{:users => { - 'foo@localhost' => { - 'max_connections_per_hour' => '1', - 'max_queries_per_hour' => '2', - 'max_updates_per_hour' => '3', - 'max_user_connections' => '4', - 'password_hash' => '*F3A2A51A9B0F2BE2468926B4132313728C250DBF' - }, - 'foo2@localhost' => {} - }}} - it { is_expected.to contain_mysql_user('foo@localhost').with( - :max_connections_per_hour => '1', - :max_queries_per_hour => '2', - :max_updates_per_hour => '3', - :max_user_connections => '4', - :password_hash => '*F3A2A51A9B0F2BE2468926B4132313728C250DBF' - )} - it { is_expected.to contain_mysql_user('foo2@localhost').with( - :max_connections_per_hour => nil, - :max_queries_per_hour => nil, - :max_updates_per_hour => nil, - :max_user_connections => nil, - :password_hash => '' - )} - end - - describe 'with grants' do - let(:params) {{:grants => { - 'foo@localhost/somedb.*' => { - 'user' => 'foo@localhost', - 'table' => 'somedb.*', - 'privileges' => ["SELECT", "UPDATE"], - 'options' => ["GRANT"], - }, - 'foo2@localhost/*.*' => { - 'user' => 'foo2@localhost', - 'table' => '*.*', - 'privileges' => ["SELECT"], - }, - }}} - it { is_expected.to contain_mysql_grant('foo@localhost/somedb.*').with( - :user => 'foo@localhost', - :table => 'somedb.*', - :privileges => ["SELECT", "UPDATE"], - :options => ["GRANT"] - )} - it { is_expected.to contain_mysql_grant('foo2@localhost/*.*').with( - :user => 'foo2@localhost', - :table => '*.*', - :privileges => ["SELECT"], - :options => nil - )} - end - - describe 'with databases' do - let(:params) {{:databases => { - 'somedb' => { - 'charset' => 'latin1', - 'collate' => 'latin1', - }, - 'somedb2' => {} - }}} - it { is_expected.to contain_mysql_database('somedb').with( - :charset => 'latin1', - :collate => 'latin1' - )} - it { is_expected.to contain_mysql_database('somedb2')} - end - end + let(:facts) {{:osfamily => 'RedHat', :root_home => '/root'}} + + context 'with defaults' do + it { should contain_class('mysql::server::install') } + it { should contain_class('mysql::server::config') } + it { should contain_class('mysql::server::service') } + it { should contain_class('mysql::server::root_password') } + it { should contain_class('mysql::server::providers') } + end + + # make sure that overriding the mysqld settings keeps the defaults for everything else + context 'with overrides' do + let(:params) {{ :override_options => { 'mysqld' => { 'socket' => '/var/lib/mysql/mysql.sock' } } }} + it do + should contain_file('/etc/my.cnf').with({ + :mode => '0644', + }).with_content(/basedir/) + end + end + + describe 'with multiple instance of an option' do + let(:params) {{ :override_options => { 'mysqld' => { 'replicate-do-db' => ['base1', 'base2', 'base3'], } }}} + it do + should contain_file('/etc/my.cnf').with_content( + /^replicate-do-db = base1$/ + ).with_content( + /^replicate-do-db = base2$/ + ).with_content( + /^replicate-do-db = base3$/ + ) + end + end + + describe 'an option set to true' do + let(:params) { + { :override_options => { 'mysqld' => { 'ssl' => true } }} + } + it do + should contain_file('/etc/my.cnf').with_content(/^\s*ssl\s*(?:$|= true)/m) + end + end + + describe 'an option set to false' do + let(:params) { + { :override_options => { 'mysqld' => { 'ssl' => false } }} + } + it do + should contain_file('/etc/my.cnf').with_content(/^\s*ssl = false/m) + end + end + + context 'with remove_default_accounts set' do + let (:params) {{ :remove_default_accounts => true }} + it { should contain_class('mysql::server::account_security') } + end + + context 'mysql::server::install' do + let(:params) {{ :package_ensure => 'present', :name => 'mysql-server' }} + it do + should contain_package('mysql-server').with({ + :ensure => :present, + :name => 'mysql-server', + }) + end + end + + context 'mysql::server::config' do + it do + should contain_file('/etc/mysql').with({ + :ensure => :directory, + :mode => '0755', + }) + end + + it do + should contain_file('/etc/mysql/conf.d').with({ + :ensure => :directory, + :mode => '0755', + }) + end + + it do + should contain_file('/etc/my.cnf').with({ + :mode => '0644', + }) + end + end + + context 'mysql::server::service' do + context 'with defaults' do + it { should contain_service('mysqld') } + end + + context 'service_enabled set to false' do + let(:params) {{ :service_enabled => false }} + + it do + should contain_service('mysqld').with({ + :ensure => :stopped + }) end end end + + context 'mysql::server::root_password' do + describe 'when defaults' do + it { should_not contain_mysql_user('root@localhost') } + it { should_not contain_file('/root/.my.cnf') } + end + describe 'when set' do + let(:params) {{:root_password => 'SET' }} + it { should contain_mysql_user('root@localhost') } + it { should contain_file('/root/.my.cnf') } + end + + end + + context 'mysql::server::providers' do + describe 'with users' do + let(:params) {{:users => { + 'foo@localhost' => { + 'max_connections_per_hour' => '1', + 'max_queries_per_hour' => '2', + 'max_updates_per_hour' => '3', + 'max_user_connections' => '4', + 'password_hash' => '*F3A2A51A9B0F2BE2468926B4132313728C250DBF' + }, + 'foo2@localhost' => {} + }}} + it { should contain_mysql_user('foo@localhost').with( + :max_connections_per_hour => '1', + :max_queries_per_hour => '2', + :max_updates_per_hour => '3', + :max_user_connections => '4', + :password_hash => '*F3A2A51A9B0F2BE2468926B4132313728C250DBF' + )} + it { should contain_mysql_user('foo2@localhost').with( + :max_connections_per_hour => nil, + :max_queries_per_hour => nil, + :max_updates_per_hour => nil, + :max_user_connections => nil, + :password_hash => '' + )} + end + + describe 'with grants' do + let(:params) {{:grants => { + 'foo@localhost/somedb.*' => { + 'user' => 'foo@localhost', + 'table' => 'somedb.*', + 'privileges' => ["SELECT", "UPDATE"], + 'options' => ["GRANT"], + }, + 'foo2@localhost/*.*' => { + 'user' => 'foo2@localhost', + 'table' => '*.*', + 'privileges' => ["SELECT"], + }, + }}} + it { should contain_mysql_grant('foo@localhost/somedb.*').with( + :user => 'foo@localhost', + :table => 'somedb.*', + :privileges => ["SELECT", "UPDATE"], + :options => ["GRANT"] + )} + it { should contain_mysql_grant('foo2@localhost/*.*').with( + :user => 'foo2@localhost', + :table => '*.*', + :privileges => ["SELECT"], + :options => nil + )} + end + + describe 'with databases' do + let(:params) {{:databases => { + 'somedb' => { + 'charset' => 'latin1', + 'collate' => 'latin1', + }, + 'somedb2' => {} + }}} + it { should contain_mysql_database('somedb').with( + :charset => 'latin1', + :collate => 'latin1' + )} + it { should contain_mysql_database('somedb2')} + end + + end + end diff --git a/mysql/spec/defines/mysql_db_spec.rb b/mysql/spec/defines/mysql_db_spec.rb index 15f433bb5..a7ee31ff8 100644 --- a/mysql/spec/defines/mysql_db_spec.rb +++ b/mysql/spec/defines/mysql_db_spec.rb @@ -17,40 +17,35 @@ end it 'should not notify the import sql exec if no sql script was provided' do - is_expected.to contain_mysql_database('test_db').without_notify + should contain_mysql_database('test_db').without_notify end it 'should subscribe to database if sql script is given' do params.merge!({'sql' => 'test_sql'}) - is_expected.to contain_exec('test_db-import').with_subscribe('Mysql_database[test_db]') + should contain_exec('test_db-import').with_subscribe('Mysql_database[test_db]') end it 'should only import sql script on creation if not enforcing' do params.merge!({'sql' => 'test_sql', 'enforce_sql' => false}) - is_expected.to contain_exec('test_db-import').with_refreshonly(true) + should contain_exec('test_db-import').with_refreshonly(true) end it 'should import sql script on creation if enforcing' do params.merge!({'sql' => 'test_sql', 'enforce_sql' => true}) - is_expected.to contain_exec('test_db-import').with_refreshonly(false) + should contain_exec('test_db-import').with_refreshonly(false) end it 'should not create database and database user' do params.merge!({'ensure' => 'absent', 'host' => 'localhost'}) - is_expected.to contain_mysql_database('test_db').with_ensure('absent') - is_expected.to contain_mysql_user('testuser@localhost').with_ensure('absent') + should contain_mysql_database('test_db').with_ensure('absent') + should contain_mysql_user('testuser@localhost').with_ensure('absent') end it 'should create with an appropriate collate and charset' do params.merge!({'charset' => 'utf8', 'collate' => 'utf8_danish_ci'}) - is_expected.to contain_mysql_database('test_db').with({ + should contain_mysql_database('test_db').with({ 'charset' => 'utf8', 'collate' => 'utf8_danish_ci', }) end - - it 'should use dbname parameter as database name instead of name' do - params.merge!({'dbname' => 'real_db'}) - is_expected.to contain_mysql_database('real_db') - end end diff --git a/mysql/spec/spec_helper.rb b/mysql/spec/spec_helper.rb index f3a260acb..1fd2563f5 100644 --- a/mysql/spec/spec_helper.rb +++ b/mysql/spec/spec_helper.rb @@ -1,10 +1,5 @@ -require 'puppetlabs_spec_helper/module_spec_helper' -require 'puppet_facts' -include PuppetFacts -RSpec.configure do |c| - c.formatter = :documentation +require 'simplecov' +SimpleCov.start do + add_filter "/spec/" end - -# The default set of platforms to test again. -ENV['UNIT_TEST_PLATFORMS'] = 'centos-6-x86_64 ubuntu-1404-x86_64' -PLATFORMS = ENV['UNIT_TEST_PLATFORMS'].split(' ') +require 'puppetlabs_spec_helper/module_spec_helper' diff --git a/mysql/spec/spec_helper_acceptance.rb b/mysql/spec/spec_helper_acceptance.rb index cfae833bd..a76c2e583 100644 --- a/mysql/spec/spec_helper_acceptance.rb +++ b/mysql/spec/spec_helper_acceptance.rb @@ -2,15 +2,16 @@ UNSUPPORTED_PLATFORMS = [ 'Windows', 'Solaris', 'AIX' ] -unless ENV['RS_PROVISION'] == 'no' or ENV['BEAKER_provision'] == 'no' - # This will install the latest available package on el and deb based - # systems fail on windows and osx, and install via gem on other *nixes - foss_opts = { :default_action => 'gem_install' } - - if default.is_pe?; then install_pe; else install_puppet( foss_opts ); end - +unless ENV['RS_PROVISION'] == 'no' hosts.each do |host| - on hosts, "mkdir -p #{host['distmoduledir']}" + # Install Puppet + if host.is_pe? + install_pe + else + install_package host, 'rubygems' + on host, 'gem install puppet --no-ri --no-rdoc' + on host, "mkdir -p #{host['distmoduledir']}" + end end end @@ -30,14 +31,18 @@ if fact('osfamily') == 'RedHat' version = fact("operatingsystemmajrelease") shell("yum localinstall -y http://yum.puppetlabs.com/puppetlabs-release-el-#{version}.noarch.rpm") - if fact('operatingsystemmajrelease') =~ /7/ || fact('operatingsystem') =~ /Fedora/ - shell("yum install -y bzip2") + if version == '6' + shell("yum localinstall -y http://mirror.pnl.gov/epel/6/i386/epel-release-6-8.noarch.rpm") + elsif version == '5' + shell("yum localinstall -y http://mirrors.servercentral.net/fedora/epel/5/i386/epel-release-5-4.noarch.rpm") + else + puts "Sorry, this version is not supported." + exit end end - shell("/bin/touch #{default['puppetpath']}/hiera.yaml") + shell("/bin/touch #{default['distmoduledir']}/hiera.yaml") shell('puppet module install puppetlabs-stdlib --version 3.2.0', { :acceptable_exit_codes => [0,1] }) - on host, puppet('module','install','stahnma/epel'), { :acceptable_exit_codes => [0,1] } end end end diff --git a/mysql/spec/spec_helper_system.rb b/mysql/spec/spec_helper_system.rb new file mode 100644 index 000000000..de20690c5 --- /dev/null +++ b/mysql/spec/spec_helper_system.rb @@ -0,0 +1,25 @@ +require 'rspec-system/spec_helper' +require 'rspec-system-puppet/helpers' +require 'rspec-system-serverspec/helpers' + +include RSpecSystemPuppet::Helpers + +RSpec.configure do |c| + # Project root + proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) + + # Enable colour + c.tty = true + + c.include RSpecSystemPuppet::Helpers + + # This is where we 'setup' the nodes before running our tests + c.before :suite do + # Install puppet + puppet_install + + # Install modules and dependencies + puppet_module_install(:source => proj_root, :module_name => 'mysql') + shell('puppet module install puppetlabs-stdlib') + end +end diff --git a/mysql/spec/system/mysql_account_delete_spec.rb b/mysql/spec/system/mysql_account_delete_spec.rb new file mode 100644 index 000000000..11f776a05 --- /dev/null +++ b/mysql/spec/system/mysql_account_delete_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper_system' + +describe 'mysql::server::account_security class' do + + describe 'running puppet code' do + # Using puppet_apply as a helper + it 'should work with no errors' do + pp = <<-EOS + class { 'mysql::server': remove_default_accounts => true } + EOS + + # Run it twice and test for idempotency + puppet_apply(pp) do |r| + r.exit_code.should_not == 1 + r.refresh + r.exit_code.should be_zero + end + end + + describe 'accounts' do + it 'should delete accounts' do + shell("mysql -e 'show grants for root@127.0.01;'") do |s| + s.exit_code.should == 1 + end + end + + it 'should delete databases' do + shell("mysql -e 'show databases;' |grep test") do |s| + s.exit_code.should == 1 + end + end + end + end + +end diff --git a/mysql/spec/system/mysql_backup_spec.rb b/mysql/spec/system/mysql_backup_spec.rb new file mode 100644 index 000000000..cdbf4ed27 --- /dev/null +++ b/mysql/spec/system/mysql_backup_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper_system' + +describe 'mysql::server::backup class' do + context 'should work with no errors' do + pp = <<-EOS + class { 'mysql::server': override_options => { 'root_password' => 'password' } } + mysql::db { 'backup1': + user => 'backup', + password => 'secret', + } + + class { 'mysql::server::backup': + backupuser => 'myuser', + backuppassword => 'mypassword', + backupdir => '/tmp/backups', + backupcompress => true, + postscript => [ + 'rm -rf /var/tmp/mysqlbackups', + 'rm -f /var/tmp/mysqlbackups.done', + 'cp -r /tmp/backups /var/tmp/mysqlbackups', + 'touch /var/tmp/mysqlbackups.done', + ], + } + EOS + + context puppet_apply(pp) do + its(:stderr) { should be_empty } + its(:exit_code) { should_not == 1 } + its(:refresh) { should be_nil } + its(:stderr) { should be_empty } + its(:exit_code) { should be_zero } + end + end + + describe 'mysqlbackup.sh' do + context 'should run mysqlbackup.sh with no errors' do + context shell("/usr/local/sbin/mysqlbackup.sh") do + its(:exit_code) { should be_zero } + its(:stderr) { should be_empty } + end + end + + context 'should dump all databases to single file' do + describe command('ls /tmp/backups/ | grep -c "mysql_backup_[0-9][0-9]*-[0-9][0-9]*.sql.bz2"') do + it { should return_stdout /1/ } + it { should return_exit_status 0 } + end + end + + context 'should create one file per database per run' do + context shell("/usr/local/sbin/mysqlbackup.sh") do + its(:exit_code) { should be_zero } + its(:stderr) { should be_empty } + end + + describe command('ls /tmp/backups/ | grep -c "mysql_backup_backup1_[0-9][0-9]*-[0-9][0-9]*.sql.bz2"') do + it { should return_stdout /2/ } + it { should return_exit_status 0 } + end + end + end +end diff --git a/mysql/spec/system/mysql_bindings_spec.rb b/mysql/spec/system/mysql_bindings_spec.rb new file mode 100644 index 000000000..694cf574d --- /dev/null +++ b/mysql/spec/system/mysql_bindings_spec.rb @@ -0,0 +1,90 @@ +require 'spec_helper_system' + +describe 'mysql::bindings class' do + let(:os) { + node.facts['osfamily'] + } + + case node.facts['osfamily'] + when 'RedHat' + java_package = 'mysql-connector-java' + perl_package = 'perl-DBD-MySQL' + python_package = 'MySQL-python' + ruby_package = 'ruby-mysql' + when 'Suse' + java_package = 'mysql-connector-java' + perl_package = 'perl-DBD-MySQL' + python_package = 'python-mysql' + case node.facts['operatingsystem'] + when /OpenSuSE/ + ruby_package = 'rubygem-mysql' + when /(SLES|SLED)/ + ruby_package = 'ruby-mysql' + end + when 'Debian' + java_package = 'libmysql-java' + perl_package = 'libdbd-mysql-perl' + python_package = 'python-mysqldb' + ruby_package = 'libmysql-ruby' + when 'FreeBSD' + java_package = 'databases/mysql-connector-java' + perl_package = 'p5-DBD-mysql' + python_package = 'databases/py-MySQLdb' + ruby_package = 'ruby-mysql' + else + case node.facts['operatingsystem'] + when 'Amazon' + java_package = 'mysql-connector-java' + perl_package = 'perl-DBD-MySQL' + python_package = 'MySQL-python' + ruby_package = 'ruby-mysql' + end + end + + describe 'running puppet code' do + # Using puppet_apply as a helper + it 'should work with no errors' do + pp = <<-EOS + class { 'mysql::bindings': } + EOS + + # Run it twice and test for idempotency + puppet_apply(pp) do |r| + r.exit_code.should_not == 1 + r.refresh + r.exit_code.should be_zero + end + end + end + + describe 'enabling bindings' do + it 'should work with no errors' do + puppet_apply(%{ + class { 'mysql::bindings': + java_enable => true, + perl_enable => true, + python_enable => true, + ruby_enable => true, + } + }) + end + + describe package(java_package) do + it { should be_installed } + end + + describe package(perl_package) do + it { should be_installed } + end + + describe package(python_package) do + it { should be_installed } + end + + describe package(ruby_package) do + it { should be_installed } + end + + end + +end diff --git a/mysql/spec/system/mysql_db_spec.rb b/mysql/spec/system/mysql_db_spec.rb new file mode 100644 index 000000000..49299e1dd --- /dev/null +++ b/mysql/spec/system/mysql_db_spec.rb @@ -0,0 +1,61 @@ +require 'spec_helper_system' + +describe 'mysql::db define' do + describe 'creating a database' do + # Using puppet_apply as a helper + it 'should work with no errors' do + pp = <<-EOS + class { 'mysql::server': override_options => { 'root_password' => 'password' } } + mysql::db { 'spec1': + user => 'root1', + password => 'password', + } + EOS + + # Run it twice and test for idempotency + puppet_apply(pp) do |r| + [0,2].should include r.exit_code + r.refresh + r.exit_code.should be_zero + end + end + + it 'should have the database' do + shell("mysql -e 'show databases;'|grep spec1") do |s| + s.exit_code.should be_zero + end + end + end + + describe 'creating a database with post-sql' do + # Using puppet_apply as a helper + it 'should work with no errors' do + pp = <<-EOS + class { 'mysql::server': override_options => { 'root_password' => 'password' } } + file { '/tmp/spec.sql': + ensure => file, + content => 'CREATE TABLE table1 (id int);', + before => Mysql::Db['spec2'], + } + mysql::db { 'spec2': + user => 'root1', + password => 'password', + sql => '/tmp/spec.sql', + } + EOS + + # Run it twice and test for idempotency + puppet_apply(pp) do |r| + [0,2].should include r.exit_code + r.refresh + r.exit_code.should be_zero + end + end + + it 'should have the table' do + shell("mysql -e 'show tables;' spec2|grep table1") do |s| + s.exit_code.should == 0 + end + end + end +end diff --git a/mysql/spec/system/mysql_server_monitor_spec.rb b/mysql/spec/system/mysql_server_monitor_spec.rb new file mode 100644 index 000000000..b7ebd0dff --- /dev/null +++ b/mysql/spec/system/mysql_server_monitor_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper_system' + +describe 'mysql::server::monitor class' do + context 'should work with no errors' do + pp = <<-EOS + class { 'mysql::server': root_password => 'password' } + + class { 'mysql::server::monitor': + mysql_monitor_username => 'monitoruser', + mysql_monitor_password => 'monitorpass', + mysql_monitor_hostname => 'localhost', + } + EOS + + context puppet_apply(pp) do + its(:stderr) { should be_empty } + its(:exit_code) { should_not == 1 } + its(:refresh) { should be_nil } + its(:stderr) { should be_empty } + its(:exit_code) { should be_zero } + end + + context 'should run mysqladmin ping with no errors' do + describe command("mysqladmin -u monitoruser -pmonitorpass -h localhost ping") do + it { should return_stdout /mysqld is alive/ } + it { should return_exit_status 0 } + end + end + end +end diff --git a/mysql/spec/system/mysql_server_root_password_spec.rb b/mysql/spec/system/mysql_server_root_password_spec.rb new file mode 100644 index 000000000..ea9cafad5 --- /dev/null +++ b/mysql/spec/system/mysql_server_root_password_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper_system' + +describe 'mysql::server::root_password class' do + + describe 'reset' do + it 'shuts down mysql' do + pp = <<-EOS + class { 'mysql::server': service_enabled => false } + EOS + + puppet_apply(pp) do |r| + r.exit_code.should_not == 1 + end + end + + it 'deletes the /root/.my.cnf password' do + shell('rm -rf /root/.my.cnf') + end + + it 'deletes all databases' do + case node.facts['osfamily'] + when 'RedHat' + shell('rm -rf `grep datadir /etc/my.cnf | cut -d" " -f 3`/*') + when 'Debian' + shell('rm -rf `grep datadir /etc/mysql/my.cnf | cut -d" " -f 3`/*') + shell('mysql_install_db') + end + end + + it 'starts up mysql' do + pp = <<-EOS + class { 'mysql::server': service_enabled => true } + EOS + + puppet_apply(pp) do |r| + r.exit_code.should_not == 1 + end + end + end + + describe 'when unset' do + it 'should work' do + pp = <<-EOS + class { 'mysql::server': root_password => 'test' } + EOS + + # Run it twice and test for idempotency + puppet_apply(pp) do |r| + r.exit_code.should_not == 1 + r.refresh + r.exit_code.should be_zero + end + end + end + + describe 'when set' do + it 'should work' do + pp = <<-EOS + class { 'mysql::server': root_password => 'new', old_root_password => 'test' } + EOS + + # Run it twice and test for idempotency + puppet_apply(pp) do |r| + r.exit_code.should_not == 1 + r.refresh + r.exit_code.should be_zero + end + end + end + +end diff --git a/mysql/spec/system/mysql_server_spec.rb b/mysql/spec/system/mysql_server_spec.rb new file mode 100644 index 000000000..b9ecabdce --- /dev/null +++ b/mysql/spec/system/mysql_server_spec.rb @@ -0,0 +1,106 @@ +require 'spec_helper_system' + +describe 'mysql class' do + case node.facts['osfamily'] + when 'RedHat' + package_name = 'mysql-server' + service_name = 'mysqld' + mycnf = '/etc/my.cnf' + when 'Suse' + package_name = 'mysql-community-server' + service_name = 'mysql' + mycnf = '/etc/my.cnf' + when 'Debian' + package_name = 'mysql-server' + service_name = 'mysql' + mycnf = '/etc/mysql/my.cnf' + end + + describe 'running puppet code' do + # Using puppet_apply as a helper + it 'should work with no errors' do + pp = <<-EOS + class { 'mysql::server': } + EOS + + # Run it twice and test for idempotency + puppet_apply(pp) do |r| + r.exit_code.should_not == 1 + r.refresh + r.exit_code.should be_zero + end + end + + describe package(package_name) do + it { should be_installed } + end + + describe service(service_name) do + it { should be_running } + it { should be_enabled } + end + end + + describe 'mycnf' do + it 'should contain sensible values' do + pp = <<-EOS + class { 'mysql::server': } + EOS + puppet_apply(pp) do |r| + r.exit_code.should_not == 1 + end + end + + describe file(mycnf) do + it { should contain 'key_buffer = 16M' } + it { should contain 'max_binlog_size = 100M' } + it { should contain 'query_cache_size = 16M' } + end + end + + describe 'my.cnf changes' do + it 'sets values' do + pp = <<-EOS + class { 'mysql::server': + override_options => { 'mysqld' => + { 'key_buffer' => '32M', + 'max_binlog_size' => '200M', + 'query_cache_size' => '32M', + } + } + } + EOS + puppet_apply(pp) do |r| + r.exit_code.should_not == 1 + end + end + + describe file(mycnf) do + it { should contain 'key_buffer = 32M' } + it { should contain 'max_binlog_size = 200M' } + it { should contain 'query_cache_size = 32M' } + end + end + + describe 'my.cnf should contain multiple instances of the same option' do + it 'sets multiple values' do + pp = <<-EOS + class { 'mysql::server': + override_options => { 'mysqld' => + { 'replicate-do-db' => ['base1', 'base2', 'base3'], } + } + } + EOS + puppet_apply(pp) do |r| + r.exit_code.should_not == 1 + end + end + + describe file(mycnf) do + it { should contain 'replicate-do-db = base1' } + it { should contain 'replicate-do-db = base2' } + it { should contain 'replicate-do-db = base3' } + end + end + +end diff --git a/mysql/spec/system/types/mysql_grant_spec.rb b/mysql/spec/system/types/mysql_grant_spec.rb new file mode 100644 index 000000000..a3bdf8282 --- /dev/null +++ b/mysql/spec/system/types/mysql_grant_spec.rb @@ -0,0 +1,314 @@ +require 'spec_helper_system' + +describe 'mysql_grant' do + + describe 'setup' do + it 'setup mysql::server' do + pp = <<-EOS + class { 'mysql::server': } + EOS + + puppet_apply(pp) + end + end + + describe 'missing privileges for user' do + it 'should fail' do + pp = <<-EOS + mysql_grant { 'test1@tester/test.*': + ensure => 'present', + table => 'test.*', + user => 'test1@tester', + } + EOS + + puppet_apply(pp) do |r| + r.stderr.should =~ /privileges parameter is required/ + end + end + + it 'should not find the user' do + shell("mysql -NBe \"SHOW GRANTS FOR test1@tester\"") do |r| + r.stderr.should =~ /There is no such grant defined for user 'test1' on host 'tester'/ + r.exit_code.should eq 1 + end + end + end + + describe 'missing table for user' do + it 'should fail' do + pp = <<-EOS + mysql_grant { 'atest@tester/test.*': + ensure => 'present', + user => 'atest@tester', + privileges => ['ALL'], + } + EOS + + puppet_apply(pp) do |r| + r.exit_code.should eq 1 + end + end + + it 'should not find the user' do + shell("mysql -NBe \"SHOW GRANTS FOR atest@tester\"") do |r| + r.stderr.should =~ /There is no such grant defined for user 'atest' on host 'tester'/ + r.exit_code.should eq 1 + end + end + end + + describe 'adding privileges' do + it 'should work without errors' do + pp = <<-EOS + mysql_grant { 'test2@tester/test.*': + ensure => 'present', + table => 'test.*', + user => 'test2@tester', + privileges => ['SELECT', 'UPDATE'], + } + EOS + + puppet_apply(pp) + end + + it 'should find the user' do + shell("mysql -NBe \"SHOW GRANTS FOR test2@tester\"") do |r| + r.stdout.should =~ /GRANT SELECT, UPDATE.*TO 'test2'@'tester'/ + r.stderr.should be_empty + r.exit_code.should be_zero + end + end + end + + describe 'adding option' do + it 'should work without errors' do + pp = <<-EOS + mysql_grant { 'test3@tester/test.*': + ensure => 'present', + table => 'test.*', + user => 'test3@tester', + options => ['GRANT'], + privileges => ['SELECT', 'UPDATE'], + } + EOS + + puppet_apply(pp) + end + + it 'should find the user' do + shell("mysql -NBe \"SHOW GRANTS FOR test3@tester\"") do |r| + r.stdout.should =~ /GRANT SELECT, UPDATE ON `test`.* TO 'test3'@'tester' WITH GRANT OPTION$/ + r.stderr.should be_empty + r.exit_code.should be_zero + end + end + end + + describe 'adding all privileges without table' do + it 'should fail' do + pp = <<-EOS + mysql_grant { 'test4@tester/test.*': + ensure => 'present', + user => 'test4@tester', + options => ['GRANT'], + privileges => ['SELECT', 'UPDATE', 'ALL'], + } + EOS + + puppet_apply(pp) do |r| + r.stderr.should =~ /table parameter is required./ + end + end + + end + + + describe 'adding all privileges' do + it 'should only try to apply ALL' do + pp = <<-EOS + mysql_grant { 'test4@tester/test.*': + ensure => 'present', + table => 'test.*', + user => 'test4@tester', + options => ['GRANT'], + privileges => ['SELECT', 'UPDATE', 'ALL'], + } + EOS + + puppet_apply(pp) + end + + it 'should find the user' do + shell("mysql -NBe \"SHOW GRANTS FOR test4@tester\"") do |r| + r.stdout.should =~ /GRANT ALL PRIVILEGES ON `test`.* TO 'test4'@'tester' WITH GRANT OPTION/ + r.stderr.should be_empty + r.exit_code.should be_zero + end + end + end + + # Test combinations of user@host to ensure all cases work. + describe 'short hostname' do + it 'should apply' do + pp = <<-EOS + mysql_grant { 'test@short/test.*': + ensure => 'present', + table => 'test.*', + user => 'test@short', + privileges => 'ALL', + } + mysql_grant { 'test@long.hostname.com/test.*': + ensure => 'present', + table => 'test.*', + user => 'test@long.hostname.com', + privileges => 'ALL', + } + mysql_grant { 'test@192.168.5.6/test.*': + ensure => 'present', + table => 'test.*', + user => 'test@192.168.5.6', + privileges => 'ALL', + } + mysql_grant { 'test@2607:f0d0:1002:0051:0000:0000:0000:0004/test.*': + ensure => 'present', + table => 'test.*', + user => 'test@2607:f0d0:1002:0051:0000:0000:0000:0004', + privileges => 'ALL', + } + mysql_grant { 'test@::1/128/test.*': + ensure => 'present', + table => 'test.*', + user => 'test@::1/128', + privileges => 'ALL', + } + EOS + + puppet_apply(pp) + end + + it 'finds short hostname' do + shell("mysql -NBe \"SHOW GRANTS FOR test@short\"") do |r| + r.stdout.should =~ /GRANT ALL PRIVILEGES ON `test`.* TO 'test'@'short'/ + r.stderr.should be_empty + r.exit_code.should be_zero + end + end + it 'finds long hostname' do + shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'long.hostname.com'\"") do |r| + r.stdout.should =~ /GRANT ALL PRIVILEGES ON `test`.* TO 'test'@'long.hostname.com'/ + r.stderr.should be_empty + r.exit_code.should be_zero + end + end + it 'finds ipv4' do + shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'192.168.5.6'\"") do |r| + r.stdout.should =~ /GRANT ALL PRIVILEGES ON `test`.* TO 'test'@'192.168.5.6'/ + r.stderr.should be_empty + r.exit_code.should be_zero + end + end + it 'finds ipv6' do + shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'2607:f0d0:1002:0051:0000:0000:0000:0004'\"") do |r| + r.stdout.should =~ /GRANT ALL PRIVILEGES ON `test`.* TO 'test'@'2607:f0d0:1002:0051:0000:0000:0000:0004'/ + r.stderr.should be_empty + r.exit_code.should be_zero + end + end + it 'finds short ipv6' do + shell("mysql -NBe \"SHOW GRANTS FOR 'test'@'::1/128'\"") do |r| + r.stdout.should =~ /GRANT ALL PRIVILEGES ON `test`.* TO 'test'@'::1\/128'/ + r.stderr.should be_empty + r.exit_code.should be_zero + end + end + end + + describe 'complex test' do + it 'setup mysql::server' do + pp = <<-EOS + $dbSubnet = '10.10.10.%' + + mysql_database { 'foo': + ensure => present, + } + + exec { 'mysql-create-table': + command => '/usr/bin/mysql -NBe "CREATE TABLE foo.bar (name VARCHAR(20))"', + environment => "HOME=${::root_home}", + unless => '/usr/bin/mysql -NBe "SELECT 1 FROM foo.bar LIMIT 1;"', + require => Mysql_database['foo'], + } + + Mysql_grant { + ensure => present, + options => ['GRANT'], + privileges => ['ALL'], + table => '*.*', + require => [ Mysql_database['foo'], Exec['mysql-create-table'] ], + } + + mysql_grant { "user1@${dbSubnet}/*.*": + user => "user1@${dbSubnet}", + } + mysql_grant { "user2@${dbSubnet}/foo.bar": + privileges => ['SELECT', 'INSERT', 'UPDATE'], + user => "user2@${dbSubnet}", + table => 'foo.bar', + } + mysql_grant { "user3@${dbSubnet}/foo.*": + privileges => ['SELECT', 'INSERT', 'UPDATE'], + user => "user3@${dbSubnet}", + table => 'foo.*', + } + mysql_grant { 'web@%/*.*': + user => 'web@%', + } + mysql_grant { "web@${dbSubnet}/*.*": + user => "web@${dbSubnet}", + } + mysql_grant { "web@${fqdn}/*.*": + user => "web@${fqdn}", + } + mysql_grant { 'web@localhost/*.*': + user => 'web@localhost', + } + EOS + + puppet_apply(pp) do |r| + r.exit_code.should_not == 1 + r.refresh + r.exit_code.should be_zero + end + end + end + + describe 'lower case privileges' do + it 'create ALL privs' do + pp = <<-EOS + mysql_grant { 'lowercase@localhost/*.*': + user => 'lowercase@localhost', + privileges => 'ALL', + table => '*.*', + } + EOS + + puppet_apply(pp) + end + + it 'create lowercase all privs' do + pp = <<-EOS + mysql_grant { 'lowercase@localhost/*.*': + user => 'lowercase@localhost', + privileges => 'all', + table => '*.*', + } + EOS + + puppet_apply(pp) do |r| + r.exit_code.should be_zero + end + end + end + +end diff --git a/mysql/spec/system/types/mysql_user_spec.rb b/mysql/spec/system/types/mysql_user_spec.rb new file mode 100644 index 000000000..7aaa9797e --- /dev/null +++ b/mysql/spec/system/types/mysql_user_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper_system' + +describe 'mysql_user' do + + describe 'setup' do + it 'should work with no errors' do + pp = <<-EOS + class { 'mysql::server': } + EOS + + puppet_apply(pp) + end + end + + describe 'adding user' do + it 'should work without errors' do + pp = <<-EOS + mysql_user { 'ashp@localhost': + password_hash => '6f8c114b58f2ce9e', + } + EOS + + puppet_apply(pp) + end + + it 'should find the user' do + shell("mysql -NBe \"select '1' from mysql.user where CONCAT(user, '@', host) = 'ashp@localhost'\"") do |r| + r.stdout.should =~ /^1$/ + r.stderr.should be_empty + r.exit_code.should be_zero + end + end + end + +end diff --git a/mysql/spec/unit/mysql_password_spec.rb b/mysql/spec/unit/mysql_password_spec.rb new file mode 100644 index 000000000..073691004 --- /dev/null +++ b/mysql/spec/unit/mysql_password_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe 'the mysql_password function' do + before :all do + Puppet::Parser::Functions.autoloader.loadall + end + + let(:scope) { PuppetlabsSpec::PuppetInternals.scope } + + it 'should exist' do + Puppet::Parser::Functions.function('mysql_password').should == 'function_mysql_password' + end + + it 'should raise a ParseError if there is less than 1 arguments' do + lambda { scope.function_mysql_password([]) }.should( raise_error(Puppet::ParseError)) + end + + it 'should raise a ParseError if there is more than 1 arguments' do + lambda { scope.function_mysql_password(%w(foo bar)) }.should( raise_error(Puppet::ParseError)) + end + + it 'should convert password into a hash' do + result = scope.function_mysql_password(%w(password)) + result.should(eq('*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19')) + end + +end diff --git a/mysql/spec/unit/puppet/functions/mysql_deepmerge_spec.rb b/mysql/spec/unit/puppet/functions/mysql_deepmerge_spec.rb index 18cb26bfa..fa9c72b78 100644 --- a/mysql/spec/unit/puppet/functions/mysql_deepmerge_spec.rb +++ b/mysql/spec/unit/puppet/functions/mysql_deepmerge_spec.rb @@ -7,7 +7,7 @@ describe 'when calling mysql_deepmerge from puppet' do it "should not compile when no arguments are passed" do - skip("Fails on 2.6.x, see bug #15912") if Puppet.version =~ /^2\.6\./ + pending("Fails on 2.6.x, see bug #15912") if Puppet.version =~ /^2\.6\./ Puppet[:code] = '$x = mysql_deepmerge()' expect { scope.compiler.compile @@ -15,7 +15,7 @@ end it "should not compile when 1 argument is passed" do - skip("Fails on 2.6.x, see bug #15912") if Puppet.version =~ /^2\.6\./ + pending("Fails on 2.6.x, see bug #15912") if Puppet.version =~ /^2\.6\./ Puppet[:code] = "$my_hash={'one' => 1}\n$x = mysql_deepmerge($my_hash)" expect { scope.compiler.compile @@ -35,57 +35,57 @@ it 'should be able to mysql_deepmerge two hashes' do new_hash = scope.function_mysql_deepmerge([{'one' => '1', 'two' => '1'}, {'two' => '2', 'three' => '2'}]) - expect(new_hash['one']).to eq('1') - expect(new_hash['two']).to eq('2') - expect(new_hash['three']).to eq('2') + new_hash['one'].should == '1' + new_hash['two'].should == '2' + new_hash['three'].should == '2' end it 'should mysql_deepmerge multiple hashes' do hash = scope.function_mysql_deepmerge([{'one' => 1}, {'one' => '2'}, {'one' => '3'}]) - expect(hash['one']).to eq('3') + hash['one'].should == '3' end it 'should accept empty hashes' do - expect(scope.function_mysql_deepmerge([{},{},{}])).to eq({}) + scope.function_mysql_deepmerge([{},{},{}]).should == {} end it 'should mysql_deepmerge subhashes' do hash = scope.function_mysql_deepmerge([{'one' => 1}, {'two' => 2, 'three' => { 'four' => 4 } }]) - expect(hash['one']).to eq(1) - expect(hash['two']).to eq(2) - expect(hash['three']).to eq({ 'four' => 4 }) + hash['one'].should == 1 + hash['two'].should == 2 + hash['three'].should == { 'four' => 4 } end it 'should append to subhashes' do hash = scope.function_mysql_deepmerge([{'one' => { 'two' => 2 } }, { 'one' => { 'three' => 3 } }]) - expect(hash['one']).to eq({ 'two' => 2, 'three' => 3 }) + hash['one'].should == { 'two' => 2, 'three' => 3 } end it 'should append to subhashes 2' do hash = scope.function_mysql_deepmerge([{'one' => 1, 'two' => 2, 'three' => { 'four' => 4 } }, {'two' => 'dos', 'three' => { 'five' => 5 } }]) - expect(hash['one']).to eq(1) - expect(hash['two']).to eq('dos') - expect(hash['three']).to eq({ 'four' => 4, 'five' => 5 }) + hash['one'].should == 1 + hash['two'].should == 'dos' + hash['three'].should == { 'four' => 4, 'five' => 5 } end it 'should append to subhashes 3' do hash = scope.function_mysql_deepmerge([{ 'key1' => { 'a' => 1, 'b' => 2 }, 'key2' => { 'c' => 3 } }, { 'key1' => { 'b' => 99 } }]) - expect(hash['key1']).to eq({ 'a' => 1, 'b' => 99 }) - expect(hash['key2']).to eq({ 'c' => 3 }) + hash['key1'].should == { 'a' => 1, 'b' => 99 } + hash['key2'].should == { 'c' => 3 } end it 'should equate keys mod dash and underscore' do hash = scope.function_mysql_deepmerge([{ 'a-b-c' => 1 } , { 'a_b_c' => 10 }]) - expect(hash['a_b_c']).to eq(10) - expect(hash).not_to have_key('a-b-c') + hash['a_b_c'].should == 10 + hash.should_not have_key('a-b-c') end it 'should keep style of the last when keys are euqal mod dash and underscore' do hash = scope.function_mysql_deepmerge([{ 'a-b-c' => 1, 'b_c_d' => { 'c-d-e' => 2, 'e-f-g' => 3 }} , { 'a_b_c' => 10, 'b-c-d' => { 'c_d_e' => 12 } }]) - expect(hash['a_b_c']).to eq(10) - expect(hash).not_to have_key('a-b-c') - expect(hash['b-c-d']).to eq({ 'e-f-g' => 3, 'c_d_e' => 12 }) - expect(hash).not_to have_key('b_c_d') + hash['a_b_c'].should == 10 + hash.should_not have_key('a-b-c') + hash['b-c-d'].should == { 'e-f-g' => 3, 'c_d_e' => 12 } + hash.should_not have_key('b_c_d') end end end diff --git a/mysql/spec/unit/puppet/functions/mysql_password_spec.rb b/mysql/spec/unit/puppet/functions/mysql_password_spec.rb deleted file mode 100644 index 77726d13f..000000000 --- a/mysql/spec/unit/puppet/functions/mysql_password_spec.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'spec_helper' - -describe 'the mysql_password function' do - before :all do - Puppet::Parser::Functions.autoloader.loadall - end - - let(:scope) { PuppetlabsSpec::PuppetInternals.scope } - - it 'should exist' do - expect(Puppet::Parser::Functions.function('mysql_password')).to eq('function_mysql_password') - end - - it 'should raise a ParseError if there is less than 1 arguments' do - expect { scope.function_mysql_password([]) }.to( raise_error(Puppet::ParseError)) - end - - it 'should raise a ParseError if there is more than 1 arguments' do - expect { scope.function_mysql_password(%w(foo bar)) }.to( raise_error(Puppet::ParseError)) - end - - it 'should convert password into a hash' do - result = scope.function_mysql_password(%w(password)) - expect(result).to(eq('*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19')) - end - -end diff --git a/mysql/spec/unit/puppet/provider/database/mysql_spec.rb b/mysql/spec/unit/puppet/provider/database/mysql_spec.rb new file mode 100644 index 000000000..e2557fc35 --- /dev/null +++ b/mysql/spec/unit/puppet/provider/database/mysql_spec.rb @@ -0,0 +1,86 @@ +require 'spec_helper' + +provider_class = Puppet::Type.type(:database).provider(:mysql) + +describe provider_class do + subject { provider_class } + + let(:root_home) { '/root' } + let(:defaults_file) { '--defaults-extra-file=/root/.my.cnf' } + + let(:raw_databases) do + <<-SQL_OUTPUT +information_schema +mydb +mysql +performance_schema +test + SQL_OUTPUT + end + + let(:parsed_databases) { %w(information_schema mydb mysql performance_schema test) } + + before :each do + @resource = Puppet::Type::Database.new( + { :charset => 'utf8', :name => 'new_database' } + ) + @provider = provider_class.new(@resource) + Facter.stubs(:value).with(:root_home).returns(root_home) + Puppet::Util.stubs(:which).with('mysql').returns('/usr/bin/mysql') + subject.stubs(:which).with('mysql').returns('/usr/bin/mysql') + subject.stubs(:defaults_file).returns('--defaults-extra-file=/root/.my.cnf') + end + + describe 'self.instances' do + it 'returns an array of databases' do + subject.stubs(:mysql).with([defaults_file, '-NBe', 'show databases']).returns(raw_databases) + + databases = subject.instances.collect {|x| x.name } + parsed_databases.should match_array(databases) + end + end + + describe 'create' do + it 'makes a user' do + subject.expects(:mysql).with([defaults_file, '-NBe', "create database `#{@resource[:name]}` character set #{@resource[:charset]}"]) + @provider.create + end + end + + describe 'destroy' do + it 'removes a user if present' do + subject.expects(:mysqladmin).with([defaults_file, '-f', 'drop', "#{@resource[:name]}"]) + @provider.destroy + end + end + + describe 'charset' do + it 'returns a charset' do + subject.expects(:mysql).with([defaults_file, '-NBe', "show create database `#{@resource[:name]}`"]).returns('mydbCREATE DATABASE `mydb` /*!40100 DEFAULT CHARACTER SET utf8 */') + @provider.charset.should == 'utf8' + end + end + + describe 'charset=' do + it 'changes the charset' do + subject.expects(:mysql).with([defaults_file, '-NBe', "alter database `#{@resource[:name]}` CHARACTER SET blah"]).returns('0') + + @provider.charset=('blah') + end + end + + describe 'exists?' do + it 'checks if user exists' do + subject.expects(:mysql).with([defaults_file, '-NBe', 'show databases']).returns('information_schema\nmydb\nmysql\nperformance_schema\ntest') + @provider.exists? + end + end + + describe 'self.defaults_file' do + it 'sets --defaults-extra-file' do + File.stubs(:file?).with('#{root_home}/.my.cnf').returns(true) + @provider.defaults_file.should == '--defaults-extra-file=/root/.my.cnf' + end + end + +end diff --git a/mysql/spec/unit/puppet/provider/database_grant/mysql_spec.rb b/mysql/spec/unit/puppet/provider/database_grant/mysql_spec.rb new file mode 100644 index 000000000..4d9484d04 --- /dev/null +++ b/mysql/spec/unit/puppet/provider/database_grant/mysql_spec.rb @@ -0,0 +1,95 @@ +require 'puppet' +require 'mocha/api' +require 'spec_helper' +RSpec.configure do |config| + config.mock_with :mocha +end +provider_class = Puppet::Type.type(:database_grant).provider(:mysql) +describe provider_class do + let(:root_home) { '/root' } + + before :each do + @resource = Puppet::Type::Database_grant.new( + { :privileges => 'all', :provider => 'mysql', :name => 'user@host'} + ) + @provider = provider_class.new(@resource) + Facter.stubs(:value).with(:root_home).returns(root_home) + File.stubs(:file?).with("#{root_home}/.my.cnf").returns(true) + end + + it 'should query privileges from the database' do + provider_class.expects(:mysql) .with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-Be', 'describe user']).returns <<-EOT +Field Type Null Key Default Extra +Host char(60) NO PRI +User char(16) NO PRI +Password char(41) NO +Select_priv enum('N','Y') NO N +Insert_priv enum('N','Y') NO N +Update_priv enum('N','Y') NO N +EOT + provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-Be', 'describe db']).returns <<-EOT +Field Type Null Key Default Extra +Host char(60) NO PRI +Db char(64) NO PRI +User char(16) NO PRI +Select_priv enum('N','Y') NO N +Insert_priv enum('N','Y') NO N +Update_priv enum('N','Y') NO N +EOT + provider_class.user_privs.should == %w(Select_priv Insert_priv Update_priv) + provider_class.db_privs.should == %w(Select_priv Insert_priv Update_priv) + end + + it 'should query set privileges' do + provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-Be', "select * from mysql.user where user='user' and host='host'"]).returns <<-EOT +Host User Password Select_priv Insert_priv Update_priv +host user Y N Y +EOT + @provider.privileges.should == %w(Select_priv Update_priv) + end + + it 'should recognize when all privileges are set' do + provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-Be', "select * from mysql.user where user='user' and host='host'"]).returns <<-EOT +Host User Password Select_priv Insert_priv Update_priv +host user Y Y Y +EOT + @provider.all_privs_set?.should == true + end + + it 'should recognize when all privileges are not set' do + provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-Be', "select * from mysql.user where user='user' and host='host'"]).returns <<-EOT +Host User Password Select_priv Insert_priv Update_priv +host user Y N Y +EOT + @provider.all_privs_set?.should == false + end + + it 'should be able to set all privileges' do + provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-NBe', "SELECT '1' FROM user WHERE user='user' AND host='host'"]).returns "1\n" + provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-Be', "update user set Select_priv = 'Y', Insert_priv = 'Y', Update_priv = 'Y' where user='user' and host='host'"]) + provider_class.expects(:mysqladmin).with(%W(--defaults-extra-file=#{root_home}/.my.cnf flush-privileges)) + @provider.privileges=(%w(all)) + end + + it 'should be able to set partial privileges' do + provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-NBe', "SELECT '1' FROM user WHERE user='user' AND host='host'"]).returns "1\n" + provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-Be', "update user set Select_priv = 'Y', Insert_priv = 'N', Update_priv = 'Y' where user='user' and host='host'"]) + provider_class.expects(:mysqladmin).with(%W(--defaults-extra-file=#{root_home}/.my.cnf flush-privileges)) + @provider.privileges=(%w(Select_priv Update_priv)) + end + + it 'should be case insensitive' do + provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-NBe', "SELECT '1' FROM user WHERE user='user' AND host='host'"]).returns "1\n" + provider_class.expects(:mysql).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'mysql', '-Be', "update user set Select_priv = 'Y', Insert_priv = 'Y', Update_priv = 'Y' where user='user' and host='host'"]) + provider_class.expects(:mysqladmin).with(["--defaults-extra-file=#{root_home}/.my.cnf", 'flush-privileges']) + @provider.privileges=(%w(SELECT_PRIV insert_priv UpDaTe_pRiV)) + end + + it 'should not pass --defaults-extra-file if $root_home/.my.cnf is absent' do + File.stubs(:file?).with("#{root_home}/.my.cnf").returns(false) + provider_class.expects(:mysql).with(['mysql', '-NBe', "SELECT '1' FROM user WHERE user='user' AND host='host'"]).returns "1\n" + provider_class.expects(:mysql).with(['mysql', '-Be', "update user set Select_priv = 'Y', Insert_priv = 'N', Update_priv = 'Y' where user='user' and host='host'"]) + provider_class.expects(:mysqladmin).with(%w(flush-privileges)) + @provider.privileges=(%w(Select_priv Update_priv)) + end +end diff --git a/mysql/spec/unit/puppet/provider/database_user/mysql_spec.rb b/mysql/spec/unit/puppet/provider/database_user/mysql_spec.rb new file mode 100644 index 000000000..0939fdd22 --- /dev/null +++ b/mysql/spec/unit/puppet/provider/database_user/mysql_spec.rb @@ -0,0 +1,119 @@ +require 'spec_helper' + +provider_class = Puppet::Type.type(:database_user).provider(:mysql) + +describe provider_class do + subject { provider_class } + + let(:root_home) { '/root' } + let(:defaults_file) { '--defaults-extra-file=/root/.my.cnf' } + let(:newhash) { '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5' } + + let(:raw_users) do + <<-SQL_OUTPUT +root@127.0.0.1 +root@::1 +@localhost +debian-sys-maint@localhost +root@localhost +usvn_user@localhost +@vagrant-ubuntu-raring-64 + SQL_OUTPUT + end + + let(:parsed_users) { %w(root@127.0.0.1 root@::1 debian-sys-maint@localhost root@localhost usvn_user@localhost) } + + before :each do + # password hash = mypass + @resource = Puppet::Type::Database_user.new( + { :password_hash => '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4', + :name => 'joe@localhost', + :max_user_connections => '10' + } + ) + @provider = provider_class.new(@resource) + Facter.stubs(:value).with(:root_home).returns(root_home) + Puppet::Util.stubs(:which).with('mysql').returns('/usr/bin/mysql') + subject.stubs(:which).with('mysql').returns('/usr/bin/mysql') + subject.stubs(:defaults_file).returns('--defaults-extra-file=/root/.my.cnf') + end + + describe 'self.instances' do + it 'returns an array of users' do + subject.stubs(:mysql).with([defaults_file, 'mysql', "-BNeselect concat(User, '@',Host) as User from mysql.user"]).returns(raw_users) + + usernames = subject.instances.collect {|x| x.name } + parsed_users.should match_array(usernames) + end + end + + describe 'create' do + it 'makes a user' do + subject.expects(:mysql).with([defaults_file, 'mysql', '-e', "grant usage on *.* to 'joe'@'localhost' identified by PASSWORD + '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4' with max_user_connections 10"]) + @provider.expects(:exists?).returns(true) + @provider.create.should be_true + end + end + + describe 'destroy' do + it 'removes a user if present' do + subject.expects(:mysql).with([defaults_file, 'mysql', '-e', "drop user 'joe'@'localhost'"]) + @provider.expects(:exists?).returns(false) + @provider.destroy.should be_true + end + end + + describe 'password_hash' do + it 'returns a hash' do + subject.expects(:mysql).with([defaults_file, 'mysql', '-NBe', "select password from mysql.user where CONCAT(user, '@', host) = 'joe@localhost'"]).returns('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4') + @provider.password_hash.should == '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4' + end + end + + describe 'password_hash=' do + it 'changes the hash' do + subject.expects(:mysql).with([defaults_file, 'mysql', '-e', "SET PASSWORD FOR 'joe'@'localhost' = '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5'"]).returns('0') + + @provider.expects(:password_hash).returns('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5') + @provider.password_hash=('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF5') + end + end + + describe 'max_user_connections' do + it 'returns max user connections' do + subject.expects(:mysql).with([defaults_file, 'mysql', '-NBe', "select max_user_connections from mysql.user where CONCAT(user, '@', host) = 'joe@localhost'"]).returns('10') + @provider.max_user_connections.should == '10' + end + end + + describe 'max_user_connections=' do + it 'changes max user connections' do + subject.expects(:mysql).with([defaults_file, 'mysql', '-e', "grant usage on *.* to 'joe'@'localhost' with max_user_connections 42"]).returns('0') + @provider.expects(:max_user_connections).returns('42') + @provider.max_user_connections=('42') + end + end + + describe 'exists?' do + it 'checks if user exists' do + subject.expects(:mysql).with([defaults_file, 'mysql', '-NBe', "select '1' from mysql.user where CONCAT(user, '@', host) = 'joe@localhost'"]).returns('1') + @provider.exists?.should be_true + end + end + + describe 'flush' do + it 'removes cached privileges' do + subject.expects(:mysqladmin).with([defaults_file, 'flush-privileges']) + @provider.flush + end + end + + describe 'self.defaults_file' do + it 'sets --defaults-extra-file' do + File.stubs(:file?).with('#{root_home}/.my.cnf').returns(true) + @provider.defaults_file.should == '--defaults-extra-file=/root/.my.cnf' + end + end + +end diff --git a/mysql/spec/unit/puppet/provider/mysql_database/mysql_spec.rb b/mysql/spec/unit/puppet/provider/mysql_database/mysql_spec.rb index 465e59dd5..10f061f5d 100644 --- a/mysql/spec/unit/puppet/provider/mysql_database/mysql_spec.rb +++ b/mysql/spec/unit/puppet/provider/mysql_database/mysql_spec.rb @@ -43,7 +43,7 @@ provider.class.stubs(:mysql).with([defaults_file, '-NBe', "show variables like '%_database'", db.chomp]).returns("character_set_database latin1\ncollation_database latin1_swedish_ci\nskip_show_database OFF") end databases = provider.class.instances.collect {|x| x.name } - expect(parsed_databases).to match_array(databases) + parsed_databases.should match_array(databases) end end @@ -56,40 +56,40 @@ describe 'create' do it 'makes a database' do - provider.expects(:mysql).with([defaults_file, '-NBe', "create database if not exists `#{resource[:name]}` character set `#{resource[:charset]}` collate `#{resource[:collate]}`"]) + provider.expects(:mysql).with([defaults_file, '-NBe', "create database if not exists `#{resource[:name]}` character set #{resource[:charset]} collate #{resource[:collate]}"]) provider.expects(:exists?).returns(true) - expect(provider.create).to be_truthy + provider.create.should be_true end end describe 'destroy' do it 'removes a database if present' do - provider.expects(:mysql).with([defaults_file, '-NBe', "drop database if exists `#{resource[:name]}`"]) + provider.expects(:mysql).with([defaults_file, '-NBe', "drop database `#{resource[:name]}`"]) provider.expects(:exists?).returns(false) - expect(provider.destroy).to be_truthy + provider.destroy.should be_true end end describe 'exists?' do it 'checks if database exists' do - expect(instance.exists?).to be_truthy + instance.exists?.should be_true end end describe 'self.defaults_file' do it 'sets --defaults-extra-file' do File.stubs(:file?).with('/root/.my.cnf').returns(true) - expect(provider.defaults_file).to eq '--defaults-extra-file=/root/.my.cnf' + provider.defaults_file.should eq '--defaults-extra-file=/root/.my.cnf' end it 'fails if file missing' do File.stubs(:file?).with('/root/.my.cnf').returns(false) - expect(provider.defaults_file).to be_nil + provider.defaults_file.should be_nil end end describe 'charset' do it 'returns a charset' do - expect(instance.charset).to eq('latin1') + instance.charset.should == 'latin1' end end @@ -103,7 +103,7 @@ describe 'collate' do it 'returns a collate' do - expect(instance.collate).to eq('latin1_swedish_ci') + instance.collate.should == 'latin1_swedish_ci' end end diff --git a/mysql/spec/unit/puppet/provider/mysql_user/mysql_spec.rb b/mysql/spec/unit/puppet/provider/mysql_user/mysql_spec.rb index dacbae4b0..34639c920 100644 --- a/mysql/spec/unit/puppet/provider/mysql_user/mysql_spec.rb +++ b/mysql/spec/unit/puppet/provider/mysql_user/mysql_spec.rb @@ -50,7 +50,7 @@ end usernames = provider.class.instances.collect {|x| x.name } - expect(parsed_users).to match_array(usernames) + parsed_users.should match_array(usernames) end end @@ -65,7 +65,7 @@ it 'makes a user' do provider.expects(:mysql).with([defaults_file, '-e', "GRANT USAGE ON *.* TO 'joe'@'localhost' IDENTIFIED BY PASSWORD '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4' WITH MAX_USER_CONNECTIONS 10 MAX_CONNECTIONS_PER_HOUR 10 MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 10"]) provider.expects(:exists?).returns(true) - expect(provider.create).to be_truthy + provider.create.should be_true end end @@ -73,30 +73,30 @@ it 'removes a user if present' do provider.expects(:mysql).with([defaults_file, '-e', "DROP USER 'joe'@'localhost'"]) provider.expects(:exists?).returns(false) - expect(provider.destroy).to be_truthy + provider.destroy.should be_true end end describe 'exists?' do it 'checks if user exists' do - expect(instance.exists?).to be_truthy + instance.exists?.should be_true end end describe 'self.defaults_file' do it 'sets --defaults-extra-file' do File.stubs(:file?).with('/root/.my.cnf').returns(true) - expect(provider.defaults_file).to eq '--defaults-extra-file=/root/.my.cnf' + provider.defaults_file.should eq '--defaults-extra-file=/root/.my.cnf' end it 'fails if file missing' do File.expects(:file?).with('/root/.my.cnf').returns(false) - expect(provider.defaults_file).to be_nil + provider.defaults_file.should be_nil end end describe 'password_hash' do it 'returns a hash' do - expect(instance.password_hash).to eq('*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4') + instance.password_hash.should == '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4' end end @@ -114,7 +114,7 @@ describe property do it "returns #{property}" do - expect(instance.send("#{property}".to_sym)).to eq('10') + instance.send("#{property}".to_sym).should == '10' end end diff --git a/mysql/spec/unit/puppet/type/mysql_database_spec.rb b/mysql/spec/unit/puppet/type/mysql_database_spec.rb index 7897d8109..e2ebd90d4 100644 --- a/mysql/spec/unit/puppet/type/mysql_database_spec.rb +++ b/mysql/spec/unit/puppet/type/mysql_database_spec.rb @@ -7,17 +7,17 @@ end it 'should accept a database name' do - expect(@user[:name]).to eq('test') + @user[:name].should == 'test' end it 'should accept a charset' do @user[:charset] = 'latin1' - expect(@user[:charset]).to eq('latin1') + @user[:charset].should == 'latin1' end it 'should accept a collate' do @user[:collate] = 'latin1_swedish_ci' - expect(@user[:collate]).to eq('latin1_swedish_ci') + @user[:collate].should == 'latin1_swedish_ci' end it 'should require a name' do diff --git a/mysql/spec/unit/puppet/type/mysql_grant_spec.rb b/mysql/spec/unit/puppet/type/mysql_grant_spec.rb index 9b33058bc..d56ba241a 100644 --- a/mysql/spec/unit/puppet/type/mysql_grant_spec.rb +++ b/mysql/spec/unit/puppet/type/mysql_grant_spec.rb @@ -7,32 +7,32 @@ end it 'should accept a grant name' do - expect(@user[:name]).to eq('foo@localhost/*.*') + @user[:name].should == 'foo@localhost/*.*' end it 'should accept ALL privileges' do @user[:privileges] = 'ALL' - expect(@user[:privileges]).to eq(['ALL']) + @user[:privileges].should == ['ALL'] end it 'should accept PROXY privilege' do @user[:privileges] = 'PROXY' - expect(@user[:privileges]).to eq(['PROXY']) + @user[:privileges].should == ['PROXY'] end it 'should accept a table' do @user[:table] = '*.*' - expect(@user[:table]).to eq('*.*') + @user[:table].should == '*.*' end it 'should accept @ for table' do @user[:table] = '@' - expect(@user[:table]).to eq('@') + @user[:table].should == '@' end it 'should accept a user' do @user[:user] = 'foo@localhost' - expect(@user[:user]).to eq('foo@localhost') + @user[:user].should == 'foo@localhost' end it 'should require a name' do @@ -41,34 +41,4 @@ }.to raise_error(Puppet::Error, 'Title or name must be provided') end - it 'should require the name to match the user and table' do - expect { - Puppet::Type.type(:mysql_grant).new(:name => 'foo', :privileges => ['ALL', 'PROXY'], :table => ['*.*','@'], :user => 'foo@localhost') - }.to raise_error /name must match user and table parameters/ - end - - describe 'it should munge privileges' do - - it 'to just ALL' do - @user = Puppet::Type.type(:mysql_grant).new( - :name => 'foo@localhost/*.*', :table => ['*.*','@'], :user => 'foo@localhost', - :privileges => ['ALL', 'PROXY'] ) - expect(@user[:privileges]).to eq(['ALL']) - end - - it 'to upcase and ordered' do - @user = Puppet::Type.type(:mysql_grant).new( - :name => 'foo@localhost/*.*', :table => ['*.*','@'], :user => 'foo@localhost', - :privileges => ['select', 'Insert'] ) - expect(@user[:privileges]).to eq(['INSERT', 'SELECT']) - end - - it 'ordered including column privileges' do - @user = Puppet::Type.type(:mysql_grant).new( - :name => 'foo@localhost/*.*', :table => ['*.*','@'], :user => 'foo@localhost', - :privileges => ['SELECT(Host,Address)', 'Insert'] ) - expect(@user[:privileges]).to eq(['INSERT', 'SELECT (Address, Host)']) - end - end - -end +end \ No newline at end of file diff --git a/mysql/spec/unit/puppet/type/mysql_user_spec.rb b/mysql/spec/unit/puppet/type/mysql_user_spec.rb index f66741c99..62aad7aef 100644 --- a/mysql/spec/unit/puppet/type/mysql_user_spec.rb +++ b/mysql/spec/unit/puppet/type/mysql_user_spec.rb @@ -2,10 +2,23 @@ require 'puppet/type/mysql_user' describe Puppet::Type.type(:mysql_user) do + before :each do + @user = Puppet::Type.type(:mysql_user).new(:name => 'foo@localhost', :password_hash => 'pass') + end + + it 'should accept a user name' do + @user[:name].should == 'foo@localhost' + end + it 'should fail with a long user name' do expect { Puppet::Type.type(:mysql_user).new({:name => '12345678901234567@localhost', :password_hash => 'pass'}) - }.to raise_error /MySQL usernames are limited to a maximum of 16 characters/ + }.to raise_error /MySQL usernames are limited to a maximum of 16 characters/ + end + + it 'should accept a password' do + @user[:password_hash] = 'foo' + @user[:password_hash].should == 'foo' end it 'should require a name' do @@ -14,29 +27,4 @@ }.to raise_error(Puppet::Error, 'Title or name must be provided') end - context 'using foo@localhost' do - before :each do - @user = Puppet::Type.type(:mysql_user).new(:name => 'foo@localhost', :password_hash => 'pass') - end - - it 'should accept a user name' do - expect(@user[:name]).to eq('foo@localhost') - end - - it 'should accept a password' do - @user[:password_hash] = 'foo' - expect(@user[:password_hash]).to eq('foo') - end - end - - context 'using foo@LocalHost' do - before :each do - @user = Puppet::Type.type(:mysql_user).new(:name => 'foo@LocalHost', :password_hash => 'pass') - end - - it 'should lowercase the user name' do - expect(@user[:name]).to eq('foo@localhost') - end - - end end diff --git a/mysql/templates/my.cnf.erb b/mysql/templates/my.cnf.erb index f84f45c3b..5aa959ba0 100644 --- a/mysql/templates/my.cnf.erb +++ b/mysql/templates/my.cnf.erb @@ -2,21 +2,17 @@ <% if v.is_a?(Hash) -%> [<%= k %>] <% v.sort.map do |ki, vi| -%> -<% if ki == 'ssl-disable' or (ki =~ /^ssl/ and v['ssl-disable'] == true) -%> -<% next %> -<% elsif vi == true or v == '' -%> +<% if vi == true or v == '' -%> <%= ki %> <% elsif vi.is_a?(Array) -%> <% vi.each do |vii| -%> <%= ki %> = <%= vii %> <% end -%> -<% elsif ![nil, '', :undef].include?(vi) -%> +<% elsif vi != :undef -%> <%= ki %> = <%= vi %> <% end -%> <% end -%> <% end %> <% end -%> -<% if @includedir and @includedir != '' %> -!includedir <%= @includedir %> -<% end %> +!includedir /etc/mysql/conf.d/ diff --git a/mysql/templates/mysqlbackup.sh.erb b/mysql/templates/mysqlbackup.sh.erb index e3ab7cc28..ff38b23af 100755 --- a/mysql/templates/mysqlbackup.sh.erb +++ b/mysql/templates/mysqlbackup.sh.erb @@ -11,7 +11,7 @@ ##### START CONFIG ################################################### USER=<%= @backupuser %> -PASS='<%= @backuppassword %>' +PASS=<%= @backuppassword %> DIR=<%= @backupdir %> ROTATE=<%= [ Integer(@backuprotate) - 1, 0 ].max %> @@ -23,7 +23,7 @@ EVENTS="--events" <% end %> ##### STOP CONFIG #################################################### -PATH=<%= @execpath %> +PATH=/usr/bin:/usr/sbin:/bin:/sbin @@ -40,7 +40,7 @@ cleanup <% end -%> <% if @backupdatabases.empty? -%> <% if @file_per_database -%> -mysql -u${USER} -p${PASS} -s -r -N -e 'SHOW DATABASES' | while read dbname +mysql -s -r -N -e 'SHOW DATABASES' | while read dbname do mysqldump -u${USER} -p${PASS} --opt --flush-logs --single-transaction \ ${EVENTS} \ @@ -53,7 +53,7 @@ mysqldump -u${USER} -p${PASS} --opt --flush-logs --single-transaction \ <% end -%> <% else -%> <% @backupdatabases.each do |db| -%> -mysqldump -u${USER} -p${PASS} --opt --flush-logs --single-transaction \ +mysqldump -u${USER} -p${PASS} --opt --flush-logs --single-transaction \ ${EVENTS} \ <%= db %><% if @backupcompress %>| bzcat -zc <% end %>> ${DIR}/${PREFIX}<%= db %>_`date +%Y%m%d-%H%M%S`.sql<% if @backupcompress %>.bz2<% end %> <% end -%> diff --git a/mysql/tests/mysql_database.pp b/mysql/tests/mysql_database.pp index 1ba24dfd9..8747f707d 100644 --- a/mysql/tests/mysql_database.pp +++ b/mysql/tests/mysql_database.pp @@ -10,8 +10,3 @@ ensure => present, charset => 'latin1', } -database{ 'test5': - ensure => present, - charset => 'binary', - collate => 'binary', -} diff --git a/mysql/tests/mysql_db.pp b/mysql/tests/mysql_db.pp deleted file mode 100644 index 1bb3592e4..000000000 --- a/mysql/tests/mysql_db.pp +++ /dev/null @@ -1,17 +0,0 @@ -class { 'mysql::server': - config_hash => {'root_password' => 'password'} -} -mysql::db { 'mydb': - user => 'myuser', - password => 'mypass', - host => 'localhost', - grant => ['SELECT', 'UPDATE'], -} -mysql::db { "mydb_${fqdn}": - user => 'myuser', - password => 'mypass', - dbname => 'mydb', - host => $::fqdn, - grant => ['SELECT', 'UPDATE'], - tag => $domain, -} diff --git a/mysql/tests/mysql_user.pp b/mysql/tests/mysql_user.pp index 70a6e1672..f63908431 100644 --- a/mysql/tests/mysql_user.pp +++ b/mysql/tests/mysql_user.pp @@ -6,18 +6,18 @@ } } -mysql_user{ 'redmine@localhost': +database_user{ 'redmine@localhost': ensure => present, password_hash => mysql_password('redmine'), require => Class['mysql::server'], } -mysql_user{ 'dan@localhost': +database_user{ 'dan@localhost': ensure => present, password_hash => mysql_password('blah') } -mysql_user{ 'dan@%': +database_user{ 'dan@%': ensure => present, password_hash => mysql_password('blah'), } diff --git a/n1k-vsm/README.md b/n1k-vsm/README.md deleted file mode 100644 index 54f9b892f..000000000 --- a/n1k-vsm/README.md +++ /dev/null @@ -1,4 +0,0 @@ -puppet-n1k-vsm -============== - -VSM deployment code for N1k. diff --git a/n1k-vsm/files/repackiso.py b/n1k-vsm/files/repackiso.py deleted file mode 100755 index f768fa835..000000000 --- a/n1k-vsm/files/repackiso.py +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/python -import shutil, tempfile, os, optparse, logging -import sys - -usage = "usage: %prog [options]" -parser = optparse.OptionParser(usage=usage) -parser.add_option("-i", "--isofile", help="ISO image", dest="isoimg") -parser.add_option("-d", "--domainid", help="Domain id ", dest="domainid") -parser.add_option("-n", "--vsmname", help="VSM name", dest="vsmname") -parser.add_option("-m", "--mgmtip", help="Management Ip address", dest="mgmtip") -parser.add_option("-s", "--mgmtsubnet", help="Management Subnet", dest="mgmtsubnet") -parser.add_option("-g", "--gateway", help="Management gateway", dest="mgmtgateway") -parser.add_option("-p", "--password", help="Admin account password", dest="adminpasswd") -parser.add_option("-r", "--vsmrole", help="VSM Role, primary ,secondary or standalone", dest="vsmrole") -parser.add_option("-f", "--file", help="Repackaged file", dest="repackediso") -(options, args) = parser.parse_args() - -isoimg = options.isoimg -domainid = int(options.domainid) -vsmname = options.vsmname -mgmtip = options.mgmtip -mgmtsubnet = options.mgmtsubnet -mgmtgateway = options.mgmtgateway -adminpasswd = options.adminpasswd -vsmrole = options.vsmrole -repackediso = options.repackediso - - -class Command(object): - """Run a command and capture it's output string, error string and exit status""" - def __init__(self, command): - self.command = command - - def run(self, shell=True): - import subprocess as sp - process = sp.Popen(self.command, shell = shell, stdout = sp.PIPE, stderr = sp.PIPE) - self.pid = process.pid - self.output, self.error = process.communicate() - self.failed = process.returncode - return self - - @property - def returncode(self): - return self.failed - -def createOvfEnvXmlFile(domain, gateway, hostname, ip, subnet, password, vsm_mode): - #TODO: write a proper xml - ovf_f = tempfile.NamedTemporaryFile(delete=False) - - st = ' \n' - st += ' $ctrlbridge, -# ensure => present -# } -# -# tapint {"$mgmttap": -# bridge => $mgmtbridge, -# ensure => present -# } -# -# tapint {"$pkttap": -# bridge => $pktbridge, -# ensure => present -# } - - - $diskfile = "/var/spool/vsm/${n1k_vsm::role}_disk" - - exec { "Exec_create_disk": - command => "/usr/bin/qemu-img create -f raw $diskfile ${n1k_vsm::disksize}G", - unless => "/usr/bin/virsh list | grep -c ' ${n1k_vsm::vsmname} .* running'", - } - -> - exec {"Debug_Exec_create_disk_debug": - command => "${n1k_vsm::Debug_Print} \"[INFO]\nExec_create_disk /usr/bin/qemu-img create -f raw $diskfile ${n1k_vsm::disksize}G\" >> ${n1k_vsm::Debug_Log}", - } - - $targetxmlfile = "/var/spool/vsm/vsm_${n1k_vsm::role}_deploy.xml" - file { "File_Target_XML_File": - path => "$targetxmlfile", - owner => 'root', - group => 'root', - mode => '666', - content => template('n1k_vsm/vsm_vm.xml.erb'), - require => Exec["Exec_create_disk"], - } - -> - exec {"Debug_File_Target_XML_FILE": - command => "${n1k_vsm::Debug_Print} \"[INFO]\nFile_Target_XML_File\n path=$targetxmlfile \n owner=root \n group=root \n mode=666 \n\" >> ${n1k_vsm::Debug_Log}", - } - - exec { "Exec_Create_VSM": - command => "/usr/bin/virsh define $targetxmlfile", - unless => "/usr/bin/virsh list | grep -c ' ${n1k_vsm::vsmname} .* running'", - } - -> - exec {"Debug_Exec_Create_VSM": - command => "${n1k_vsm::Debug_Print} \"[INFO]\nExec_Launch_VSM \n command=/bin/echo /usr/bin/virsh define $targetxmlfile \n unless=/usr/bin/virsh list --all | grep -c ' ${n1k_vsm::vsmname} ' \" >> ${n1k_vsm::Debug_Log}", - } - - exec { "Exec_Launch_VSM": - command => "/usr/bin/virsh start ${n1k_vsm::vsmname}", - unless => "/usr/bin/virsh list --all | grep -c ' ${n1k_vsm::vsmname} .* running '", - } - -> - exec {"Debug_Exec_Launch_VSM": - command => "${n1k_vsm::Debug_Print} \"[INFO]\nExec_Launch_VSM \n command=/bin/echo /usr/bin/virsh start ${n1k_vsm::vsmname} \n unless=/usr/bin/virsh list --all | grep -c ' ${n1k_vsm::vsmname} .* running' \" >> ${n1k_vsm::Debug_Log}", - } - - Exec["Exec_create_disk"] -> File["File_Target_XML_File"] -> Exec["Exec_Create_VSM"] -> Exec["Exec_Launch_VSM"] -} - diff --git a/n1k-vsm/manifests/init.pp b/n1k-vsm/manifests/init.pp deleted file mode 100644 index b546f9644..000000000 --- a/n1k-vsm/manifests/init.pp +++ /dev/null @@ -1,46 +0,0 @@ -class n1k_vsm( - $configureovs = false, - $ovsbridge, - $physicalinterfaceforovs = 'enp1s0f0', - $nodeip, - $nodenetmask, - $nodegateway, - $vsmname, - $consolepts = 2, - $role = 'primary', - $domainid, - $adminpasswd, - $mgmtip, - $mgmtnetmask, - $mgmtgateway, - $ctrlinterface, - $mgmtinterface, - $pktinterface, - $memory = 4096000, - $vcpu = 2, - $disksize = 4, - $n1kv_source = "puppet:///modules/n1k_vsm/vsm.iso", - $n1kv_version = "latest", - ) -{ - - $imgfile = "/var/spool/vsm/${role}_repacked.iso" - $diskfile = "/var/spool/vsm/${role}_disk" - - $Debug_Print = "/usr/bin/printf" - $Debug_Log = "/tmp/n1kv_vsm_puppet.log" - - # - # Clean up debug log - # - file {"File_$Debug_Log": - path => $Debug_Log, - ensure => "absent", - } - - include n1k_vsm::pkgprep_ovscfg - include n1k_vsm::vsmprep - include n1k_vsm::deploy - - File["File_$Debug_Log"] -> Class['n1k_vsm::pkgprep_ovscfg'] -> Class['n1k_vsm::vsmprep'] -> Class['n1k_vsm::deploy'] -} diff --git a/n1k-vsm/manifests/pkgprep_ovscfg.pp b/n1k-vsm/manifests/pkgprep_ovscfg.pp deleted file mode 100644 index 9ee8cb925..000000000 --- a/n1k-vsm/manifests/pkgprep_ovscfg.pp +++ /dev/null @@ -1,264 +0,0 @@ -class n1k_vsm::pkgprep_ovscfg { - - # Definition of sync points - - $Sync_Point_KVM = "##SYNC_POINT_KVM" - $Sync_Point_Virsh_Network = "##SYNC_POINT_VIRSH_NETWORK" - - case "$::osfamily" { - "RedHat": { - # - # Order indepedent resources - # - service {"Service_network": - name => "network", - ensure => "running", - restart => "/sbin/service network restart || /bin/true", - } - -> - exec {"Debug_Service_network": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Service_network\n name=network\n ensure=running\n enable=true\n restart=/sbin/service network restart\n\" >> ${n1k_vsm::Debug_Log}", - } - # VSM dependent packages installation section - # - # Eng note - # cwchang: Ideally we should have either of this logic - # 1. Have an iteration thru the package list in the $pkgs.each ... - # Somehow this syntax needs to turn on future parser by document - # 2. package resource should be able to run a name list - # Neither one works. We go for rudimentary one-by-one here for now. - # Pitfalls observed: - # 1. We cannot reassign variables for some reason - # 2. We cannot leave spaces in name - # qemu-kvm-rhev - package {"Package_qemu-kvm": - name => "qemu-kvm", - ensure => "installed", - before => Notify["$Sync_Point_KVM"], - } - -> - exec {"Debug_Package_qemu-kvm": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Package_qemu-kvm \n name=qemu-kvm \n ensure=installed\n\" >> ${n1k_vsm::Debug_Log}", - } - - package {"Package_virt-viewer": - name => "virt-viewer", - ensure => "installed", - before => Notify["$Sync_Point_KVM"], - } - -> - exec {"Debug_Package_virt-viewer": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Package_virt-viewer \n name=virt-viewer \n ensure=installed \n\" >> ${n1k_vsm::Debug_Log}", - } - - package {"Package_virt-manager": - name => "virt-manager", - ensure => "installed", - before => Notify["$Sync_Point_KVM"], - } - -> - exec {"Debug_Package_virt-manager": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Package_virt-manager \n name=virt-manager \n ensure=installed\n\" >> ${n1k_vsm::Debug_Log}", - } - - package {"Package_libvirt": - name => "libvirt", - ensure => "installed", - before => Notify["$Sync_Point_KVM"], - } - -> - exec {"Debug_Package_libvirt": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Package_libvirt \n name=libvirt \n ensure=installed\n\" >> ${n1k_vsm::Debug_Log}", - } - - package {"Package_libvirt-python": - name => "libvirt-python", - ensure => "installed", - before => Notify["$Sync_Point_KVM"], - } - -> - exec {"Debug_Package_libvirt-python": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Package_libvirt-python \n name=libvirt-python \n ensure=installed\n\" >> ${n1k_vsm::Debug_Log}", - } - - #package {"Package_python-virtinst": - # name => "python-virtinst", - # ensure => "installed", - # before => Notify["$Sync_Point_KVM"], - #} - #-> - #exec {"Debug_Package_python-virtinst": - # command => "${n1k_vsm::Debug_Print} \"[INFO]\n Package_python-virtinst \n name=python-virtinst \n ensure=installed \n\" >> ${n1k_vsm::Debug_Log}", - #} - - #package {"Package_genisoimage": - # name => "genisoimage", - # ensure => "installed", - # before => Notify["$Sync_Point_KVM"], - #} - #-> - #exec {"Debug_Package_genisoimage": - # command => "${n1k_vsm::Debug_Print} \"[INFO]\n Package_genisoimage \n name=genisoimage \n ensure=installed \n\" >> ${n1k_vsm::Debug_Log}", - #} - - package {"Package_ebtables": - name => "ebtables", - #ensure => "purged", - ensure => "installed", - before => Notify["$Sync_Point_KVM"], - } - -> - exec {"Debug_Package_ebtables": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Package_ebtables \n name=ebtables \n ensure=purged \n\" >> ${n1k_vsm::Debug_Log}", - } - - notify{"$Sync_Point_KVM":} - - service {"Service_libvirtd": - name => "libvirtd", - ensure => "running", - } - -> - exec {"Debug_Service_libvirtd": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Service_libvirtd\n name=libvirtd \n ensure=running \n\" >> ${n1k_vsm::Debug_Log}", - } - - # - # Virsh network exec configuration section - # - exec {"Exec_removenet": - command => "/usr/bin/virsh net-destroy default || /bin/true", - unless => "/usr/bin/virsh net-info default | /bin/grep -c 'Active: .* no'", - before => Notify["$Sync_Point_Virsh_Network"], - } - -> - exec {"Debug_Exec_removenet": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Exec_removenet \n command=/usr/bin/virsh net-destroy default || /bin/true \n unless=/usr/bin/virsh net-info default | /bin/grep -c 'Active: .* no'\n\" >> ${n1k_vsm::Debug_Log}", - } - - exec {"Exec_disableautostart": - command => "/usr/bin/virsh net-autostart --disable default || /bin/true", - unless => "/usr/bin/virsh net-info default | /bin/grep -c 'Autostart: .* no'", - before => Notify["$Sync_Point_Virsh_Network"], - } - -> - exec {"Debug_Exec_disableautostart": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Exec_disableautostart' \n command=/usr/bin/virsh net-autostart --disable default || /bin/true \n unless /usr/bin/virsh net-info default | grep -c 'Autostart: .* no'\" >> ${n1k_vsm::Debug_Log}", - } - - notify{"$Sync_Point_Virsh_Network":} - - package {"Package_openvswitch": - name => "openvswitch", - ensure => "installed", - } - -> - exec {"Debug_Package_openvswitch": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Package_openvswitch \n name=openvswitch \n ensure=installed\n\" >> ${n1k_vsm::Debug_Log}", - } - # - # bring up OVS and perform interface configuration - # - - service {"Service_openvswitch": - name => "openvswitch", - ensure => "running", - enable => "true", - } - -> - exec {"Debug_Service_openvswitch": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Service_openvswitch \n name=openvswitch \n ensure=running \n enable=true\n\" >> ${n1k_vsm::Debug_Log}", - } - - - exec {"Exec_AddOvsBr": - command => "/usr/bin/ovs-vsctl -- --may-exist add-br $n1k_vsm::ovsbridge", - } - -> - exec {"Debug_Exec_AddOvsBr": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Exec_AddOvsBr \n command=/usr/bin/ovs-vsctl -- --may-exist add-br $n1k_vsm::ovsbridge \n \" >> ${n1k_vsm::Debug_Log}", - } - - # - # Modify Ovs bridge inteface configuation file - # - augeas {"Augeas_modify_ifcfg-ovsbridge": - name => "$n1k_vsm::ovsbridge", - context => "/files/etc/sysconfig/network-scripts/ifcfg-$n1k_vsm::ovsbridge", - changes => [ - "set DEVICE $n1k_vsm::ovsbridge", - "set BOOTPROTO none", - "set IPADDR $n1k_vsm::nodeip", - "set NETMASK $n1k_vsm::nodenetmask", - "set ONBOOT yes", - "set TYPE OVSBridge", - "set DEVICETYPE ovs", - ], - notify => Service["Service_network"], - } - -> - exec {"Debug_Augeas_modify_ifcfg-ovsbridge": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Augeas_modify_ifcfg-$n1k_vsm::ovsbridge \n name=$n1k_vsm::ovsbridge \n context=/files/etc/sysconfig/network-scripts/ifcfg-$n1k_vsm::ovsbridge \n\" >> ${n1k_vsm::Debug_Log}", - } - - # - # Modify Physical Interface config file - # - augeas {"Augeas_modify_ifcfg-physicalinterfaceforovs": - name => "$n1k_vsm::physicalinterfaceforovs", - context => "/files/etc/sysconfig/network-scripts/ifcfg-$n1k_vsm::physicalinterfaceforovs", - changes => [ - "set ONBOOT yes", - "set BOOTPROTO none", - "set TYPE OVSPort", - "set DEVICETYPE ovs", - "set OVS_BRIDGE $n1k_vsm::ovsbridge", - "rm IPADDR", - "rm NETMASK", - ], - } - -> - exec {"Debug_Augeas_modify_ifcfg-physicalinterfaceforovs": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Augeas_modify_ifcfg-physicalinterfaceforovs \n name=$n1k_vsm::physicalinterfaceforovs \n context=/files/etc/sysconfig/network-scripts/ifcfg-$n1k_vsm::physicalinterfaceforovs\n\" >> ${n1k_vsm::Debug_Log}", - } - - $intf=$n1k_vsm::physicalinterfaceforovs - $phy_bridge="/tmp/phy_bridge" - # - # Move physical port around from host bridge if any, to ovs bridge - # - #exec {"Exec_phy_bridge": - # command => "/usr/sbin/brctl show | /bin/grep $intf | /bin/sed 's/[\t ].*//' > $phy_bridge", - #} - #-> - #exec {"Debug_Exec_phy_bridge": - # command => "${n1k_vsm::Debug_Print} \"[INFO]\n Exec_phy_bridge \n /usr/sbin/brctl show | /bin/grep $intf | /bin/sed 's/[\t ].*//' > $phy_bridge \n \" >> ${n1k_vsm::Debug_Log}", - #} - - exec {"Exec_rebridge": - #command => "/usr/bin/test -s $phy_bridge && /usr/sbin/brctl delif \$(cat $phy_bridge) $intf || /bin/true; /usr/bin/ovs-vsctl -- --may-exist add-port $n1k_vsm::ovsbridge $intf", - command => "/usr/bin/ovs-vsctl -- --may-exist add-port $n1k_vsm::ovsbridge $intf", - #notify => Service["Service_network"], - } - -> - exec {"Debug_Exec_rebridge": - #command => "${n1k_vsm::Debug_Print} \"[INFO]\n Exec_rebridge \n /usr/bin/test -s $phy_bridge && /usr/sbin/brctl delif \$(cat $phy_bridge) $intf || /bin/true; /usr/bin/ovs-vsctl -- --may-exist add-port $n1k_vsm::ovsbridge $intf; /bin/rm -f $phy_bridge \n\" >> ${n1k_vsm::Debug_Log}", - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Exec_rebridge \n /usr/bin/ovs-vsctl -- --may-exist add-port $n1k_vsm::ovsbridge $intf \n\" >> ${n1k_vsm::Debug_Log}", - } - - # - # Order enforcement logic - # - #Notify["$Sync_Point_KVM"] -> Service["Service_libvirtd"] -> Notify["$Sync_Point_Virsh_Network"] -> Package["Package_openvswitch"] -> Service["Service_openvswitch"] -> Exec["Exec_AddOvsBr"]->Augeas["Augeas_modify_ifcfg-ovsbridge"]->Augeas["Augeas_modify_ifcfg-physicalinterfaceforovs"]->Exec["Exec_phy_bridge"]->Exec["Exec_rebridge"] - Notify["$Sync_Point_KVM"] -> Service["Service_libvirtd"] -> Notify["$Sync_Point_Virsh_Network"] -> Package["Package_openvswitch"] -> Service["Service_openvswitch"] -> Exec["Exec_AddOvsBr"]->Augeas["Augeas_modify_ifcfg-ovsbridge"]->Augeas["Augeas_modify_ifcfg-physicalinterfaceforovs"]->Exec["Exec_rebridge"] - } - "Ubuntu": { - } - default: { - # - # bail out for unsupported OS - # - fail(": os[$os] is not supported") - } - } -} diff --git a/n1k-vsm/manifests/vsmprep.pp b/n1k-vsm/manifests/vsmprep.pp deleted file mode 100644 index 4c9ae8695..000000000 --- a/n1k-vsm/manifests/vsmprep.pp +++ /dev/null @@ -1,162 +0,0 @@ -class n1k_vsm::vsmprep { - include 'stdlib' - - # - # VSM package source parsing logic - # - $source = $n1k_vsm::n1kv_source - - $source_method = regsubst($source, "^(.+):.*", '\1') - $dest = inline_template('<%= File.basename(source) %>') - - - $VSM_Bin_Prepare_Sync_Point="##VSM_BIN_PREPARE_SYNC_POINT" - $VSM_Spool_Dir="/var/spool/vsm" - $VSM_RPM_Install_Dir="/opt/cisco/vsm" - $VSM_Repackage_Script_Name="repackiso.py" - $VSM_Repackage_Script="/tmp/$VSM_Repackage_Script_Name" - $VSM_DEST="$VSM_Spool_Dir/$dest" - $VSM_PKG_NAME="nexus-1000v-vsm" - $VSM_ISO="vsm.iso" - - # - # prepare vsm spool folder - # - file {"File_VSM_Spool_Dir": - path => "$VSM_Spool_Dir", - ensure => "directory", - owner => "root", - group => "root", - mode => "664", - } - -> - exec {"Debug_File_VSM_Spool_Dir": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n File_VSM_Spool_Dir\n path=$VSM_Spool_Dir \n ensure=directory \n owner=root \n group=root \n mode=664 \n\" >> ${n1k_vsm::Debug_Log}", - } - - - case "$source_method" { - "http": { - yumrepo {"http-cisco-foreman": - baseurl => "$n1k_vsm::n1kv_source", - descr => "Internal repo for Foreman", - enabled => "1", - gpgcheck => "1", - proxy => "_none_", - gpgkey => "${n1k_vsm::n1kv_source}/RPM-GPG-KEY", - } - -> - package {"Package_VSM": - name => "$VSM_PKG_NAME", - ensure => "${n1k_vsm::n1kv_version}", - } - -> - exec {"Copy_VSM": - command => "/bin/cp $VSM_RPM_Install_Dir/*.iso $VSM_Spool_Dir/$VSM_ISO", - before => Notify["$VSM_Bin_Prepare_Sync_Point"], - } - -> - exec {"Debug-http-cisco-os and Package_VSM": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Debug-http-cisco-os and Package_VSM \n baseurl=$n1k_vsm::n1kv_source \n descr=>Internal repo for Foreman \n enabled = 1 \n gpgcheck=1 \n gpgkey => $n1kv_source::n1kv_source/RPM-GPG-KEY\n\" >> ${n1k_vsm::Debug_Log}", - } - } - - "ftp": { - package {"ftp": - name => "ftp", - ensure => "installed", - } - -> - yumrepo {"ftp-cisco-foreman": - baseurl => "$n1k_vsm::n1kv_source", - descr => "Internal repo for Foreman", - enabled => "1", - gpgcheck => "1", - proxy => "_none_", - gpgkey => "${n1k_vsm::n1kv_source}/RPM-GPG-KEY", - } - -> - package {"Package_VSM": - name => "$VSM_PKG_NAME", - ensure => "${n1k_vsm::n1kv_version}", - } - -> - exec {"Copy_VSM": - command => "/bin/cp $VSM_RPM_Install_Dir/*.iso $VSM_Spool_Dir/$VSM_ISO", - before => Notify["$VSM_Bin_Prepare_Sync_Point"], - } - -> - exec {"Debug-ftp-cisco-os and Package_VSM": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Debug-ftp-cisco-os and Package_VSM \n baseurl=$n1k_vsm::n1kv_source \n descr=>Internal repo for Foreman \n enabled = 1 \n gpgcheck=1 \n gpgkey => $n1kv_source::n1kv_source/RPM-GPG-KEY\n\" >> ${n1k_vsm::Debug_Log}", - } - - } - "puppet": { - # - # make sure the file does not exist - # - exec {"File_VSM_Bin_Remove": - command => "/bin/rm -f $VSM_DEST || /bin/true", - before => Notify["$VSM_Bin_Prepare_Sync_Point"], - } - -> - file {"File_VSM_Bin_Prepare": - path => "$VSM_DEST", - ensure => "present", - owner => "root", - group => "root", - mode => "664", - source => "$n1k_vsm::n1kv_source", - before => Notify["$VSM_Bin_Prepare_Sync_Point"], - } - -> - exec {"Exec_RPM_TO_ISO": - # - # If it's an RPM, we do a local rpm installation ..." - # - command => "/bin/rpm -i --force $VSM_DEST && /bin/cp $VSM_RPM_Install_Dir/*.iso $VSM_Spool_Dir/$VSM_ISO", - unless => "/usr/bin/file $VSM_DEST | /bin/grep -c ' ISO '", - before => Notify["$VSM_Bin_Prepare_Sync_Point"], - } - -> - exec {"Debug_File_VSM_Bin_Prepare_Exec_RPM_TO_ISO": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Debug_File_VSM_Bin_Prepare_Exec_RPM_TO_ISO \n path=$VSM_DEST \n ensure=directory \n owner=root\n group=root\n mode=664\n source=$n1k_vsm::n1kv_source\n \" >> ${n1k_vsm::Debug_Log}", - } - } - default: { - fail(": Unknown sourcing method [$source_method] is not supported") - } - } - - notify {"$VSM_Bin_Prepare_Sync_Point":} - - # - # copy repackiso.py to local place - # - file {"File_VSM_Repackage_Script_Name": - path => "$VSM_Repackage_Script", - ensure => "present", - owner => "root", - group => "root", - mode => "774", - source => "puppet:///modules/n1k_vsm/$VSM_Repackage_Script_Name", - } - -> - exec {"Debug_File_VSM_Repackage_Script_Name": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Debug_VSM_Repackage_Script_Name \n path=$VSM_Repackage_Script \n ensure=present \n owner=root \n group=root \n mode=774\n source=puppet:///modules/n1k_vsm/$VSM_REPACKAGE_SCRIPT_NAME \n\" >> ${n1k_vsm::Debug_Log}", - } - - # - # Now generate ovf xml file and repackage the iso - # - exec {"Exec_VSM_Repackage_Script_Name": - command => "${VSM_Repackage_Script} -i$VSM_Spool_Dir/$VSM_ISO -d${n1k_vsm::domainid} -n${n1k_vsm::vsmname} -m${n1k_vsm::mgmtip} -s${n1k_vsm::mgmtnetmask} -g${n1k_vsm::mgmtgateway} -p${n1k_vsm::adminpasswd} -r${n1k_vsm::role} -f${VSM_Spool_Dir}/${n1k_vsm::role}_repacked.iso >> ${n1k_vsm::Debug_Log}", - } - -> - exec {"Debug_Exec_VSM_Repackage_Script_Name": - command => "${n1k_vsm::Debug_Print} \"[INFO]\n Exec_VSM_Repackage_Script_Name\n command=$VSM_Repackage_Script -i$VSM_ISO -d${n1k_vsm::domainid} -n${n1k_vsm::vsmname} -m${n1k_vsm::mgmtip} -s${n1k_vsm::mgmtnetmask} -g${n1k_vsm::mgmtgateway} -p${n1k_vsm::adminpasswd} -r${n1k_vsm::role} -f${VSM_Spool_Dir}/${n1k_vsm::role}_repacked.iso \n\" >> ${n1k_vsm::Debug_Log}" - } - - File["File_VSM_Spool_Dir"]-> Notify["$VSM_Bin_Prepare_Sync_Point"]->File["File_VSM_Repackage_Script_Name"]->Exec["Exec_VSM_Repackage_Script_Name"] - -} diff --git a/n1k-vsm/templates/vsm_vm.xml.erb b/n1k-vsm/templates/vsm_vm.xml.erb deleted file mode 100644 index 74d017eff..000000000 --- a/n1k-vsm/templates/vsm_vm.xml.erb +++ /dev/null @@ -1,86 +0,0 @@ - - <%= scope.lookupvar('n1k_vsm::vsmname') %> - <%= scope.lookupvar('n1k_vsm::memory') %> - <%= scope.lookupvar('n1k_vsm::vcpu') %> - - - hvm - - - - - - - - destroy - restart - restart - - - /usr/libexec/qemu-kvm - - - '/> - - - - - - '/> - - - - - - -
- - - - - - - - -
- - - - - - - - -
- - - - - - - - -
- - - - - - - - -