From 2aa0ab91156705a25be372f1bee6b72c179893bc Mon Sep 17 00:00:00 2001 From: Jan Klare Date: Fri, 8 May 2015 16:56:31 +0200 Subject: [PATCH] replaced libraries/database with lwrp - defined lwrp openstack_common_database - removed libraries/database.rb - created _openstack_common_database_spec.rb recipe for testing the provider - comments for db2 in provider since this resource does not exist in any availabe or linked opensource cookbook - updated README.md related-Change-Id: Idb47c9b7e3ce954ddd1a544b71b96eed734e46e5 Change-Id: I1940cd63aa1ae95586e6ecbed9476f7ce5fe19ab --- .rubocop.yml | 2 +- .rubocop_todo.yml | 6 +- Berksfile | 4 + README.md | 21 +++- libraries/database.rb | 110 ---------------- libraries/matchers.rb | 5 + providers/database.rb | 117 +++++++++++++++++ resources/database.rb | 6 + .../metadata.rb | 8 ++ .../recipes/default.rb | 6 + spec/database_provider_spec.rb | 119 ++++++++++++++++++ spec/database_spec.rb | 38 ------ 12 files changed, 289 insertions(+), 153 deletions(-) delete mode 100644 libraries/database.rb create mode 100644 providers/database.rb create mode 100644 resources/database.rb create mode 100644 spec/cookbooks/test-openstack-common-database/metadata.rb create mode 100644 spec/cookbooks/test-openstack-common-database/recipes/default.rb create mode 100644 spec/database_provider_spec.rb delete mode 100644 spec/database_spec.rb diff --git a/.rubocop.yml b/.rubocop.yml index dc5fb268..e63c4753 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -30,4 +30,4 @@ WordArray: MinSize: 3 MethodLength: - Max: 15 \ No newline at end of file + Max: 15 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 6c8948ac..0076b117 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,5 +1,5 @@ # This configuration was generated by `rubocop --auto-gen-config` -# on 2015-05-28 12:53:22 -0500 using RuboCop version 0.29.1. +# on 2015-06-04 16:44:34 +0200 using RuboCop version 0.28.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -7,13 +7,13 @@ # Offense count: 9 Metrics/AbcSize: - Max: 56 + Max: 31 # Offense count: 1 Metrics/PerceivedComplexity: Max: 14 -# Offense count: 11 +# Offense count: 10 # Configuration parameters: EnforcedStyle, SupportedStyles. Style/ClassAndModuleChildren: Enabled: false diff --git a/Berksfile b/Berksfile index 967b9a78..585cde3d 100644 --- a/Berksfile +++ b/Berksfile @@ -1,3 +1,7 @@ source "https://supermarket.chef.io" metadata + +# cookbook for testing database provider: +cookbook 'test-openstack-common-database', + path: 'spec/cookbooks/test-openstack-common-database' diff --git a/README.md b/README.md index 1df06860..0a0792a4 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,26 @@ Documentation for Attributes for selecting databag format can be found in the at Documentation for format of these Databags can be found in the [Openstack Chef Repo](https://github.com/stackforge/openstack-chef-repo#databags) repository. +LWRPs +===== + +This cookbook provides the openstack_common_database LWRP, which replaces the old database library function 'db_create_with_user'. +When this coobook is included as dependency, this LWRP can be used to create databases needed by the openstack services. + +```ruby +depends 'openstack-common' +``` + +```ruby +openstack_common_database 'compute' do + service 'compute' # name_attribute + user 'nova' + pass 'supersecret' +end +``` + +An example of the usage can be seen here https://github.com/stackforge/cookbook-openstack-ops-database/blob/master/recipes/openstack-db.rb. + Libraries ========= @@ -121,7 +141,6 @@ This cookbook exposes a set of default library routines: * `endpoints` -- Useful for operating on all OpenStack endpoints * `db` -- Returns a Hash of information about a named OpenStack database * `db_uri` -- Returns the SQLAlchemy RFC-1738 DB URI (see: http://rfc.net/rfc1738.html) for a named OpenStack database -* `db_create_with_user` -- Creates a database and database user for a named OpenStack database * `secret` -- Returns the value of an encrypted data bag for a named OpenStack secret key and key-section * `get_password` -- Ease-of-use helper that returns the decrypted password for a named database, service or keystone user. * `matchers` -- A custom matcher(render_config_file) for testing ini format file section content by with_section_content. diff --git a/libraries/database.rb b/libraries/database.rb deleted file mode 100644 index ac51c5cd..00000000 --- a/libraries/database.rb +++ /dev/null @@ -1,110 +0,0 @@ -# encoding: UTF-8 - -# -# Cookbook Name:: openstack-common -# library:: default -# -# Copyright 2012-2013, AT&T Services, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Database methods -module ::Openstack - # Library routine that uses the database cookbook to create the - # service's database and grant read/write access to the - # given user and password. - # - # A privileged 'super user' and password is determined from the - # underlying database cookbooks. For instance, if a MySQL database - # is used, the node['mysql']['server_root_password'] is used along - # with the 'root' (super)user. - def db_create_with_user(service, user, pass) # rubocop:disable CyclomaticComplexity, MethodLength - info = db service - if info - host = info['host'] - port = info['port'].to_s - type = info['service_type'] - db_name = info['db_name'] - case type - when 'postgresql', 'pgsql' - include_recipe 'database::postgresql' - db_prov = ::Chef::Provider::Database::Postgresql - user_prov = ::Chef::Provider::Database::PostgresqlUser - super_user = 'postgres' - user_key = node['openstack']['db']['root_user_key'] - super_password = get_password 'user', user_key - when 'mysql', 'mariadb' - db_prov = ::Chef::Provider::Database::Mysql - user_prov = ::Chef::Provider::Database::MysqlUser - super_user = 'root' - user_key = node['openstack']['db']['root_user_key'] - super_password = get_password 'user', user_key - when 'db2' - db2_database 'create database' do - db_name db_name - action :create - end - - db2_user 'create database user' do - db_user user - db_pass pass - db_name db_name - action :create - end - - return info - else - ::Chef::Log.error("Unsupported database type #{type}") - end - - connection_info = { - host: host, - port: port.to_i, - username: super_user, - password: super_password - } - - # create database - database "create database #{db_name}" do - provider db_prov - connection connection_info - database_name db_name - encoding node['openstack']['db']['charset'][type] - action :create - end - - # create user - database_user "create database user #{user}" do - provider user_prov - connection connection_info - username user - password pass - action :create - end - - # grant privs to user - database_user "grant database user #{user}" do - provider user_prov - connection connection_info - username user - password pass - database_name db_name - host '%' - privileges [:all] - action :grant - end - end - info - end -end diff --git a/libraries/matchers.rb b/libraries/matchers.rb index 21676a52..aeacae74 100644 --- a/libraries/matchers.rb +++ b/libraries/matchers.rb @@ -99,4 +99,9 @@ if defined?(ChefSpec) end # rubocop:enable MethodLength, CyclomaticComplexity end + + ## matchers for openstack_database LWRP + def create_openstack_common_database(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:openstack_common_database, :create, resource_name) + end end diff --git a/providers/database.rb b/providers/database.rb new file mode 100644 index 00000000..a0187e5b --- /dev/null +++ b/providers/database.rb @@ -0,0 +1,117 @@ +# Provider that uses the database cookbook to create the +# service's database and grant read/write access to the +# given user and password. +# +# A privileged 'super user' and password is determined from the +# underlying database cookbooks. For instance, if a MySQL database +# is used, the node['mysql']['server_root_password'] is used along +# with the 'root' (super)user. + +include ::Openstack + +use_inline_resources if defined?(use_inline_resources) + +action :create do + info + ### db2 can only be used with an IBM internal cookbook + if @db_type == 'db2' + create_db2(@db_name) # create database + create_db2_user(@user, @pass, @db_name) # create user + else + create_db(@db_name, @db_prov, @connection_info, @db_type) # create database + create_db_user(@user, @user_prov, @connection_info, @pass) # create user + grant_db_privileges(@user, @user_prov, @connection_info, @pass, @db_name) # grant privileges + end +end + +private + +def info + info = node['openstack']['endpoints']['db'] + service_info = db new_resource.service + @host = service_info['host'] || info['host'] + @port = service_info['port'] || info['port'] + user_key = node['openstack']['db']['root_user_key'] + @super_password = get_password 'user', user_key + @db_type = service_info['service_type'] + @db_name = service_info['db_name'] + @user = new_resource.user + @pass = new_resource.pass + db_types unless @db_type == 'db2' ## db2 is only IBM internal + connection_info +end + +def db_types + case @db_type + when 'postgresql', 'pgsql' + @db_prov = ::Chef::Provider::Database::Postgresql + @user_prov = ::Chef::Provider::Database::PostgresqlUser + @super_user = 'postgres' + when 'mysql', 'mariadb', 'percona-cluster' + @db_prov = ::Chef::Provider::Database::Mysql + @user_prov = ::Chef::Provider::Database::MysqlUser + @super_user = 'root' + else + fail "Unsupported database type #{@db_type}" + end +end + +def connection_info + @connection_info = { + host: @host, + port: @port.to_i, + username: @super_user, + password: @super_password + } +end + +### this db2 resource does only exist in an IBM internal cookbook +def create_db2(db_name) + db2_database "create database #{db_name}" do + db_name db_name + action :create + end +end + +### this db2 resource does only exist in an IBM internal cookbook +def create_db2_user(user, pass, db_name) + db2_user "create database user #{user}" do + db_user user + db_pass pass + db_name db_name + action :create + end +end + +def create_db(db_name, db_prov, connection_info, db_type) + database "create database #{db_name}" do + provider db_prov + connection connection_info + database_name db_name + encoding node['openstack']['db']['charset'][db_type] + action :create + end +end + +def create_db_user(user, user_prov, connection_info, pass) + database_user "create database user #{user}" do + provider user_prov + connection connection_info + username user + password pass + action :create + end +end + +def grant_db_privileges(user, user_prov, connection_info, pass, db_name) + database_user "grant database user #{user}" do + provider user_prov + connection connection_info + username user + password pass + database_name db_name + host '%' + privileges [:all] + action :grant + end +end diff --git a/resources/database.rb b/resources/database.rb new file mode 100644 index 00000000..677ae4a5 --- /dev/null +++ b/resources/database.rb @@ -0,0 +1,6 @@ +actions :create +default_action :create + +attribute :service, kind_of: String, name_attribute: true, required: true +attribute :user, kind_of: String, required: true +attribute :pass, kind_of: String, required: true diff --git a/spec/cookbooks/test-openstack-common-database/metadata.rb b/spec/cookbooks/test-openstack-common-database/metadata.rb new file mode 100644 index 00000000..de998f9e --- /dev/null +++ b/spec/cookbooks/test-openstack-common-database/metadata.rb @@ -0,0 +1,8 @@ +name 'test-openstack-common-database' +maintainer 'openstack-chef' +maintainer_email 'opscode-chef-openstack@googlegroups.com' +license 'Apache 2.0' +description 'Test LWRP openstack_common_databse' +version '1.0.0' + +depends 'openstack-common' diff --git a/spec/cookbooks/test-openstack-common-database/recipes/default.rb b/spec/cookbooks/test-openstack-common-database/recipes/default.rb new file mode 100644 index 00000000..bde39817 --- /dev/null +++ b/spec/cookbooks/test-openstack-common-database/recipes/default.rb @@ -0,0 +1,6 @@ +## this recipe is just for the spec of the lwrp openstack_common_database + +openstack_common_database('service') do + user 'db_user' + pass 'db_pass' +end diff --git a/spec/database_provider_spec.rb b/spec/database_provider_spec.rb new file mode 100644 index 00000000..1c0073be --- /dev/null +++ b/spec/database_provider_spec.rb @@ -0,0 +1,119 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'test-openstack-common-database::default' do + let(:runner) do + ChefSpec::SoloRunner.new(platform: 'ubuntu', + version: '14.04', + log_level: :fatal, + step_into: ['openstack_common_database']) + end + let(:node) { runner.node } + let(:chef_run) do + node.override['openstack']['use_databags'] = false + node.set['openstack']['db']['service'] = { service_type: 'mysql', port: 3306, db_name: 'service_db' } + node.set['openstack']['secret']['mysqlroot']['user'] = 'root_pass' + runner.converge(described_recipe) + end + + it 'uses the lwrp openstack_common_database' do + expect(chef_run).to create_openstack_common_database('service') + .with(user: 'db_user', pass: 'db_pass') + end + + it 'creates the database with the database resource' do + expect(chef_run).to create_database('create database service_db') + .with( + provider: ::Chef::Provider::Database::Mysql, + connection: { host: '127.0.0.1', port: 3306, username: 'root', password: 'root_pass' }, + database_name: 'service_db', + encoding: 'utf8' + ) + end + + it 'creates the database use with the database_user resource' do + expect(chef_run).to create_database_user('create database user db_user') + .with( + provider: ::Chef::Provider::Database::MysqlUser, + connection: { host: '127.0.0.1', port: 3306, username: 'root', password: 'root_pass' }, + username: 'db_user', + password: 'db_pass' + ) + end + + it 'grants database privileges to the user with the database_user resource' do + expect(chef_run).to grant_database_user('grant database user db_user') + .with( + provider: ::Chef::Provider::Database::MysqlUser, + connection: { host: '127.0.0.1', port: 3306, username: 'root', password: 'root_pass' }, + username: 'db_user', + password: 'db_pass', + database_name: 'service_db', + host: '%', + privileges: [:all] + ) + end + + context 'postgresql' do + before do + node.override['openstack']['db']['service'] = { service_type: 'postgresql', port: 5432, db_name: 'service_postgres' } + end + + it 'creates the database with the database resource' do + expect(chef_run).to create_database('create database service_postgres') + .with( + provider: ::Chef::Provider::Database::Postgresql, + connection: { host: '127.0.0.1', port: 5432, username: 'postgres', password: 'root_pass' }, + database_name: 'service_postgres', + encoding: 'DEFAULT' + ) + end + + it 'creates the database use with the database_user resource' do + expect(chef_run).to create_database_user('create database user db_user') + .with( + provider: ::Chef::Provider::Database::PostgresqlUser, + connection: { host: '127.0.0.1', port: 5432, username: 'postgres', password: 'root_pass' }, + username: 'db_user', + password: 'db_pass' + ) + end + + it 'grants database privileges to the user with the database_user resource' do + expect(chef_run).to grant_database_user('grant database user db_user') + .with( + provider: ::Chef::Provider::Database::PostgresqlUser, + connection: { host: '127.0.0.1', port: 5432, username: 'postgres', password: 'root_pass' }, + username: 'db_user', + password: 'db_pass', + database_name: 'service_postgres', + host: '%', + privileges: [:all] + ) + end + end + + context 'db2' do + before do + node.override['openstack']['db']['service'] = { service_type: 'db2', db_name: 'service_db2' } + end + it 'creates the database with the db2 resource' do + pending('db2 resource is currently only available from an IBM internal cookbook') + expect(chef_run).to create_db2_database('create database service_db2') + .with( + database_name: 'service_db' + ) + end + + it 'creates the database use with the db2_user resource' do + pending('db2 resource is currently only available from an IBM internal cookbook') + expect(chef_run).to create_database_user('create database user db_user') + .with( + db_user: 'db_user', + db_pass: 'db_pass', + db_name: 'service_db2' + ) + end + end +end diff --git a/spec/database_spec.rb b/spec/database_spec.rb deleted file mode 100644 index a4f4b7c7..00000000 --- a/spec/database_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -# encoding: UTF-8 -require_relative 'spec_helper' -require ::File.join ::File.dirname(__FILE__), '..', 'libraries', 'database' - -describe 'openstack-common::default' do - describe 'Openstack Database' do - let(:runner) { ChefSpec::SoloRunner.new(CHEFSPEC_OPTS) } - let(:node) { runner.node } - let(:chef_run) { runner.converge(described_recipe) } - let(:subject) { Object.new.extend(Openstack) } - - include_context 'library-stubs' - - describe '#db_create_with_user' do - it 'returns nil when no such service was found' do - expect( - subject.db_create_with_user('nonexisting', 'user', 'pass') - ).to be_nil - end - - it 'returns db info and creates database with user when service found' do - %w(mysql, mariadb, pgsql, postgresql).each do |db_type| - encoding = node['openstack']['db']['charset'][db_type] - if encoding.nil? - allow(subject).to receive(:database).and_return({}) - else - allow(subject).to receive(:database).with(encoding: encoding).and_return({}) - end - end - allow(subject).to receive(:database_user).and_return({}) - allow(subject).to receive(:get_password).with('user', 'mysqlroot').and_return('admin') - result = subject.db_create_with_user('compute', 'user', 'pass') - expect(result['host']).to eq('127.0.0.1') - expect(result['port']).to eq('3306') - end - end - end -end