diff --git a/.rubocop.yml b/.rubocop.yml index 5500e6d0..8ac2ed6f 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -22,3 +22,6 @@ LineLength: WordArray: MinSize: 3 + +MethodLength: + Max: 15 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e5f78e11..889263c1 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ This file is used to list changes made in each version of cookbook-openstack-common. +## 9.6.0 +* Add an option to store passwords in attributes instead of data bags. Deprecates the get_secret method and the development_mode. + ## 9.5.2 * Adds new python_packages attributes for database client packages diff --git a/attributes/default.rb b/attributes/default.rb index 7bb74bff..2c56cbcb 100644 --- a/attributes/default.rb +++ b/attributes/default.rb @@ -34,8 +34,24 @@ default['openstack']['common']['custom_template_banner'] = ' # pass = secret "passwords", "nova" # # The value of pass will be "nova" +# +# This attribute is now DEPRECATED and will be removed. Use the default +# attributes below instead. default['openstack']['developer_mode'] = false +# Use data bags for storing passwords +# Set this to false in order to get the passwords from attributes like: +# node['openstack']['secret'][key][type] +default['openstack']['use_databags'] = true + +# Default attributes when not using data bags (use_databags = false) +%w{block-storage object-storage compute database dashboard image identity + telemetry network object-storage orchestration}.each do |service| + %w{user service db token}.each do |type| + default['openstack']['secret'][service][type] = "#{service}-#{type}" + end +end + # The type of token signing to use (uuid or pki) default['openstack']['auth']['strategy'] = 'pki' diff --git a/libraries/passwords.rb b/libraries/passwords.rb index 26ebb161..13c8fdf1 100644 --- a/libraries/passwords.rb +++ b/libraries/passwords.rb @@ -5,6 +5,7 @@ # library:: passwords # # Copyright 2012-2013, AT&T Services, Inc. +# Copyright 2014, SUSE Linux, GmbH. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -47,7 +48,14 @@ module ::Openstack # rubocop:disable Documentation # # The nova_password will == 'nova_password' def secret(bag_name, index) - return (node['openstack']['secret'][index] || index) if node['openstack']['developer_mode'] + if node['openstack']['developer_mode'] + ::Chef::Log.warn( + "Developer mode for reading passwords is DEPRECATED and will "\ + "be removed. Please use attributes (and the get_password method) "\ + "instead.") + + return (node['openstack']['secret'][index] || index) + end key_path = node['openstack']['secret']['key_path'] ::Chef::Log.info "Loading encrypted databag #{bag_name}.#{index} using key at #{key_path}" secret = ::Chef::EncryptedDataBagItem.load_secret key_path @@ -57,17 +65,38 @@ module ::Openstack # rubocop:disable Documentation # Ease-of-use/standarization routine that returns a secret from the # attribute-specified openstack secrets databag. def get_secret(key) - secret node['openstack']['secret']['secrets_data_bag'], key + ::Chef::Log.warn( + "The get_secret method is DEPRECATED. "\ + "Use get_password(key, 'token') instead") + + if node['openstack']['use_databags'] + secret node['openstack']['secret']['secrets_data_bag'], key + else + node['openstack']['secret'][key]['token'] + end end - # Ease-of-use/standarization routine that returns a service/database/user - # password for a named OpenStack service/database/user. Accepts 'user', - # 'service' or 'db' as the type. + # Return a password using either data bags or attributes for + # storage. The storage mechanism used is determined by the + # node['openstack']['use_databags'] attribute. + # @param [String] type of password, one of 'user', 'service', 'db' or 'token' + # @param [String] the identifier of the password (usually the + # component name, but can also be a token name + # e.g. openstack_identity_bootstrap_token def get_password(type, key) - if ['db', 'user', 'service'].include?(type) - secret node['openstack']['secret']["#{type}_passwords_data_bag"], key - else + unless %w{db user service token}.include?(type) ::Chef::Log.error("Unsupported type for get_password: #{type}") + return + end + + if node['openstack']['use_databags'] + if type == 'token' + secret node['openstack']['secret']['secrets_data_bag'], key + else + secret node['openstack']['secret']["#{type}_passwords_data_bag"], key + end + else + node['openstack']['secret'][key][type] end end end diff --git a/metadata.rb b/metadata.rb index 7fde85e8..a847878a 100755 --- a/metadata.rb +++ b/metadata.rb @@ -4,7 +4,7 @@ maintainer_email 'cookbooks@lists.tfoundry.com' license 'Apache 2.0' description 'Common OpenStack attributes, libraries and recipes.' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version '9.5.2' +version '9.6.0' recipe 'openstack-common', 'Installs/Configures common recipes' recipe 'openstack-common::set_endpoints_by_interface', 'Set endpoints by interface' diff --git a/spec/password_spec.rb b/spec/password_spec.rb index 19364ec2..4bd3860b 100644 --- a/spec/password_spec.rb +++ b/spec/password_spec.rb @@ -11,39 +11,66 @@ describe 'openstack-common::default' do include_context 'library-stubs' - describe '#secret' do - it 'returns databag' do - value = { 'nova' => 'this' } - ::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret') - ::Chef::EncryptedDataBagItem.stub(:load).with('passwords', 'nova', 'secret').and_return(value) - expect(subject.secret('passwords', 'nova')).to eq('this') - end - end - - describe '#get_secret' do - it 'returns databag' do - value = { 'nova' => 'this' } - ::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret') - ::Chef::EncryptedDataBagItem.stub(:load).with('secrets', 'nova', 'secret').and_return(value) - expect(subject.get_secret('nova')).to eq('this') - end - - it 'returns secret from an alternate databag when secrets_data_bag set' do - node.set['openstack']['secret']['secrets_data_bag'] = 'myothersecrets' - value = { 'nova' => 'this' } - ::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret') - ::Chef::EncryptedDataBagItem.stub(:load).with('myothersecrets', 'nova', 'secret').and_return(value) - expect(subject.get_secret('nova')).to eq('this') - end - end - - describe '#get_password' do - ['service', 'db', 'user'].each do |type| - it "returns databag for #{type}" do + context 'stored in data bags by default' do + describe '#secret' do + it 'returns databag' do value = { 'nova' => 'this' } ::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret') - ::Chef::EncryptedDataBagItem.stub(:load).with("#{type}_passwords", 'nova', 'secret').and_return(value) - expect(subject.get_password(type, 'nova')).to eq('this') + ::Chef::EncryptedDataBagItem.stub(:load).with('passwords', 'nova', 'secret').and_return(value) + expect(subject.secret('passwords', 'nova')).to eq('this') + end + end + + describe '#get_secret' do + it 'returns databag value' do + value = { 'nova' => 'this' } + ::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret') + ::Chef::EncryptedDataBagItem.stub(:load).with('secrets', 'nova', 'secret').and_return(value) + expect(subject.get_secret('nova')).to eq('this') + end + + it 'returns secret from an alternate databag when secrets_data_bag set' do + node.set['openstack']['secret']['secrets_data_bag'] = 'myothersecrets' + value = { 'nova' => 'this' } + ::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret') + ::Chef::EncryptedDataBagItem.stub(:load).with('myothersecrets', 'nova', 'secret').and_return(value) + expect(subject.get_secret('nova')).to eq('this') + end + end + + describe '#get_password' do + ['service', 'db', 'user'].each do |type| + it "returns databag value for #{type}" do + value = { 'nova' => 'this' } + ::Chef::EncryptedDataBagItem.stub(:load_secret).with('/etc/chef/openstack_data_bag_secret').and_return('secret') + ::Chef::EncryptedDataBagItem.stub(:load).with("#{type}_passwords", 'nova', 'secret').and_return(value) + expect(subject.get_password(type, 'nova')).to eq('this') + end + end + + it 'returns nil for an invalid type' do + expect(subject.get_password('invalid_type', 'nova')).to be_nil + end + + it 'returns tokens from the secrets_data_bag' do + bag_content = { 'nova' => 'mysecret' } + ::Chef::EncryptedDataBagItem.stub(:load_secret).with( + '/etc/chef/openstack_data_bag_secret').and_return('secret') + ::Chef::EncryptedDataBagItem.stub(:load).with( + 'secrets', 'nova', 'secret').and_return(bag_content) + expect(subject.get_password('token', 'nova')).to eq('mysecret') + end + end + end + + context 'stored in attributes as an alternative' do + before { node.set['openstack']['use_databags'] = false } + + describe '#get_password' do + %w{service db user token}.each do |type| + it "returns the set attribute for #{type}" do + expect(subject.get_password(type, 'compute')).to eq("compute-#{type}") + end end end end