Skip to content

Commit

Permalink
PSQLADM-327 : proxysql-admin --syncusers does not work for async repl…
Browse files Browse the repository at this point in the history
…ication

https://jira.percona.com/browse/PSQLADM-327

Issue
Running --syncusers or --sync-multi-cluster-users does not work if
the node being synced is not a cluster node.  The code checks to
see if the node is part of the cluster and errors out otherwise.

Solution
Added the --server option. If this is specified with --syncusers or
--sync-multi-cluster-users, then the check for cluster membership
is skipped, thus allowing standalone nodes to be synced.
  • Loading branch information
kennt-percona committed Oct 23, 2021
1 parent 2ffaf72 commit ebd528c
Show file tree
Hide file tree
Showing 5 changed files with 368 additions and 68 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,9 @@ $
with the ProxySQL database except password-less users and admin users.
It also deletes ProxySQL users not in Percona XtraDB Cluster from the ProxySQL database.
The __--server__ option can be used with __--syncusers__ to specify a
specific server that will be synced (rather than a PXC cluster).
```bash
$ /usr/bin/proxysql-admin --syncusers

Expand Down Expand Up @@ -482,12 +485,17 @@ mysql> select user,host from mysql.user where authentication_string!='' and user
mysql>

```
__5) --sync-multi-cluster-users__
This option works in the same way as --syncusers but it does not delete ProxySQL users
that are not present in the Percona XtraDB Cluster. It is to be used when syncing proxysql
instances that manage multiple clusters.
The __--server__ option can be used with __--sync-multi-cluster-users__ to specify a
specific server that will be synced (rather than a PXC cluster).
__6) --add-query-rule__
Create query rules for synced mysql user. This is applicable only for singlewrite mode and
Expand Down Expand Up @@ -792,6 +800,11 @@ variable settings. If this option is specified, then the values of
the admin-checksum_mysql_query_rules, admin-checksum_mysql_servers,
and admin-checksum_mysql_users will be set to 'false'.
__vi) --server__
This option is used with __--syncusers__ or __--sync-multi-cluster-users__ to specify
a single server to sync, rather than a PXC cluster. This server does not have to belong
to a PXC cluster and can be a standalone MySQL node.
## ProxySQL Status
Expand Down
181 changes: 141 additions & 40 deletions proxysql-admin
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ declare -i CHECK_IF_ENABLED=0
declare -i FORCE=0
declare -i REPORT_STATUS=0

# This can be specifed by the "--server=IP:PORT" option for
# a single address. This is used by --syncusers to sync a
# single node (that does not need to be part of a cluster)
declare SINGLE_SERVER=""

declare -i USE_EXISTING_MONITOR_PASSWORD=0
declare -i WITH_CLUSTER_APP_USER=1

