From 0442a4764f4d13b84f6779fe39cfca117d37056c Mon Sep 17 00:00:00 2001 From: Bobby Ryterski Date: Mon, 21 Sep 2015 08:43:15 -0500 Subject: [PATCH 1/4] Support all Fog OpenStack options --- README.md | 2 ++ lib/chef/knife/cloud/openstack_service.rb | 35 +++++++++++++++-------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 4f8b577f..be22449e 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ In order to communicate with an OpenStack API you will need to tell Knife your O knife[:openstack_tenant] = "Your OpenStack tenant name" knife[:openstack_region] = "Your OpenStack Region" +All of Fog's `openstack` options (`openstack_domain_name`, `openstack_project_name`, ...) are supported. This includes support for the OpenStack Identity v3 API. + If your knife.rb file will be checked into a SCM system (ie readable by others) you may want to read the values from environment variables. For example, using the conventions of [OpenStack's RC file](http://docs.openstack.org/user-guide/content/cli_openrc.html) (note the `openstack_auth_url`): knife[:openstack_auth_url] = "#{ENV['OS_AUTH_URL']}/tokens" diff --git a/lib/chef/knife/cloud/openstack_service.rb b/lib/chef/knife/cloud/openstack_service.rb index ae991339..5dd2171a 100644 --- a/lib/chef/knife/cloud/openstack_service.rb +++ b/lib/chef/knife/cloud/openstack_service.rb @@ -18,18 +18,7 @@ def initialize(options = {}) Chef::Log.debug("openstack_insecure #{Chef::Config[:knife][:openstack_insecure]}") Chef::Log.debug("openstack_region #{Chef::Config[:knife][:openstack_region]}") - super(options.merge(auth_params: { - provider: 'OpenStack', - openstack_username: Chef::Config[:knife][:openstack_username], - openstack_api_key: Chef::Config[:knife][:openstack_password], - openstack_auth_url: Chef::Config[:knife][:openstack_auth_url], - openstack_endpoint_type: Chef::Config[:knife][:openstack_endpoint_type], - openstack_tenant: Chef::Config[:knife][:openstack_tenant], - openstack_region: Chef::Config[:knife][:openstack_region], - connection_options: { - ssl_verify_peer: !Chef::Config[:knife][:openstack_insecure] - } - })) + super(options.merge(auth_params: get_auth_params)) end # add alternate user defined api_endpoint value. @@ -54,6 +43,28 @@ def get_server(search_term) rescue Excon::Errors::BadRequest => e handle_excon_exception(CloudExceptions::KnifeCloudError, e) end + + def get_auth_params + load_fog_gem + params = { + provider: 'OpenStack', + connection_options: { + ssl_verify_peer: !Chef::Config[:knife][:openstack_insecure] + } + } + + ( + Fog::Compute::OpenStack.requirements + + Fog::Compute::OpenStack.recognized - + [:openstack_api_key] + ).each do |k| + next unless k.to_s.start_with?('openstack') + params[k] = Chef::Config[:knife][k] + end + params[:openstack_api_key] = Chef::Config[:knife][:openstack_password] || Chef::Config[:knife][:openstack_api_key] + + params + end end end end From 10ba3a7f37caf9f3462137ba3dc5396f4648e397 Mon Sep 17 00:00:00 2001 From: Bobby Ryterski Date: Mon, 28 Sep 2015 15:49:46 -0500 Subject: [PATCH 2/4] Fix tests * Use be_nil matchers * Update test with addresses format from #178 * Update test with ohai hint from #171 --- spec/functional/server_create_func_spec.rb | 4 +-- spec/functional/server_list_func_spec.rb | 37 +++++++++++----------- spec/unit/openstack_server_create_spec.rb | 2 +- spec/unit/openstack_service_spec.rb | 8 ++--- 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/spec/functional/server_create_func_spec.rb b/spec/functional/server_create_func_spec.rb index aff9573a..3477d238 100644 --- a/spec/functional/server_create_func_spec.rb +++ b/spec/functional/server_create_func_spec.rb @@ -75,7 +75,7 @@ context 'for Linux' do before do - @config = { openstack_floating_ip: '-1', bootstrap_ip_address: '75.101.253.10', ssh_password: 'password' } + @config = { openstack_floating_ip: '-1', bootstrap_ip_address: '75.101.253.10', ssh_password: 'password', hints: { "openstack" => {} }} @knife_openstack_create.config[:distro] = 'chef-full' @bootstrapper = Chef::Knife::Cloud::Bootstrapper.new(@config) @ssh_bootstrap_protocol = Chef::Knife::Cloud::SshBootstrapProtocol.new(@config) @@ -95,7 +95,7 @@ context 'for Windows' do before do - @config = { openstack_floating_ip: '-1', image_os_type: 'windows', bootstrap_ip_address: '75.101.253.10', bootstrap_protocol: 'winrm', ssh_password: 'password' } + @config = { openstack_floating_ip: '-1', image_os_type: 'windows', bootstrap_ip_address: '75.101.253.10', bootstrap_protocol: 'winrm', ssh_password: 'password', hints: { "openstack" => {} } } @knife_openstack_create.config[:image_os_type] = 'windows' @knife_openstack_create.config[:bootstrap_protocol] = 'winrm' @knife_openstack_create.config[:distro] = 'windows-chef-client-msi' diff --git a/spec/functional/server_list_func_spec.rb b/spec/functional/server_list_func_spec.rb index cc66c40e..cd71a9f8 100644 --- a/spec/functional/server_list_func_spec.rb +++ b/spec/functional/server_list_func_spec.rb @@ -39,9 +39,10 @@ end it 'lists formatted list of resources' do - expect(instance.ui).to receive(:list).with(['Name', 'Instance ID', 'Public IP', 'Private IP', 'Flavor', 'Image', 'Keypair', 'State', 'Availability Zone', - 'ubuntu01', 'resource-1', '172.31.6.132', '172.31.6.133', '1', 'image1', 'keypair', 'ACTIVE', 'test zone', - 'windows2008', 'resource-2', '172.31.6.132', nil, 'id2', 'image2', 'keypair', 'ACTIVE', 'test zone', 'windows2008', 'resource-3-err', nil, nil, 'id2', 'image2', 'keypair', 'ERROR', 'test zone'], :uneven_columns_across, 9) + expect(instance.ui).to receive(:list).with(['Name', 'Instance ID', 'Addresses', 'Flavor', 'Image', 'Keypair', 'State', 'Availability Zone', + 'ubuntu01', 'resource-1', 'public:IPv4: 172.31.6.132', '1', 'image1', 'keypair', 'ACTIVE', 'test zone', + 'windows2008', 'resource-2', 'public:IPv4: 172.31.6.132', 'id2', 'image2', 'keypair', 'ACTIVE', 'test zone', + 'windows2008', 'resource-3-err', '', 'id2', 'image2', 'keypair', 'ERROR', 'test zone'], :uneven_columns_across, 8) instance.run end @@ -54,22 +55,22 @@ end it 'lists formatted list of resources on chef data option set' do - expect(instance.ui).to receive(:list).with(['Name', 'Instance ID', 'Public IP', 'Private IP', 'Flavor', 'Image', 'Keypair', 'State', 'Availability Zone', 'Chef Node Name', 'Environment', 'FQDN', 'Runlist', 'Tags', 'Platform', - 'server-4', 'server-4', '172.31.6.132', '172.31.6.133', '1', 'image1', 'keypair', 'ACTIVE', 'test zone', 'server-4', '_default', 'testfqdnnode.us', '[]', '[]', 'ubuntu', - 'ubuntu01', 'resource-1', '172.31.6.132', '172.31.6.133', '1', 'image1', 'keypair', 'ACTIVE', 'test zone', '', '', '', '', '', '', - 'windows2008', 'resource-2', '172.31.6.132', nil, 'id2', 'image2', 'keypair', 'ACTIVE', 'test zone', '', '', '', '', '', '', - 'windows2008', 'resource-3-err', nil, nil, 'id2', 'image2', 'keypair', 'ERROR', 'test zone', '', '', '', '', '', ''], :uneven_columns_across, 15) + expect(instance.ui).to receive(:list).with(['Name', 'Instance ID', 'Addresses', 'Flavor', 'Image', 'Keypair', 'State', 'Availability Zone', 'Chef Node Name', 'Environment', 'FQDN', 'Runlist', 'Tags', 'Platform', + 'server-4', 'server-4', 'public:IPv4: 172.31.6.132', '1', 'image1', 'keypair', 'ACTIVE', 'test zone', 'server-4', '_default', 'testfqdnnode.us', '[]', '[]', 'ubuntu', + 'ubuntu01', 'resource-1', 'public:IPv4: 172.31.6.132', '1', 'image1', 'keypair', 'ACTIVE', 'test zone', '', '', '', '', '', '', + 'windows2008', 'resource-2', 'public:IPv4: 172.31.6.132', 'id2', 'image2', 'keypair', 'ACTIVE', 'test zone', '', '', '', '', '', '', + 'windows2008', 'resource-3-err', '', 'id2', 'image2', 'keypair', 'ERROR', 'test zone', '', '', '', '', '', ''], :uneven_columns_across, 14) instance.run end it 'lists formatted list of resources on chef-data and chef-node-attribute option set' do instance.config[:chef_node_attribute] = 'platform_family' expect(@node).to receive(:attribute?).with('platform_family').and_return(true) - expect(instance.ui).to receive(:list).with(['Name', 'Instance ID', 'Public IP', 'Private IP', 'Flavor', 'Image', 'Keypair', 'State', 'Availability Zone', 'Chef Node Name', 'Environment', 'FQDN', 'Runlist', 'Tags', 'Platform', 'platform_family', - 'server-4', 'server-4', '172.31.6.132', '172.31.6.133', '1', 'image1', 'keypair', 'ACTIVE', 'test zone', 'server-4', '_default', 'testfqdnnode.us', '[]', '[]', 'ubuntu', 'debian', - 'ubuntu01', 'resource-1', '172.31.6.132', '172.31.6.133', '1', 'image1', 'keypair', 'ACTIVE', 'test zone', '', '', '', '', '', '', '', - 'windows2008', 'resource-2', '172.31.6.132', nil, 'id2', 'image2', 'keypair', 'ACTIVE', 'test zone', '', '', '', '', '', '', '', - 'windows2008', 'resource-3-err', nil, nil, 'id2', 'image2', 'keypair', 'ERROR', 'test zone', '', '', '', '', '', '', ''], :uneven_columns_across, 16) + expect(instance.ui).to receive(:list).with(['Name', 'Instance ID', 'Addresses', 'Flavor', 'Image', 'Keypair', 'State', 'Availability Zone', 'Chef Node Name', 'Environment', 'FQDN', 'Runlist', 'Tags', 'Platform', 'platform_family', + 'server-4', 'server-4', 'public:IPv4: 172.31.6.132', '1', 'image1', 'keypair', 'ACTIVE', 'test zone', 'server-4', '_default', 'testfqdnnode.us', '[]', '[]', 'ubuntu', 'debian', + 'ubuntu01', 'resource-1', 'public:IPv4: 172.31.6.132', '1', 'image1', 'keypair', 'ACTIVE', 'test zone', '', '', '', '', '', '', '', + 'windows2008', 'resource-2', 'public:IPv4: 172.31.6.132', 'id2', 'image2', 'keypair', 'ACTIVE', 'test zone', '', '', '', '', '', '', '', + 'windows2008', 'resource-3-err', '', 'id2', 'image2', 'keypair', 'ERROR', 'test zone', '', '', '', '', '', '', ''], :uneven_columns_across, 15) instance.run end @@ -84,11 +85,11 @@ it 'not display chef-data on chef-node-attribute set but chef-data option missing' do instance.config[:chef_data] = false instance.config[:chef_node_attribute] = 'platform_family' - expect(instance.ui).to receive(:list).with(['Name', 'Instance ID', 'Public IP', 'Private IP', 'Flavor', 'Image', 'Keypair', 'State', 'Availability Zone', - 'server-4', 'server-4', '172.31.6.132', '172.31.6.133', '1', 'image1', 'keypair', 'ACTIVE', 'test zone', - 'ubuntu01', 'resource-1', '172.31.6.132', '172.31.6.133', '1', 'image1', 'keypair', 'ACTIVE', 'test zone', - 'windows2008', 'resource-2', '172.31.6.132', nil, 'id2', 'image2', 'keypair', 'ACTIVE', 'test zone', - 'windows2008', 'resource-3-err', nil, nil, 'id2', 'image2', 'keypair', 'ERROR', 'test zone'], :uneven_columns_across, 9) + expect(instance.ui).to receive(:list).with(['Name', 'Instance ID', 'Addresses', 'Flavor', 'Image', 'Keypair', 'State', 'Availability Zone', + 'server-4', 'server-4', 'public:IPv4: 172.31.6.132', '1', 'image1', 'keypair', 'ACTIVE', 'test zone', + 'ubuntu01', 'resource-1', 'public:IPv4: 172.31.6.132', '1', 'image1', 'keypair', 'ACTIVE', 'test zone', + 'windows2008', 'resource-2', 'public:IPv4: 172.31.6.132', 'id2', 'image2', 'keypair', 'ACTIVE', 'test zone', + 'windows2008', 'resource-3-err', '', 'id2', 'image2', 'keypair', 'ERROR', 'test zone'], :uneven_columns_across, 8) instance.run end end diff --git a/spec/unit/openstack_server_create_spec.rb b/spec/unit/openstack_server_create_spec.rb index ddecfca5..94d0dfbc 100644 --- a/spec/unit/openstack_server_create_spec.rb +++ b/spec/unit/openstack_server_create_spec.rb @@ -148,7 +148,7 @@ it 'ensures default value for metadata' do options = @instance.options - expect(options[:metadata][:default]).to be.nil? + expect(options[:metadata][:default]).to be_nil end end diff --git a/spec/unit/openstack_service_spec.rb b/spec/unit/openstack_service_spec.rb index e293daf3..02a3cd01 100644 --- a/spec/unit/openstack_service_spec.rb +++ b/spec/unit/openstack_service_spec.rb @@ -33,21 +33,21 @@ end it 'sets the api_endpoint in auth params' do - expect(@instance.instance_variable_get(:@auth_params)[:openstack_auth_url]).to be.nil? + expect(@instance.instance_variable_get(:@auth_params)[:openstack_auth_url]).to be_nil @instance.add_api_endpoint expect(@instance.instance_variable_get(:@auth_params)[:openstack_auth_url]).to be == @api_endpoint end it 'does not set the endpoint when --api-endpoint option is missing' do Chef::Config[:knife][:api_endpoint] = nil - expect(@instance.instance_variable_get(:@auth_params)[:openstack_auth_url]).to be.nil? + expect(@instance.instance_variable_get(:@auth_params)[:openstack_auth_url]).to be_nil @instance.add_api_endpoint expect(@instance.instance_variable_get(:@auth_params)[:openstack_auth_url]).to_not be == @api_endpoint - expect(@instance.instance_variable_get(:@auth_params)[:openstack_auth_url]).to be.nil? + expect(@instance.instance_variable_get(:@auth_params)[:openstack_auth_url]).to be_nil end it "doesn't set an OpenStack endpoint type by default" do - expect(Chef::Config[:knife][:openstack_endpoint_type]).to be.nil? + expect(Chef::Config[:knife][:openstack_endpoint_type]).to be_nil end end From 69cb6bfec760fb8d241b0b68a218cfd5194537a4 Mon Sep 17 00:00:00 2001 From: Bobby Ryterski Date: Tue, 29 Sep 2015 14:40:52 -0500 Subject: [PATCH 3/4] Add identity v3 example to README --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index be22449e..64bea0df 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,13 @@ In order to communicate with an OpenStack API you will need to tell Knife your O knife[:openstack_tenant] = "Your OpenStack tenant name" knife[:openstack_region] = "Your OpenStack Region" -All of Fog's `openstack` options (`openstack_domain_name`, `openstack_project_name`, ...) are supported. This includes support for the OpenStack Identity v3 API. +All of Fog's `openstack` options (`openstack_domain_name`, `openstack_project_name`, ...) are supported. This includes support for the OpenStack Identity v3 API: + + knife[:openstack_auth_url] = "http://cloud.mycompany.com:5000/v3/auth/tokens" + knife[:openstack_username] = "Your OpenStack Dashboard username" + knife[:openstack_password] = "Your OpenStack Dashboard password" + knife[:openstack_project_name] = "Your OpenStack project" + knife[:openstack_domain_name] = "Your OpenStack domain" If your knife.rb file will be checked into a SCM system (ie readable by others) you may want to read the values from environment variables. For example, using the conventions of [OpenStack's RC file](http://docs.openstack.org/user-guide/content/cli_openrc.html) (note the `openstack_auth_url`): From 4321d75148cf01f72a9ae094ca480c0875f3ed97 Mon Sep 17 00:00:00 2001 From: Bobby Ryterski Date: Tue, 29 Sep 2015 16:25:12 -0500 Subject: [PATCH 4/4] Add tests for get_auth_params --- spec/unit/openstack_service_spec.rb | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/spec/unit/openstack_service_spec.rb b/spec/unit/openstack_service_spec.rb index 02a3cd01..03c82ac1 100644 --- a/spec/unit/openstack_service_spec.rb +++ b/spec/unit/openstack_service_spec.rb @@ -86,4 +86,42 @@ end end end + + describe '#get_auth_params' do + let(:auth_params) do + Chef::Knife::Cloud::OpenstackService.new.instance_variable_get(:@auth_params) + end + + it 'sets ssl_verify_peer to false when openstack_insecure is true' do + Chef::Config[:knife][:openstack_insecure] = true + expect(auth_params[:connection_options][:ssl_verify_peer]).to be false + end + + it 'only copies openstack options from Fog' do + params = auth_params.keys - [:provider, :connection_options] + expect(params.all? { |p| p.to_s.start_with?('openstack') }).to be true + end + + context 'when openstack_password is set' do + before(:each) do + @expected = 'password' + Chef::Config[:knife][:openstack_password] = @expected + end + + it 'sets openstack_api_key from openstack_password' do + expect(auth_params[:openstack_api_key]).to be == @expected + end + + it 'prefers openstack_password over openstack_api_key' do + Chef::Config[:knife][:openstack_api_key] = 'unexpected' + expect(auth_params[:openstack_api_key]).to be == @expected + end + end + + it 'uses openstack_api_key if openstack_password is not set' do + @expected = 'password' + Chef::Config[:knife][:openstack_api_key] = @expected + expect(auth_params[:openstack_api_key]).to be == @expected + end + end end