First commit with basic manifests

Signed-off-by: Emilien Macchi <emilien.macchi@enovance.com>
This commit is contained in:
Emilien Macchi 2013-11-25 20:59:48 +01:00
parent 0f0092dc0f
commit e5b4e458ab
22 changed files with 988 additions and 28 deletions

14
Gemfile Normal file
View File

@ -0,0 +1,14 @@
source 'https://rubygems.org'
group :development, :test do
gem 'puppetlabs_spec_helper', :require => false
gem 'puppet-lint', '~> 0.3.2'
end
if puppetversion = ENV['PUPPET_GEM_VERSION']
gem 'puppet', puppetversion, :require => false
else
gem 'puppet', :require => false
end
# vim:ft=ruby

41
Gemfile.lock Normal file
View File

@ -0,0 +1,41 @@
GEM
remote: https://rubygems.org/
specs:
diff-lcs (1.2.4)
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.2)
facter (~> 1.6)
hiera (~> 1.0)
rgen (~> 0.6)
puppet-lint (0.3.2)
puppetlabs_spec_helper (0.4.1)
mocha (>= 0.10.5)
rake
rspec (>= 2.9.0)
rspec-puppet (>= 0.1.1)
rake (10.1.0)
rgen (0.6.5)
rspec (2.13.0)
rspec-core (~> 2.13.0)
rspec-expectations (~> 2.13.0)
rspec-mocks (~> 2.13.0)
rspec-core (2.13.1)
rspec-expectations (2.13.0)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.13.1)
rspec-puppet (0.1.6)
rspec
PLATFORMS
ruby
DEPENDENCIES
puppet
puppet-lint (~> 0.3.2)
puppetlabs_spec_helper

28
LICENSE
View File

@ -1,3 +1,4 @@
Apache License Apache License
Version 2.0, January 2004 Version 2.0, January 2004
http://www.apache.org/licenses/ http://www.apache.org/licenses/
@ -173,30 +174,3 @@ Apache License
incurred by, or claims asserted against, such Contributor by reason incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability. 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.

6
Rakefile Normal file
View File

@ -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')

View File

