Skip to content

Commit 9bdfca7

Browse files
authored
Fix/1698 (#1699)
* Fix of 1698 Turned into a major usage clean-up as well, as the code to address #1698 was merged upstream into es_client. As such, other redundant code shared with es_client has been removed. That should clear up some of the cruft and code sprawl that's been in Curator for a while. Tests have all been verified clean. Copyright years bumped where applicable. * A few changes noticed while going over the code in the last commit.
1 parent b41743a commit 9bdfca7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+510
-1088
lines changed

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# syntax=docker/dockerfile:1
2-
ARG PYVER=3.11.4
3-
ARG ALPTAG=3.17
2+
ARG PYVER=3.11.7
3+
ARG ALPTAG=3.18
44
FROM python:${PYVER}-alpine${ALPTAG} as builder
55

66
# Add the community repo for access to patchelf binary package

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright 2011–2023 Elasticsearch <http://elastic.co> and contributors.
1+
Copyright 2011–2024 Elasticsearch <http://elastic.co> and contributors.
22

33
Licensed under the Apache License, Version 2.0 (the "License");
44
you may not use this file except in compliance with the License.

curator/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from curator.exceptions import *
55
from curator.defaults import *
66
from curator.validators import *
7-
from curator.logtools import *
87
from curator.indexlist import IndexList
98
from curator.snapshotlist import SnapshotList
109
from curator.actions import *

curator/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
"""Curator Version"""
2-
__version__ = '8.0.8'
2+
__version__ = '8.0.9'

curator/classdef.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
"""Other Classes"""
22
import logging
3-
from es_client.exceptions import ConfigurationError as ESclient_ConfigError
3+
from es_client.exceptions import FailedValidation
4+
from es_client.helpers.schemacheck import password_filter
45
from es_client.helpers.utils import get_yaml
56
from curator import IndexList, SnapshotList
67
from curator.actions import CLASS_MAP
78
from curator.exceptions import ConfigurationError
8-
from curator.config_utils import password_filter
99
from curator.helpers.testers import validate_actions
1010

1111
# Let me tell you the story of the nearly wasted afternoon and the research that went into this
@@ -62,7 +62,7 @@ def get_validated(self, action_file):
6262
"""
6363
try:
6464
return validate_actions(get_yaml(action_file))
65-
except (ESclient_ConfigError, UnboundLocalError) as err:
65+
except (FailedValidation, UnboundLocalError) as err:
6666
self.logger.critical('Configuration Error: %s', err)
6767
raise ConfigurationError from err
6868

curator/cli.py

Lines changed: 50 additions & 171 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,20 @@
11
"""Main CLI for Curator"""
22
import sys
33
import logging
4-
import pathlib
54
import click
6-
from es_client.builder import ClientArgs, OtherArgs
7-
from es_client.helpers.utils import get_yaml, check_config, prune_nones, verify_url_schema
5+
from es_client.defaults import LOGGING_SETTINGS
6+
from es_client.helpers.config import cli_opts, context_settings, get_args, get_client, get_config
7+
from es_client.helpers.logging import configure_logging
8+
from es_client.helpers.utils import option_wrapper, prune_nones
89
from curator.exceptions import ClientException
910
from curator.classdef import ActionsFile
10-
from curator.config_utils import check_logging_config, set_logging
11-
from curator.defaults import settings
11+
from curator.defaults.settings import CLICK_DRYRUN, default_config_file, footer, snapshot_actions
1212
from curator.exceptions import NoIndices, NoSnapshots
13-
from curator.helpers.getters import get_client
1413
from curator.helpers.testers import ilm_policy_check
15-
from curator.cli_singletons.utils import get_width
1614
from curator._version import __version__
1715

18-
def configfile_callback(ctx, param, value):
19-
"""Callback to validate whether the provided config file exists and is writeable
20-
21-
:param ctx: The click context
22-
:param param: The click parameter object
23-
:param value: The value of the parameter
24-
25-
:type ctx: Click context
26-
:type param: Click object
27-
:type value: Any
28-
29-
:returns: Config file path or None
30-
:rtype: str
31-
"""
32-
logger = logging.getLogger(__name__)
33-
logger.debug('Click ctx = %s', ctx)
34-
logger.debug('Click param = %s', param)
35-
logger.debug('Click value = %s', value)
36-
path = pathlib.Path(value)
37-
if path.is_file():
38-
return value
39-
logger.warning('Config file not found: %s', value)
40-
return None
41-
42-
def override_logging(config, loglevel, logfile, logformat):
43-
"""Get logging config and override from command-line options
44-
45-
:param config: The configuration from file
46-
:param loglevel: The log level
47-
:param logfile: The log file to write
48-
:param logformat: Which log format to use
49-
50-
:type config: dict
51-
:type loglevel: str
52-
:type logfile: str
53-
:type logformat: str
54-
55-
:returns: Log configuration ready for validation
56-
:rtype: dict
57-
"""
58-
# Check for log settings from config file
59-
init_logcfg = check_logging_config(config)
60-
61-
# Override anything with options from the command-line
62-
if loglevel:
63-
init_logcfg['loglevel'] = loglevel
64-
if logfile:
65-
init_logcfg['logfile'] = logfile
66-
if logformat:
67-
init_logcfg['logformat'] = logformat
68-
return init_logcfg
69-
70-
def cli_hostslist(hosts):
71-
"""
72-
:param hosts: One or more hosts.
73-
:type hosts: str or list
74-
75-
:returns: A list of hosts that came in from the command-line, or ``None``
76-
:rtype: list or ``None``
77-
"""
78-
hostslist = []
79-
if hosts:
80-
for host in list(hosts):
81-
hostslist.append(verify_url_schema(host))
82-
else:
83-
hostslist = None
84-
return hostslist
16+
ONOFF = {'on': '', 'off': 'no-'}
17+
click_opt_wrap = option_wrapper()
8518

8619
def ilm_action_skip(client, action_def):
8720
"""
@@ -97,7 +30,7 @@ def ilm_action_skip(client, action_def):
9730
:rtype: bool
9831
"""
9932
logger = logging.getLogger(__name__)
100-
if not action_def.allow_ilm and action_def.action not in settings.snapshot_actions():
33+
if not action_def.allow_ilm and action_def.action not in snapshot_actions():
10134
if action_def.action == 'rollover':
10235
if ilm_policy_check(client, action_def.options['name']):
10336
logger.info('Alias %s is associated with ILM policy.', action_def.options['name'])
@@ -261,35 +194,35 @@ def run(client_args, other_args, action_file, dry_run=False):
261194
logger.info('Action ID: %s, "%s" completed.', idx, action_def.action)
262195
logger.info('All actions completed.')
263196

264-
# pylint: disable=unused-argument, redefined-builtin
265-
@click.command(context_settings=get_width())
266-
@click.option('--config', help='Path to configuration file.', type=str, default=settings.config_file(), callback=configfile_callback)
267-
@click.option('--hosts', help='Elasticsearch URL to connect to', multiple=True)
268-
@click.option('--cloud_id', help='Shorthand to connect to Elastic Cloud instance')
269-
@click.option('--api_token', help='The base64 encoded API Key token', type=str)
270-
@click.option('--id', help='API Key "id" value', type=str)
271-
@click.option('--api_key', help='API Key "api_key" value', type=str)
272-
@click.option('--username', help='Username used to create "basic_auth" tuple')
273-
@click.option('--password', help='Password used to create "basic_auth" tuple')
274-
@click.option('--bearer_auth', type=str)
275-
@click.option('--opaque_id', type=str)
276-
@click.option('--request_timeout', help='Request timeout in seconds', type=float)
277-
@click.option('--http_compress', help='Enable HTTP compression', is_flag=True, default=None)
278-
@click.option('--verify_certs', help='Verify SSL/TLS certificate(s)', is_flag=True, default=None)
279-
@click.option('--ca_certs', help='Path to CA certificate file or directory')
280-
@click.option('--client_cert', help='Path to client certificate file')
281-
@click.option('--client_key', help='Path to client certificate key')
282-
@click.option('--ssl_assert_hostname', help='Hostname or IP address to verify on the node\'s certificate.', type=str)
283-
@click.option('--ssl_assert_fingerprint', help='SHA-256 fingerprint of the node\'s certificate. If this value is given then root-of-trust verification isn\'t done and only the node\'s certificate fingerprint is verified.', type=str)
284-
@click.option('--ssl_version', help='Minimum acceptable TLS/SSL version', type=str)
285-
@click.option('--master-only', help='Only run if the single host provided is the elected master', is_flag=True, default=None)
286-
@click.option('--skip_version_test', help='Do not check the host version', is_flag=True, default=None)
287-
@click.option('--dry-run', is_flag=True, help='Do not perform any changes.')
288-
@click.option('--loglevel', help='Log level', type=click.Choice(['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']))
289-
@click.option('--logfile', help='log file')
290-
@click.option('--logformat', help='Log output format', type=click.Choice(['default', 'logstash', 'json', 'ecs']))
197+
# pylint: disable=unused-argument, redefined-builtin, too-many-arguments, too-many-locals, line-too-long
198+
@click.command(context_settings=context_settings(), epilog=footer(__version__, tail='command-line.html'))
199+
@click_opt_wrap(*cli_opts('config'))
200+
@click_opt_wrap(*cli_opts('hosts'))
201+
@click_opt_wrap(*cli_opts('cloud_id'))
202+
@click_opt_wrap(*cli_opts('api_token'))
203+
@click_opt_wrap(*cli_opts('id'))
204+
@click_opt_wrap(*cli_opts('api_key'))
205+
@click_opt_wrap(*cli_opts('username'))
206+
@click_opt_wrap(*cli_opts('password'))
207+
@click_opt_wrap(*cli_opts('bearer_auth'))
208+
@click_opt_wrap(*cli_opts('opaque_id'))
209+
@click_opt_wrap(*cli_opts('request_timeout'))
210+
@click_opt_wrap(*cli_opts('http_compress', onoff=ONOFF))
211+
@click_opt_wrap(*cli_opts('verify_certs', onoff=ONOFF))
212+
@click_opt_wrap(*cli_opts('ca_certs'))
213+
@click_opt_wrap(*cli_opts('client_cert'))
214+
@click_opt_wrap(*cli_opts('client_key'))
215+
@click_opt_wrap(*cli_opts('ssl_assert_hostname'))
216+
@click_opt_wrap(*cli_opts('ssl_assert_fingerprint'))
217+
@click_opt_wrap(*cli_opts('ssl_version'))
218+
@click_opt_wrap(*cli_opts('master-only', onoff=ONOFF))
219+
@click_opt_wrap(*cli_opts('skip_version_test', onoff=ONOFF))
220+
@click_opt_wrap(*cli_opts('dry-run', settings=CLICK_DRYRUN))
221+
@click_opt_wrap(*cli_opts('loglevel', settings=LOGGING_SETTINGS))
222+
@click_opt_wrap(*cli_opts('logfile', settings=LOGGING_SETTINGS))
223+
@click_opt_wrap(*cli_opts('logformat', settings=LOGGING_SETTINGS))
291224
@click.argument('action_file', type=click.Path(exists=True), nargs=1)
292-
@click.version_option(version=__version__)
225+
@click.version_option(__version__, '-v', '--version', prog_name="curator")
293226
@click.pass_context
294227
def cli(
295228
ctx, config, hosts, cloud_id, api_token, id, api_key, username, password, bearer_auth,
@@ -298,75 +231,21 @@ def cli(
298231
dry_run, loglevel, logfile, logformat, action_file
299232
):
300233
"""
301-
Curator for Elasticsearch indices.
302-
303-
See http://elastic.co/guide/en/elasticsearch/client/curator/current
304-
"""
305-
client_args = ClientArgs()
306-
other_args = OtherArgs()
307-
if config:
308-
from_yaml = get_yaml(config)
309-
else:
310-
# Use empty defaults.
311-
from_yaml = {'elasticsearch': {'client': {}, 'other_settings': {}}, 'logging': {}}
312-
raw_config = check_config(from_yaml)
313-
client_args.update_settings(raw_config['client'])
314-
other_args.update_settings(raw_config['other_settings'])
315-
316-
set_logging(check_logging_config(
317-
{'logging': override_logging(from_yaml, loglevel, logfile, logformat)}))
318-
319-
hostslist = cli_hostslist(hosts)
234+
Curator for Elasticsearch indices
320235
321-
cli_client = prune_nones({
322-
'hosts': hostslist,
323-
'cloud_id': cloud_id,
324-
'bearer_auth': bearer_auth,
325-
'opaque_id': opaque_id,
326-
'request_timeout': request_timeout,
327-
'http_compress': http_compress,
328-
'verify_certs': verify_certs,
329-
'ca_certs': ca_certs,
330-
'client_cert': client_cert,
331-
'client_key': client_key,
332-
'ssl_assert_hostname': ssl_assert_hostname,
333-
'ssl_assert_fingerprint': ssl_assert_fingerprint,
334-
'ssl_version': ssl_version
335-
})
236+
The default $HOME/.curator/curator.yml configuration file (--config)
237+
can be used but is not needed.
238+
239+
Command-line settings will always override YAML configuration settings.
336240
337-
cli_other = prune_nones({
338-
'master_only': master_only,
339-
'skip_version_test': skip_version_test,
340-
'username': username,
341-
'password': password,
342-
'api_key': {
343-
'id': id,
344-
'api_key': api_key,
345-
'token': api_token,
346-
}
347-
})
348-
# Remove `api_key` root key if `id` and `api_key` and `token` are all None
349-
if id is None and api_key is None and api_token is None:
350-
del cli_other['api_key']
241+
Some less-frequently used client configuration options are now hidden. To see the full list,
242+
run:
351243
352-
# If hosts are in the config file, but cloud_id is specified at the command-line,
353-
# we need to remove the hosts parameter as cloud_id and hosts are mutually exclusive
354-
if cloud_id:
355-
click.echo('cloud_id provided at CLI, superseding any other configured hosts')
356-
client_args.hosts = None
357-
cli_client.pop('hosts', None)
358-
359-
# Likewise, if hosts are provided at the command-line, but cloud_id was in the config file,
360-
# we need to remove the cloud_id parameter from the config file-based dictionary before merging
361-
if hosts:
362-
click.echo('hosts specified manually, superseding any other cloud_id or hosts')
363-
client_args.hosts = None
364-
client_args.cloud_id = None
365-
cli_client.pop('cloud_id', None)
366-
367-
# Update the objects if we have settings after pruning None values
368-
if cli_client:
369-
client_args.update_settings(cli_client)
370-
if cli_other:
371-
other_args.update_settings(cli_other)
244+
curator_cli -h
245+
"""
246+
ctx.obj = {}
247+
ctx.obj['dry_run'] = dry_run
248+
cfg = get_config(ctx.params, default_config_file())
249+
configure_logging(cfg, ctx.params)
250+
client_args, other_args = get_args(ctx.params, cfg)
372251
run(client_args, other_args, action_file, dry_run)

curator/cli_singletons/alias.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
"""Alias Singleton"""
22
import logging
33
import click
4+
from es_client.helpers.config import context_settings
45
from curator.cli_singletons.object_class import CLIAction
5-
from curator.cli_singletons.utils import get_width, json_to_dict, validate_filter_json
6+
from curator.cli_singletons.utils import json_to_dict, validate_filter_json
67

7-
@click.command(context_settings=get_width())
8+
@click.command(context_settings=context_settings())
89
@click.option('--name', type=str, help='Alias name', required=True)
910
@click.option(
1011
'--add',
@@ -48,12 +49,6 @@ def alias(ctx, name, add, remove, warn_if_no_indices, extra_settings, allow_ilm_
4849
logger.debug('manual_options %s', manual_options)
4950
# ctx.info_name is the name of the function or name specified in @click.command decorator
5051
ignore_empty_list = warn_if_no_indices
51-
logger.debug('ctx.info_name %s', ctx.info_name)
52-
logger.debug('ignore_empty_list %s', ignore_empty_list)
53-
logger.debug('add %s', add)
54-
logger.debug('remove %s', remove)
55-
logger.debug('warn_if_no_indices %s', warn_if_no_indices)
56-
logger.debug("ctx.obj['dry_run'] %s", ctx.obj['dry_run'])
5752
action = CLIAction(
5853
ctx.info_name,
5954
ctx.obj['config'],
@@ -62,5 +57,4 @@ def alias(ctx, name, add, remove, warn_if_no_indices, extra_settings, allow_ilm_
6257
ignore_empty_list,
6358
add=add, remove=remove, warn_if_no_indices=warn_if_no_indices, # alias specific kwargs
6459
)
65-
logger.debug('We did not get here, did we?')
6660
action.do_singleton_action(dry_run=ctx.obj['dry_run'])

curator/cli_singletons/allocation.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
"""Allocation Singleton"""
22
import click
3+
from es_client.helpers.config import context_settings
34
from curator.cli_singletons.object_class import CLIAction
4-
from curator.cli_singletons.utils import get_width, validate_filter_json
5+
from curator.cli_singletons.utils import validate_filter_json
56

6-
@click.command(context_settings=get_width())
7+
@click.command(context_settings=context_settings())
78
@click.option('--key', type=str, required=True, help='Node identification tag')
89
@click.option('--value', type=str, default=None, help='Value associated with --key')
910
@click.option('--allocation_type', type=click.Choice(['require', 'include', 'exclude']))

curator/cli_singletons/close.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
"""Close Singleton"""
22
import click
3+
from es_client.helpers.config import context_settings
34
from curator.cli_singletons.object_class import CLIAction
4-
from curator.cli_singletons.utils import get_width, validate_filter_json
5+
from curator.cli_singletons.utils import validate_filter_json
56

6-
@click.command(context_settings=get_width())
7+
@click.command(context_settings=context_settings())
78
@click.option('--delete_aliases', is_flag=True, help='Delete all aliases from indices to be closed')
89
@click.option('--skip_flush', is_flag=True, help='Skip flush phase for indices to be closed')
910
@click.option(

curator/cli_singletons/delete.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
"""Delete Index and Delete Snapshot Singletons"""
22
import click
3+
from es_client.helpers.config import context_settings
34
from curator.cli_singletons.object_class import CLIAction
4-
from curator.cli_singletons.utils import get_width, validate_filter_json
5+
from curator.cli_singletons.utils import validate_filter_json
56

67
#### Indices ####
7-
@click.command(context_settings=get_width())
8+
@click.command(context_settings=context_settings())
89
@click.option(
910
'--ignore_empty_list',
1011
is_flag=True,
@@ -38,7 +39,7 @@ def delete_indices(ctx, ignore_empty_list, allow_ilm_indices, filter_list):
3839
action.do_singleton_action(dry_run=ctx.obj['dry_run'])
3940

4041
#### Snapshots ####
41-
@click.command(context_settings=get_width())
42+
@click.command(context_settings=context_settings())
4243
@click.option('--repository', type=str, required=True, help='Snapshot repository name')
4344
@click.option('--retry_count', type=int, help='Number of times to retry (max 3)')
4445
@click.option('--retry_interval', type=int, help='Time in seconds between retries')

0 commit comments

Comments
 (0)