Use system scope credentials in providers

This change enforces usage of system scope credentials to manage
flavors, aggregates, and services, following the new policy rules for
SRBAC support in nova.

The logic to look up credential for the nova service user from
[keystone_authtoken] is left to keep backward compatibility but is
deprecated and will be removed.

Depends-on: https://review.opendev.org/806474
Depends-on: https://review.opendev.org/828025
Depends-on: https://review.opendev.org/828874
Change-Id: I71779f0f1459d64914589a94a440336386266306
This commit is contained in:
Takashi Kajinami
2022-02-11 13:01:50 +09:00
parent 77138476e0
commit 0ed626e146
8 changed files with 196 additions and 165 deletions

View File

@@ -1,9 +1,3 @@
# Run test ie with: rspec spec/unit/provider/nova_spec.rb
# Add openstacklib code to $LOAD_PATH so that we can load this during
# standalone compiles without error.
File.expand_path('../../../../openstacklib/lib', File.dirname(__FILE__)).tap { |dir| $LOAD_PATH.unshift(dir) unless $LOAD_PATH.include?(dir) }
require 'puppet/util/inifile' require 'puppet/util/inifile'
require 'puppet/provider/openstack' require 'puppet/provider/openstack'
require 'puppet/provider/openstack/auth' require 'puppet/provider/openstack/auth'
@@ -13,15 +7,24 @@ class Puppet::Provider::Nova < Puppet::Provider::Openstack
extend Puppet::Provider::Openstack::Auth extend Puppet::Provider::Openstack::Auth
def self.request(service, action, properties=nil) def self.project_request(service, action, properties=nil, options={})
self.request(service, action, properties, options, 'project')
end
def self.system_request(service, action, properties=nil, options={})
self.request(service, action, properties, options, 'system')
end
def self.request(service, action, properties=nil, options={}, scope='project')
begin begin
super super
rescue Puppet::Error::OpenstackAuthInputError => error rescue Puppet::Error::OpenstackAuthInputError => error
nova_request(service, action, error, properties) nova_request(service, action, error, properties, options)
end end
end end
def self.nova_request(service, action, error, properties=nil) def self.nova_request(service, action, error, properties=nil, options={})
warning('Usage of keystone_authtoken parameters is deprecated.')
properties ||= [] properties ||= []
@credentials.username = nova_credentials['username'] @credentials.username = nova_credentials['username']
@credentials.password = nova_credentials['password'] @credentials.password = nova_credentials['password']
@@ -33,7 +36,7 @@ class Puppet::Provider::Nova < Puppet::Provider::Openstack
@credentials.region_name = nova_credentials['region_name'] @credentials.region_name = nova_credentials['region_name']
end end
raise error unless @credentials.set? raise error unless @credentials.set?
Puppet::Provider::Openstack.request(service, action, properties, @credentials) Puppet::Provider::Openstack.request(service, action, properties, @credentials, options)
end end
def self.nova_manage_request(*args) def self.nova_manage_request(*args)

View File