@ -0,0 +1,150 @@
require 'csv'
require 'puppet/util/inifile'
class Puppet::Provider::Ironic < Puppet::Provider
def self.conf_filename
'/etc/ironic/ironic.conf'
end
def self.withenv(hash, &block)
saved = ENV.to_hash
hash.each do |name, val|
ENV[name.to_s] = val
end
yield
ensure
ENV.clear
saved.each do |name, val|
ENV[name] = val
end
end
def self.ironic_credentials
@ironic_credentials ||= get_ironic_credentials
end
def self.get_ironic_credentials
auth_keys = ['auth_host', 'auth_port', 'auth_protocol',
'admin_tenant_name', 'admin_user', 'admin_password']
conf = ironic_conf
if conf and conf['keystone_authtoken'] and
auth_keys.all?{|k| !conf['keystone_authtoken'][k].nil?}
return Hash[ auth_keys.map \
{ |k| [k, conf['keystone_authtoken'][k].strip] } ]
else
raise(Puppet::Error, "File: #{conf_filename} does not contain all \
required sections. Ironic types will not work if ironic is not \
correctly configured.")
end
end
def ironic_credentials
self.class.ironic_credentials
end
def self.auth_endpoint
@auth_endpoint ||= get_auth_endpoint
end
def self.get_auth_endpoint
q = ironic_credentials
"#{q['auth_protocol']}://#{q['auth_host']}:#{q['auth_port']}/v2.0/"
end
def self.ironic_conf
return @ironic_conf if @ironic_conf
@ironic_conf = Puppet::Util::IniConfig::File.new
@ironic_conf.read(conf_filename)
@ironic_conf
end
def self.auth_ironic(*args)
q = ironic_credentials
authenv = {
:OS_AUTH_URL => self.auth_endpoint,
:OS_USERNAME => q['admin_user'],
:OS_TENANT_NAME => q['admin_tenant_name'],
:OS_PASSWORD => q['admin_password']
}
begin
withenv authenv do
ironic(args)
end
rescue Exception => e
if (e.message =~ /\[Errno 111\] Connection refused/) or
(e.message =~ /\(HTTP 400\)/)
sleep 10
withenv authenv do
ironic(args)
end
else
raise(e)
end
end
end
def auth_ironic(*args)
self.class.auth_ironic(args)
end
def self.reset
@ironic_conf = nil
@ironic_credentials = nil
end
def self.list_ironic_resources(type)
ids = []
list = auth_ironic("#{type}-list", '--format=csv',
'--column=id', '--quote=none')
(list.split("\n")[1..-1] || []).compact.collect do |line|
ids << line.strip
end
return ids
end
def self.get_ironic_resource_attrs(type, id)
attrs = {}
net = auth_ironic("#{type}-show", '--format=shell', id)
last_key = nil
(net.split("\n") || []).compact.collect do |line|
if line.include? '='
k, v = line.split('=', 2)
attrs[k] = v.gsub(/\A"|"\Z/, '')
last_key = k
else
# Handle the case of a list of values
v = line.gsub(/\A"|"\Z/, '')
attrs[last_key] = [attrs[last_key], v]
end
end
return attrs
end
def self.get_tenant_id(catalog, name)
instance_type = 'keystone_tenant'
instance = catalog.resource("#{instance_type.capitalize!}[#{name}]")
if ! instance
instance = Puppet::Type.type(instance_type).instances.find do |i|
i.provider.name == name
end
end
if instance
return instance.provider.id
else
fail("Unable to find #{instance_type} for name #{name}")
end
end
def self.parse_creation_output(data)
hash = {}
data.split("\n").compact.each do |line|
if line.include? '='
hash[line.split('=').first] = line.split('=', 2)[1].gsub(/\A"|"\Z/, '')
end
end
hash
end
end

View File

@ -0,0 +1,22 @@
Puppet::Type.type(:ironic_config).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 file_path
'/etc/ironic/ironic.conf'
end
end

View File

@ -0,0 +1,18 @@
Puppet::Type.newtype(:ironic_config) do
ensurable
newparam(:name, :namevar => true) do
desc 'Section/setting name to manage from ironic.conf'
newvalues(/\S+\/\S+/)
end
newproperty(:value) do
desc 'The value of the setting to be defined.'
munge do |value|
value = value.to_s.strip
value.capitalize! if value =~ /^(true|false)$/i
value
end
end
end

39
manifests/client.pp Normal file
View File

@ -0,0 +1,39 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.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.
# ironic::client
#
# Manages the ironic client package on systems
#
# === Parameters:
#
# [*package_ensure*]
# (optional) The state of the package
# Defaults to present
#
class ironic::client (
$package_ensure = present
) {
include ironic::params
package { 'python-ironicclient':
ensure => $package_ensure,
name => $::ironic::params::client_package,
}
}

55
manifests/db/mysql.pp Normal file
View File

@ -0,0 +1,55 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.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.
#
# ironic::db::mysql
#
class ironic::db::mysql (
$password,
$dbname = 'ironic',
$user = 'ironic',
$host = '127.0.0.1',
$allowed_hosts = undef,
$charset = 'latin1',
$cluster_id = 'localzone'
) {
require mysql::python
mysql::db { $dbname:
user => $user,
password => $password,
host => $host,
charset => $charset,
require => Class['mysql::config'],
}
# Check allowed_hosts to avoid duplicate resource declarations
if is_array($allowed_hosts) and delete($allowed_hosts,$host) != [] {
$real_allowed_hosts = delete($allowed_hosts,$host)
} elsif is_string($allowed_hosts) and ($allowed_hosts != $host) {
$real_allowed_hosts = $allowed_hosts
}
if $real_allowed_hosts {
ironic::db::mysql::host_access { $real_allowed_hosts:
user => $user,
password => $password,
database => $dbname,
}
}
}

View File

