From 856c3a24ab6fde07ddd2f154df3ad46276efba53 Mon Sep 17 00:00:00 2001 From: Jan Klare Date: Mon, 11 May 2015 10:46:47 +0200 Subject: [PATCH] add percona-cluster recipes - add recipes and specs to deploy percona-cluster as db backend Depends-On: Iae7e302973805af3cb44be1b29d0e61e76eb0aa0 Implements Blueprint: galera-and-percona-support Change-Id: Ie69e71dce8fa22ef5edc17ed094840fcfb9d4c82 --- metadata.rb | 1 + recipes/openstack-db.rb | 2 +- recipes/percona-cluster-client.rb | 28 +++++++++++++ recipes/percona-cluster-server.rb | 62 +++++++++++++++++++++++++++++ spec/percona-cluster-client_spec.rb | 28 +++++++++++++ spec/percona-cluster-server_spec.rb | 19 +++++++++ spec/spec_helper.rb | 5 +++ 7 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 recipes/percona-cluster-client.rb create mode 100644 recipes/percona-cluster-server.rb create mode 100644 spec/percona-cluster-client_spec.rb create mode 100644 spec/percona-cluster-server_spec.rb diff --git a/metadata.rb b/metadata.rb index fbb1070..38064ff 100755 --- a/metadata.rb +++ b/metadata.rb @@ -20,6 +20,7 @@ recipe 'openstack-db', 'Creates necessary tables, users, and grants for OpenStac end depends 'mariadb', '~> 0.3.1' +depends 'percona', '~> 0.16.1' depends 'mysql', '~> 6.0.13' depends 'mysql2_chef_gem', '~> 1.0.1' depends 'postgresql', '~> 3.4.18' diff --git a/recipes/openstack-db.rb b/recipes/openstack-db.rb index cb487f0..cf3efd1 100644 --- a/recipes/openstack-db.rb +++ b/recipes/openstack-db.rb @@ -29,7 +29,7 @@ node['openstack']['common']['services'].each do |service, project| user node['openstack']['db'][service]['username'] pass password end - rescue Net::HTTPServerException + rescue Net::HTTPServerException, ChefVault::Exceptions::KeysNotFound log "No databag item containing the database password for #{project} was found, so no database was created" end end diff --git a/recipes/percona-cluster-client.rb b/recipes/percona-cluster-client.rb new file mode 100644 index 0000000..21e1be6 --- /dev/null +++ b/recipes/percona-cluster-client.rb @@ -0,0 +1,28 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-ops-database +# Recipe:: percona-cluster-client +# +# 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. +# + +include_recipe 'percona::client' + +mysql2_chef_gem 'default' do + provider Chef::Provider::Mysql2ChefGem::Percona + action :install +end + +node['openstack']['db']['python_packages']['percona-cluster'].each do |pkg| + package pkg +end diff --git a/recipes/percona-cluster-server.rb b/recipes/percona-cluster-server.rb new file mode 100644 index 0000000..72e5b2e --- /dev/null +++ b/recipes/percona-cluster-server.rb @@ -0,0 +1,62 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-ops-database +# Recipe:: percona-cluster-server +# +# 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. +# + +class ::Chef::Recipe # rubocop:disable Documentation + include ::Openstack +end + +## INFO: to use this recipe, set node['percona']['server']['role'] = %w(cluster) in your environment + +bind_db = node['openstack']['bind_service']['db'] +if bind_db['interface'] + listen_address = address_for bind_db['interface'] + node.normal['percona']['cluster']['wsrep_sst_receive_address'] = listen_address + node.normal['percona']['server']['bind_address'] = listen_address + node.normal['percona']['server']['port'] = bind_db['port'] +else + listen_address = bind_db['host'] + node.normal['percona']['server']['bind_address'] = listen_address + node.normal['percona']['server']['port'] = node['openstack']['endpoints']['db']['port'] +end + +## CLUSTER SPECIFIC CONFIG +node.normal['percona']['cluster']['wsrep_node_name'] = node['fqdn'] +node.normal['percona']['cluster']['wsrep_cluster_name'] = 'openstack' +node.normal['percona']['cluster']['wsrep_provider_options'] = "\"gmcast.listen_addr=tcp://#{listen_address}:4567;\"" +# query_cache is not supported with wsrep +node.normal['percona']['server']['query_cache_size'] = 0 +# find all nodes in the percona cluster +cluster_nodes = search(:node, 'recipes:"percona\:\:cluster"') +# if it's the first node make sure that wsrep_cluster_address is set to nothing to be able to bootstrap. +is_first_node = cluster_nodes.empty? || (cluster_nodes.size == 1 && cluster_nodes.first['fqdn'] == node['fqdn']) +if is_first_node + node.normal['percona']['cluster']['wsrep_cluster_address'] = 'gcomm://' +else + # otherwise set the correct cluster address with all cluster nodes + family = node['openstack']['endpoints']['family'] + cluster_nodes_addresses = [] + cluster_nodes.each do |cluster_node| + address = address_for bind_db['interface'], family, cluster_node + cluster_nodes_addresses << address + end + cluster_address = cluster_nodes_addresses.join(',') + node.normal['percona']['cluster']['wsrep_cluster_address'] = "gcomm://#{cluster_address}" +end + +include_recipe 'openstack-ops-database::percona-cluster-client' +include_recipe 'percona::cluster' diff --git a/spec/percona-cluster-client_spec.rb b/spec/percona-cluster-client_spec.rb new file mode 100644 index 0000000..f380f25 --- /dev/null +++ b/spec/percona-cluster-client_spec.rb @@ -0,0 +1,28 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-ops-database::percona-cluster-client' do + include_context 'database-stubs' + describe 'ubuntu' do + let(:runner) { ChefSpec::SoloRunner.new(UBUNTU_OPTS) } + let(:node) do + runner.node.set['openstack']['db']['service_type'] = 'percona-cluster' + runner.node + end + let(:chef_run) { runner.converge(described_recipe) } + + it 'includes percona client recipes' do + expect(chef_run).to include_recipe('percona::client') + end + + it 'install mysql2 gem package' do + expect(chef_run).to install_mysql2_chef_gem('default') + .with(provider: Chef::Provider::Mysql2ChefGem::Percona) + end + + it 'installs percona python client packages' do + expect(chef_run).to install_package('python-mysqldb') + end + end +end diff --git a/spec/percona-cluster-server_spec.rb b/spec/percona-cluster-server_spec.rb new file mode 100644 index 0000000..844a1b2 --- /dev/null +++ b/spec/percona-cluster-server_spec.rb @@ -0,0 +1,19 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-ops-database::percona-cluster-server' do + include_context 'database-stubs' + describe 'ubuntu' do + let(:runner) { ChefSpec::SoloRunner.new(UBUNTU_OPTS) } + let(:node) do + runner.node.set['openstack']['db']['service_type'] = 'percona-cluster' + runner.node + end + let(:chef_run) { runner.converge(described_recipe) } + + it 'includes percona client recipes' do + expect(chef_run).to include_recipe('percona::cluster') + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8977f12..62dce8f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -22,12 +22,17 @@ shared_context 'database-stubs' do stub_command("/usr/bin/mysql -u root -e 'show databases;'") # for debian stub_command("\"/usr/bin/mysql\" -u root -e 'show databases;'") + stub_command("mysqladmin --user=root --password='' version") # for postgresql stub_command('ls /var/lib/postgresql/9.3/main/recovery.conf') + stub_search('node', "recipes:\"percona\\:\\:cluster\"").and_return([]) allow_any_instance_of(Chef::Recipe).to receive(:address_for) .with('lo') .and_return('127.0.0.1') + allow_any_instance_of(Chef::Recipe).to receive(:address_for) + .with('all') + .and_return('0.0.0.0') allow_any_instance_of(Chef::Recipe).to receive(:get_password) .with('db', anything) .and_return('test-pass')