@@ -13,17 +13,17 @@ Puppet::Type.type(:nova_aggregate).provide(
mk_resource_methods mk_resource_methods
def self.instances def self.instances
request('aggregate', 'list').collect do |el| system_request('aggregate', 'list').collect do |el|
attrs = request('aggregate', 'show', el[:name]) attrs = system_request('aggregate', 'show', el[:name])
properties = parsestring(attrs[:properties]) rescue nil properties = parsestring(attrs[:properties]) rescue nil
new( new(
:ensure => :present, :ensure => :present,
:name => attrs[:name], :name => attrs[:name],
:id => attrs[:id], :id => attrs[:id],
:availability_zone => attrs[:availability_zone], :availability_zone => attrs[:availability_zone],
:metadata => properties, :metadata => properties,
:hosts => string2list(attrs[:hosts]).sort, :hosts => string2list(attrs[:hosts]).sort,
:filter_hosts => attrs[:filter_hosts] :filter_hosts => attrs[:filter_hosts]
) )
end end
end end
@@ -43,7 +43,7 @@ Puppet::Type.type(:nova_aggregate).provide(
def self.get_known_hosts def self.get_known_hosts
# get list of hosts known to be active from openstack # get list of hosts known to be active from openstack
return request('compute service', 'list', ['--service', 'nova-compute']).map{|el| el[:host]} return system_request('compute service', 'list', ['--service', 'nova-compute']).map{|el| el[:host]}
end end
def exists? def exists?
@@ -53,9 +53,9 @@ Puppet::Type.type(:nova_aggregate).provide(
def destroy def destroy
@property_hash[:hosts].each do |h| @property_hash[:hosts].each do |h|
properties = [@property_hash[:name], h] properties = [@property_hash[:name], h]
self.class.request('aggregate', 'remove host', properties) self.class.system_request('aggregate', 'remove host', properties)
end end
self.class.request('aggregate', 'delete', @property_hash[:name]) self.class.system_request('aggregate', 'delete', @property_hash[:name])
@property_hash.clear @property_hash.clear
end end
@@ -69,7 +69,7 @@ Puppet::Type.type(:nova_aggregate).provide(
properties << "--property" << "#{key}=#{value}" properties << "--property" << "#{key}=#{value}"
end end
end end
@property_hash = self.class.request('aggregate', 'create', properties) @property_hash = self.class.system_request('aggregate', 'create', properties)
if not @resource[:hosts].nil? and not @resource[:hosts].empty? if not @resource[:hosts].nil? and not @resource[:hosts].empty?
# filter host list by known hosts if filter_hosts is set # filter host list by known hosts if filter_hosts is set
if @resource[:filter_hosts] == :true if @resource[:filter_hosts] == :true
@@ -77,14 +77,14 @@ Puppet::Type.type(:nova_aggregate).provide(
end end
@resource[:hosts].each do |host| @resource[:hosts].each do |host|
properties = [@property_hash[:name], host] properties = [@property_hash[:name], host]
self.class.request('aggregate', 'add host', properties) self.class.system_request('aggregate', 'add host', properties)
end end
end end
@property_hash[:ensure] = :present @property_hash[:ensure] = :present
end end
def availability_zone=(value) def availability_zone=(value)
self.class.request('aggregate', 'set', [ @resource[:name], '--zone', @resource[:availability_zone] ]) self.class.system_request('aggregate', 'set', [ @resource[:name], '--zone', @resource[:availability_zone] ])
end end
def metadata=(value) def metadata=(value)
@@ -94,13 +94,13 @@ Puppet::Type.type(:nova_aggregate).provide(
(@property_hash[:metadata].keys - @resource[:metadata].keys).each do |key| (@property_hash[:metadata].keys - @resource[:metadata].keys).each do |key|
properties << "--property" << "#{key}" properties << "--property" << "#{key}"
end end
self.class.request('aggregate', 'unset', properties) self.class.system_request('aggregate', 'unset', properties)
end end
properties = [@resource[:name] ] properties = [@resource[:name] ]
@resource[:metadata].each do |key, value| @resource[:metadata].each do |key, value|
properties << "--property" << "#{key}=#{value}" properties << "--property" << "#{key}=#{value}"
end end
self.class.request('aggregate', 'set', properties) self.class.system_request('aggregate', 'set', properties)
end end
def hosts=(value) def hosts=(value)
@@ -111,11 +111,11 @@ Puppet::Type.type(:nova_aggregate).provide(
if not @property_hash[:hosts].nil? if not @property_hash[:hosts].nil?
# remove hosts that are not present in update # remove hosts that are not present in update
(@property_hash[:hosts] - value).each do |host| (@property_hash[:hosts] - value).each do |host|
self.class.request('aggregate', 'remove host', [@property_hash[:id], host]) self.class.system_request('aggregate', 'remove host', [@property_hash[:id], host])
end end
# add hosts that are not already present # add hosts that are not already present
(value - @property_hash[:hosts]).each do |host| (value - @property_hash[:hosts]).each do |host|
self.class.request('aggregate', 'add host', [@property_hash[:id], host]) self.class.system_request('aggregate', 'add host', [@property_hash[:id], host])
end end
end end
end end

View File

@@ -26,28 +26,28 @@ Puppet::Type.type(:nova_flavor).provide(
(opts << '--vcpus' << @resource[:vcpus]) if @resource[:vcpus] (opts << '--vcpus' << @resource[:vcpus]) if @resource[:vcpus]
(opts << '--swap' << @resource[:swap]) if @resource[:swap] (opts << '--swap' << @resource[:swap]) if @resource[:swap]
(opts << '--rxtx-factor' << @resource[:rxtx_factor]) if @resource[:rxtx_factor] (opts << '--rxtx-factor' << @resource[:rxtx_factor]) if @resource[:rxtx_factor]
@property_hash = self.class.request('flavor', 'create', opts) @property_hash = self.class.system_request('flavor', 'create', opts)
if @resource[:properties] and !(@resources[:properties].empty?) if @resource[:properties] and !(@resources[:properties].empty?)
prop_opts = [@resource[:name]] prop_opts = [@resource[:name]]
prop_opts << props_to_s(@resource[:properties]) prop_opts << props_to_s(@resource[:properties])
self.class.request('flavor', 'set', prop_opts) self.class.system_request('flavor', 'set', prop_opts)
end end
if @resource[:project] and @resource[:project] != '' if @resource[:project] and @resource[:project] != ''
proj_opts = [@resource[:name]] proj_opts = [@resource[:name]]
proj_opts << '--project' << @resource[:project] proj_opts << '--project' << @resource[:project]
self.class.request('flavor', 'set', proj_opts) self.class.system_request('flavor', 'set', proj_opts)
project = self.class.request('project', 'show', @resource[:project]) project = self.class.system_request('project', 'show', @resource[:project])
@property_hash[:project_name] = project[:name] @property_hash[:project_name] = project[:name]
@property_hash[:project] = project[:id] @property_hash[:project] = project[:id]
elsif @resource[:project_name] and @resource[:project_name] != '' elsif @resource[:project_name] and @resource[:project_name] != ''
proj_opts = [@resource[:name]] proj_opts = [@resource[:name]]
proj_opts << '--project' << @resource[:project_name] proj_opts << '--project' << @resource[:project_name]
self.class.request('flavor', 'set', proj_opts) self.class.system_request('flavor', 'set', proj_opts)
project = self.class.request('project', 'show', @resource[:project_name]) project = self.class.system_request('project', 'show', @resource[:project_name])
@property_hash[:project_name] = project[:name] @property_hash[:project_name] = project[:name]
@property_hash[:project] = project[:id] @property_hash[:project] = project[:id]
end end
@@ -60,7 +60,7 @@ Puppet::Type.type(:nova_flavor).provide(
end end
def destroy def destroy
self.class.request('flavor', 'delete', @property_hash[:id]) self.class.system_request('flavor', 'delete', @property_hash[:id])
@property_hash.clear @property_hash.clear
end end
@@ -93,8 +93,8 @@ Puppet::Type.type(:nova_flavor).provide(
end end
def self.instances def self.instances
request('flavor', 'list', ['--long', '--all']).collect do |attrs| system_request('flavor', 'list', ['--long', '--all']).collect do |attrs|
project = request('flavor', 'show', [attrs[:id], '-c', 'access_project_ids']) project = system_request('flavor', 'show', [attrs[:id], '-c', 'access_project_ids'])
access_project_ids = project[:access_project_ids] access_project_ids = project[:access_project_ids]
# Client can return None and this should be considered as '' # Client can return None and this should be considered as ''
@@ -110,7 +110,7 @@ Puppet::Type.type(:nova_flavor).provide(
project_value = project_value.gsub('\'', '') project_value = project_value.gsub('\'', '')
if project_value != '' if project_value != ''
project = request('project', 'show', project_value) project = system_request('project', 'show', project_value)
project_id = project[:id] project_id = project[:id]
project_name = project[:name] project_name = project[:name]
else else
@@ -151,7 +151,7 @@ Puppet::Type.type(:nova_flavor).provide(
opts = [@resource[:name]] opts = [@resource[:name]]
opts << props_to_s(@property_flush[:properties]) opts << props_to_s(@property_flush[:properties])
self.class.request('flavor', 'set', opts) self.class.system_request('flavor', 'set', opts)
@property_flush.clear @property_flush.clear
end end
@@ -159,20 +159,20 @@ Puppet::Type.type(:nova_flavor).provide(
if @project_flush[:project] if @project_flush[:project]
if @property_hash[:project] and @property_hash[:project] != '' if @property_hash[:project] and @property_hash[:project] != ''
opts = [@resource[:name], '--project', @property_hash[:project]] opts = [@resource[:name], '--project', @property_hash[:project]]
self.class.request('flavor', 'unset', opts) self.class.system_request('flavor', 'unset', opts)
end end
if @project_flush[:project] != '' if @project_flush[:project] != ''
opts = [@resource[:name], '--project', @project_flush[:project]] opts = [@resource[:name], '--project', @project_flush[:project]]
self.class.request('flavor', 'set', opts) self.class.system_request('flavor', 'set', opts)
end end
elsif @project_flush[:project_name] elsif @project_flush[:project_name]
if @property_hash[:project_name] and @property_hash[:project_name] != '' if @property_hash[:project_name] and @property_hash[:project_name] != ''
opts = [@resource[:name], '--project', @property_hash[:project_name]] opts = [@resource[:name], '--project', @property_hash[:project_name]]
self.class.request('flavor', 'unset', opts) self.class.system_request('flavor', 'unset', opts)
end end
if @project_flush[:project_name] != '' if @project_flush[:project_name] != ''
opts = [@resource[:name], '--project', @project_flush[:project_name]] opts = [@resource[:name], '--project', @project_flush[:project_name]]
self.class.request('flavor', 'set', opts) self.class.system_request('flavor', 'set', opts)
end end
end end
@project_flush.clear @project_flush.clear

View File

@@ -14,7 +14,7 @@ Puppet::Type.type(:nova_service).provide(
def self.instances def self.instances
hosts = {} hosts = {}
request('compute service', 'list').collect do |host_svc| system_request('compute service', 'list').collect do |host_svc|
hname = host_svc[:host] hname = host_svc[:host]
if hosts[hname].nil? if hosts[hname].nil?
hosts[hname] = Hash.new {|h,k| h[k]=[]} hosts[hname] = Hash.new {|h,k| h[k]=[]}
@@ -53,7 +53,7 @@ Puppet::Type.type(:nova_service).provide(
svcname_id_map.each do |service_name, id| svcname_id_map.each do |service_name, id|
if (@resource[:service_name].empty? || if (@resource[:service_name].empty? ||
(@resource[:service_name].include? service_name)) (@resource[:service_name].include? service_name))
self.class.request('compute service', 'delete', id) self.class.system_request('compute service', 'delete', id)
end end
end end
@property_hash.clear @property_hash.clear

View File

@@ -0,0 +1,22 @@
---
upgrade:
- |
Now the following resource types uses system scope credentail instead of
project scope credential when sending requests to Nova API.
- ``nova_aggregate``
- ``nova_flavor``
- ``nova_service``
deprecations:
- |
The following resource types have been using the credential written in
the ``[keystone_authtoken]`` section of ``nova.conf``. However this
behavior has been deprecated and now these resource types first look for
the yaml files in ``/etc/openstack/puppet``. Make sure one of
``clouds.yaml`` or ``admin-clouds.yaml`` (which is created by
puppet-keystone) is created in that directory.
- ``nova_aggregate``
- ``nova_flavor``
- ``nova_service``

View File

@@ -2,14 +2,12 @@ require 'puppet'
require 'spec_helper' require 'spec_helper'
require 'puppet/provider/nova_aggregate/openstack' require 'puppet/provider/nova_aggregate/openstack'
provider_class = Puppet::Type.type(:nova_aggregate).provider(:openstack) describe Puppet::Type.type(:nova_aggregate).provider(:openstack) do
describe provider_class do let(:set_env) do
shared_examples 'authenticated with environment variables' do
ENV['OS_USERNAME'] = 'test' ENV['OS_USERNAME'] = 'test'
ENV['OS_PASSWORD'] = 'abc123' ENV['OS_PASSWORD'] = 'abc123'
ENV['OS_PROJECT_NAME'] = 'test' ENV['OS_SYSTEM_SCOPE'] = 'all'
ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3' ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3'
end end
@@ -30,95 +28,94 @@ describe provider_class do
end end
let(:provider) do let(:provider) do
provider_class.new(resource) described_class.new(resource)
end end
it_behaves_like 'authenticated with environment variables' do before(:each) do
describe '#instances' do set_env
it 'finds existing aggregates' do end
provider_class.expects(:openstack)
.with('aggregate', 'list', '--quiet', '--format', 'csv', []) describe '#instances' do
.returns('"ID","Name","Availability Zone" it 'finds existing aggregates' do
described_class.expects(:openstack)
.with('aggregate', 'list', '--quiet', '--format', 'csv', [])
.returns('"ID","Name","Availability Zone"
just,"simple","just" just,"simple","just"
') ')
provider_class.expects(:openstack) described_class.expects(:openstack)
.with('aggregate', 'show', '--format', 'shell', 'simple') .with('aggregate', 'show', '--format', 'shell', 'simple')
.returns('"id="just" .returns('"id="just"
name="simple" name="simple"
availability_zone=just" availability_zone=just"
properties="key1=\'tomato\', key2=\'mushroom\'" properties="key1=\'tomato\', key2=\'mushroom\'"
hosts="[]" hosts="[]"
') ')
instances = provider_class.instances instances = described_class.instances
expect(instances.count).to eq(1) expect(instances.count).to eq(1)
expect(instances[0].name).to eq('simple') expect(instances[0].name).to eq('simple')
expect(instances[0].metadata).to eq({"key1"=>"tomato", "key2"=>"mushroom"}) expect(instances[0].metadata).to eq({"key1"=>"tomato", "key2"=>"mushroom"})
end
end end
end
describe '#create' do describe '#create' do
it 'creates aggregate' do it 'creates aggregate' do
provider.class.stubs(:openstack) described_class.expects(:openstack)
.with('aggregate', 'list', '--quiet', '--format', 'csv', '--long') .with('aggregate', 'create', '--format', 'shell',
.returns('"ID","Name","Availability Zone","Properties" ['just', '--zone', 'simple', '--property', 'nice=cookie' ])
') .returns('name="just"
provider.class.stubs(:openstack)
.with('aggregate', 'create', '--format', 'shell', ['just', '--zone', 'simple', '--property', 'nice=cookie' ])
.returns('name="just"
id="just" id="just"
availability_zone="simple" availability_zone="simple"
properties="{u\'nice\': u\'cookie\'}" properties="{u\'nice\': u\'cookie\'}"
hosts="[]" hosts="[]"
') ')
provider.class.stubs(:openstack) described_class.expects(:openstack)
.with('aggregate', 'add host', ['just', 'example']) .with('aggregate', 'add host', ['just', 'example'])
.returns('name="just" .returns('name="just"
id="just" id="just"
availability_zone="simple" availability_zone="simple"
properties="{u\'nice\': u\'cookie\'}" properties="{u\'nice\': u\'cookie\'}"
hosts="[u\'example\']" hosts="[u\'example\']"
') ')
provider.create
expect(provider.exists?).to be_truthy provider.create
end expect(provider.exists?).to be_truthy
end
end
describe '#destroy' do
it 'removes aggregate with hosts' do
described_class.expects(:openstack)
.with('aggregate', 'remove host', ['just', 'example'])
described_class.expects(:openstack)
.with('aggregate', 'delete', 'just')
provider.instance_variable_set(:@property_hash, aggregate_attrs)
provider.destroy
expect(provider.exists?).to be_falsey
end
end
describe '#pythondict2hash' do
it 'should return a hash with key-value when provided with a unicode python dict' do
s = "{u'key': 'value', u'key2': 'value2'}"
expect(described_class.pythondict2hash(s)).to eq({"key"=>"value", "key2"=>"value2"})
end end
describe '#destroy' do it 'should return a hash with key-value when provided with a python dict' do
it 'removes aggregate with hosts' do s = "{'key': 'value', 'key2': 'value2'}"
provider_class.expects(:openstack) expect(described_class.pythondict2hash(s)).to eq({"key"=>"value", "key2"=>"value2"})
.with('aggregate', 'remove host', ['just', 'example']) end
provider_class.expects(:openstack) end
.with('aggregate', 'delete', 'just')
provider.instance_variable_set(:@property_hash, aggregate_attrs) describe '#parsestring' do
provider.destroy it 'should call string2hash when provided with a string' do
expect(provider.exists?).to be_falsey s = "key='value', key2='value2'"
end expect(described_class.parsestring(s)).to eq({"key"=>"value", "key2"=>"value2"})
end end
describe '#pythondict2hash' do it 'should call pythondict2hash when provided with a hash' do
it 'should return a hash with key-value when provided with a unicode python dict' do s = "{u'key': 'value', u'key2': 'value2'}"
s = "{u'key': 'value', u'key2': 'value2'}" expect(described_class.parsestring(s)).to eq({"key"=>"value", "key2"=>"value2"})
expect(provider_class.pythondict2hash(s)).to eq({"key"=>"value", "key2"=>"value2"})
end
it 'should return a hash with key-value when provided with a python dict' do
s = "{'key': 'value', 'key2': 'value2'}"
expect(provider_class.pythondict2hash(s)).to eq({"key"=>"value", "key2"=>"value2"})
end
end end
describe '#parsestring' do
it 'should call string2hash when provided with a string' do
s = "key='value', key2='value2'"
expect(provider_class.parsestring(s)).to eq({"key"=>"value", "key2"=>"value2"})
end
it 'should call pythondict2hash when provided with a hash' do
s = "{u'key': 'value', u'key2': 'value2'}"
expect(provider_class.parsestring(s)).to eq({"key"=>"value", "key2"=>"value2"})
end
end
end end
end end
@@ -142,83 +139,83 @@ hosts="[u\'example\']"
end end
let(:provider) do let(:provider) do
provider_class.new(resource) described_class.new(resource)
end end
it_behaves_like 'authenticated with environment variables' do before(:each) do
set_env
end
# create an aggregate and actually aggregate hosts to it # create an aggregate and actually aggregate hosts to it
describe 'create aggregate and add/remove hosts with filter_hosts toggled' do describe 'create aggregate and add/remove hosts with filter_hosts toggled' do
it 'creates aggregate with filter_hosts toggled' do it 'creates aggregate with filter_hosts toggled' do
provider.class.stubs(:get_known_hosts) provider.class.stubs(:get_known_hosts)
.returns(['known', 'known_too']) .returns(['known', 'known_too'])
# these expectations are the actual tests that check the provider's behaviour # these expectations are the actual tests that check the provider's behaviour
# and make sure only known hosts ('known' is the only known host) will be # and make sure only known hosts ('known' is the only known host) will be
# aggregated. # aggregated.
provider_class.expects(:openstack) described_class.expects(:openstack)
.with('aggregate', 'create', '--format', 'shell', ['just', '--zone', 'simple', "--property", "nice=cookie"]) .with('aggregate', 'create', '--format', 'shell', ['just', '--zone', 'simple', "--property", "nice=cookie"])
.once .once
.returns('name="just" .returns('name="just"
id="just" id="just"
availability_zone="simple" availability_zone="simple"
properties="{u\'nice\': u\'cookie\'}" properties="{u\'nice\': u\'cookie\'}"
hosts="[]" hosts="[]"
') ')
provider_class.expects(:openstack) described_class.expects(:openstack)
.with('aggregate', 'add host', ['just', 'known']) .with('aggregate', 'add host', ['just', 'known'])
.once .once
.returns('name="just" .returns('name="just"
id="just" id="just"
availability_zone="simple" availability_zone="simple"
properties="{u\'nice\': u\'cookie\'}" properties="{u\'nice\': u\'cookie\'}"
hosts="[u\'known\']" hosts="[u\'known\']"
') ')
provider_class.expects(:openstack) described_class.expects(:openstack)
.with('aggregate', 'add host', ['just', 'known_too']) .with('aggregate', 'add host', ['just', 'known_too'])
.once .once
.returns('name="just" .returns('name="just"
id="just" id="just"
availability_zone="simple" availability_zone="simple"
properties="{u\'nice\': u\'cookie\'}" properties="{u\'nice\': u\'cookie\'}"
hosts="[u\'known\', u\'known_too\']" hosts="[u\'known\', u\'known_too\']"
') ')
provider_class.expects(:openstack) described_class.expects(:openstack)
.with('aggregate', 'remove host', ['just', 'known']) .with('aggregate', 'remove host', ['just', 'known'])
.once .once
.returns('name="just" .returns('name="just"
id="just" id="just"
availability_zone="simple" availability_zone="simple"
properties="{u\'nice\': u\'cookie\'}" properties="{u\'nice\': u\'cookie\'}"
hosts="[u\'known_too\']" hosts="[u\'known_too\']"
') ')
# this creates a provider with the attributes defined above as 'aggregate_attrs' # this creates a provider with the attributes defined above as 'aggregate_attrs'
# and tries to add some hosts and then to remove some hosts. # and tries to add some hosts and then to remove some hosts.
# the hosts will be filtered against known active hosts and the expectations # the hosts will be filtered against known active hosts and the expectations
# described above are the actual tests that check the provider's behaviour # described above are the actual tests that check the provider's behaviour
provider.create provider.create
property_hash = provider.instance_variable_get(:@property_hash) property_hash = provider.instance_variable_get(:@property_hash)
property_hash[:hosts] = ['known'] property_hash[:hosts] = ['known']
provider.instance_variable_set(:@property_hash, property_hash) provider.instance_variable_set(:@property_hash, property_hash)
provider.hosts = ['known', 'known_too', 'unknown'] provider.hosts = ['known', 'known_too', 'unknown']
property_hash = provider.instance_variable_get(:@property_hash) property_hash = provider.instance_variable_get(:@property_hash)
property_hash[:hosts] = ['known', 'known_too'] property_hash[:hosts] = ['known', 'known_too']
provider.instance_variable_set(:@property_hash, property_hash) provider.instance_variable_set(:@property_hash, property_hash)
provider.hosts = ['known_too'] provider.hosts = ['known_too']
end
end end
end end
end end

View File

@@ -2,19 +2,24 @@ require 'puppet'
require 'spec_helper' require 'spec_helper'
require 'puppet/provider/nova_flavor/openstack' require 'puppet/provider/nova_flavor/openstack'
provider_class = Puppet::Type.type(:nova_flavor).provider(:openstack) describe Puppet::Type.type(:nova_flavor).provider(:openstack) do
describe provider_class do let(:set_env) do
ENV['OS_USERNAME'] = 'test'
ENV['OS_PASSWORD'] = 'abc123'
ENV['OS_SYSTEM_SCOPE'] = 'all'
ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3'
end
describe 'managing flavors' do describe 'managing flavors' do
let(:flavor_attrs) do let(:flavor_attrs) do
{ {
:name => 'example', :name => 'example',
:id => '1', :id => '1',
:ram => '512', :ram => '512',
:disk => '1', :disk => '1',
:vcpus => '1', :vcpus => '1',
:ensure => 'present', :ensure => 'present',
} }
end end
@@ -23,7 +28,11 @@ describe provider_class do
end end
let(:provider) do let(:provider) do
provider_class.new(resource) described_class.new(resource)
end
before(:each) do
set_env
end end
describe '#create' do describe '#create' do
@@ -121,7 +130,7 @@ domain_id="domain_one_id"
describe '#destroy' do describe '#destroy' do
it 'removes flavor' do it 'removes flavor' do
provider_class.expects(:openstack) described_class.expects(:openstack)
.with('flavor', 'delete', '1') .with('flavor', 'delete', '1')
provider.instance_variable_set(:@property_hash, flavor_attrs) provider.instance_variable_set(:@property_hash, flavor_attrs)
provider.destroy provider.destroy

View File

@@ -9,7 +9,7 @@ describe provider_class do
shared_examples 'authenticated with environment variables' do shared_examples 'authenticated with environment variables' do
ENV['OS_USERNAME'] = 'test' ENV['OS_USERNAME'] = 'test'
ENV['OS_PASSWORD'] = 'abc123' ENV['OS_PASSWORD'] = 'abc123'
ENV['OS_PROJECT_NAME'] = 'test' ENV['OS_SYSTEM_SCOPE'] = 'all'
ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3' ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3'
end end