@ -0,0 +1,33 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.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.
#
# Used to grant access to the ironic mysql DB
#
define ironic::db::mysql::host_access ($user, $password, $database) {
database_user { "${user}@${name}":
password_hash => mysql_password($password),
provider => 'mysql',
require => Database[$database],
}
database_grant { "${user}@${name}/${database}":
# TODO figure out which privileges to grant.
privileges => 'all',
provider => 'mysql',
require => Database_user["${user}@${name}"]
}
}

19
manifests/init.pp Normal file
View File

@ -0,0 +1,19 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.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.
#
# ironic
#

113
manifests/keystone/auth.pp Normal file
View File

@ -0,0 +1,113 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.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.
#
# ironic::keystone::auth
#
# Configures Ironic user, service and endpoint in Keystone.
#
# === Parameters
#
# [*password*]
# (required) Password for Ironic user.
#
# [*auth_name*]
# Username for Ironic service. Defaults to 'ironic'.
#
# [*email*]
# Email for Ironic user. Defaults to 'ironic@localhost'.
#
# [*tenant*]
# Tenant for Ironic user. Defaults to 'services'.
#
# [*configure_endpoint*]
# Should Ironic endpoint be configured? Defaults to 'true'.
#
# [*service_type*]
# Type of service. Defaults to 'baremetal'.
#
# [*public_protocol*]
# Protocol for public endpoint. Defaults to 'http'.
#
# [*public_address*]
# Public address for endpoint. Defaults to '127.0.0.1'.
#
# [*admin_address*]
# Admin address for endpoint. Defaults to '127.0.0.1'.
#
# [*internal_address*]
# Internal address for endpoint. Defaults to '127.0.0.1'.
#
# [*port*]
# Port for endpoint. Defaults to '6385'.
#
# [*public_port*]
# Port for public endpoint. Defaults to $port.
#
# [*region*]
# Region for endpoint. Defaults to 'RegionOne'.
#
class ironic::keystone::auth (
$password,
$auth_name = 'ironic',
$email = 'ironic@localhost',
$tenant = 'services',
$configure_endpoint = true,
$service_type = 'baremetal',
$public_protocol = 'http',
$public_address = '127.0.0.1',
$admin_address = '127.0.0.1',
$internal_address = '127.0.0.1',
$port = '6385',
$public_port = undef,
$region = 'RegionOne'
) {
Keystone_user_role["${auth_name}@${tenant}"] ~> Service <| name == 'ironic-server' |>
Keystone_endpoint["${region}/${auth_name}"] ~> Service <| name == 'ironic-server' |>
if ! $public_port {
$real_public_port = $port
} else {
$real_public_port = $public_port
}
keystone_user { $auth_name:
ensure => present,
password => $password,
email => $email,
tenant => $tenant,
}
keystone_user_role { "${auth_name}@${tenant}":
ensure => present,
roles => 'admin',
}
keystone_service { $auth_name:
ensure => present,
type => $service_type,
description => 'Ironic Networking Service',
}
if $configure_endpoint {
keystone_endpoint { "${region}/${auth_name}":
ensure => present,
public_url => "${public_protocol}://${public_address}:${real_public_port}/",
internal_url => "http://${internal_address}:${port}/",
admin_url => "http://${admin_address}:${port}/",
}
}
}

46
manifests/params.pp Normal file
View File

@ -0,0 +1,46 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.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.
#
# ironic::params
#
class ironic::params {
if($::osfamily == 'Redhat') {
$package_name = 'openstack-ironic'
$api_package = false
$conductor_package = false
$api_service = 'ironic-api'
$conductor_service = 'ironic-conductor'
$client_package = 'python-ironicclient'
} elsif($::osfamily == 'Debian') {
$package_name = 'ironic-common'
$api_service = 'ironic-api'
$conductor_service = 'ironic-conductor'
$api_package = 'ironic-api'
$conductor_package = 'ironic-conductor'
$client_package = 'python-ironicclient'
} else {
fail("Unsupported osfamily ${::osfamily}")
}
}

