Elasticli
The missing Command Line Interface to interact with Elasticsearch (or yet another one).
Even if elasticsearch APIs are super easy to invoke and very well documented, I always forget the basic commands and methods to do indexing or searches, so I decided to write my own CLI.
I hope you find it useful, it is a work in progress.
Features
Written in Rust 🦀, it uses the following crates:
- clap for arguments parsing
- hydroconf for configuration management
- reqwest for http requests toward elasticsearch
If opportunely configured, the command can run inside an SSH tunnel (with auto close after a number of seconds).
Currently, you can use elasticli
to:
- get info about the target elasticsearch version;
- create, read,
update, delete an index; - create, read, update, delete a document;
Changelog
0.1.2: cross platform building
0.1.1: basic authentication
0.1.0: first release
Build & Run
Option 1
- Build it for your platform with
cargo build --release
(or just typemake
), then go totarget/release
and run the binaryelasticli
.
Option 2
-
Alternatively, download the pre-built binary for your platform in the releases page. The executable is available for the following targets:
- x86_64-unknown-linux-gnu -> 64-bit Linux (kernel 3.2+, glibc 2.17+) # CROSS BUILD
- x86_64-pc-windows-msvc -> 64-bit MSVC (Windows 7+) # BUILT ON WINDOWS ** ***
- x86_64-apple-darwin -> 64-bit macOS (10.7+, Lion+) # BUILT ON MAC x86_64 **
- aarch64-unknown-linux-gnu -> ARM64 Linux (kernel 4.1, glibc 2.17+) # CROSS BUILD
- aarch64-apple-darwin -> ARM64 macOS (11.0+, Big Sur+) # BUILT ON MAC aarch64
**: cross
uses docker images to cross compile, unfortunately windows and apple images are not available at the moment.
***: on windows, apart from install rust, you may need to install perl to build elasticli
.
You can get the complete list of rust targets with:
rustc --print target-list
Your platform is shown in the host
property of the output of:
rustc -vV
Option 3
If you want to experience the cross-compilation, install cross (you may need other dependencies), grab a beer, then run:
make <YOUR-TARGET>
where the target is one of the outputs of rustc --print target-list
. Anyway, I have already cross-compiled some of them for you.
Other
- If you want to run unit tests run
cargo test
. - If you want to build and run directly from sources
cargo run -- <your elasticli options and command here>
.
Configuration
- Every command can use the
hydroconf
features to override default configurations. For example you can override some defaults passing environment name as env var to the command line:
ENV_FOR_HYDRO=production elasticli info
or per single prop, specifying it as an env var to the command line:
HYDRO_ELASTIC__PASSWORD="an even stronger password" elasticli info
Look at the hydroconf doc for major details.
- You pass the config root directory with
-c
option before one of the commands (info, index, document). The directory must contain the configuration files (look in thedefault
folder for the latest version):
.secrets.toml environment based sensitive configurations (tipically elastic user and password).
[default]
elastic.username = 'elastic'
elastic.password = 'secure_password'
[production]
elastic.username = 'elastic'
elastic.password = 'changeme'
settings.toml environment based configurations
[default]
# your target elasticsearch host, port, protocol and version
elastic.host = 'otherhost'
elastic.port = 9200
elastic.protocol = 'http'
elastic.version = '8.8.0'
# if enabled, the main command will be executed inside an ssh tunnel. It is the same as running
### ssh -i <proxy.key> <proxy.remote_user>@<proxy.host> -f -L <elastic.port>:<elastic.host>:<elastic.port> sleep <proxy.timeout>
### ssh -i .ssh/some_id_rsa centos@bastion-host -f -L 9200:remote-es.server.es:9200 sleep 3
# but rust does it for you
proxy.enabled = false
proxy.host = 'proxyhost'
proxy.port = 9201
proxy.protocol = 'http'
proxy.user = 'ec2-user'
proxy.remote_user = 'ec2-remote-user'
proxy.key = 'path to ssh key'
proxy.timeout = 3
Commands Showcase
Learn from examples! Here a few sample commands (and sometimes the output). I hope it is understandable enough.
General
- Get help about the command, options and subcommand
elasticli --help
elasticli <info | index | document> --help
Info
- Get basic info about elasticsearch
elasticli info
{
"cluster_name": "docker-cluster",
"cluster_uuid": "b9c-xKsSRs2HQAvZ8wGsIw",
"name": "aecf011cca00",
"tagline": "You Know, for Search",
"version": {
"build_date": "2023-05-23T17:16:07.179039820Z",
"build_flavor": "default",
"build_hash": "c01029875a091076ed42cdb3a41c10b1a9a5a20f",
"build_snapshot": false,
"build_type": "docker",
"lucene_version": "9.6.0",
"minimum_index_compatibility_version": "7.0.0",
"minimum_wire_compatibility_version": "7.17.0",
"number": "8.8.0"
}
}
Index
- Get index, same operation, multiple ways (it is Clap's magic)
elasticli index -i test1
elasticli index -i=test1
elasticli index --index-name=test1
elasticli index -o read --index-name=test1
- Get indexes ('_all' and '*' are Elasticsearch wildcards)
elasticli index -i _all
elasticli index --index-name='*'
- Create index
elasticli index -o create --index-name='pippo'
elasticli index -o create --index-name='pippo_2' -b '{"settings": { "index": { "number_of_shards": 3, "number_of_replicas": 2 } } }'
{
"acknowledged": true,
"index": "pippo",
"shards_acknowledged": true
}
- Update Index
NOT YET IMPLEMENTED
- Delete Index
elasticli index -o delete -i 'pippo2'
elasticli -c ./samples/default index -o delete --index-name='pippo2'
Document
test1
- Create a document in elasticli document -o create -i test1 -b '{"name":"giufus", "language": "rust"}'
{
"_id": "5Lms-YgBu6r1vXY7vPX_",
"_index": "test1",
"_primary_term": 3,
"_seq_no": 2,
"_shards": {
"failed": 0,
"successful": 1,
"total": 2
},
"_version": 1,
"result": "created"
}
- Update an existing document (you need to put your updates in 'doc')
elasticli document -o update -i test1 -b '{"doc": { "language": "zig"} }' --id 5Lms-YgBu6r1vXY7vPX_
{
"_id": "5Lms-YgBu6r1vXY7vPX_",
"_index": "test1",
"_primary_term": 3,
"_seq_no": 3,
"_shards": {
"failed": 0,
"successful": 1,
"total": 2
},
"_version": 2,
"result": "updated"
}
- Search all docs in test1 index
elasticli document -o read -i test1
- Search all docs in test1 with an Elasticsearch query
elasticli document -o read -i test1 -b '{ "query": { "term": { "name": "giufus" } }}'
- Delete an existing document
elasticli document -o delete -i test1 --id 5rm3-YgBu6r1vXY7S_Xb
{
"_id": "5Lms-YgBu6r1vXY7vPX_",
"_index": "test1",
"_primary_term": 3,
"_seq_no": 9,
"_shards": {
"failed": 0,
"successful": 1,
"total": 2
},
"_version": 3,
"result": "deleted"
}
Defaults
There are some defaults in the code in case you don't specify them:
http
as protocol127.0.0.1
as host9200
as port_doc
as type (used in document deletion)read
as operation8.8.0
as es version
To do
- write better documentation
- handle elasticsearch versions (e.g. providing multiple implementations of the trait)
- integration tests (maybe using something like testcontainers)
Run elasticsearch locally with docker and no security
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e "xpack.security.enabled=false" elasticsearch:8.8.0
Run elasticsearch locally with docker and basic authentication
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e "xpack.security.enabled=true" -e "ELASTIC_PASSWORD=changeme" elasticsearch:8.8.0