Application resource implementation
Change-Id: I5627f94d4fc0d0fa1a63968cfa7ed67350fcc769
This commit is contained in:
parent
28d2663030
commit
914a2d9c69
87
lib/puppet/provider/murano.rb
Normal file
87
lib/puppet/provider/murano.rb
Normal file
@ -0,0 +1,87 @@
|
||||
require 'puppet/util/inifile'
|
||||
|
||||
class Puppet::Provider::Murano < Puppet::Provider
|
||||
|
||||
def self.conf_filename
|
||||
'/etc/murano/murano.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.murano_conf
|
||||
return @murano_conf if @murano_conf
|
||||
@murano_conf = Puppet::Util::IniConfig::File.new
|
||||
@murano_conf.read(conf_filename)
|
||||
@murano_conf
|
||||
end
|
||||
|
||||
def self.murano_credentials
|
||||
@murano_credentials ||= get_murano_credentials
|
||||
end
|
||||
|
||||
def murano_credentials
|
||||
self.class.murano_credentials
|
||||
end
|
||||
|
||||
def self.get_murano_credentials
|
||||
#needed keys for authentication
|
||||
auth_keys = ['auth_uri', 'admin_tenant_name', 'admin_user', 'admin_password']
|
||||
conf = murano_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. Murano types will not work if murano is not ' +
|
||||
'correctly configured.')
|
||||
end
|
||||
end
|
||||
|
||||
def self.auth_murano(*args)
|
||||
m = murano_credentials
|
||||
authenv = {
|
||||
:OS_AUTH_URL => m['auth_uri'],
|
||||
:OS_USERNAME => m['admin_user'],
|
||||
:OS_TENANT_NAME => m['admin_tenant_name'],
|
||||
:OS_PASSWORD => m['admin_password'],
|
||||
:OS_ENDPOINT_TYPE => 'internalURL'
|
||||
}
|
||||
begin
|
||||
withenv authenv do
|
||||
murano(args)
|
||||
end
|
||||
rescue Exception => e
|
||||
if (e.message =~ /\[Errno 111\] Connection refused/) or
|
||||
(e.message =~ /\(HTTP 400\)/)
|
||||
sleep 10
|
||||
withenv authenv do
|
||||
murano(args)
|
||||
end
|
||||
else
|
||||
raise(e)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def auth_murano(*args)
|
||||
self.class.auth_murano(args)
|
||||
end
|
||||
|
||||
def self.reset
|
||||
@murano_conf = nil
|
||||
@murano_credentials = nil
|
||||
end
|
||||
|
||||
end
|
38
lib/puppet/provider/murano_application/murano.rb
Normal file
38
lib/puppet/provider/murano_application/murano.rb
Normal file
@ -0,0 +1,38 @@
|
||||
require File.join(File.dirname(__FILE__), '..','..','..',
|
||||
'puppet/provider/murano')
|
||||
|
||||
Puppet::Type.type(:murano_application).provide(
|
||||
:murano,
|
||||
:parent => Puppet::Provider::Murano
|
||||
) do
|
||||
|
||||
desc 'Manage murano applications'
|
||||
|
||||
commands :murano => 'murano'
|
||||
|
||||
mk_resource_methods
|
||||
|
||||
def exists?
|
||||
packages = auth_murano('package-list')
|
||||
return packages.split("\n")[1..-1].detect do |n|
|
||||
n =~ /^(\S+)\s+(#{resource[:name]})/
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
auth_murano('package-delete', resource[:name])
|
||||
end
|
||||
|
||||
def create
|
||||
opts = [ resource[:package_path] ]
|
||||
|
||||
unless resource[:category].nil?
|
||||
opts.push('-c').push(resource[:category])
|
||||
end
|
||||
|
||||
opts.push('--is-public').push('--exists-action').push('u')
|
||||
|
||||
auth_murano('package-import', opts)
|
||||
end
|
||||
|
||||
end
|
60
lib/puppet/type/murano_application.rb
Normal file
60
lib/puppet/type/murano_application.rb
Normal file
@ -0,0 +1,60 @@
|
||||
# murano_application type
|
||||
#
|
||||
# == Parameters
|
||||
# [*name*]
|
||||
# Name for the new application
|
||||
# Required
|
||||
#
|
||||
# [*package_path*]
|
||||
# Path to package file
|
||||
# Required
|
||||
#
|
||||
# [*category*]
|
||||
# Category for the new application
|
||||
# Optional
|
||||
#
|
||||
|
||||
require 'puppet'
|
||||
|
||||
Puppet::Type.newtype(:murano_application) do
|
||||
|
||||
@doc = 'Manage creation of Murano applications.'
|
||||
|
||||
ensurable
|
||||
|
||||
newparam(:name, :namevar => true) do
|
||||
desc 'Name for the new application'
|
||||
validate do |value|
|
||||
unless value.is_a? String
|
||||
raise ArgumentError, 'name parameter must be a String'
|
||||
end
|
||||
unless value =~ /^[a-z0-9\.\-_]+$/
|
||||
raise ArgumentError, "#{value} is not a valid name"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newproperty(:package_path) do
|
||||
desc 'Path to package file'
|
||||
validate do |value|
|
||||
unless value.is_a? String
|
||||
raise ArgumentError, 'package_path parameter must be a String'
|
||||
end
|
||||
end
|
||||
newvalues(/\S+/)
|
||||
end
|
||||
|
||||
newproperty(:category) do
|
||||
desc 'Package category'
|
||||
validate do |value|
|
||||
unless value.is_a? String
|
||||
raise ArgumentError, 'category parameter must be a String'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
validate do
|
||||
raise ArgumentError, 'Name and package path must be set' unless self[:name] and self[:package_path]
|
||||
end
|
||||
|
||||
end
|
32
manifests/application.pp
Normal file
32
manifests/application.pp
Normal file
@ -0,0 +1,32 @@
|
||||
# == Resource: murano::application
|
||||
#
|
||||
# murano application importer
|
||||
#
|
||||
# === Parameters
|
||||
#
|
||||
# [*package_ensure*]
|
||||
# (Optional) Ensure state for package
|
||||
# Defaults to 'present'
|
||||
#
|
||||
# [*package_name*]
|
||||
# (Optional) Application package name
|
||||
# Defaults to $title
|
||||
#
|
||||
# [*package_category*]
|
||||
# (Optional) Application category
|
||||
# Defaults to 'undef'
|
||||
#
|
||||
define murano::application (
|
||||
$package_ensure = 'present',
|
||||
$package_name = $title,
|
||||
$package_category = undef,
|
||||
) {
|
||||
|
||||
$package_path="/var/cache/murano/meta/${package_name}.zip"
|
||||
|
||||
murano_application { $package_name:
|
||||
ensure => $package_ensure,
|
||||
package_path => $package_path,
|
||||
category => $package_category,
|
||||
}
|
||||
}
|
27
spec/defines/murano_application_spec.rb
Normal file
27
spec/defines/murano_application_spec.rb
Normal file
@ -0,0 +1,27 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'murano::application' do
|
||||
|
||||
let(:title) { 'io.murano' }
|
||||
|
||||
describe 'with default parameters' do
|
||||
it { is_expected.to contain_murano_application('io.murano').with(
|
||||
:ensure => 'present',
|
||||
:package_path => '/var/cache/murano/meta/io.murano.zip'
|
||||
)}
|
||||
end
|
||||
|
||||
describe 'with package_category override' do
|
||||
let :params do {
|
||||
:package_category => 'library'
|
||||
}
|
||||
end
|
||||
|
||||
it { is_expected.to contain_murano_application('io.murano').with(
|
||||
:ensure => 'present',
|
||||
:package_path => '/var/cache/murano/meta/io.murano.zip',
|
||||
:category => 'library',
|
||||
)}
|
||||
end
|
||||
|
||||
end
|
62
spec/unit/provider/murano_application/murano_spec.rb
Normal file
62
spec/unit/provider/murano_application/murano_spec.rb
Normal file
@ -0,0 +1,62 @@
|
||||
require 'puppet'
|
||||
require 'puppet/provider/murano_application/murano'
|
||||
require 'tempfile'
|
||||
|
||||
provider_class = Puppet::Type.type(:murano_application).provider(:murano)
|
||||
|
||||
describe provider_class do
|
||||
|
||||
let :app_attrs do
|
||||
{
|
||||
:name => 'io.murano',
|
||||
:package_path => '/tmp/io.murano.zip',
|
||||
:ensure => 'present',
|
||||
}
|
||||
end
|
||||
|
||||
let :resource do
|
||||
Puppet::Type::Murano_application.new(app_attrs)
|
||||
end
|
||||
|
||||
let :provider do
|
||||
provider_class.new(resource)
|
||||
end
|
||||
|
||||
shared_examples 'murano_application' do
|
||||
describe '#exists?' do
|
||||
it 'should check existsing application' do
|
||||
provider.class.stubs(:application_exists?).returns(true)
|
||||
provider.expects(:auth_murano).with("package-list")
|
||||
.returns('"+----------------------------------+--------------------+----------------------------------------+---------------+-----------+\n| ID | Name | FQN | Author | Is Public |\n+----------------------------------+--------------------+----------------------------------------+---------------+-----------+\n| 9a23e4aea548462d82b66f2aee0f196e | Core library | io.murano | murano.io | True |\n+----------------------------------+--------------------+----------------------------------------+---------------+-----------+\n"')
|
||||
provider.exists?
|
||||
end
|
||||
|
||||
it 'should check non-existsing application' do
|
||||
resource[:name] = 'io.murano.qwe'
|
||||
provider.class.stubs(:application_exists?).returns(false)
|
||||
provider.expects(:auth_murano).with("package-list")
|
||||
.returns('"+----------------------------------+--------------------+----------------------------------------+---------------+-----------+\n| ID | Name | FQN | Author | Is Public |\n+----------------------------------+--------------------+----------------------------------------+---------------+-----------+\n| 9a23e4aea548462d82b66f2aee0f196e | Core library | io.murano | murano.io | True |\n+----------------------------------+--------------------+----------------------------------------+---------------+-----------+\n"')
|
||||
provider.exists?
|
||||
end
|
||||
end
|
||||
|
||||
describe '#create' do
|
||||
it 'should create application' do
|
||||
provider.expects(:auth_murano).with("package-import", ['/tmp/io.murano.zip', '--is-public', '--exists-action', 'u'] )
|
||||
.returns('')
|
||||
provider.create
|
||||
end
|
||||
end
|
||||
|
||||
describe '#destroy' do
|
||||
it 'should destroy application' do
|
||||
resource[:ensure] = :absent
|
||||
provider.expects(:auth_murano).with("package-delete", 'io.murano')
|
||||
.returns('')
|
||||
provider.destroy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like('murano_application')
|
||||
end
|
83
spec/unit/provider/murano_spec.rb
Normal file
83
spec/unit/provider/murano_spec.rb
Normal file
@ -0,0 +1,83 @@
|
||||
require 'puppet'
|
||||
require 'spec_helper'
|
||||
require 'puppet/provider/murano'
|
||||
require 'rspec/mocks'
|
||||
|
||||
describe Puppet::Provider::Murano do
|
||||
|
||||
def klass
|
||||
described_class
|
||||
end
|
||||
|
||||
let :credential_hash do
|
||||
{
|
||||
'auth_uri' => 'https://192.168.56.210:35357',
|
||||
'admin_tenant_name' => 'admin_tenant',
|
||||
'admin_user' => 'admin',
|
||||
'admin_password' => 'password',
|
||||
}
|
||||
end
|
||||
|
||||
let :credential_error do
|
||||
/Murano 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(:murano_conf).returns(conf)
|
||||
expect do
|
||||
klass.murano_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(:murano_conf).returns(conf)
|
||||
expect do
|
||||
klass.murano_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(:murano_conf).returns(conf)
|
||||
expect do
|
||||
klass.murano_credentials
|
||||
end.to raise_error(Puppet::Error, credential_error)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe 'when invoking the murano cli' do
|
||||
|
||||
it 'should set auth credentials in the environment' do
|
||||
authenv = {
|
||||
:OS_AUTH_URL => credential_hash['auth_uri'],
|
||||
:OS_USERNAME => credential_hash['admin_user'],
|
||||
:OS_TENANT_NAME => credential_hash['admin_tenant_name'],
|
||||
:OS_PASSWORD => credential_hash['admin_password'],
|
||||
:OS_ENDPOINT_TYPE => 'internalURL',
|
||||
}
|
||||
klass.expects(:get_murano_credentials).with().returns(credential_hash)
|
||||
klass.expects(:withenv).with(authenv)
|
||||
klass.auth_murano('test_retries')
|
||||
end
|
||||
|
||||
['[Errno 111] Connection refused',
|
||||
'(HTTP 400)'].reverse.each do |valid_message|
|
||||
it "should retry when murano cli returns with error #{valid_message}" do
|
||||
klass.expects(:get_murano_credentials).with().returns({})
|
||||
klass.expects(:sleep).with(10).returns(nil)
|
||||
klass.expects(:murano).twice.with(['test_retries']).raises(
|
||||
Exception, valid_message).then.returns('')
|
||||
klass.auth_murano('test_retries')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
25
spec/unit/type/murano_application_spec.rb
Normal file
25
spec/unit/type/murano_application_spec.rb
Normal file
@ -0,0 +1,25 @@
|
||||
require 'puppet'
|
||||
require 'puppet/type/murano_application'
|
||||
describe 'Puppet::Type.type(:murano_application)' do
|
||||
it 'should fail without package path' do
|
||||
expect { Puppet::Type.type(:murano_application).new(:name => 'io.murano') }.to raise_error(Puppet::Error, /Name and package path must be set/)
|
||||
end
|
||||
|
||||
it 'should reject an invalid name' do
|
||||
expect { Puppet::Type.type(:murano_application).new(:name => 8082, :package_path => '/tmp/io.zip') }.to raise_error(Puppet::Error, /name parameter must be a String/)
|
||||
expect { Puppet::Type.type(:murano_application).new(:name => 'io.murano.@', :package_path => '/tmp/io.zip') }.to raise_error(Puppet::Error, /is not a valid name/)
|
||||
end
|
||||
|
||||
it 'should reject an invalid package path' do
|
||||
expect { Puppet::Type.type(:murano_application).new(:name => 'io.murano', :package_path => 8082) }.to raise_error(Puppet::Error, /package_path parameter must be a String/)
|
||||
end
|
||||
|
||||
it 'should reject an invalid category' do
|
||||
expect { Puppet::Type.type(:murano_application).new(:name => 'io.murano', :package_path => '/tmp/io.zip', :category => 8082) }.to raise_error(Puppet::Error, /category parameter must be a String/)
|
||||
end
|
||||
|
||||
it 'should accept valid parameters' do
|
||||
Puppet::Type.type(:murano_application).new(:name => 'io.murano', :package_path => '/tmp/io.zip', :category => 'library')
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue
Block a user