Expand Down Expand Up @@ -247,6 +252,9 @@ Options:
--remove-all-servers When used with --update-cluster, this will remove all
servers belonging to the current cluster before
updating the list.
--server=<IPADDRESS>:<PORT> Specifies the IP address and port for a single server. This can
be used with --syncusers or --sync-multi-cluster-users
to sync a single non-cluster server node.
--add-query-rule Create query rules for synced mysql user. This is applicable only
for singlewrite mode and works only with --syncusers
and --sync-multi-cluster-users options.
Expand Down Expand Up @@ -277,10 +285,13 @@ One of the options below must be provided.
from a node in the cluster.
--quick-demo Setup a quick demo with no authentication
--syncusers Sync user accounts currently configured in MySQL to ProxySQL
May be used with --enable.
May be used with --enable. --server may be used with this
to specify a single server to sync.
(deletes ProxySQL users not in MySQL)
--sync-multi-cluster-users Sync user accounts currently configured in MySQL to ProxySQL
May be used with --enable.
May be used with --enable. --server may be used with this
to specify a single server to sync.
(doesn't delete ProxySQL users not in MySQL)
--is-enabled Checks if the current configuration is enabled in ProxySQL.
--status Returns a status report on the current configuration.
Expand Down Expand Up @@ -587,10 +598,10 @@ function proxysql_connection_check() {
#
function cluster_connection_check() {
mysql_exec "$LINENO" "SELECT @@PORT" >/dev/null
check_cmd $? "$LINENO" "PXC connection check failed."\
"\n-- Could not connect to the PXC cluster at $CLUSTER_HOSTNAME:$CLUSTER_PORT"\
"\n-- Please check the PXC connection parameters and status."
debug "$LINENO" "cluster connection check succeeded"
check_cmd $? "$LINENO" "Server connection check failed."\
"\n-- Could not connect to the server at $CLUSTER_HOSTNAME:$CLUSTER_PORT"\
"\n-- Please check the connection parameters and status."
debug "$LINENO" "server connection check succeeded"
}


Expand Down Expand Up @@ -1876,34 +1887,54 @@ function add_query_rule()
# SYNCUSERS
# CLUSTER_HOSTNAME (overwrites)
# CLUSTER_PORT (overwrites)
# SINGLE_SERVER
#
# Arguments:
# None
#
function syncusers() {
debug "$LINENO" "syncusers ()"

cluster_in_proxysql_check $WRITER_HOSTGROUP_ID

local mysql_version
local password_field
local cluster_node
local changes_made=0

# Get current MySQL users, filter out header row and mysql.sys user
# Find a cluster node that belongs to the cluster with $WRITER_HOSTGROUP_ID
cluster_node=$(find_cluster_node "$WRITER_HOSTGROUP_ID")
check_cmd $? "$LINENO" "Could not find a primary cluster node"
# If a single server has been specified with --server, use that server
# and skip the cluster check (it may be a standalone node)
if [[ -n $SINGLE_SERVER ]]; then
local ws_address

# Reset the central cluster node (so that calls to mysql_exec) will
# work with this new node, rather than the node in the config file
CLUSTER_HOSTNAME=$(echo -e "$cluster_node" | cut -f1)
CLUSTER_PORT=$(echo -e "$cluster_node" | cut -f2)
ws_address=$(separate_ip_port_from_address "$SINGLE_SERVER")
CLUSTER_HOSTNAME=$(echo "$ws_address" | cut -d' ' -f1)
CLUSTER_PORT=$(echo "$ws_address" | cut -d' ' -f2)

echo -e "\nSyncing user accounts from server(${CLUSTER_HOSTNAME}:${CLUSTER_PORT}) to ProxySQL"

else
cluster_in_proxysql_check $WRITER_HOSTGROUP_ID

local cluster_node

# Get current MySQL users, filter out header row and mysql.sys user
# Find a cluster node that belongs to the cluster with $WRITER_HOSTGROUP_ID
cluster_node=$(find_cluster_node "$WRITER_HOSTGROUP_ID")
check_cmd $? "$LINENO" "Could not find a primary cluster node"

# Reset the central cluster node (so that calls to mysql_exec) will
# work with this new node, rather than the node in the config file
CLUSTER_HOSTNAME=$(echo -e "$cluster_node" | cut -f1)
CLUSTER_PORT=$(echo -e "$cluster_node" | cut -f2)

echo -e "\nSyncing user accounts from PXC(${CLUSTER_HOSTNAME}:${CLUSTER_PORT}) to ProxySQL"

fi

# Check that we can connect to CLUSTER_HOSTNAME:CLUSTER_PORT
cluster_connection_check

mysql_version=$(mysql_exec "$LINENO" "SELECT VERSION();" | tail -1 | cut -d'.' -f1,2 )
check_cmd $? "$LINENO" "Could not connect to the PXC node."\
"\n-- Please check the PXC connection parameters and status."
check_cmd $? "$LINENO" "Could not connect to the server."\
"\n-- Please check the server connection parameters and status."

case $mysql_version in
5.6)
Expand All @@ -1912,22 +1943,7 @@ function syncusers() {
5.7 | 8.0)
password_field="authentication_string"
;;
10.0)
password_field="Password"
;;
10.1)
password_field="Password"
;;
10.2)
password_field="Password"
;;
10.3)
password_field="Password"
;;
10.4)
password_field="Password"
;;
10.5)
10.0 | 10.1 | 10.2 | 10.3 | 10.4 | 10.5)
password_field="Password"
;;
*)
Expand All @@ -1942,15 +1958,13 @@ function syncusers() {
grep -E -v "^(mysql.sys|mysql.session|mysql.infoschema|mysql.pxc)" |
sort |
uniq )
check_cmd $? "$LINENO" "Failed to load the user list from PXC."\
"\n-- Please check the PXC connection parameters and status."
check_cmd $? "$LINENO" "Failed to load the user list from the server."\
"\n-- Please check the server connection parameters and status."

#Checking whether user is part of proxysql admin user list
# Get current ProxySQL users and filter out header row
proxysql_users=$(get_proxysql_users)

echo -e "\nSyncing user accounts from PXC to ProxySQL"

# TEST FOR USERS THAT EXIST IN MYSQL BUT NOT IN PROXYSQL HERE AND ADD

