diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..15c55ef --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +pkg/ +Gemfile.lock +vendor/ +spec/fixtures/ +.vagrant/ +.bundle/ +coverage/ +.idea/ +*.swp +*.iml +openstack/ diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..efeba2a --- /dev/null +++ b/Gemfile @@ -0,0 +1,40 @@ +source ENV['GEM_SOURCE'] || "https://rubygems.org" + +group :development, :test do + gem 'puppetlabs_spec_helper', :require => 'false' + gem 'rspec-puppet', '~> 2.2.0', :require => 'false' + gem 'rspec-puppet-facts', :require => 'false' + gem 'metadata-json-lint', :require => 'false' + gem 'puppet-lint-param-docs', :require => 'false' + gem 'puppet-lint-absolute_classname-check', :require => 'false' + gem 'puppet-lint-absolute_template_path', :require => 'false' + gem 'puppet-lint-trailing_newline-check', :require => 'false' + gem 'puppet-lint-unquoted_string-check', :require => 'false' + gem 'puppet-lint-leading_zero-check', :require => 'false' + gem 'puppet-lint-variable_contains_upcase', :require => 'false' + gem 'puppet-lint-numericvariable', :require => 'false' + gem 'json', :require => 'false' + gem 'puppet-openstack_spec_helper', + :git => 'https://git.openstack.org/openstack/puppet-openstack_spec_helper', + :require => false +end + +group :system_tests do + gem 'beaker-rspec', :require => 'false' + gem 'beaker-puppet_install_helper', :require => 'false' + gem 'r10k', :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/LICENSE b/LICENSE new file mode 100644 index 0000000..8c06f5e --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright 2015 OpenStack Foundation + +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/README.md b/README.md new file mode 100644 index 0000000..43238b9 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +ec2api +======= + +#### Table of Contents + +1. [Overview - What is the ec2api module?](#overview) +2. [Module Description - What does the module do?](#module-description) +3. [Development - Guide for contributing to the module](#development) +4. [Contributors](#contributors) + +Overview +-------- + +The puppet-ec2api is the puppet module for module [OpenStack EC2 API](https://github.com/openstack/ec2-api) project. + +Please note that this is WIP and this puppet module is not ready for production environments yet. + +Module Description +------------------ + +The ec2api module is a thorough attempt to make Puppet capable of managing the entirety of OpenStack EC2 API project. This includes manifests to provision region specific endpoint and database connections. Types are shipped as part of the ec2api module to assist in manipulation of configuration files. + +Development +----------- + +Developer documentation for the entire puppet-openstack project. + +* https://wiki.openstack.org/wiki/Puppet#Developer_documentation + +Contributors +------------ + +* https://github.com/openstack/puppet-ec2api/graphs/contributors diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..168d108 --- /dev/null +++ b/Rakefile @@ -0,0 +1 @@ +require 'puppet-openstack_spec_helper/rake_tasks' diff --git a/lib/puppet/provider/ec2api_api_paste_ini/ini_setting.rb b/lib/puppet/provider/ec2api_api_paste_ini/ini_setting.rb new file mode 100644 index 0000000..d35dd18 --- /dev/null +++ b/lib/puppet/provider/ec2api_api_paste_ini/ini_setting.rb @@ -0,0 +1,27 @@ +Puppet::Type.type(:ec2api_api_paste_ini).provide( + :ini_setting, + :parent => Puppet::Type.type(:ini_setting).provider(:ruby) +) do + + def section + resource[:name].split('/', 2).first + end + + def setting + resource[:name].split('/', 2).last + end + + def separator + '=' + end + + def self.file_path + '/etc/ec2api/api-paste.ini' + end + + # added for backwards compatibility with older versions of inifile + def file_path + self.class.file_path + end + +end diff --git a/lib/puppet/provider/ec2api_config/ini_setting.rb b/lib/puppet/provider/ec2api_config/ini_setting.rb new file mode 100644 index 0000000..898cd5e --- /dev/null +++ b/lib/puppet/provider/ec2api_config/ini_setting.rb @@ -0,0 +1,22 @@ +Puppet::Type.type(:ec2api_config).provide( + :ini_setting, + :parent => Puppet::Type.type(:ni_setting).provider(:ruby) +) do + + def section + resource[:name].split('/', 2).first + end + + def setting + resource[:name].split('/', 2).last + end + + def separator + '=' + end + + def self.file_path + '/etc/ec2api/ec2api.conf' + end + +end diff --git a/lib/puppet/type/ec2api_api_paste_ini.rb b/lib/puppet/type/ec2api_api_paste_ini.rb new file mode 100644 index 0000000..78ab46a --- /dev/null +++ b/lib/puppet/type/ec2api_api_paste_ini.rb @@ -0,0 +1,9 @@ +Puppet::Type.newtype(:ec2api_api_paste_ini) do + + ensurable + + newparam(:name, :namevar => true) do + desc 'Section/setting name to manage from /etc/ec2api/api-paste.ini' + newvalues(/\S+\/\S+/) + end +end diff --git a/lib/puppet/type/ec2api_config.rb b/lib/puppet/type/ec2api_config.rb new file mode 100644 index 0000000..1b3b287 --- /dev/null +++ b/lib/puppet/type/ec2api_config.rb @@ -0,0 +1,12 @@ +Puppet::Type.newtype(:ec2api_config) do + + ensurable do + defaultvalues + defaultto :present + end + + newparam(:name, :namevar => true) do + desc 'Section/setting name to manage from ec2api.conf' + newvalues(/\S+\/\S+/) + end +end diff --git a/manifests/api.pp b/manifests/api.pp new file mode 100644 index 0000000..2ed95fa --- /dev/null +++ b/manifests/api.pp @@ -0,0 +1,298 @@ +# == Class: ec2api::api +# +# EC2 API class to configure the API service via puppet. +# +# === Parameters +# +# [*debug*] +# Print debugging output (set logging level to DEBUG instead of default +# WARNING level). Defaults to false +# +# [*verbose*] +# Print more verbose output (set logging level to INFO instead of default +# WARNING level). Defaults to false +# +# [*admin_user*] +# The user does not need admin credentials into the project +# Admin use. Defaults to undef +# +# [*admin_password*] +# Admin password. Defaults to undef +# +# [*admin_tenant_name*] +# Admin tenant name. Defaults to undef +# +# [*api_paste_config*] +# File name for the paste.deploy config for ec2api (string +# value). Defaults to 'api-paste.ini' +# +# [*log_file*] +# (optional) File where logs should be stored. +# If set to boolean false, it will not log to any file. +# Defaults to '/var/log/ec2api/ec2api.log' +# +# [*ca_file*] +# (optional) CA certificate file to use to verify connecting clients +# Defaults to false, not set +# +# [*cert_file*] +# (optinal) Certificate file to use when starting API server securely +# Defaults to false, not set +# +# [*key_file*] +# (optional) Private key file to use when starting API server securely +# Defaults to false, not set +# +# [*keystone_url*] +# URL to get token from ec2 request. (string value). +# Defaults to 'http://localhost:5000/v2.0'# +# +# [*keystone_ec2_tokens_url*] +# URL to get token from ec2 request.(string value). +# Defaults to '$keystone_url/ec2tokens' +# +class ec2api::api ( + $manage_service = true, + $enabled = true, + $debug = false, + $verbose = false, + $admin_user = undef, + $admin_password = undef, + $admin_tenant_name = undef, + $fatal_exception_format_errors = false, + $ec2api_listen = '0.0.0.0', + $ec2api_listen_port = 8788, + $ec2api_use_ssl = false, + $ec2api_workers = undef, + $metadata_listen = '0.0.0.0', + $metadata_listen_port = 8789, + $metadata_use_ssl = false, + $metadata_workers = undef, + $service_down_time = 60, + $api_paste_config = 'api-paste.ini', + $log_dir = '/var/log/ec2api', + $use_ssl = false, + $wsgi_ssl_ca_file = undef, + $wsgi_ssl_cert_file = undef, + $wsgi_ssl_key_file = undef, + $database_use_tpool = false, + $database_connection = 'sqlite:////var/lib/ec2api/ec2api.sqlite', + $keystone_url = 'http://localhost:5000/v2.0', + $keystone_ec2_tokens_url = 'http://localhost:5000/v2.0/ec2tokens', + $ec2_timestamp_expiry = 300, + $api_rate_limit = false, + $use_forwarded_for = false, + $internal_service_availability_zone = internal, + $my_ip = '10.0.0.1', + $ec2_host = $my_ip, + $ec2_port = 8788, + $ec2_scheme = 'http', + $ec2_path = '/', + $region_list = undef, + $full_vpc_support = true, + $network_device_mtu = 1500, + $cert_topic = 'cert', + $image_decryption_dir = '/tmp', + $s3_host = '10.0.0.1', + $s3_use_ssl = false, + $s3_affix_tenant = false, + $ec2_private_dns_show_ip = false, + $external_network = undef, +# [keystone_authtoken] + $auth_admin_prefix = undef, + $auth_host = '127.0.0.1', + $auth_port = 35357, + $auth_protocol = 'https', + $auth_uri = 'http://localhost:5000/', + $identity_uri = 'http://localhost:35357/', + $auth_version = 'v2.0', + $delay_auth_decision = false, + $http_connect_timeout = undef, + $http_request_max_retries = 3, + $admin_token = undef, + $keystone_admin_user = 'ec2api', + $keystone_admin_tenant_name = 'services', + $keystone_admin_password = undef, + $keystone_certfile = undef, + $keystone_keyfile = undef, + $keystone_cafile = undef, + $insecure = false, + $signing_dir = undef, + $memcached_servers = undef, + $token_cache_time = 300, + $revocation_cache_time = 10, + $memcache_security_strategy = undef, + $memcache_secret_key = undef, + $include_service_catalog = true, + $enforce_token_bind = permissive, + $check_revocations_for_cached = false, + $hash_algorithms = 'md5', +# [metadata] + $nova_metadata_ip = '127.0.0.1', + $nova_metadata_port = 8775, + $nova_metadata_protocol = 'http', + $nova_metadata_insecure = false, + $auth_ca_cert = undef, + $nova_client_cert = undef, + $nova_client_priv_key = undef, + $metadata_proxy_shared_secret = undef + +) inherits ec2api { + + Package[$ec2api::params::package_name] -> Ec2api_config<||> + Package[$ec2api::params::package_name] -> Ec2api_api_paste_ini<||> + + if $use_ssl { + if !$wsgi_ssl_cert_file { + fail("The wsgi_ssl_cert_file parameter is required when use_ssl is \ + set to true") + } + if !$wsgi_ssl_key_file { + fail("The wsgi_ssl_key_file parameter is required when use_ssl is \ + set to true") + } + } + + # Set values to ec2api.conf file + ec2api_config { + 'DEFAULT/debug': value => $debug; + 'DEFAULT/verbose': value => $verbose; + 'DEFAULT/admin_user': value => $admin_user; + 'DEFAULT/admin_password': + value => $admin_password; + 'DEFAULT/admin_tenant_name': value => $admin_tenant_name; + 'DEFAULT/fatal_exception_format_errors': + value => $fatal_exception_format_errors; + 'DEFAULT/ec2api_listen': value => $ec2api_listen; + 'DEFAULT/ec2api_listen_port': value => $ec2api_listen_port; + 'DEFAULT/ec2api_use_ssl': value => $ec2api_use_ssl; + 'DEFAULT/ec2api_workers': value => $ec2api_workers; + 'DEFAULT/metadata_listen': value => $metadata_listen; + 'DEFAULT/metadata_listen_port': value => $metadata_listen_port; + 'DEFAULT/metadata_use_ssl': value => $metadata_use_ssl; + 'DEFAULT/metadata_workers': value => $metadata_workers; + 'DEFAULT/service_down_time': value => $service_down_time; + 'DEFAULT/api_paste_config': value => $api_paste_config; + 'database/use_tpool': value => $database_use_tpool; + 'database/connection': value => $database_connection; + 'DEFAULT/keystone_url': value => $keystone_url; + 'DEFAULT/keystone_ec2_tokens_url': + value => $keystone_ec2_tokens_url; + 'DEFAULT/ec2_timestamp_expiry': + value => $ec2_timestamp_expiry; + 'DEFAULT/api_rate_limit': value => $api_rate_limit; + 'DEFAULT/use_forwarded_for': value => $use_forwarded_for; + 'DEFAULT/internal_service_availability_zone': + value => $internal_service_availability_zone; + 'DEFAULT/my_ip': value => $my_ip; + #'DEFAULT/ec2_host': value => $ec2_host; + 'DEFAULT/ec2_port': value => $ec2_port; + 'DEFAULT/ec2_scheme': value => $ec2_scheme; + 'DEFAULT/ec2_path': value => $ec2_path; + 'DEFAULT/region_list': value => $region_list; + 'DEFAULT/full_vpc_support': value => $full_vpc_support; + 'DEFAULT/network_device_mtu': value => $network_device_mtu; + 'DEFAULT/cert_topic': value => $cert_topic; + 'DEFAULT/image_decryption_dir': + value => $image_decryption_dir; + 'DEFAULT/s3_host': value => $s3_host; + 'DEFAULT/s3_use_ssl': value => $s3_use_ssl; + 'DEFAULT/s3_affix_tenant': value => $s3_affix_tenant; + 'DEFAULT/ec2_private_dns_show_ip': + value => $ec2_private_dns_show_ip; + 'DEFAULT/external_network': value => $external_network; + 'keystone_authtoken/auth_admin_prefix': value => $auth_admin_prefix; + 'keystone_authtoken/auth_host': value => $auth_host; + 'keystone_authtoken/auth_port': value => $auth_port; + 'keystone_authtoken/auth_protocol': value => $auth_protocol; + 'keystone_authtoken/auth_uri': value => $auth_uri; + 'keystone_authtoken/identity_uri': value => $identity_uri; + 'keystone_authtoken/auth_version': value => $auth_version; + 'keystone_authtoken/delay_auth_decision': value => $delay_auth_decision; + 'keystone_authtoken/http_connect_timeout': + value => $http_connect_timeout; + 'keystone_authtoken/http_request_max_retries': + value => $http_request_max_retries; + 'keystone_authtoken/admin_token': value =>$admin_token; + 'keystone_authtoken/admin_user': value => $keystone_admin_user; + 'keystone_authtoken/admin_tenant': + value => $keystone_admin_tenant_name; + 'keystone_authtoken/admin_password': + value => $keystone_admin_password; + 'keystone_authtoken/certfile': value => $keystone_certfile; + 'keystone_authtoken/keyfile': value => $keystone_keyfile; + 'keystone_authtoken/cafile': value => $keystone_cafile; + 'keystone_authtoken/insecure': value => $insecure; + 'keystone_authtoken/signing_dir': value => $signing_dir; + 'keystone_authtoken/memcached_servers': value => $memcached_servers; + 'keystone_authtoken/token_cache_time': value => $token_cache_time; + 'keystone_authtoken/revocation_cache_time': + value => $revocation_cache_time; + 'keystone_authtoken/memcache_security_strategy': + value => $memcache_security_strategy; + 'keystone_authtoken/memcache_secret_key': + value => $memcache_secret_key; + 'keystone_authtoken/include_service_catalog': + value => $include_service_catalog; + 'keystone_authtoken/enforce_token_bind': value => $enforce_token_bind; + 'keystone_authtoken/check_revocations_for_cached': + value => $check_revocations_for_cached; + 'keystone_authtoken/hash_algorithms': value => $hash_algorithms; + 'metadata/nova_metadata_ip': + value => $nova_metadata_ip; + 'metadata/nova_metadata_port': + value => $nova_metadata_port; + 'metadata/nova_metadata_protocol': + value => $nova_metadata_protocol; + 'metadata/nova_metadata_insecure': + value => $nova_metadata_insecure; + 'metadata/auth_ca_cert': + value => $auth_ca_cert; + 'metadata/nova_client_cert': + value => $nova_client_cert; + 'metadata/nova_client_priv_key': + value => $nova_client_priv_key; + 'metadata/metadata_proxy_shared_secret': + value => $metadata_proxy_shared_secret; + } + + # SSL options + if $use_ssl { + ec2api_config { + 'DEFAULT/ssl_ca_file': value => $wsgi_ssl_ca_file; + 'DEFAULT/ssl_cert_file': value => $wsgi_ssl_cert_file; + 'DEFAULT/ssl_key_file': value => $wsgi_ssl_key_file; + } + if $wsgi_ssl_ca_file { + ec2api_config { + 'DEFAULT/ssl_ca_file' : value => $wsgi_ssl_ca_file, + } + } else { + ec2api_config { + 'DEFAULT/ssl_ca_file' : ensure => absent, + } + } + } else { + ec2api_config { + 'DEFAULT/ssl_cert_file' : ensure => absent; + 'DEFAULT/ssl_key_file' : ensure => absent; + 'DEFAULT/ssl_ca_file' : ensure => absent; + } + } + + if $manage_service { + if $enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' + } + } + + service { 'ec2api-api-service': + ensure => $service_ensure, + name => $::ec2api::params::api_service_name, + enable => $enabled, + hasstatus => true, + hasrestart => true, + } +} diff --git a/manifests/config.pp b/manifests/config.pp new file mode 100644 index 0000000..cbadda1 --- /dev/null +++ b/manifests/config.pp @@ -0,0 +1,39 @@ +# == Class: ec2api::config +# +# This class is used to manage arbitrary ec2api configurations. +# +# === Parameters +# +# [*ec2api_config*] +# (optional) Allow configuration of arbitrary ec2api configurations. +# The value is an hash of ec2api_config resources. Example: +# { 'DEFAULT/foo' => { value => 'fooValue'}, +# 'DEFAULT/bar' => { value => 'barValue'} +# } +# In yaml format, Example: +# ec2api_config: +# DEFAULT/foo: +# value: fooValue +# DEFAULT/bar: +# value: barValue +# +# [**ec2api_config**] +# (optional) Allow configuration of ec2api.conf configurations. +# +# [**api_paste_ini_config**] +# (optional) Allow configuration of /etc/ec2api/api-paste.ini configurations. +# +# NOTE: The configuration MUST NOT be already handled by this module +# or Puppet catalog compilation will fail with duplicate resources. +# +class ec2api::config ( + $ec2api_config = {}, + $api_paste_ini_config = {}, +) { + + validate_hash($ec2api_config) + validate_hash($api_paste_ini_config) + + create_resources('ec2api_config', $ec2api_config) + create_resources('ec2api_api_paste_ini', $api_paste_ini_config) +} diff --git a/manifests/db/mysql.pp b/manifests/db/mysql.pp new file mode 100644 index 0000000..18b3a21 --- /dev/null +++ b/manifests/db/mysql.pp @@ -0,0 +1,69 @@ +# The ec2api::db::mysql class implements mysql backend for ec2api +# +# This class can be used to create tables, users and grant +# privelege for a mysql ec2api database. +# +# == parameters +# +# [*password*] +# (Mandatory) Password to connect to the database. +# Defaults to 'false'. +# +# [*dbname*] +# (Optional) Name of the database. +# Defaults to 'ec2api'. +# +# [*user*] +# (Optional) User to connect to the database. +# Defaults to 'ec2api'. +# +# [*host*] +# (Optional) The default source host user is allowed to connect from. +# Defaults to '127.0.0.1' +# +# [*allowed_hosts*] +# (Optional) Other hosts the user is allowed to connect from. +# Defaults to 'undef'. +# +# [*charset*] +# (Optional) The database charset. +# Defaults to 'utf8' +# +# [*collate*] +# (Optional) The database collate. +# Only used with mysql modules >= 2.2. +# Defaults to 'utf8_general_ci' +# +# == Dependencies +# Class['mysql::server'] +# +# == Examples +# +# == Authors +# +# == Copyright +# +class ec2api::db::mysql( + $password, + $dbname = 'ec2api', + $user = 'ec2api', + $host = '127.0.0.1', + $charset = 'utf8', + $collate = 'utf8_general_ci', + $allowed_hosts = undef +) { + + validate_string($password) + + ::openstacklib::db::mysql { 'ec2api': + user => $user, + password_hash => mysql_password($password), + dbname => $dbname, + host => $host, + charset => $charset, + collate => $collate, + allowed_hosts => $allowed_hosts, + } + + ::Openstacklib::Db::Mysql['ec2api'] ~> Exec<| title == 'ec2-api-manage db_sync' |> +} diff --git a/manifests/db/postgresql.pp b/manifests/db/postgresql.pp new file mode 100644 index 0000000..150674f --- /dev/null +++ b/manifests/db/postgresql.pp @@ -0,0 +1,55 @@ +# == Class: ec2api::db::postgresql +# +# Class that configures postgresql for ec2api +# Requires the Puppetlabs postgresql module. +# +# === Parameters +# +# [*password*] +# (Required) Password to connect to the database. +# +# [*dbname*] +# (Optional) Name of the database. +# Defaults to 'ec2api'. +# +# [*user*] +# (Optional) User to connect to the database. +# Defaults to 'ec2api'. +# +# [*encoding*] +# (Optional) The charset to use for the database. +# Default to undef. +# +# [*privileges*] +# (Optional) Privileges given to the database user. +# Default to 'ALL' +# +# == Dependencies +# +# == Examples +# +# == Authors +# +# == Copyright +# +class ec2api::db::postgresql( + $password, + $dbname = 'ec2api', + $user = 'ec2api', + $encoding = undef, + $privileges = 'ALL', +) { + + Class['ec2api::db::postgresql'] -> Service<| title == 'ec2api' |> + + ::openstacklib::db::postgresql { 'ec2api': + password_hash => postgresql_password($user, $password), + dbname => $dbname, + user => $user, + encoding => $encoding, + privileges => $privileges, + } + + ::Openstacklib::Db::Postgresql['ec2api'] ~> Exec<| title == 'ec2-api-manage db_sync' |> + +} diff --git a/manifests/db/sync.pp b/manifests/db/sync.pp new file mode 100644 index 0000000..ec07b21 --- /dev/null +++ b/manifests/db/sync.pp @@ -0,0 +1,14 @@ +# +# Class to execute "ec2api-manage db_sync +# +class ec2api::db::sync { + exec { 'ec2api-manage db_sync': + path => '/usr/bin', + user => 'ec2api', + refreshonly => true, + subscribe => [Package['ec2api'], Ec2api_config['database/connection']], + require => User['ec2api'], + } + + Exec['ec2-api-manage db_sync'] ~> Service<| title == 'ec2api' |> +} diff --git a/manifests/init.pp b/manifests/init.pp new file mode 100644 index 0000000..82305e2 --- /dev/null +++ b/manifests/init.pp @@ -0,0 +1,33 @@ +# == Class: ec2api +# +# Main EC2 API class to configure the service via puppet. +# +class ec2api { + + include ec2api::params + + # Install the package + package { 'ec2api': + ensure => present, + name => $ec2api::params::package_name, + } + + + # Chanage onwer, group and permissions to config files + file { $ec2api::params::ec2api_config: + ensure => present, + owner => 'ec2api', + group => 'ec2api', + mode => '0644', + require => Package['ec2api'], + } + + file { $ec2api::params::ec2api_api_paste_ini: + ensure => present, + owner => 'ec2api', + group => 'ec2api', + mode => '0644', + require => Package['ec2api'], + } + +} diff --git a/manifests/keystone/auth.pp b/manifests/keystone/auth.pp new file mode 100644 index 0000000..afdba9a --- /dev/null +++ b/manifests/keystone/auth.pp @@ -0,0 +1,116 @@ +# == Class: ec2api::keystone::auth +# +# Configures ec2api user, service and endpoint in Keystone. +# +# === Parameters +# +# [*password*] +# (required) Password for ec2api user. +# +# [*auth_name*] +# Username for ec2api service. Defaults to 'ec2api'. +# +# [*email*] +# Email for ec2api user. Defaults to 'ec2api@localhost'. +# +# [*tenant*] +# Tenant for ec2api user. Defaults to 'services'. +# +# [*configure_endpoint*] +# Should ec2api endpoint be configured? Defaults to 'true'. +# +# [*configure_user*] +# (Optional) Should the service user be configured? +# Defaults to 'true'. +# +# [*configure_user_role*] +# (Optional) Should the admin role be configured for the service user? +# Defaults to 'true'. +# +# [*service_type*] +# Type of service. Defaults to 'FIXME'. +# +# [*public_protocol*] +# Protocol for public endpoint. Defaults to 'http'. +# +# [*public_address*] +# Public address for endpoint. Defaults to '127.0.0.1'. +# +# [*admin_protocol*] +# Protocol for admin endpoint. Defaults to 'http'. +# +# [*admin_address*] +# Admin address for endpoint. Defaults to '127.0.0.1'. +# +# [*internal_protocol*] +# Protocol for internal endpoint. Defaults to 'http'. +# +# [*internal_address*] +# Internal address for endpoint. Defaults to '127.0.0.1'. +# +# [*port*] +# Port for endpoint. Defaults to 'FIXME'. +# +# [*public_port*] +# Port for public endpoint. Defaults to $port. +# +# [*region*] +# Region for endpoint. Defaults to 'RegionOne'. +# +# [*service_name*] +# (optional) Name of the service. +# Defaults to the value of auth_name. +# +# +class ec2api::keystone::auth ( + $password, + $auth_name = 'ec2api', + $email = 'ec2api@localhost', + $tenant = 'services', + $configure_endpoint = true, + $configure_user = true, + $configure_user_role = true, + $service_name = undef, + $service_type = 'FIXME', + $public_protocol = 'http', + $public_address = '127.0.0.1', + $admin_protocol = 'http', + $admin_address = '127.0.0.1', + $internal_protocol = 'http', + $internal_address = '127.0.0.1', + $port = 'FIXME', + $public_port = undef, + $region = 'RegionOne' +) { + + $real_service_name = pick($service_name, $auth_name) + + if $configure_user_role { + Keystone_user_role["${auth_name}@${tenant}"] ~> Service <| name == 'ec2api-server' |> + } + Keystone_endpoint["${region}/${real_service_name}"] ~> Service <| name == 'ec2api-server' |> + + if ! $public_port { + $real_public_port = $port + } else { + $real_public_port = $public_port + } + + keystone::resource::service_identity { 'ec2api': + configure_user => $configure_user, + configure_user_role => $configure_user_role, + configure_endpoint => $configure_endpoint, + service_name => $real_service_name, + service_type => $service_type, + service_description => 'ec2api FIXME Service', + region => $region, + auth_name => $auth_name, + password => $password, + email => $email, + tenant => $tenant, + public_url => "${public_protocol}://${public_address}:${real_public_port}/", + internal_url => "${internal_protocol}://${internal_address}:${port}/", + admin_url => "${admin_protocol}://${admin_address}:${port}/", + } + +} diff --git a/manifests/logging.pp b/manifests/logging.pp new file mode 100644 index 0000000..4c9cb51 --- /dev/null +++ b/manifests/logging.pp @@ -0,0 +1,211 @@ +# Class ec2api::logging +# +# ec2api extended logging configuration +# +# == 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_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_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 +# +# [*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', +# 'oslo.messaging' => 'INFO', 'iso8601' => 'WARN', +# 'requests.packages.urllib3.connectionpool' => 'WARN', +# 'urllib3.connectionpool' => 'WARN', +# 'websocket' => 'WARN', 'ec2apimiddleware' => 'WARN', +# 'routes.middleware' => 'WARN', stevedore => 'WARN' } +# +# [*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). +# +# [*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] ' + +# [*log_date_format*] +# (optional) Format string for %%(asctime)s in log records. +# Defaults to undef. +# Example: 'Y-%m-%d %H:%M:%S' + +class ec2api::logging( + $logging_context_format_string = undef, + $logging_default_format_string = undef, + $logging_debug_format_suffix = undef, + $logging_exception_prefix = undef, + $log_config_append = undef, + $default_log_levels = undef, + $publish_errors = undef, + $fatal_deprecations = undef, + $instance_format = undef, + $instance_uuid_format = undef, + $log_date_format = undef, +) { + + if $logging_context_format_string { + ec2api_config { + 'DEFAULT/logging_context_format_string' : + value => $logging_context_format_string; + } + } + else { + ec2api_config { + 'DEFAULT/logging_context_format_string' : ensure => absent; + } + } + + if $logging_default_format_string { + ec2api_config { + 'DEFAULT/logging_default_format_string' : + value => $logging_default_format_string; + } + } + else { + ec2api_config { + 'DEFAULT/logging_default_format_string' : ensure => absent; + } + } + + if $logging_debug_format_suffix { + ec2api_config { + 'DEFAULT/logging_debug_format_suffix' : + value => $logging_debug_format_suffix; + } + } + else { + ec2api_config { + 'DEFAULT/logging_debug_format_suffix' : ensure => absent; + } + } + + if $logging_exception_prefix { + ec2api_config { + 'DEFAULT/logging_exception_prefix' : value => $logging_exception_prefix; + } + } + else { + ec2api_config { + 'DEFAULT/logging_exception_prefix' : ensure => absent; + } + } + + if $log_config_append { + ec2api_config { + 'DEFAULT/log_config_append' : value => $log_config_append; + } + } + else { + ec2api_config { + 'DEFAULT/log_config_append' : ensure => absent; + } + } + + if $default_log_levels { + ec2api_config { + 'DEFAULT/default_log_levels' : + value => join(sort(join_keys_to_values($default_log_levels, '=')), ','); + } + } + else { + ec2api_config { + 'DEFAULT/default_log_levels' : ensure => absent; + } + } + + if $publish_errors { + ec2api_config { + 'DEFAULT/publish_errors' : value => $publish_errors; + } + } + else { + ec2api_config { + 'DEFAULT/publish_errors' : ensure => absent; + } + } + + if $fatal_deprecations { + ec2api_config { + 'DEFAULT/fatal_deprecations' : value => $fatal_deprecations; + } + } + else { + ec2api_config { + 'DEFAULT/fatal_deprecations' : ensure => absent; + } + } + + if $instance_format { + ec2api_config { + 'DEFAULT/instance_format' : value => $instance_format; + } + } + else { + ec2api_config { + 'DEFAULT/instance_format' : ensure => absent; + } + } + + if $instance_uuid_format { + ec2api_config { + 'DEFAULT/instance_uuid_format' : value => $instance_uuid_format; + } + } + else { + ec2api_config { + 'DEFAULT/instance_uuid_format' : ensure => absent; + } + } + + if $log_date_format { + ec2api_config { + 'DEFAULT/log_date_format' : value => $log_date_format; + } + } + else { + ec2api_config { + 'DEFAULT/log_date_format' : ensure => absent; + } + } + + +} diff --git a/manifests/metadata.pp b/manifests/metadata.pp new file mode 100644 index 0000000..898fafc --- /dev/null +++ b/manifests/metadata.pp @@ -0,0 +1,32 @@ +# === Parameters +# +# +class ec2api::metadata ( + $manage_service = true, + $enabled = true, + $nova_metadata_ip = '127.0.0.1', + $nova_metadata_port = 8775, + $nova_metadata_protocol = 'http', + $nova_metadata_insecure = false, + $auth_ca_cert = unset, + $nova_client_cert = unset, + $nova_client_priv_key = unset, + $metadata_proxy_shared_secret = unset, +) inherits ec2api { + + if $manage_service { + if $enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' + } + } + + service { 'openstack-ec2-api-metadata': + ensure => $service_ensure, + name => $::ec2api::params::metadata_service_name, + enable => $enabled, + hasstatus => true, + hasrestart => true, + } +} \ No newline at end of file diff --git a/manifests/params.pp b/manifests/params.pp new file mode 100644 index 0000000..8b6f968 --- /dev/null +++ b/manifests/params.pp @@ -0,0 +1,12 @@ +# == Class: ec2api::params +# +# These parameters need to be accessed from several locations and +# should be considered to be constant +class ec2api::params { + $package_name = 'openstack-ec2-api' + $api_service_name = 'openstack-ec2-api' + $ec2api_config = '/etc/ec2api/ec2api.conf' + $ec2api_api_paste_ini = '/etc/ec2api/api-paste.ini' + $s3_service_name = 'openstack-ec2-api-s3' + $metadata_service_name = 'openstack-ec2-api-metadata' +} diff --git a/manifests/s3.pp b/manifests/s3.pp new file mode 100644 index 0000000..7f6a52c --- /dev/null +++ b/manifests/s3.pp @@ -0,0 +1,34 @@ +# === Parameters +# +# +class ec2api::s3 ( + $manage_service = true, + $enabled = true, + $buckets_path = undef, + $s3_listen = '0.0.0.0', + $s3_listen_port = 3334, +) inherits ec2api { + +# Configuration + ec2api_config { + 'DEFAULT/buckets_path': value => $buckets_path; + 'DEFAULT/s3_listen': value => $s3_listen; + 'DEFAULT/s3_listen_port': value => $s3_listen_port; + } + + if $manage_service { + if $enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' + } + } + + service { 'openstack-ec2-api-s3': + ensure => $service_ensure, + name => $::ec2api::params::s3_service_name, + enable => $enabled, + hasstatus => true, + hasrestart => true, + } +} \ No newline at end of file diff --git a/metadata.json b/metadata.json new file mode 100644 index 0000000..d403b67 --- /dev/null +++ b/metadata.json @@ -0,0 +1,34 @@ +{ + "name": "puppet-ec2api", + "version": "0.0.1", + "author": "OpenStack Contributors", + "summary": "Puppet module for OpenStack Ec2api", + "license": "Apache-2.0", + "source": "git://github.com/openstack/puppet-ec2api.git", + "project_page": "https://launchpad.net/puppet-ec2api", + "issues_url": "https://bugs.launchpad.net/puppet-ec2api", + "description": "Installs and configures OpenStack Ec2api.", + "operatingsystem_support": [ + { + "operatingsystem": "Debian", + "operatingsystemrelease": ["8"] + }, + { + "operatingsystem": "Fedora", + "operatingsystemrelease": ["21","22"] + }, + { + "operatingsystem": "RedHat", + "operatingsystemrelease": ["7"] + }, + { + "operatingsystem": "Ubuntu", + "operatingsystemrelease": ["14.04"] + } + ], + "dependencies": [ + { "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 <6.0.0" } + ] +} diff --git a/spec/classes/ec2api_db_mysql_spec.rb b/spec/classes/ec2api_db_mysql_spec.rb new file mode 100644 index 0000000..161365a --- /dev/null +++ b/spec/classes/ec2api_db_mysql_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper' + +describe 'ec2api::db::mysql' do + + let :pre_condition do + [ + 'include mysql::server', + 'include ec2api::db::sync' + ] + end + + let :facts do + { :osfamily => 'Debian' } + end + + let :params do + { + 'password' => 'pwd', + } + end + + describe 'with only required params' do + it { is_expected.to contain_openstacklib__db__mysql('ec2api').with( + 'user' => 'ec2api', + 'password_hash' => '*0000000', + 'dbname' => 'ec2api', + 'host' => '127.0.0.1', + 'charset' => 'utf8', + :collate => 'utf8_general_ci', + )} + end + + describe "overriding allowed_hosts param to array" do + let :params do + { + :password => 'pwd', + :allowed_hosts => ['127.0.0.1','%'] + } + end + + end + describe "overriding allowed_hosts param to string" do + let :params do + { + :password => 'pwd', + :allowed_hosts => '192.168.1.1' + } + end + + end + + describe "overriding allowed_hosts param equals to host param " do + let :params do + { + :password => 'pwd', + :allowed_hosts => '127.0.0.1' + } + end + + end + +end diff --git a/spec/classes/ec2api_db_postgresql_spec.rb b/spec/classes/ec2api_db_postgresql_spec.rb new file mode 100644 index 0000000..3bc869c --- /dev/null +++ b/spec/classes/ec2api_db_postgresql_spec.rb @@ -0,0 +1,58 @@ +require 'spec_helper' + +describe 'ec2api::db::postgresql' do + + let :req_params do + { :password => 'pw' } + end + + let :pre_condition do + 'include postgresql::server' + end + + context 'on a RedHat osfamily' do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '7.0', + :concat_basedir => '/var/lib/puppet/concat' + } + end + + context 'with only required parameters' do + let :params do + req_params + end + + it { is_expected.to contain_postgresql__server__db('ec2api').with( + :user => 'ec2api', + :password => 'pwd' + )} + end + + end + + context 'on a Debian osfamily' do + let :facts do + { + :operatingsystemrelease => '7.8', + :operatingsystem => 'Debian', + :osfamily => 'Debian', + :concat_basedir => '/var/lib/puppet/concat' + } + end + + context 'with only required parameters' do + let :params do + req_params + end + + it { is_expected.to contain_postgresql__server__db('ec2api').with( + :user => 'ec2api', + :password => 'pwd' + )} + end + + end + +end diff --git a/spec/classes/ec2api_keystone_auth_spec.rb b/spec/classes/ec2api_keystone_auth_spec.rb new file mode 100644 index 0000000..f7747fa --- /dev/null +++ b/spec/classes/ec2api_keystone_auth_spec.rb @@ -0,0 +1,127 @@ +# +# Unit tests for ec2api::keystone::auth +# + +require 'spec_helper' + +describe 'ec2api::keystone::auth' do + + let :facts do + { :osfamily => 'Debian' } + end + + describe 'with default class parameters' do + let :params do + { :password => 'pwd', + :tenant => 'services' } + end + + it { is_expected.to contain_keystone_user('ec2api').with( + :ensure => 'present', + :password => 'pwd', + :tenant => 'services' + ) } + + it { is_expected.to contain_keystone_user_role('ec2api@services').with( + :ensure => 'present', + :roles => ['admin'] + )} + + it { is_expected.to contain_keystone_service('ec2api').with( + :ensure => 'present', + :type => 'ec2', + :description => 'ec2api Service' + ) } + + it { is_expected.to contain_keystone_endpoint('RegionOne/ec2api').with( + :ensure => 'present', + :public_url => "http://127.0.0.1:8788/", + :admin_url => "http://127.0.0.1:8788/", + :internal_url => "http://127.0.0.1:8788/" + ) } + end + + describe 'when overriding public_protocol, public_port and public address' do + let :params do + { :password => 'pwd', + :public_protocol => 'https', + :public_port => '80', + :public_address => '10.10.10.10', + :port => '81', + :internal_address => '10.10.10.11', + :admin_address => '10.10.10.12' } + end + + it { is_expected.to contain_keystone_endpoint('RegionOne/ec2api').with( + :ensure => 'present', + :public_url => "https://10.10.10.10:80/", + :internal_url => "http://10.10.10.11:81/", + :admin_url => "http://10.10.10.12:81/" + ) } + end + + describe 'when overriding auth name' do + let :params do + { :password => 'foo', + :auth_name => 'ec2apiy' } + end + + it { is_expected.to contain_keystone_user('ec2apiy') } + it { is_expected.to contain_keystone_user_role('ec2apiy@services') } + it { is_expected.to contain_keystone_service('ec2apiy') } + it { is_expected.to contain_keystone_endpoint('RegionOne/ec2apiy') } + end + + describe 'when overriding service name' do + let :params do + { :service_name => 'ec2api_service', + :auth_name => 'ec2api', + :password => 'pwd' } + end + + it { is_expected.to contain_keystone_user('ec2api') } + it { is_expected.to contain_keystone_user_role('ec2api@services') } + it { is_expected.to contain_keystone_service('ec2api_service') } + it { is_expected.to contain_keystone_endpoint('RegionOne/ec2api_service') } + end + + describe 'when disabling user configuration' do + + let :params do + { + :password => 'ec2api_password', + :configure_user => false + } + end + + it { is_expected.not_to contain_keystone_user('ec2api') } + it { is_expected.to contain_keystone_user_role('ec2api@services') } + it { is_expected.to contain_keystone_service('ec2api').with( + :ensure => 'present', + :type => 'ec2', + :description => 'ec2api Service' + ) } + + end + + describe 'when disabling user and user role configuration' do + + let :params do + { + :password => 'ec2api_password', + :configure_user => false, + :configure_user_role => false + } + end + + it { is_expected.not_to contain_keystone_user('ec2api') } + it { is_expected.not_to contain_keystone_user_role('ec2api@services') } + it { is_expected.to contain_keystone_service('ec2api').with( + :ensure => 'present', + :type => 'ec2', + :description => 'ec2api Service' + ) } + + end + +end diff --git a/spec/classes/ec2api_logging_spec.rb b/spec/classes/ec2api_logging_spec.rb new file mode 100644 index 0000000..14e14a8 --- /dev/null +++ b/spec/classes/ec2api_logging_spec.rb @@ -0,0 +1,107 @@ +require 'spec_helper' + +describe 'ec2api::logging' do + + let :params do + { + } + end + + let :log_params do + { + :logging_context_format_string => '%(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [%(request_id)s %(user_identity)s] %(instance)s%(message)s', + :logging_default_format_string => '%(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [-] %(instance)s%(message)s', + :logging_debug_format_suffix => '%(funcName)s %(pathname)s:%(lineno)d', + :logging_exception_prefix => '%(asctime)s.%(msecs)03d %(process)d TRACE %(name)s %(instance)s', + :log_config_append => '/etc/ec2api/logging.conf', + :publish_errors => true, + :default_log_levels => { + 'amqp' => 'WARN', 'amqplib' => 'WARN', 'boto' => 'WARN', + 'qpid' => 'WARN', 'sqlalchemy' => 'WARN', 'suds' => 'INFO', + 'iso8601' => 'WARN', + 'requests.packages.urllib3.connectionpool' => 'WARN' }, + :fatal_deprecations => true, + :instance_format => '[instance: %(uuid)s] ', + :instance_uuid_format => '[instance: %(uuid)s] ', + :log_date_format => '%Y-%m-%d %H:%M:%S', + } + end + + shared_examples_for 'ec2api-logging' do + + context 'with extended logging options' do + before { params.merge!( log_params ) } + it_configures 'logging params set' + end + + context 'without extended logging options' do + it_configures 'logging params unset' + end + + end + + shared_examples_for 'logging params set' do + it 'enables logging params' do + is_expected.to contain_ec2api_config('DEFAULT/logging_context_format_string').with_value( + '%(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [%(request_id)s %(user_identity)s] %(instance)s%(message)s') + + is_expected.to contain_ec2api_config('DEFAULT/logging_default_format_string').with_value( + '%(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [-] %(instance)s%(message)s') + + is_expected.to contain_ec2api_config('DEFAULT/logging_debug_format_suffix').with_value( + '%(funcName)s %(pathname)s:%(lineno)d') + + is_expected.to contain_ec2api_config('DEFAULT/logging_exception_prefix').with_value( + '%(asctime)s.%(msecs)03d %(process)d TRACE %(name)s %(instance)s') + + is_expected.to contain_ec2api_config('DEFAULT/log_config_append').with_value( + '/etc/ec2api/logging.conf') + is_expected.to contain_ec2api_config('DEFAULT/publish_errors').with_value( + true) + + is_expected.to contain_ec2api_config('DEFAULT/default_log_levels').with_value( + 'amqp=WARN,amqplib=WARN,boto=WARN,iso8601=WARN,qpid=WARN,requests.packages.urllib3.connectionpool=WARN,sqlalchemy=WARN,suds=INFO') + + is_expected.to contain_ec2api_config('DEFAULT/fatal_deprecations').with_value( + true) + + is_expected.to contain_ec2api_config('DEFAULT/instance_format').with_value( + '[instance: %(uuid)s] ') + + is_expected.to contain_ec2api_config('DEFAULT/instance_uuid_format').with_value( + '[instance: %(uuid)s] ') + + is_expected.to contain_ec2api_config('DEFAULT/log_date_format').with_value( + '%Y-%m-%d %H:%M:%S') + end + end + + + shared_examples_for 'logging params unset' do + [ :logging_context_format_string, :logging_default_format_string, + :logging_debug_format_suffix, :logging_exception_prefix, + :log_config_append, :publish_errors, + :default_log_levels, :fatal_deprecations, + :instance_format, :instance_uuid_format, + :log_date_format, ].each { |param| + it { is_expected.to contain_ec2api_config("DEFAULT/#{param}").with_ensure('absent') } + } + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'ec2api-logging' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'ec2api-logging' + end + +end diff --git a/spec/shared_examples.rb b/spec/shared_examples.rb new file mode 100644 index 0000000..fec0eac --- /dev/null +++ b/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/spec/unit/provider/ec2api_config/ini_setting_spec.rb b/spec/unit/provider/ec2api_config/ini_setting_spec.rb new file mode 100644 index 0000000..86aad4e --- /dev/null +++ b/spec/unit/provider/ec2api_config/ini_setting_spec.rb @@ -0,0 +1,37 @@ +# these tests are a little concerning b/c they are hacking around the +# modulepath, so these tests will not catch issues that may eventually arise +# related to loading these plugins. +# I could not, for the life of me, figure out how to programatcally set the modulepath +$LOAD_PATH.push( + File.join( + File.dirname(__FILE__), + '..', + '..', + '..', + 'fixtures', + 'modules', + 'inifile', + 'lib') +) +require 'spec_helper' +provider_class = Puppet::Type.type(:ec2api_config).provider(:ini_setting) +describe provider_class do + + it 'should default to the default setting when no other one is specified' do + resource = Puppet::Type::Ec2api_config.new( + {:name => 'DEFAULT/foo', :value => 'bar'} + ) + provider = provider_class.new(resource) + expect(provider.section).to eq('DEFAULT') + expect(provider.setting).to eq('foo') + end + + it 'should allow setting to be set explicitly' do + resource = Puppet::Type::Ec2api_config.new( + {:name => 'dude/foo', :value => 'bar'} + ) + provider = provider_class.new(resource) + expect(provider.section).to eq('dude') + expect(provider.setting).to eq('foo') + end +end diff --git a/spec/unit/type/ec2api_config_spec.rb b/spec/unit/type/ec2api_config_spec.rb new file mode 100644 index 0000000..be66df3 --- /dev/null +++ b/spec/unit/type/ec2api_config_spec.rb @@ -0,0 +1,52 @@ +require 'puppet' +require 'puppet/type/ec2api_config' +describe 'Puppet::Type.type(:ec2api_config)' do + before :each do + @ec2api_config = Puppet::Type.type(:ec2api_config).new(:name => 'DEFAULT/foo', :value => 'bar') + end + + it 'should require a name' do + expect { + Puppet::Type.type(:ec2api_config).new({}) + }.to raise_error(Puppet::Error, 'Title or name must be provided') + end + + it 'should not expect a name with whitespace' do + expect { + Puppet::Type.type(:ec2api_config).new(:name => 'f oo') + }.to raise_error(Puppet::Error, /Parameter name failed/) + end + + it 'should fail when there is no section' do + expect { + Puppet::Type.type(:ec2api_config).new(:name => 'foo') + }.to raise_error(Puppet::Error, /Parameter name failed/) + end + + it 'should not require a value when ensure is absent' do + Puppet::Type.type(:ec2api_config).new(:name => 'DEFAULT/foo', :ensure => :absent) + end + + it 'should accept a valid value' do + @ec2api_config[:value] = 'bar' + expect(@ec2api_config[:value]).to eq('bar') + end + + it 'should not accept a value with whitespace' do + @ec2api_config[:value] = 'b ar' + expect(@ec2api_config[:value]).to eq('b ar') + end + + it 'should accept valid ensure values' do + @ec2api_config[:ensure] = :present + expect(@ec2api_config[:ensure]).to eq(:present) + @ec2api_config[:ensure] = :absent + expect(@ec2api_config[:ensure]).to eq(:absent) + end + + it 'should not accept invalid ensure values' do + expect { + @ec2api_config[:ensure] = :latest + }.to raise_error(Puppet::Error, /Invalid value/) + end +end diff --git a/templates/example.erb b/templates/example.erb new file mode 100644 index 0000000..f0e5f3d --- /dev/null +++ b/templates/example.erb @@ -0,0 +1,3 @@ +<% if @myvar %> +myvar has <%= @myvar %> value +<% end %> diff --git a/tests/init.pp b/tests/init.pp new file mode 100644 index 0000000..d28d309 --- /dev/null +++ b/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 ::ec2api