From d4073c27216a9432d9df312997be5fad91b4c0f5 Mon Sep 17 00:00:00 2001 From: Gilles Dubreuil Date: Thu, 2 Apr 2015 16:33:44 +1100 Subject: [PATCH] Targeting Keystone V3 API support The default domain (id 'default', name 'Default') is where the V2 tenants/users are defined. So V3, which is now the default API's version can and should be used. Beeing able to use V3 domains needs to be supported by specifying the domain name for a project/user. This patch : - Adds project and user domain names - Renames tenant (v2) as project (v3) - Renames os-auth-url to os-url, when using an authicated token against a service url, to distinct them from each other, as in OSC (opentackclient) - Updates newparam(:auth) accordingly to describe v2/v3 credential examples Note: Keystone API v2 is deprecated [1] [1] http://docs.openstack.org/developer/keystone/http-api.html#should-i-use-v2-0-or-v3 Change-Id: I72f79129a6875eb433eeb8a62f928e7210db134a --- lib/puppet/provider/openstack.rb | 38 ++++++++------- lib/puppet/util/openstack.rb | 43 ++++++++++++----- spec/unit/provider/openstack_spec.rb | 72 ++++++++++++++++++---------- 3 files changed, 100 insertions(+), 53 deletions(-) diff --git a/lib/puppet/provider/openstack.rb b/lib/puppet/provider/openstack.rb index ec1cffe2..391e327f 100644 --- a/lib/puppet/provider/openstack.rb +++ b/lib/puppet/provider/openstack.rb @@ -106,46 +106,51 @@ class Puppet::Provider::Openstack < Puppet::Provider private def password_credentials_set?(auth_params) - auth_params && auth_params['username'] && auth_params['password'] && auth_params['tenant_name'] && auth_params['auth_url'] + auth_params && auth_params['username'] && auth_params['password'] && auth_params['project_name'] && auth_params['auth_url'] end - def openrc_set?(auth_params) auth_params && auth_params['openrc'] end - def service_credentials_set?(auth_params) - auth_params && auth_params['token'] && auth_params['auth_url'] + auth_params && auth_params['token'] && auth_params['url'] end - def self.env_vars_set? - ENV['OS_USERNAME'] && ENV['OS_PASSWORD'] && ENV['OS_TENANT_NAME'] && ENV['OS_AUTH_URL'] + ENV['OS_USERNAME'] && ENV['OS_PASSWORD'] && ENV['OS_PROJECT_NAME'] && ENV['OS_AUTH_URL'] end - def env_vars_set? self.class.env_vars_set? end - - def self.password_auth_args(credentials) - ['--os-username', credentials['username'], - '--os-password', credentials['password'], - '--os-tenant-name', credentials['tenant_name'], - '--os-auth-url', credentials['auth_url']] + creds = [ '--os-username', credentials['username'], + '--os-password', credentials['password'], + '--os-project-name', credentials['project_name'], + '--os-auth-url', credentials['auth_url'] ] + + if credentials.include?('project_domain_name') + creds << '--os-project-domain-name' + creds << credentials['project_domain_name'] + end + + if credentials.include?('user_domain_name') + creds << '--os-user-domain-name' + creds << credentials['user_domain_name'] + end + + creds end def password_auth_args(credentials) self.class.password_auth_args(credentials) end - def self.token_auth_args(credentials) - ['--os-token', credentials['token'], - '--os-url', credentials['auth_url']] + [ '--os-token', credentials['token'], + '--os-url', credentials['url'] ] end def token_auth_args(credentials) @@ -163,7 +168,6 @@ class Puppet::Provider::Openstack < Puppet::Provider return creds end - def self.get_credentials_from_env env = ENV.to_hash.dup.delete_if { |key, _| ! (key =~ /^OS_/) } credentials = {} diff --git a/lib/puppet/util/openstack.rb b/lib/puppet/util/openstack.rb index 867964af..916aa8e1 100644 --- a/lib/puppet/util/openstack.rb +++ b/lib/puppet/util/openstack.rb @@ -5,27 +5,46 @@ module Puppet::Util::Openstack type.newparam(:auth) do desc < { - 'username' => 'test', - 'password' => 'passw0rd', - 'tenant_name' => 'test', - 'auth_url' => 'http://localhost:35357/v2.0', + 'username' => 'test', + 'password' => 'changeme', + 'project_name' => 'test', + 'auth_url' => 'http://localhost:35357/v2.0' } -or a path to an openrc file containing these credentials, e.g.: - +or altenatively for Keystone API V3: auth => { - 'openrc' => '/root/openrc', + 'username' => 'test', + 'password' => 'changeme', + 'project_name' => 'test', + 'project_domain_name' => 'domain1', + 'user_domain_name' => 'domain1', + 'auth_url' => 'http://localhost:35357/v3' } -or a service token and host, e.g.: +2. Using a path to an openrc file containing these credentials auth => { - 'service_token' => 'ADMIN', - 'auth_url' => 'http://localhost:35357/v2.0', + 'openrc' => '/root/openrc' +} + +3. Using a service token + +For Keystone API V2: +auth => { + 'token' => 'example', + 'url' => 'http://localhost:35357/v2.0' +} + +Alternatively for Keystone API V3: +auth => { + 'token' => 'example', + 'url' => 'http://localhost:35357/v3.0' } If not present, the provider will look for environment variables for diff --git a/spec/unit/provider/openstack_spec.rb b/spec/unit/provider/openstack_spec.rb index c5f5efe1..558c4908 100644 --- a/spec/unit/provider/openstack_spec.rb +++ b/spec/unit/provider/openstack_spec.rb @@ -8,10 +8,10 @@ require 'puppet/provider/openstack' describe Puppet::Provider::Openstack do before(:each) do - ENV['OS_USERNAME'] = nil - ENV['OS_PASSWORD'] = nil - ENV['OS_TENANT_NAME'] = nil - ENV['OS_AUTH_URL'] = nil + ENV['OS_USERNAME'] = nil + ENV['OS_PASSWORD'] = nil + ENV['OS_PROJECT_NAME'] = nil + ENV['OS_AUTH_URL'] = nil end let(:type) do @@ -22,12 +22,36 @@ describe Puppet::Provider::Openstack do end end - shared_examples 'authenticating with environment variables' do + shared_examples 'authenticating with environment variables using API v2' do it 'makes a successful request' do - ENV['OS_USERNAME'] = 'test' - ENV['OS_PASSWORD'] = 'abc123' - ENV['OS_TENANT_NAME'] = 'test' - ENV['OS_AUTH_URL'] = 'http://127.0.0.1:35357/v2.0' + ENV['OS_USERNAME'] = 'test' + ENV['OS_PASSWORD'] = 'abc123' + ENV['OS_PROJECT_NAME'] = 'test' + ENV['OS_AUTH_URL'] = 'http://127.0.0.1:35357/v2.0' + if provider.class == Class + provider.stubs(:openstack) + .with('project', 'list', '--quiet', '--format', 'csv', [[ '--long' ]]) + .returns('"ID","Name","Description","Enabled" +"1cb05cfed7c24279be884ba4f6520262","test","Test tenant",True +') + else + provider.class.stubs(:openstack) + .with('project', 'list', '--quiet', '--format', 'csv', [[ '--long' ]]) + .returns('"ID","Name","Description","Enabled" +"1cb05cfed7c24279be884ba4f6520262","test","Test tenant",True +') + end + response = provider.request('project', 'list', nil, nil, '--long' ) + expect(response.first[:description]).to match /Test tenant/ + end + end + + shared_examples 'authenticating with environment variables using API v3' do + it 'makes a successful request' do + ENV['OS_USERNAME'] = 'test' + ENV['OS_PASSWORD'] = 'abc123' + ENV['OS_PROJECT_NAME'] = 'test' + ENV['OS_AUTH_URL'] = 'http://127.0.0.1:35357/v3' if provider.class == Class provider.stubs(:openstack) .with('project', 'list', '--quiet', '--format', 'csv', [[ '--long' ]]) @@ -59,10 +83,10 @@ describe Puppet::Provider::Openstack do { :name => 'stubresource', :auth => { - 'username' => 'test', - 'password' => 'abc123', - 'tenant_name' => 'test', - 'auth_url' => 'http://127.0.0.1:5000/v2.0', + 'username' => 'test', + 'password' => 'abc123', + 'project_name' => 'test', + 'auth_url' => 'http://127.0.0.1:5000/v2.0', } } end @@ -72,7 +96,7 @@ describe Puppet::Provider::Openstack do it 'makes a successful request' do provider.class.stubs(:openstack) - .with('project', 'list', '--quiet', '--format', 'csv', [['--long', '--os-username', 'test', '--os-password', 'abc123', '--os-tenant-name', 'test', '--os-auth-url', 'http://127.0.0.1:5000/v2.0']]) + .with('project', 'list', '--quiet', '--format', 'csv', [['--long', '--os-username', 'test', '--os-password', 'abc123', '--os-project-name', 'test', '--os-auth-url', 'http://127.0.0.1:5000/v2.0']]) .returns('"ID","Name","Description","Enabled" "1cb05cfed7c24279be884ba4f6520262","test","Test tenant",True ') @@ -82,7 +106,7 @@ describe Puppet::Provider::Openstack do end context 'with valid openrc file in parameters' do - mock = "export OS_USERNAME='test'\nexport OS_PASSWORD='abc123'\nexport OS_TENANT_NAME='test'\nexport OS_AUTH_URL='http://127.0.0.1:5000/v2.0'" + mock = "export OS_USERNAME='test'\nexport OS_PASSWORD='abc123'\nexport OS_PROJECT_NAME='test'\nexport OS_AUTH_URL='http://127.0.0.1:5000/v2.0'" let(:resource_attrs) do { :name => 'stubresource', @@ -98,7 +122,7 @@ describe Puppet::Provider::Openstack do it 'makes a successful request' do File.expects(:open).with('/root/openrc').returns(StringIO.new(mock)) provider.class.stubs(:openstack) - .with('project', 'list', '--quiet', '--format', 'csv', [['--long', '--os-username', 'test', '--os-password', 'abc123', '--os-tenant-name', 'test', '--os-auth-url', 'http://127.0.0.1:5000/v2.0']]) + .with('project', 'list', '--quiet', '--format', 'csv', [['--long', '--os-username', 'test', '--os-password', 'abc123', '--os-project-name', 'test', '--os-auth-url', 'http://127.0.0.1:5000/v2.0']]) .returns('"ID","Name","Description","Enabled" "1cb05cfed7c24279be884ba4f6520262","test","Test tenant",True ') @@ -113,7 +137,7 @@ describe Puppet::Provider::Openstack do :name => 'stubresource', :auth => { 'token' => 'secrettoken', - 'auth_url' => 'http://127.0.0.1:5000/v2.0' + 'url' => 'http://127.0.0.1:5000/v2.0' } } end @@ -149,7 +173,7 @@ Enabled="True" end context 'with valid password credentials in environment variables' do - it_behaves_like 'authenticating with environment variables' do + it_behaves_like 'authenticating with environment variables using API v2' do let(:resource_attrs) do { :name => 'stubresource', @@ -179,10 +203,10 @@ Enabled="True" { :name => 'stubresource', :auth => { - 'username' => 'test', - 'password' => 'abc123', - 'tenant_name' => 'test', - 'auth_url' => 'http://127.0.0.1:5000/v2.0', + 'username' => 'test', + 'password' => 'abc123', + 'project_name' => 'test', + 'auth_url' => 'http://127.0.0.1:5000/v2.0', } } end @@ -191,7 +215,7 @@ Enabled="True" end it 'retries' do provider.class.stubs(:openstack) - .with('project', 'list', '--quiet', '--format', 'csv', [['--long', '--os-username', 'test', '--os-password', 'abc123', '--os-tenant-name', 'test', '--os-auth-url', 'http://127.0.0.1:5000/v2.0']]) + .with('project', 'list', '--quiet', '--format', 'csv', [['--long', '--os-username', 'test', '--os-password', 'abc123', '--os-project-name', 'test', '--os-auth-url', 'http://127.0.0.1:5000/v2.0']]) .raises(Puppet::ExecutionFailure, 'Unable to establish connection') .then .returns('') @@ -205,7 +229,7 @@ Enabled="True" describe '::request' do context 'with valid password credentials in environment variables' do - it_behaves_like 'authenticating with environment variables' do + it_behaves_like 'authenticating with environment variables using API v2' do let(:resource_attrs) do { :name => 'stubresource',