Skip to content

Commit

Permalink
Merge pull request #58 from MaxFedotov/proxysql_cluster
Browse files Browse the repository at this point in the history
add support for Proxysql cluster
  • Loading branch information
mcrauwel authored Sep 3, 2018
2 parents 10a4ffc + 1a9ff67 commit 5ed0236
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 18 deletions.
25 changes: 25 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,19 @@ group of the datadir and config_file, defaults to 'root'
##### `override_config_settings`
Which configuration variables should be overriden. Hash, defaults to `{}` (empty hash).

##### `cluster_name`
If set, proxysql_servers with the same cluster_name will be automatically added to the same cluster and will synchronize their configuration parameters.
Defaults to undef

##### `cluster_username`
The username ProxySQL will use to connect to the configured mysql_clusters
Defaults to 'cluster'

##### `cluster_password`
The password ProxySQL will use to connect to the configured mysql_clusters. Defaults to 'cluster'

##### `mysql_client_package_name`
The name of the mysql client package in your package manager. Defaults to undef

## Types
#### proxy_global_variable
Expand All @@ -208,6 +221,18 @@ Specifies wheter the resource should be immediately save to disk. Boolean, defau
##### `value`
The value of the variable.

#### proxy_cluster
`proxy_cluster` manages an entry in the ProxySQL `proxysql_clusters` admin table.

##### `name`
The name of the resource.

##### `hostname`
Hostname of the server. Required.

##### `port`
Port of the server. Required. Defaults to 3306.

#### proxy_mysql_replication_hostgroup
`proxy_mysql_replication_hostgroup` manages an entry in the ProxySQL `mysql_replication_hostgroups` admin table.

Expand Down
128 changes: 128 additions & 0 deletions lib/puppet/provider/proxy_cluster/proxysql.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'proxysql'))
Puppet::Type.type(:proxy_cluster).provide(:proxysql, parent: Puppet::Provider::Proxysql) do
desc 'Manage cluster for a ProxySQL instance.'
commands mysql: 'mysql'

def self.mysql_running
system("mysql #{defaults_file} -NBe 'SELECT 1' >out 2>&1", out: '/dev/null')
end

# Build a property_hash containing all the discovered information about MySQL
# servers.
def self.instances
instances = []
if mysql_running
servers = mysql([defaults_file, '-NBe',
'SELECT `hostname`, `port` FROM `proxysql_servers`'].compact).split(%r{\n})

# To reduce the number of calls to MySQL we collect all the properties in
# one big swoop.
servers.each do |line|
hostname, port = line.split(%r{\t})
query = 'SELECT `hostname`, `port`, `weight`, `comment`'
query << ' FROM `proxysql_servers`'
query << " WHERE `hostname` = '#{hostname}' AND `port` = #{port}"

@hostname, @port, @weight, @comment = mysql([defaults_file, '-NBe', query].compact).chomp.split(%r{\t})
name = "#{hostname}:#{port}"

instances << new(
name: name,
ensure: :present,
hostname: @hostname,
port: @port,
weight: @weight,
comment: @comment
)
end
end
instances
end

# We iterate over each proxy_mysql_server entry in the catalog and compare it against
# the contents of the property_hash generated by self.instances
def self.prefetch(resources)
servers = instances
resources.keys.each do |name|
provider = servers.find { |server| server.name == name }
resources[name].provider = provider if provider
end
end

def create
_name = @resource[:name]
hostname = @resource.value(:hostname)
port = @resource.value(:port) || 6032
weight = @resource.value(:weight) || 0
comment = @resource.value(:comment) || ''

query = 'INSERT INTO proxysql_servers (`hostname`, `port`, `weight`, `comment`)'
query << " VALUES ('#{hostname}', #{port}, #{weight}, '#{comment}')"
mysql([defaults_file, '-e', query].compact)
@property_hash[:ensure] = :present

exists? ? (return true) : (return false)
end

def destroy
hostname = @property_hash[:hostname]
port = @property_hash[:port]
query = 'DELETE FROM `proxysql_servers`'
query << " WHERE `hostname` = '#{hostname}' AND `port` = #{port}"
mysql([defaults_file, '-e', query].compact)

@property_hash.clear
exists? ? (return false) : (return true)
end

def exists?
@property_hash[:ensure] == :present || false
end

def initialize(value = {})
super(value)
@property_flush = {}
end

def flush
update_server(@property_flush) if @property_flush
@property_hash.clear

