Skip to content

Commit 62937ff

Browse files
authored
Merge pull request amazon-archives#24 from zhongnansu/test
Provide user option to toggle to use aws sigV4 authentication
2 parents cbe91d8 + 3583bf1 commit 62937ff

23 files changed

+122
-103
lines changed

README.md

+34-27
Original file line numberDiff line numberDiff line change
@@ -3,52 +3,59 @@
33
ODFE: Open Distro for Elasticsearch
44

55
ODFE SQL CLI is a stand alone Python application and can be launched by a wake word `odfesql`. It serves as a support only for
6-
[Open Distro SQL plugin for Elasticsearch](https://opendistro.github.io/for-elasticsearch-docs/docs/sql/). You can run
7-
it on any OS we support, and connect to any valid remote endpoint without installing Elasticsearch.
6+
[Open Distro SQL plugin for Elasticsearch](https://opendistro.github.io/for-elasticsearch-docs/docs/sql/). User must have ODFE SQL
7+
plugin installed to the Elasticsearch instance to connect to. Usr can run this CLI on any OS we support, and connect to any valid
8+
remote Elasticsearch endpoint.
89

910

1011
## Installation
1112
- `pip install odfesql`
12-
- odfe sql cli is compatible with Python 3, because Python 2 is no longer maintained since 01/01/2020 https://pythonclock.org/
13+
- ODFE SQL CLI is only compatible with Python 3, because Python 2 is no longer maintained since 01/01/2020 https://pythonclock.org/
1314

1415

1516
## Configuration
16-
- A config file is automatically created at `~/.config/escli/config` at first launch.
17-
See the file itself for a description of all available options.
18-
17+
- A config file is automatically created at `~/.config/odfesql-cli/config` at first launch (for MacOS and Linux).
18+
Check out [clirc](./odfesql_cli/conf/clirc) for details of all available configurations.
19+
- It can be configured and will be auto-loaded next time.
1920

2021
## Features
2122
- Multiline input
22-
- Auto-completion with index suggestion
23+
- Input Auto-completion with index suggestion
24+
- Syntax highlighting
2325
- Formatted output
2426
- Tabular format
2527
- Fields name with color
26-
- Enable horizontal display (by default) and vertical display when output is too wide
28+
- Enable horizontal display (by default) and vertical display when output is too wide, for better visualization
2729
- Pagination for long output
28-
- Syntax highlighting
29-
- Connect to Elasticsearch node/cluster with/without security on either **ES localhost, Open Distro ES, or AWS Elasticsearch Domain**.
30-
Refer to [test plan](./tests/test_plan.md) on how to connect to different instance with/without security
31-
- Load Config file
32-
- Run single query from Command Line with parameters
33-
- *endpoint:* no need to specify a parameter, anything follow by wake word `odfesql` should be the endpoint.
34-
By default, it’s http://localhost:9200
35-
- *--help:* help page for options and params
36-
- *-q:* follow by a single query user wants to run.
30+
31+
- Connect to Elasticsearch with/without security enabled on either **Elasticsarch, Elasticsarch OSS, or AWS Elasticsearch Domain**.
32+
- Load config file
33+
34+
## Basic Usage
35+
- ![](./screenshots/usage.gif)
36+
- The CLI supports all types of query that ODFE SQL supports. Refer to [ODFE SQL basic usage](https://github.com/opendistro-for-elasticsearch/sql#basic-usage)
37+
38+
- Configurable connection properties
39+
- *endpoint:* no need to specify an option, anything follow by launch word `odfesql` is regarded as the endpoint.
40+
If user doesn't provide an endpoint, by default it will try to connect to http://localhost:9200
41+
- *-u/-w:* username and password. User needs to provide credentials when connecting to:
42+
- Elasticsearch with X-pack security enabled
43+
- Elasticsearch OSS with [Open distro Security Plugin](https://opendistro.github.io/for-elasticsearch-docs/docs/install/plugins/) installed
44+
- IP-based AWS Elasticsearch domain with [Fine Grained Access Control](https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/fgac.html) enabled
45+
46+
- *--aws-auth:* turn on to use AWS sigV4 authentication. It can be configured by AWS CLI `aws configure` command. ODFE SQL
47+
CLI will try to retrieve this config to connect.
48+
49+
- Run single query from command line with options
50+
- *--help:* help page for options
51+
- *-q:* follow by a single query
3752
- *-f:* support *jdbc/raw* format output
3853
- *-v:* display data vertically
39-
- *-u:* username to connect to Elasticsearch
40-
- *-w:* password for username
4154
- *-e:* translate sql to DSL
4255

43-
- Run the CLI with parameters
56+
- Run the CLI with options
4457
- *-p*: always use pager to display output
45-
- *--esclirc*: provide path of config file to load.
46-
47-
48-
49-
## Basic Usage
50-
- The CLI supports all types of query that ODFE SQL supports. See [ODFE SQL basic usage](https://github.com/opendistro-for-elasticsearch/sql#basic-usage)
51-
- ![](./screenshots/usage.gif)
58+
- *--clirc*: provide path of config file to load
5259

5360

5461

development_guide.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ any endpoint, it uses http://localhost:9200 by default.
3333
### Workflow
3434

3535
1. Update version number
36-
1. Modify the version number in `_init_.py` under `escli` package. It will be used by `setup.py` for release.
36+
1. Modify the version number in `_init_.py` under `odfesql_cli` package. It will be used by `setup.py` for release.
3737
2. Create/Update `setup.py` (if needed)
3838
1. For more details refer to https://packaging.python.org/tutorials/packaging-projects/#creating-setup-py
3939
3. Update README.md, Legal and copyright files(if needed)
@@ -42,6 +42,7 @@ any endpoint, it uses http://localhost:9200 by default.
4242
4. Generate distribution archives
4343
1. Make sure you have the latest versions of `setuptools` and `wheel` installed: `pip install --user --upgrade setuptools wheel`
4444
2. Run this command from the same directory where `setup.py` is located: `python3 setup.py sdist bdist_wheel`
45+
3. Check artifacts under `sql-cli/dist/`, there should be a tar file and a whi file with correct version. Remove other deprecated artifacts.
4546
5. Upload the distribution archives to TestPyPI
4647
1. Register an account on [testPyPI](https://test.pypi.org/)
4748
2. `pip install --user —upgrade twine `
File renamed without changes.
File renamed without changes.

escli/conf/esclirc odfesql_cli/conf/clirc

+6-6
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,22 @@
2020
# lines. End of line (return) is considered as the end of the statement.
2121
multi_line = True
2222

23-
# If multi_line_mode is set to "escli", in multi-line mode, [Enter] will execute
23+
# If multi_line_mode is set to "odfesql_cli", in multi-line mode, [Enter] will execute
2424
# the current input if the input ends in a semicolon.
2525
# If multi_line_mode is set to "safe", in multi-line mode, [Enter] will always
2626
# insert a newline, and [Esc] [Enter] or [Alt]-[Enter] must be used to execute
2727
# a command.
28-
multi_line_mode = escli
28+
multi_line_mode = odfesql_cli
2929

3030
# log_file location.
31-
# In Unix/Linux: ~/.conf/escli/log
32-
# In Windows: %USERPROFILE%\AppData\Local\dbcli\escli\log
31+
# In Unix/Linux: ~/.conf/odfesql-cli/log
32+
# In Windows: %USERPROFILE%\AppData\Local\dbcli\odfesql-cli\log
3333
# %USERPROFILE% is typically C:\Users\{username}
3434
log_file = default
3535

3636
# history_file location.
37-
# In Unix/Linux: ~/.conf/escli/history
38-
# In Windows: %USERPROFILE%\AppData\Local\dbcli\escli\history
37+
# In Unix/Linux: ~/.conf/odfesql-cli/history
38+
# In Windows: %USERPROFILE%\AppData\Local\dbcli\odfesql-cli\history
3939
# %USERPROFILE% is typically C:\Users\{username}
4040
history_file = default
4141

escli/config.py odfesql_cli/config.py

+10-10
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@
2424
def config_location():
2525
"""Return absolute conf file path according to different OS."""
2626
if "XDG_CONFIG_HOME" in os.environ:
27-
return "%s/escli/" % expanduser(os.environ["XDG_CONFIG_HOME"])
27+
return "%s/odfesql-cli/" % expanduser(os.environ["XDG_CONFIG_HOME"])
2828
elif platform.system() == "Windows":
2929
# USERPROFILE is typically C:\Users\{username}
30-
return "%s\\AppData\\Local\\dbcli\\escli\\" % os.getenv("USERPROFILE")
30+
return "%s\\AppData\\Local\\dbcli\\odfesql-cli\\" % os.getenv("USERPROFILE")
3131
else:
32-
return expanduser("~/.config/escli/")
32+
return expanduser("~/.config/odfesql-cli/")
3333

3434

3535
def _load_config(user_config, default_config=None):
@@ -66,20 +66,20 @@ def _write_default_config(source, destination, overwrite=False):
6666

6767

6868
# https://stackoverflow.com/questions/40193112/python-setuptools-distribute-configuration-files-to-os-specific-directories
69-
def get_config(esclirc_file=None):
69+
def get_config(clirc_file=None):
7070
"""
71-
Get config for escli.
71+
Get config for odfesql_cli.
7272
7373
This config comes from either existing config in the OS, or create a config file in the OS, and write default config
7474
including in the package to it.
7575
"""
76-
from escli.conf import __file__ as package_root
76+
from odfesql_cli.conf import __file__ as package_root
7777

7878
package_root = os.path.dirname(package_root)
7979

80-
esclirc_file = esclirc_file or "%sconfig" % config_location()
81-
default_config = os.path.join(package_root, "esclirc")
80+
clirc_file = clirc_file or "%sconfig" % config_location()
81+
default_config = os.path.join(package_root, "clirc")
8282

83-
_write_default_config(default_config, esclirc_file)
83+
_write_default_config(default_config, clirc_file)
8484

85-
return _load_config(esclirc_file, default_config)
85+
return _load_config(clirc_file, default_config)

escli/esbuffer.py odfesql_cli/esbuffer.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@
1919
from prompt_toolkit.application import get_app
2020

2121

22-
def es_is_multiline(escli):
22+
def es_is_multiline(odfesql_cli):
2323
"""Return function that returns boolean to enable/unable multiline mode."""
2424

2525
@Condition
2626
def cond():
2727
doc = get_app().layout.get_buffer_by_name(DEFAULT_BUFFER).document
2828

29-
if not escli.multi_line:
29+
if not odfesql_cli.multi_line:
3030
return False
31-
if escli.multiline_mode == "safe":
31+
if odfesql_cli.multiline_mode == "safe":
3232
return True
3333
else:
3434
return not _multiline_exception(doc.text)

escli/esconnection.py odfesql_cli/esconnection.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class ESConnection:
3030
as well as send user's SQL query to Elasticsearch.
3131
"""
3232

33-
def __init__(self, endpoint=None, http_auth=None):
33+
def __init__(self, endpoint=None, http_auth=None, use_aws_authentication=False):
3434
"""Initialize an ESConnection instance.
3535
3636
Set up client and get indices list.
@@ -46,6 +46,7 @@ def __init__(self, endpoint=None, http_auth=None):
4646
self.indices_list = []
4747
self.endpoint = endpoint
4848
self.http_auth = http_auth
49+
self.use_aws_authentication = use_aws_authentication
4950

5051
def get_indices(self):
5152
if self.client:
@@ -61,7 +62,7 @@ def get_aes_client(self):
6162
if credentials is not None:
6263
self.aws_auth = AWS4Auth(credentials.access_key, credentials.secret_key, region, service)
6364
else:
64-
click.secho(message="Can not retrieve credentials, check your aws config", fg="red")
65+
click.secho(message="Can not retrieve your AWS credentials, check your AWS config", fg="red")
6566

6667
aes_client = Elasticsearch(
6768
hosts=[{"host": str(self.endpoint), "port": 443}],
@@ -96,9 +97,8 @@ def set_connection(self, is_reconnect=False):
9697
if self.http_auth:
9798
es_client = self.get_open_distro_client()
9899

99-
elif str(self.endpoint).endswith("es.amazonaws.com"):
100+
elif self.use_aws_authentication:
100101
es_client = self.get_aes_client()
101-
102102
else:
103103
es_client = Elasticsearch([self.endpoint], verify_certs=True)
104104

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

escli/main.py odfesql_cli/main.py

+17-9
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from .config import config_location
2121
from .esconnection import ESConnection
2222
from .utils import OutputSettings
23-
from .essqlcli import ESSqlCli
23+
from .odfesql_cli import OdfeSqlCli
2424
from .formatter import Formatter
2525

2626
click.disable_unicode_literals_warning = True
@@ -31,10 +31,10 @@
3131
@click.option("-q", "--query", "query", type=click.STRING, help="Run single query in non-interactive mode")
3232
@click.option("-e", "--explain", "explain", is_flag=True, help="Explain SQL to ES DSL")
3333
@click.option(
34-
"--esclirc",
34+
"--clirc",
3535
default=config_location() + "config",
36-
envvar="ESCLIRC",
37-
help="Location of esclirc file.",
36+
envvar="CLIRC",
37+
help="Location of clirc file.",
3838
type=click.Path(dir_okay=False),
3939
)
4040
@click.option(
@@ -64,7 +64,15 @@
6464
help="Always use pager to display output. If not specified, smart pager mode will be used according to the \
6565
length/width of output",
6666
)
67-
def cli(endpoint, query, explain, esclirc, result_format, is_vertical, username, password, always_use_pager):
67+
@click.option(
68+
"--aws-auth",
69+
"use_aws_authentication",
70+
is_flag=True,
71+
default=False,
72+
help="Use AWS sigV4 to connect to AWS ELasticsearch domain",
73+
)
74+
def cli(endpoint, query, explain, clirc, result_format, is_vertical, username, password, always_use_pager,
75+
use_aws_authentication):
6876
"""
6977
Provide endpoint for Elasticsearch client.
7078
By default, it uses http://localhost:9200 to connect.
@@ -79,7 +87,7 @@ def cli(endpoint, query, explain, esclirc, result_format, is_vertical, username,
7987

8088
# handle single query without more interaction with user
8189
if query:
82-
es_executor = ESConnection(endpoint, http_auth)
90+
es_executor = ESConnection(endpoint, http_auth, use_aws_authentication)
8391
es_executor.set_connection()
8492
if explain:
8593
output = es_executor.execute_query(query, explain=True, use_console=False)
@@ -95,9 +103,9 @@ def cli(endpoint, query, explain, esclirc, result_format, is_vertical, username,
95103
sys.exit(0)
96104

97105
# use console to interact with user
98-
escli = ESSqlCli(esclirc_file=esclirc, always_use_pager=always_use_pager)
99-
escli.connect(endpoint, http_auth)
100-
escli.run_cli()
106+
odfesql_cli = OdfeSqlCli(clirc_file=clirc, always_use_pager=always_use_pager, use_aws_authentication=use_aws_authentication)
107+
odfesql_cli.connect(endpoint, http_auth)
108+
odfesql_cli.run_cli()
101109

102110

103111
if __name__ == "__main__":

escli/essqlcli.py odfesql_cli/odfesql_cli.py

+8-7
Original file line numberDiff line numberDiff line change
@@ -44,25 +44,26 @@
4444
click.disable_unicode_literals_warning = True
4545

4646

47-
class ESSqlCli:
48-
"""ESSqlCli instance is used to build and run the ODFE SQL CLI."""
47+
class OdfeSqlCli:
48+
"""OdfeSqlCli instance is used to build and run the ODFE SQL CLI."""
4949

50-
def __init__(self, esclirc_file=None, always_use_pager=False):
50+
def __init__(self, clirc_file=None, always_use_pager=False, use_aws_authentication=False):
5151
# Load conf file
52-
config = self.config = get_config(esclirc_file)
52+
config = self.config = get_config(clirc_file)
5353
literal = self.literal = self._get_literals()
5454

5555
self.prompt_app = None
5656
self.es_executor = None
5757
self.always_use_pager = always_use_pager
58+
self.use_aws_authentication = use_aws_authentication
5859
self.keywords_list = literal["keywords"]
5960
self.functions_list = literal["functions"]
6061
self.syntax_style = config["main"]["syntax_style"]
6162
self.cli_style = config["colors"]
6263
self.table_format = config["main"]["table_format"]
6364
self.multiline_continuation_char = config["main"]["multiline_continuation_char"]
6465
self.multi_line = config["main"].as_bool("multi_line")
65-
self.multiline_mode = config["main"].get("multi_line_mode", "escli")
66+
self.multiline_mode = config["main"].get("multi_line_mode", "odfesql_cli")
6667
self.null_string = config["main"].get("null_string", "null")
6768
self.style_output = style_factory_output(self.syntax_style, self.cli_style)
6869

@@ -164,7 +165,7 @@ def echo_via_pager(self, text, color=None):
164165
click.echo(text, color=color)
165166

166167
def connect(self, endpoint, http_auth=None):
167-
self.es_executor = ESConnection(endpoint, http_auth)
168+
self.es_executor = ESConnection(endpoint, http_auth, self.use_aws_authentication)
168169
self.es_executor.set_connection()
169170

170171
def _get_literals(self):
@@ -173,7 +174,7 @@ def _get_literals(self):
173174
174175
:return: a dict that is parsed from esliterals.json
175176
"""
176-
from escli.esliterals import __file__ as package_root
177+
from odfesql_cli.esliterals import __file__ as package_root
177178

178179
package_root = os.path.dirname(package_root)
179180

File renamed without changes.

setup.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131
_version_re = re.compile(r"__version__\s+=\s+(.*)")
3232

33-
with open("escli/__init__.py", "rb") as f:
33+
with open("odfesql_cli/__init__.py", "rb") as f:
3434
version = str(
3535
ast.literal_eval(_version_re.search(f.read().decode("utf-8")).group(1))
3636
)
@@ -48,12 +48,12 @@
4848
license="Apache 2.0",
4949
url="https://opendistro.github.io/for-elasticsearch-docs/",
5050
packages=find_packages(),
51-
package_data={"escli": ["conf/esclirc", "esliterals/esliterals.json"]},
51+
package_data={"odfesql_cli": ["conf/clirc", "esliterals/esliterals.json"]},
5252
description=description,
5353
long_description=long_description,
5454
long_description_content_type="text/markdown",
5555
install_requires=install_requirements,
56-
entry_points={"console_scripts": ["odfesql=escli.main:cli"]},
56+
entry_points={"console_scripts": ["odfesql=odfesql_cli.main:cli"]},
5757
classifiers=[
5858
"Intended Audience :: Developers",
5959
"License :: OSI Approved :: Apache Software License",
@@ -72,4 +72,5 @@
7272
"Topic :: Software Development",
7373
"Topic :: Software Development :: Libraries :: Python Modules",
7474
],
75+
python_requires='>=3.0'
7576
)

tests/conftest.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ def connection():
3434

3535
@pytest.fixture(scope="function")
3636
def default_config_location():
37-
from escli.conf import __file__ as package_root
37+
from odfesql_cli.conf import __file__ as package_root
3838

3939
package_root = os.path.dirname(package_root)
40-
default_config = os.path.join(package_root, "esclirc")
40+
default_config = os.path.join(package_root, "clirc")
4141

4242
yield default_config
4343

tests/test_config.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import stat
1717
import pytest
1818

19-
from escli.config import ensure_dir_exists
19+
from odfesql_cli.config import ensure_dir_exists
2020

2121

2222
class TestConfig:

0 commit comments

Comments
 (0)