Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for Proxysql cluster #58

Merged
merged 21 commits into from
Sep 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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