load_to_runtime = @resource[:load_to_runtime]
mysql([defaults_file, '-NBe', 'LOAD PROXYSQL SERVERS TO RUNTIME'].compact) if load_to_runtime == :true

save_to_disk = @resource[:save_to_disk]
mysql([defaults_file, '-NBe', 'SAVE PROXYSQL SERVERS TO DISK'].compact) if save_to_disk == :true
end

def update_server(properties)
hostname = @resource.value(:hostname)
port = @resource.value(:port)

return false if properties.empty?

values = []
properties.each do |field, value|
values.push("`#{field}` = '#{value}'")
end

query = 'UPDATE proxysql_servers SET '
query << values.join(', ')
query << " WHERE `hostname` = '#{hostname}' AND `port` = #{port}"
mysql([defaults_file, '-e', query].compact)

@property_hash.clear
exists? ? (return false) : (return true)
end

# Generates method for all properties of the property_hash
mk_resource_methods

def weight=(value)
@property_flush[:weight] = value
end

def comment=(value)
@property_flush[:comment] = value
end
end
52 changes: 52 additions & 0 deletions lib/puppet/type/proxy_cluster.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# This has to be a separate type to enable collecting
Puppet::Type.newtype(:proxy_cluster) do
@doc = 'Manage a ProxySQL cluster.'

ensurable

autorequire(:file) { '/root/.my.cnf' }
autorequire(:class) { 'mysql::client' }
autorequire(:service) { 'proxysql' }

validate do
raise('name parameter is required.') if (self[:ensure] == :present) && self[:name].nil?
raise('hostname parameter is required.') if (self[:ensure] == :present) && self[:hostname].nil?
raise('port parameter is required.') if (self[:ensure] == :present) && self[:port].nil?
end

newparam(:name, namevar: true) do
desc 'name for cluster to manage.'
end

newparam(:load_to_runtime) do
desc 'Load this entry to the active runtime.'
defaultto :true
newvalues(:true, :false)
end

newparam(:save_to_disk) do
desc 'Perist this entry to the disk.'
defaultto :true
newvalues(:true, :false)
end

newproperty(:hostname) do
desc 'The hostname of the server.'
newvalue(%r{\w+})
end

newproperty(:port) do
desc 'The port of the server.'
newvalue(%r{\d+})
end

newproperty(:weight) do
desc 'Currently unused, but in the roadmap for future enhancements.'
newvalue(%r{\d+})
end

newproperty(:comment) do
desc 'free form comment field.'
newvalue(%r{[\w+]})
end
end
74 changes: 62 additions & 12 deletions manifests/init.pp
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,38 @@
# * `override_config_settings`
# Which configuration variables should be overriden. Hash, defaults to {} (empty hash).
#
# * `cluster_name`
# If set, proxysql_servers with the same cluster_name will be automatically added to the same cluster and will
# synchronize their configuration parameters. Defaults to undef
#
# * `cluster_username`
# The username ProxySQL will use to connect to the configured mysql_clusters
# Defaults to 'cluster'
#
# * `cluster_password`
# The password ProxySQL will use to connect to the configured mysql_clusters. Defaults to 'cluster'
#
# * `mysql_client_package_name`
# The name of the mysql client package in your package manager. Defaults to undef
#
# * `cluster_name`
# If set, proxysql_servers with the same cluster_name will be automatically added to the same cluster and will
# synchronize their configuration parameters. Defaults to undef
#
# * `cluster_username`
# The username ProxySQL will use to connect to the configured mysql_clusters
# Defaults to 'cluster'
#
# * `cluster_password`
# The password ProxySQL will use to connect to the configured mysql_clusters. Defaults to 'cluster'
#
# * `mysql_client_package_name`
# The name of the mysql client package in your package manager. Defaults to undef
#
class proxysql (
Optional[String] $cluster_name = $proxysql::params::cluster_name,
String $package_name = $proxysql::params::package_name,
Optional[String] $mysql_client_package_name = $proxysql::params::mysql_client_package_name,
String $package_ensure = $proxysql::params::package_ensure,
Array[String] $package_install_options = $proxysql::params::package_install_options,
String $service_name = $proxysql::params::service_name,
Expand Down Expand Up @@ -146,7 +176,12 @@
String $sys_owner = $proxysql::params::sys_owner,
String $sys_group = $proxysql::params::sys_group,

String $cluster_username = $proxysql::params::cluster_username,
Sensitive[String] $cluster_password = $proxysql::params::cluster_password,

Hash $override_config_settings = {},

String $node_name = "${facts['fqdn']}:${admin_listen_port}",
) inherits ::proxysql::params {

# lint:ignore:80chars
Expand All @@ -162,23 +197,38 @@
monitor_password => $monitor_password.unwrap,
},
}
$config_settings = deep_merge($proxysql::params::config_settings, $settings, $override_config_settings)