View File

@ -0,0 +1,40 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.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.
#
# Unit tests for ironic::client
#
require 'spec_helper'
describe 'ironic::client' do
context 'on Debian platforms' do
let :facts do
{ :osfamily => 'Debian' }
end
it { should contain_class('ironic::client') }
end
context 'on RedHat platforms' do
let :facts do
{ :osfamily => 'RedHat' }
end
it { should contain_class('ironic::client') }
end
end

View File

@ -0,0 +1,103 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.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.
#
# Unit tests for ironic::db::mysql
#
require 'spec_helper'
describe 'ironic::db::mysql' do
let :pre_condition do
'include mysql::server'
end
let :params do
{ :password => 'passw0rd' }
end
let :facts do
{ :osfamily => 'Debian' }
end
context 'on Debian platforms' do
let :facts do
{ :osfamily => 'Debian' }
end
it { should contain_class('ironic::db::mysql') }
end
context 'on RedHat platforms' do
let :facts do
{ :osfamily => 'RedHat' }
end
it { should contain_class('ironic::db::mysql') }
end
describe "overriding allowed_hosts param to array" do
let :params do
{
:password => 'ironicpass',
:allowed_hosts => ['127.0.0.1','%']
}
end
it {should_not contain_ironic__db__mysql__host_access("127.0.0.1").with(
:user => 'ironic',
:password => 'ironicpass',
:database => 'ironic'
)}
it {should contain_ironic__db__mysql__host_access("%").with(
:user => 'ironic',
:password => 'ironicpass',
:database => 'ironic'
)}
end
describe "overriding allowed_hosts param to string" do
let :params do
{
:password => 'ironicpass2',
:allowed_hosts => '192.168.1.1'
}
end
it {should contain_ironic__db__mysql__host_access("192.168.1.1").with(
:user => 'ironic',
:password => 'ironicpass2',
:database => 'ironic'
)}
end
describe "overriding allowed_hosts param equals to host param " do
let :params do
{
:password => 'ironicpass2',
:allowed_hosts => '127.0.0.1'
}
end
it {should_not contain_ironic__db__mysql__host_access("127.0.0.1").with(
:user => 'ironic',
:password => 'ironicpass2',
:database => 'ironic'
)}
end
end

View File

@ -0,0 +1,19 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.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.
#
# Unit tests for ironic
#

View File

