From dcb5113f9d937db498847cea2f0ad92b301d43cc Mon Sep 17 00:00:00 2001 From: Feike Steenbergen Date: Fri, 13 Nov 2015 12:55:38 +0100 Subject: [PATCH] Refactoring from patronicli to patronictl --- Dockerfile | 5 ++-- patroni/{cli.py => ctl.py} | 38 +++++++++++++++++------------- patronicli.py => patronictl.py | 4 ++-- tests/{test_cli.py => test_ctl.py} | 10 ++++---- 4 files changed, 32 insertions(+), 25 deletions(-) rename patroni/{cli.py => ctl.py} (92%) rename patronicli.py => patronictl.py (56%) rename tests/{test_cli.py => test_ctl.py} (84%) diff --git a/Dockerfile b/Dockerfile index 61d85a74..84b9cdf6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,8 +13,9 @@ RUN apt-get update -y RUN apt-get upgrade -y ENV PGVERSION 9.4 -RUN apt-get install python python-psycopg2 python-yaml python-requests python-boto postgresql-${PGVERSION} python-dnspython python-kazoo python-pip -y -RUN pip install python-etcd +RUN apt-get install python python-yaml python-requests python-boto postgresql-${PGVERSION} python-dnspython python-kazoo python-pip -y +RUN apt-get install python-dev postgresql-server-dev-${PGVERSION} -y +RUN pip install python-etcd psycopg2 ENV PATH /usr/lib/postgresql/${PGVERSION}/bin:$PATH diff --git a/patroni/cli.py b/patroni/ctl.py similarity index 92% rename from patroni/cli.py rename to patroni/ctl.py index f11d0587..ed24ef93 100644 --- a/patroni/cli.py +++ b/patroni/ctl.py @@ -1,5 +1,5 @@ ''' -Patroni Command Line Client +Patroni Control ''' import click @@ -16,8 +16,8 @@ import logging from .etcd import Etcd CONFIG_DIR_PATH = click.get_app_dir('patroni') -CONFIG_FILE_PATH = os.path.join(CONFIG_DIR_PATH, 'patronicli.yaml') -LOGLEVEL = 'DEBUG' +CONFIG_FILE_PATH = os.path.join(CONFIG_DIR_PATH, 'patronictl.yaml') +LOGLEVEL = 'INFO' def parse_dcs(dcs): @@ -86,7 +86,7 @@ option_force = click.option('--force', is_flag=True, help='Do not ask for confir @click.group() @click.pass_context -def cli(ctx): +def ctl(ctx): logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s', level=LOGLEVEL) @@ -99,6 +99,13 @@ def get_dcs(config, scope): raise Exception('Can not find suitable configuration of distributed configuration store') +def get_patroni(member, endpoint): + url = urlparse(member.api_url) + logging.debug(url) + r = requests.get('{}://{}/{}'.format(url.scheme, url.netloc, endpoint)) + return r + + def post_patroni(member, endpoint, content, headers={'Content-Type': 'application/json'}): url = urlparse(member.api_url) logging.debug(url) @@ -137,7 +144,7 @@ def watching(w, watch): yield 0 -@cli.command('remove', help='Remove cluster from DCS') +@ctl.command('remove', help='Remove cluster from DCS') @click.argument('cluster_name') @option_config_file @option_format @@ -168,12 +175,12 @@ def remove(config_file, cluster_name, format, dcs): raise Exception("You did not specify the current master of the cluster") if isinstance(dcs, Etcd): - dcs.client.delete(dcs._base_path, recursive=True) + dcs.ctlent.delete(dcs._base_path, recursive=True) else: raise Exception("We have not implemented this for DCS of type {}", type(dcs)) -def wait_for_master(dcs, timeout=30): +def wait_for_leader(dcs, timeout=30): t_stop = time.time() + timeout timeout /= 2 @@ -217,7 +224,7 @@ def empty_post_to_members(cluster_name, member_names, config_file, dcs, force, e click.echo('Succesful {} on member {}'.format(endpoint, mn)) -@cli.command('restart', help='Restart cluster member') +@ctl.command('restart', help='Restart cluster member') @click.argument('cluster_name') @click.argument('member_names', nargs=-1) @option_config_file @@ -227,7 +234,7 @@ def restart(cluster_name, member_names, config_file, dcs, force): empty_post_to_members(cluster_name, member_names, config_file, dcs, force, 'restart') -@cli.command('reinit', help='Reinitialize cluster member') +@ctl.command('reinit', help='Reinitialize cluster member') @click.argument('cluster_name') @click.argument('member_names', nargs=-1) @option_config_file @@ -237,7 +244,7 @@ def reinit(cluster_name, member_names, config_file, dcs, force): empty_post_to_members(cluster_name, member_names, config_file, dcs, force, 'reinitialize') -@cli.command('failover', help='Failover to a replica') +@ctl.command('failover', help='Failover to a replica') @click.argument('cluster_name') @click.option('--master', help='The name of the current master', default=None) @click.option('--candidate', help='The name of the candidate', default=None) @@ -249,7 +256,7 @@ def failover(config_file, cluster_name, master, candidate, force, dcs): We want to trigger a failover for the specified cluster name. We verify that the cluster name, master name and candidate name are correct. - If so, we trigger a failover and keep the client up to date. + If so, we trigger a failover and keep the ctlent up to date. """ config = load_config(config_file, dcs) dcs = get_dcs(config, cluster_name) @@ -268,13 +275,12 @@ def failover(config_file, cluster_name, master, candidate, force, dcs): raise Exception('Member {} is not the leader of cluster {}'.format(master, cluster_name)) candidate_names = [str(m.name) for m in cluster.members if m.name != master] - ## We sort the names for consistent output to the client + # We sort the names for consistent output to the ctlent candidate_names.sort() if len(candidate_names) == 0: raise Exception('No candidates found to failover to') - if candidate is None and not force: candidate = click.prompt('Candidate '+str(candidate_names), type=str, default='') @@ -312,7 +318,7 @@ def failover(config_file, cluster_name, master, candidate, force, dcs): click.echo(timestamp()+' Initialized failover from master {}'.format(master)) # The failover process should within a minute update the failover key, we will keep watching it until it changes # or we timeout - cluster = wait_for_master(dcs, timeout=60) + cluster = wait_for_leader(dcs, timeout=60) click.echo(timestamp()+' Failover completed in {:0.1f} seconds, new leader is {}'.format( time.time() - t_started, str(cluster.leader.member.name))) @@ -340,7 +346,7 @@ def output_members(cluster, name=None, format='pretty'): print_output(['Cluster', 'Member', 'Leader'], rows, {'Cluster': 'l', 'Member': 'l'}, format) -@cli.command('list', help='List the Patroni members for a given Patroni') +@ctl.command('list', help='List the Patroni members for a given Patroni') @click.argument('cluster_names', nargs=-1) @option_config_file @option_format @@ -364,7 +370,7 @@ def timestamp(): return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f") -@cli.command('configure', help='Create configuration file') +@ctl.command('configure', help='Create configuration file') @click.option('--config-file', '-c', help='Configuration file', prompt='Configuration file', default=CONFIG_FILE_PATH) @click.option('--dcs', '-d', help='The DCS connect url', prompt='DCS connect url', default='etcd://localhost:4001') @click.option('--namespace', '-n', help='The namespace', prompt='Namespace', default='/service/') diff --git a/patronicli.py b/patronictl.py similarity index 56% rename from patronicli.py rename to patronictl.py index 3e7afb05..5b06c153 100755 --- a/patronicli.py +++ b/patronictl.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -from patroni.cli import cli +from patroni.ctl import ctl if __name__ == '__main__': - cli() + ctl() diff --git a/tests/test_cli.py b/tests/test_ctl.py similarity index 84% rename from tests/test_cli.py rename to tests/test_ctl.py index 1f5f77c2..2c732de7 100644 --- a/tests/test_cli.py +++ b/tests/test_ctl.py @@ -5,10 +5,10 @@ import os import pytest from click.testing import CliRunner -from patroni.cli import cli, members, store_config, load_config, output_members +from patroni.ctl import ctl, members, store_config, load_config, output_members from test_ha import get_cluster_initialized_with_leader -CONFIG_FILE_PATH = './test-cli.yaml' +CONFIG_FILE_PATH = './test-ctl.yaml' def test_output_members(): @@ -34,12 +34,12 @@ def test_rw_config(): load_config(CONFIG_FILE_PATH, None) -def test_cli(): +def test_ctl(): runner = CliRunner() - runner.invoke(cli, ['list']) + runner.invoke(ctl, ['list']) - result = runner.invoke(cli, ['--help']) + result = runner.invoke(ctl, ['--help']) assert 'Usage:' in result.output