if $cluster_name {
$settings_cluster = {
admin_variables => {
admin_credentials => "${admin_username}:${admin_password.unwrap};${cluster_username}:${cluster_password.unwrap}",
cluster_username => $cluster_username,
cluster_password => "${cluster_password.unwrap}",
},
}
} else {
$settings_cluster = undef
}

$settings_result = deep_merge($settings, $settings_cluster)

$config_settings = deep_merge($proxysql::params::config_settings, $settings_result, $override_config_settings)
# lint:endignore

anchor { '::proxysql::begin': }
-> class { '::proxysql::repo':}
-> class { '::proxysql::install':}
-> class { '::proxysql::config':}
-> class { '::proxysql::service':}
-> class { '::proxysql::admin_credentials':}
-> anchor { '::proxysql::end': }
anchor { 'proxysql::begin': }
-> class { 'proxysql::repo':}
-> class { 'proxysql::install':}
-> class { 'proxysql::config':}
-> class { 'proxysql::service':}
-> class { 'proxysql::admin_credentials':}
-> anchor { 'proxysql::end': }

Class['::proxysql::install']
~> Class['::proxysql::service']
Class['proxysql::install']
~> Class['proxysql::service']

if $restart {
Class['::proxysql::config']
~> Class['::proxysql::service']
Class['proxysql::config']
~> Class['proxysql::service']
}

}
1 change: 1 addition & 0 deletions manifests/install.pp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
}

class { '::mysql::client':
package_name => $proxysql::mysql_client_package_name,
bindings_enable => false,
}

Expand Down
6 changes: 5 additions & 1 deletion manifests/params.pp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#
class proxysql::params {
$package_name = 'proxysql'
$mysql_client_package_name = undef
$package_ensure = 'installed'
$package_install_options = []

Expand Down Expand Up @@ -111,7 +112,6 @@
}
}


$monitor_username = 'monitor'
$monitor_password = Sensitive('monitor')

Expand All @@ -128,6 +128,10 @@
$load_to_runtime = true
$save_to_disk = true

$cluster_name = undef
$cluster_username = 'cluster'
$cluster_password = Sensitive('cluster')

$config_settings = {
datadir => $datadir,
admin_variables => {
Expand Down
4 changes: 2 additions & 2 deletions manifests/service.pp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
}

exec { 'wait_for_admin_socket_to_open':
command => "test -S ${::proxysql::admin_listen_socket}",
unless => "test -S ${::proxysql::admin_listen_socket}",
command => "test -S ${proxysql::admin_listen_socket}",
unless => "test -S ${proxysql::admin_listen_socket}",
tries => '3',
try_sleep => '10',
require => Service[$proxysql::service_name],
Expand Down
8 changes: 5 additions & 3 deletions spec/classes/proxysql_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@
it { is_expected.to compile.with_all_deps }

it { is_expected.to contain_class('proxysql::params') }
it { is_expected.to contain_anchor('proxysql::begin').that_comes_before('Class[proxysql::repo]') }
it { is_expected.to contain_class('proxysql::repo').that_comes_before('Class[proxysql::install]') }
it { is_expected.to contain_class('proxysql::install').that_comes_before('Class[proxysql::config]') }
it { is_expected.to contain_class('proxysql::config').that_comes_before('Class[proxysql::service]') }
it { is_expected.to contain_class('proxysql::service').that_comes_before('Class[proxysql::admin_credentials]') }
it { is_expected.to contain_class('proxysql::admin_credentials').that_comes_before('Anchor[proxysql::end]') }

it { is_expected.to contain_class('proxysql::service').that_subscribes_to('Class[proxysql::install]') }

it { is_expected.to contain_anchor('::proxysql::begin').that_comes_before('Class[proxysql::repo]') }
it { is_expected.to contain_anchor('::proxysql::end') }
it { is_expected.to contain_class('proxysql::service').that_comes_before('Anchor[::proxysql::end]') }
it { is_expected.to contain_anchor('proxysql::end') }

it { is_expected.to contain_class('proxysql::install').that_notifies('Class[proxysql::service]') }

Expand Down

0 comments on commit 5ed0236

Please sign in to comment.