@ -0,0 +1,120 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.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.
#
# Unit tests for ironic::keystone::auth
#
require 'spec_helper'
describe 'ironic::keystone::auth' do
describe 'with default class parameters' do
let :params do
{
:password => 'ironic_password',
:tenant => 'foobar'
}
end
it { should contain_keystone_user('ironic').with(
:ensure => 'present',
:password => 'ironic_password',
:tenant => 'foobar'
) }
it { should contain_keystone_user_role('ironic@foobar').with(
:ensure => 'present',
:roles => 'admin'
)}
it { should contain_keystone_service('ironic').with(
:ensure => 'present',
:type => 'network',
:description => 'Ironic Networking Service'
) }
it { should contain_keystone_endpoint('RegionOne/ironic').with(
:ensure => 'present',
:public_url => "http://127.0.0.1:6385/",
:admin_url => "http://127.0.0.1:6385/",
:internal_url => "http://127.0.0.1:6385/"
) }
end
describe 'when configuring ironic-server' do
let :pre_condition do
"class { 'ironic::server': auth_password => 'test' }"
end
let :facts do
{ :osfamily => 'Debian' }
end
let :params do
{
:password => 'ironic_password',
:tenant => 'foobar'
}
end
it { should contain_keystone_endpoint('RegionOne/ironic').with_notify('Service[ironic-server]') }
end
describe 'when overriding public_protocol, public_port and public address' do
let :params do
{
:password => 'ironic_password',
: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 { should contain_keystone_endpoint('RegionOne/ironic').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 => 'ironicy'
}
end
it { should contain_keystone_user('ironicy') }
it { should contain_keystone_user_role('ironicy@services') }
it { should contain_keystone_service('ironicy') }
it { should contain_keystone_endpoint('RegionOne/ironicy') }
end
end

View File

@ -0,0 +1,25 @@
require 'spec_helper'
describe 'ironic::db::mysql::host_access' do
let :pre_condition do
'include mysql'
end
let :title do
'127.0.0.1'
end
let :params do
{ :user => 'ironic',
:password => 'passw0rd',
:database => 'ironic' }
end
let :facts do
{ :osfamily => 'Debian' }
end
it { should contain_database_user('ironic@127.0.0.1') }
it { should contain_database_grant('ironic@127.0.0.1/ironic') }
end

0
spec/fixtures/manifests/site.pp vendored Normal file
View File

5
spec/shared_examples.rb Normal file
View File

@ -0,0 +1,5 @@
shared_examples_for "a Puppet::Error" do |description|
it "with message matching #{description.inspect}" do
expect { should have_class_count(1) }.to raise_error(Puppet::Error, description)
end
end

7
spec/spec_helper.rb Normal file
View File

@ -0,0 +1,7 @@
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'
end

View File

@ -0,0 +1,111 @@
require 'puppet'
require 'spec_helper'
require 'puppet/provider/ironic'
require 'tempfile'
describe Puppet::Provider::Ironic do
def klass
described_class
end
let :credential_hash do
{
'auth_host' => '192.168.56.210',
'auth_port' => '35357',
'auth_protocol' => 'https',
'admin_tenant_name' => 'admin_tenant',
'admin_user' => 'admin',
'admin_password' => 'password',
}
end
let :auth_endpoint do
'https://192.168.56.210:35357/v2.0/'
end
let :credential_error do
/Ironic types will not work/
end
after :each do
klass.reset
end
describe 'when determining credentials' do
it 'should fail if config is empty' do
conf = {}
klass.expects(:ironic_conf).returns(conf)
expect do
klass.ironic_credentials
end.to raise_error(Puppet::Error, credential_error)
end
it 'should fail if config does not have keystone_authtoken section.' do
conf = {'foo' => 'bar'}
klass.expects(:ironic_conf).returns(conf)
expect do
klass.ironic_credentials
end.to raise_error(Puppet::Error, credential_error)
end
it 'should fail if config does not contain all auth params' do
conf = {'keystone_authtoken' => {'invalid_value' => 'foo'}}
klass.expects(:ironic_conf).returns(conf)
expect do
klass.ironic_credentials
end.to raise_error(Puppet::Error, credential_error)
end
it 'should use specified host/port/protocol in the auth endpoint' do
conf = {'keystone_authtoken' => credential_hash}
klass.expects(:ironic_conf).returns(conf)
klass.get_auth_endpoint.should == auth_endpoint
end
end
describe 'when invoking the ironic cli' do
it 'should set auth credentials in the environment' do
authenv = {
:OS_AUTH_URL => auth_endpoint,
:OS_USERNAME => credential_hash['admin_user'],
:OS_TENANT_NAME => credential_hash['admin_tenant_name'],
:OS_PASSWORD => credential_hash['admin_password'],
}
klass.expects(:get_ironic_credentials).with().returns(credential_hash)
klass.expects(:withenv).with(authenv)
klass.auth_ironic('test_retries')
end
['[Errno 111] Connection refused',
'(HTTP 400)'].reverse.each do |valid_message|
it "should retry when ironic cli returns with error #{valid_message}" do
klass.expects(:get_ironic_credentials).with().returns({})
klass.expects(:sleep).with(10).returns(nil)
klass.expects(:ironic).twice.with(['test_retries']).raises(
Exception, valid_message).then.returns('')
klass.auth_ironic('test_retries')
end
end
end
describe 'when listing ironic resources' do
it 'should exclude the column header' do
output = <<-EOT
id
net1
net2
EOT
klass.expects(:auth_ironic).returns(output)
result = klass.list_ironic_resources('foo')
result.should eql(['net1', 'net2'])
end
end
end