# Escape all backslashes here, because the read will evaluate
Expand Down Expand Up @@ -2571,11 +2585,65 @@ function report_status()
function parse_args() {
local go_out=""

local argument_list=(
"config-file:"
"login-file:"
"login-password:"
"login-password-file:"
"proxysql-datadir:"
"writer-hg:"
"backup-writer-hg:"
"reader-hg:"
"offline-hg:"
"proxysql-username:"
"proxysql-password::"
"proxysql-hostname:"
"proxysql-port:"
"cluster-username:"
"cluster-password::"
"cluster-hostname:"
"cluster-port:"
"monitor-username:"
"monitor-password:"
"cluster-app-username:"
"cluster-app-password:"
"node-check-interval:"
"quick-demo"
"mode:"
"write-node:"
"use-existing-monitor-password"
"without-cluster-app-user"
"enable"
"disable"
"update-cluster"
"is-enabled"
"adduser"
"syncusers"
"sync-multi-cluster-users"
"update-mysql-version"
"add-query-rule"
"status"
"max-connections:"
"max-transactions-behind:"
"use-ssl:"
"writers-are-readers:"
"remove-all-servers"
"disable-updates"
"force"
"use-stdin-for-credentials"
"server:"
"version"
"debug"
"help"
)

# TODO: kennt, what happens if we don't have a functional getopt()?
# Check if we have a functional getopt(1)
if ! getopt --test; then
go_out="$(getopt --options=edv --longoptions=config-file:,login-file:,login-password:,login-password-file:,proxysql-datadir:,writer-hg:,backup-writer-hg:,reader-hg:,offline-hg:,proxysql-username:,proxysql-password::,proxysql-hostname:,proxysql-port:,cluster-username:,cluster-password::,cluster-hostname:,cluster-port:,monitor-username:,monitor-password:,cluster-app-username:,cluster-app-password:,node-check-interval:,quick-demo,mode:,write-node:,use-existing-monitor-password,without-cluster-app-user,enable,disable,update-cluster,is-enabled,adduser,syncusers,sync-multi-cluster-users,update-mysql-version,add-query-rule,status,max-connections:,max-transactions-behind:,use-ssl:,writers-are-readers:,remove-all-servers,disable-updates,force,use-stdin-for-credentials,version,debug,help \
--name="$(basename "$0")" -- "$@")"
go_out="$(getopt \
--options=edv \
--longoptions "$(printf "%s," "${argument_list[@]}")" \
--name="$(basename "$0")" -- "$@")"
check_cmd $? "$LINENO" "Script error: getopt() failed with arguments: $*"
eval set -- "$go_out"
fi
Expand Down Expand Up @@ -2973,6 +3041,10 @@ function parse_args() {
USE_STDIN_FOR_CREDENTIALS=1
shift
;;
--server )
SINGLE_SERVER=$2
shift 2
;;
-v | --version )
# Detection of this setting is done before this
shift
Expand Down Expand Up @@ -3011,6 +3083,35 @@ function parse_args() {
fi
readonly WRITE_NODE

# SINGLE_SERVER should only be used with syncusers
if [[ -n $SINGLE_SERVER && $SYNCUSERS -ne 1 && $SYNCMULTICLUSTERUSERS -ne 1 ]]; then
error "$LINENO" "The --server option can only be used with --syncusers" \
"\nand --sync-multi-cluster-users."
exit 1
fi

# SINGLE_SERVER validity check (check to see if the port is included)
if [[ -n $SINGLE_SERVER ]]; then
if [[ $SINGLE_SERVER =~ , ]]; then
error "$LINENO" "The --server option expects to receive the address" \
"\n-- of a single node, therefore commas ',' are not allowed."
exit 1
fi
local ws_address
local ws_port

ws_address=$(separate_ip_port_from_address "$SINGLE_SERVER")
ws_port=$(echo "$ws_address" | cut -d' ' -f2)

# Check that we have a port and that it only contains digits
if [[ -z $ws_port || ! $ws_port =~ ^[[:digit:]]*$ ]]; then
error "$LINENO" "--server : expected 'address:port' found '$SINGLE_SERVER'"
exit 1
fi
fi
readonly SINGLE_SERVER



if [[ ! $MODE =~ ^(loadbal|singlewrite)$ ]]; then
error "" "Invalid --mode passed: '$MODE'"
Expand Down
31 changes: 31 additions & 0 deletions tests/generic-test.bats
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,37 @@ PROXYSQL_BASEDIR=$WORKDIR/proxysql-bin
[[ "${lines[0]}" =~ ERROR.*--write-node.*expects.* ]]
}

@test "run proxysql-admin --server without parameters" {
run sudo $WORKDIR/proxysql-admin --server
echo "$output" >&2
[ "$status" -eq 1 ]
[[ "${lines[0]}" =~ .*--server.* ]]
}

@test "run proxysql-admin --server with missing port" {
run sudo $WORKDIR/proxysql-admin --server=1.1.1.1 --syncusers
echo "$output" >&2
[ "$status" -eq 1 ]
[[ "${lines[0]}" =~ ERROR.*--server.*expected.* ]]

run sudo $WORKDIR/proxysql-admin --server=[1:1:1:1] --syncusers
echo "$output" >&2
[ "$status" -eq 1 ]
[[ "${lines[0]}" =~ ERROR.*--server.*expected.* ]]
}

@test "run proxysql-admin --server with unsupported commands" {
run sudo $WORKDIR/proxysql-admin --server=1.1.1.1 --disable
echo "$output" >&2
[ "$status" -eq 1 ]
[[ "${lines[0]}" =~ ERROR.*--server.*can.only.be.used.* ]]

run sudo $WORKDIR/proxysql-admin --server=[1:1:1:1] --update-cluster
echo "$output" >&2
[ "$status" -eq 1 ]
[[ "${lines[0]}" =~ ERROR.*--server.*can.only.be.used.* ]]
}

@test 'run proxysql-admin --max-connections without parameters' {
run sudo $WORKDIR/proxysql-admin --max-connections
echo "$output" >&2
Expand Down
Loading

0 comments on commit ebd528c

Please sign in to comment.