├── .editorconfig ├── .env ├── .github └── workflows │ ├── lint.yml │ └── test.yml ├── .markdownlint.yaml ├── .yamllint ├── LICENSE ├── NOTICE ├── README.md ├── start-local.sh └── tests ├── basic_test.sh ├── expire_license_test.sh ├── install_esonly_test.sh ├── install_from_curl_test.sh ├── install_with_version_test.sh ├── start_stop_test.sh ├── uninstall_test.sh └── utility.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | ### Common Settings ################################################################################ 2 | 3 | # Documentation: 4 | # http://docs.editorconfig.org/en/master/editorconfig-format.html 5 | 6 | # This file is the top-most EditorConfig file 7 | root = true 8 | 9 | ### All Files -------------------------------------------------------------------------------------- 10 | 11 | [*] 12 | charset = utf-8 13 | indent_style = space 14 | indent_size = 4 15 | insert_final_newline = true 16 | trim_trailing_whitespace = true 17 | 18 | ### File Extension Settings ------------------------------------------------------------------------ 19 | 20 | # JSON Files 21 | [*.{json,json5}] 22 | indent_size = 2 23 | 24 | # YAML Files 25 | [*.{yml,yaml}] 26 | indent_size = 2 27 | 28 | # Markdown Files 29 | [*.{md,mdx}] 30 | indent_size = 2 31 | trim_trailing_whitespace = false 32 | 33 | # Batch Scripts 34 | [*.{bat,cmd,ps1}] 35 | indent_size = 2 36 | end_of_line = crlf 37 | 38 | # Bash Scripts 39 | [*.{bash,fish,sh,zsh}] 40 | indent_size = 2 41 | end_of_line = lf 42 | 43 | ### ------------------------------------------------------------------------------------------------ 44 | 45 | #################################################################################################### 46 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | BASHUNIT_DEFAULT_PATH=tests 2 | BASHUNIT_SHOW_EXECUTION_TIME=true 3 | BASHUNIT_STOP_ON_FAILURE=false -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: # yamllint disable-line rule:truthy 4 | pull_request: 5 | types: [opened, synchronize, reopened] 6 | push: 7 | paths-ignore: 8 | - 'README.md' 9 | - '.editorconfig' 10 | 11 | jobs: 12 | shellcheck: 13 | name: ShellCheck 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | 20 | - name: Lint 21 | uses: reviewdog/action-shellcheck@5ebd09ddbe2ebb471646ce234c6c8dd18663ca7c # v1.30.0 22 | with: 23 | reporter: github-pr-check 24 | filter_mode: nofilter 25 | reviewdog_flags: '-fail-level=any' 26 | 27 | markdownlint: 28 | name: MarkdownLint 29 | runs-on: ubuntu-latest 30 | 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v4 34 | 35 | - name: Lint 36 | uses: reviewdog/action-markdownlint@3667398db9118d7e78f7a63d10e26ce454ba5f58 # v0.26.2 37 | with: 38 | reporter: github-pr-check 39 | filter_mode: nofilter 40 | reviewdog_flags: '-fail-level=any' 41 | 42 | yamllint: 43 | name: YamlLint 44 | runs-on: ubuntu-latest 45 | 46 | steps: 47 | - name: Checkout 48 | uses: actions/checkout@v4 49 | 50 | - name: Lint 51 | uses: reviewdog/action-yamllint@f01d8a48fd8d89f89895499fca2cff09f9e9e8c0 # v1.21.0 52 | with: 53 | reporter: github-pr-check 54 | filter_mode: nofilter 55 | reviewdog_flags: '-fail-level=any' 56 | 57 | actionlint: 58 | name: ActionLint 59 | runs-on: ubuntu-latest 60 | 61 | steps: 62 | - name: Checkout 63 | uses: actions/checkout@v4 64 | 65 | - name: Lint 66 | uses: reviewdog/action-actionlint@a5524e1c19e62881d79c1f1b9b6f09f16356e281 # v1.65.2 67 | with: 68 | reporter: github-pr-check 69 | filter_mode: nofilter 70 | reviewdog_flags: '-fail-level=any' 71 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Installer test 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, reopened] 6 | push: 7 | paths-ignore: 8 | - 'README.md' 9 | - '.editorconfig' 10 | env: 11 | START_LOCAL_URL: "http://localhost:8080/start-local" 12 | 13 | jobs: 14 | test: 15 | name: Test 16 | runs-on: ${{ matrix.os }} 17 | 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | os: [ubuntu-22.04, ubuntu-24.04] 22 | 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@v4 26 | 27 | - name: Install bashunit 28 | run: | 29 | curl -s https://bashunit.typeddevs.com/install.sh | bash 30 | 31 | - name: Setup PHP for running a local web server 32 | uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2.32.0 33 | with: 34 | php-version: '8.3' 35 | 36 | - name: Cache Docker images 37 | uses: ScribeMD/docker-cache@fb28c93772363301b8d0a6072ce850224b73f74e # 0.5.0 38 | with: 39 | key: docker-${{ runner.os }}-${{ hashFiles('start-local.sh') }} 40 | 41 | - name: Run the tests 42 | run: | 43 | ./lib/bashunit 44 | -------------------------------------------------------------------------------- /.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | # MD013/line-length : Line length : https://github.com/DavidAnson/markdownlint/blob/v0.36.1/doc/md013.md 2 | MD013: false 3 | 4 | # MD028/no-blanks-blockquote : Blank line inside blockquote : https://github.com/DavidAnson/markdownlint/blob/v0.36.1/doc/md028.md 5 | MD028: false 6 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | rules: 2 | # https://yamllint.readthedocs.io/en/stable/rules.html#module-yamllint.rules.document_start 3 | document-start: disable 4 | # https://yamllint.readthedocs.io/en/stable/rules.html#module-yamllint.rules.line_length 5 | line-length: 6 | max: 140 7 | allow-non-breakable-words: true 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | start-local 2 | Copyright 2024 Elasticsearch B.V. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🚀 Try Elasticsearch and Kibana locally 2 | 3 | Run Elasticsearch and Kibana on your local machine using a simple shell script. This setup uses [Docker](https://www.docker.com/) behind the scenes to install and run the services. 4 | 5 | > [!IMPORTANT] 6 | > This script is for local testing only. Do not use it in production! 7 | > For production installations refer to the official documentation for [Elasticsearch](https://www.elastic.co/downloads/elasticsearch) and [Kibana](https://www.elastic.co/downloads/kibana). 8 | 9 | ## 🌟 Features 10 | 11 | This script comes with a one-month trial license. 12 | After the trial period, the license reverts to [Free and open - Basic](https://www.elastic.co/subscriptions). 13 | 14 | - **Trial**: Includes **All** features like the [Playground](https://www.elastic.co/docs/current/serverless/elasticsearch/playground), [ELSER](https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-elser.html), [semantic retrieval model](https://www.elastic.co/guide/en/machine-learning/8.15/ml-nlp-text-emb-vector-search-example.html), the [Elastic Inference API](https://www.elastic.co/guide/en/elasticsearch/reference/current/inference-apis.html) and much more. 15 | - **Free and open - Basic**: Includes features like [vector search](https://www.elastic.co/what-is/vector-search), [ES|QL](https://www.elastic.co/guide/en/elasticsearch/reference/current/esql.html) and much more. 16 | 17 | For a complete list of subscriptions and features, see our [subscriptions page](https://www.elastic.co/subscriptions). 18 | 19 | ## 💻 System requirements 20 | 21 | - 5 GB of available disk space 22 | - [Docker](https://www.docker.com/) 23 | - Works on Linux and macOS 24 | - On Microsoft Windows it works using [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/install) 25 | 26 | ## 🏃‍♀️‍➡️ Getting started 27 | 28 | ### Setup 29 | 30 | Run the `start-local` script using [curl](https://curl.se/): 31 | 32 | ```bash 33 | curl -fsSL https://elastic.co/start-local | sh 34 | ``` 35 | 36 | This script creates an `elastic-start-local` folder containing: 37 | 38 | - `docker-compose.yml`: Docker Compose configuration for Elasticsearch and Kibana 39 | - `.env`: Environment settings, including the Elasticsearch password 40 | - `start.sh` and `stop.sh`: Scripts to start and stop Elasticsearch and Kibana 41 | - `uninstall.sh`: The script to uninstall Elasticsearch and Kibana 42 | 43 | ### Select the version to install 44 | 45 | By default, `start-local` uses the latest stable version of Elastic Stack. If you want, you can specify 46 | a different version using the `-v` parameter, as follows: 47 | 48 | ```bash 49 | curl -fsSL https://elastic.co/start-local | sh -s -- -v 8.16.0 50 | ``` 51 | 52 | The previous command installs Elasticsearch and Kibana `8.16.0`. 53 | 54 | Using the `-v` parameter, you can also install beta releases, this can be useful for testing an 55 | upcoming release. For instance, you can install the `9.0.0-beta1` using the following 56 | command: 57 | 58 | ```bash 59 | curl -fsSL https://elastic.co/start-local | sh -s -- -v 9.0.0-beta1 60 | ``` 61 | 62 | The `9.0.0-beta1` version was released on February 18, 2025. 63 | 64 | ### Install only Elasticsearch 65 | 66 | If you want to install only Elasticsearch, without Kibana, you can use the `-esonly` option 67 | as follows: 68 | 69 | ```bash 70 | curl -fsSL https://elastic.co/start-local | sh -s -- -esonly 71 | ``` 72 | 73 | This command can be useful if you don't have enough resources and want to test only Elasticsearch. 74 | 75 | ### 🌐 Endpoints 76 | 77 | After running the script: 78 | 79 | - Elasticsearch will be running at 80 | - Kibana will be running at 81 | 82 | The script generates a random password for the `elastic` user, displayed at the end of the installation and stored in the `.env` file. 83 | 84 | > [!CAUTION] 85 | > HTTPS is disabled, and Basic authentication is used for Elasticsearch. This configuration is for local testing only. For security, Elasticsearch and Kibana are accessible only via `localhost`. 86 | 87 | ### 🔑 API key 88 | 89 | An API key for Elasticsearch is generated and stored in the `.env` file as `ES_LOCAL_API_KEY`. Use this key to connect to Elasticsearch with the [Elastic SDK](https://www.elastic.co/guide/en/elasticsearch/client) or [REST API](https://www.elastic.co/guide/en/elasticsearch/reference/current/rest-apis.html). 90 | 91 | Check the connection to Elasticsearch using `curl` in the `elastic-start-local` folder: 92 | 93 | ```bash 94 | source .env 95 | curl $ES_LOCAL_URL -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}" 96 | ``` 97 | 98 | ## 🐳 Start and stop the services 99 | 100 | You can use the `start` and `stop` commands available in the `elastic-start-local` folder. 101 | 102 | To **stop** the Elasticsearch and Kibana Docker services, use the `stop` command: 103 | 104 | ```bash 105 | cd elastic-start-local 106 | ./stop.sh 107 | ``` 108 | 109 | To **start** the Elasticsearch and Kibana Docker services, use the `start` command: 110 | 111 | ```bash 112 | cd elastic-start-local 113 | ./start.sh 114 | ``` 115 | 116 | [Docker Compose](https://docs.docker.com/reference/cli/docker/compose/). 117 | 118 | ## 🗑️ Uninstallation 119 | 120 | To remove the `start-local` installation: 121 | 122 | ```bash 123 | cd elastic-start-local 124 | ./uninstall.sh 125 | ``` 126 | 127 | > [!WARNING] 128 | > This erases all data permanently. 129 | 130 | ## 📝 Logging 131 | 132 | If the installation fails, an error log is created in `error-start-local.log`. This file contains logs from Elasticsearch and Kibana, captured using the [docker logs](https://docs.docker.com/reference/cli/docker/container/logs/) command. 133 | 134 | ## ⚙️ Customizing settings 135 | 136 | To change settings (e.g., Elasticsearch password), edit the `.env` file. Example contents: 137 | 138 | ```bash 139 | ES_LOCAL_VERSION=8.15.2 140 | ES_LOCAL_URL=http://localhost:9200 141 | ES_LOCAL_CONTAINER_NAME=es-local-dev 142 | ES_LOCAL_DOCKER_NETWORK=elastic-net 143 | ES_LOCAL_PASSWORD=hOalVFrN 144 | ES_LOCAL_PORT=9200 145 | KIBANA_LOCAL_CONTAINER_NAME=kibana-local-dev 146 | KIBANA_LOCAL_PORT=5601 147 | KIBANA_LOCAL_PASSWORD=YJFbhLJL 148 | ES_LOCAL_API_KEY=df34grtk...== 149 | ``` 150 | 151 | > [!IMPORTANT] 152 | > After changing the `.env` file, restart the services using `stop` and `start`: 153 | > 154 | > ```bash 155 | > cd elastic-start-local 156 | > ./stop.sh 157 | > ./start.sh 158 | > ``` 159 | 160 | ## 🧪 Testing the installer 161 | 162 | We use [bashunit](https://bashunit.typeddevs.com/) to test the script. Tests are in the `/tests` folder. 163 | 164 | ### Running tests 165 | 166 | 1. Install bashunit: 167 | 168 | ```bash 169 | curl -s https://bashunit.typeddevs.com/install.sh | bash 170 | ``` 171 | 172 | 2. Run tests: 173 | 174 | ```bash 175 | lib/bashunit 176 | ``` 177 | 178 | The tests run `start-local.sh` and check if Elasticsearch and Kibana are working. 179 | 180 | > [!NOTE] 181 | > For URL pipeline testing, a local web server is used. This requires [PHP](https://www.php.net/). 182 | -------------------------------------------------------------------------------- /start-local.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # -------------------------------------------------------- 3 | # Run Elasticsearch and Kibana for local testing 4 | # Note: do not use this script in a production environment 5 | # -------------------------------------------------------- 6 | # 7 | # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one 8 | # or more contributor license agreements. See the NOTICE file distributed with 9 | # this work for additional information regarding copyright 10 | # ownership. Elasticsearch B.V. licenses this file to you under 11 | # the Apache License, Version 2.0 (the "License"); you may 12 | # not use this file except in compliance with the License. 13 | # You may obtain a copy of the License at 14 | # 15 | # http://www.apache.org/licenses/LICENSE-2.0 16 | # 17 | # Unless required by applicable law or agreed to in writing, 18 | # software distributed under the License is distributed on an 19 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 | # KIND, either express or implied. See the License for the 21 | # specific language governing permissions and limitations 22 | # under the License. 23 | set -eu 24 | 25 | parse_args() { 26 | # Parse the script parameters 27 | while [ "$#" -gt 0 ]; do 28 | case "$1" in 29 | -v) 30 | # Check that there is another argument for the version 31 | if [ $# -lt 2 ]; then 32 | echo "Error: -v requires a version value (eg. -v 8.17.0)" 33 | exit 1 34 | fi 35 | es_version="$2" 36 | shift 2 37 | ;; 38 | 39 | --esonly) 40 | esonly=true 41 | shift 42 | ;; 43 | 44 | --) 45 | # End of options; shift and exit the loop 46 | shift 47 | break 48 | ;; 49 | 50 | -*) 51 | # Unknown or unsupported option 52 | echo "Error: Unknown option '$1'" 53 | exit 1 54 | ;; 55 | 56 | *) 57 | # We've hit a non-option argument; stop parsing options 58 | break 59 | ;; 60 | esac 61 | done 62 | } 63 | 64 | startup() { 65 | echo 66 | echo ' ______ _ _ _ ' 67 | echo ' | ____| | | | (_) ' 68 | echo ' | |__ | | __ _ ___| |_ _ ___ ' 69 | echo ' | __| | |/ _` / __| __| |/ __|' 70 | echo ' | |____| | (_| \__ \ |_| | (__ ' 71 | echo ' |______|_|\__,_|___/\__|_|\___|' 72 | echo '-------------------------------------------------' 73 | echo '🚀 Run Elasticsearch and Kibana for local testing' 74 | echo '-------------------------------------------------' 75 | echo 76 | echo 'ℹ️ Do not use this script in a production environment' 77 | echo 78 | 79 | # Version 80 | version="0.9.1" 81 | 82 | # Folder name for the installation 83 | installation_folder="elastic-start-local" 84 | # API key name for Elasticsearch 85 | api_key_name="elastic-start-local" 86 | # Name of the error log 87 | error_log="error-start-local.log" 88 | # Minimum version for docker-compose 89 | min_docker_compose="1.29.0" 90 | # Elasticsearch container name 91 | elasticsearch_container_name="es-local-dev" 92 | # Kibana container name 93 | kibana_container_name="kibana-local-dev" 94 | # Minimum disk space required for docker images + services (in GB) 95 | min_disk_space_required=5 96 | } 97 | 98 | # Check for ARM64 architecture 99 | is_arm64() { 100 | arch="$(uname -m)" 101 | if [ "$arch" = "arm64" ] || [ "$arch" = "aarch64" ]; then 102 | return 0 # Return 0 (true) 103 | else 104 | return 1 # Return 1 (false) 105 | fi 106 | } 107 | 108 | # Alternative to sort -V, which is not available in BSD-based systems (e.g., macOS) 109 | version_sort() { 110 | awk -F'.' ' 111 | { 112 | printf("%d %d %d %s\n", $1, $2, $3, $0) 113 | }' | sort -n -k1,1 -k2,2 -k3,3 | awk '{print $4}' 114 | } 115 | 116 | # Function to check if the format is a valid semantic version (major.minor.patch) 117 | is_valid_version() { 118 | echo "$1" | grep -E -q '^[0-9]+\.[0-9]+\.[0-9]+$' 119 | } 120 | 121 | # Get the latest stable version of Elasticsearch 122 | # Note: It removes all the beta or candidate releases from the list 123 | # but includes the GA releases (e.g. new major) 124 | get_latest_version() { 125 | versions="$(curl -s "https://artifacts.elastic.co/releases/stack.json")" 126 | latest_version=$(echo "$versions" | awk -F'"' '/"version": *"/ {print $4}' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+( GA)?$' | version_sort | tail -n 1) 127 | # Remove the GA prefix from the version, if present 128 | latest_version=$(echo "$latest_version" | awk '{ gsub(/ GA$/, "", $0); print }') 129 | 130 | # Check if the latest version is empty 131 | if [ -z "$latest_version" ]; then 132 | echo "Error: the latest Elasticsearch version is empty" 133 | exit 1 134 | fi 135 | # Check if the latest version is valid 136 | if ! is_valid_version "$latest_version"; then 137 | echo "Error: {$latest_version} is not a valid Elasticsearch stable version" 138 | exit 1 139 | fi 140 | 141 | echo "$latest_version" 142 | } 143 | 144 | # Detect if running on LXC container 145 | detect_lxc() { 146 | # Check /proc/1/environ for LXC container identifier 147 | if grep -qa "container=lxc" /proc/1/environ 2>/dev/null; then 148 | return 0 149 | fi 150 | # Check /proc/self/cgroup for LXC references 151 | if grep -q "lxc" /proc/self/cgroup 2>/dev/null; then 152 | return 0 153 | fi 154 | # Check for LXC in /sys/fs/cgroup 155 | if grep -q "lxc" /sys/fs/cgroup/* 2>/dev/null; then 156 | return 0 157 | fi 158 | # Use systemd-detect-virt if available 159 | if command -v systemd-detect-virt >/dev/null 2>&1; then 160 | if [ "$(systemd-detect-virt)" = "lxc" ]; then 161 | return 0 162 | fi 163 | fi 164 | return 1 165 | } 166 | 167 | # Get linux distribution 168 | get_os_info() { 169 | if [ -f /etc/os-release ]; then 170 | # Most modern Linux distributions have this file 171 | . /etc/os-release 172 | echo "Distribution: $NAME" 173 | echo "Version: $VERSION" 174 | elif [ -f /etc/lsb-release ]; then 175 | # For older distributions using LSB (Linux Standard Base) 176 | . /etc/lsb-release 177 | echo "Distribution: $DISTRIB_ID" 178 | echo "Version: $DISTRIB_RELEASE" 179 | elif [ -f /etc/debian_version ]; then 180 | # For Debian-based distributions without os-release or lsb-release 181 | echo "Distribution: Debian" 182 | echo "Version: $(cat /etc/debian_version)" 183 | elif [ -f /etc/redhat-release ]; then 184 | # For Red Hat-based distributions 185 | echo "Distribution: $(cat /etc/redhat-release)" 186 | elif [ -n "${OSTYPE+x}" ]; then 187 | if [ "${OSTYPE#darwin}" != "$OSTYPE" ]; then 188 | # macOS detection 189 | echo "Distribution: macOS" 190 | echo "Version: $(sw_vers -productVersion)" 191 | elif [ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ] || [ "$OSTYPE" = "win32" ]; then 192 | # Windows detection in environments like Git Bash, Cygwin, or MinGW 193 | echo "Distribution: Windows" 194 | echo "Version: $(cmd.exe /c ver | tr -d '\r')" 195 | elif [ "$OSTYPE" = "linux-gnu" ] && uname -r | grep -q "Microsoft"; then 196 | # Windows Subsystem for Linux (WSL) detection 197 | echo "Distribution: Windows (WSL)" 198 | echo "Version: $(uname -r)" 199 | fi 200 | else 201 | echo "Unknown operating system" 202 | fi 203 | if [ -f /proc/version ]; then 204 | # Check if running on WSL2 or WSL1 for Microsoft 205 | if grep -q "WSL2" /proc/version; then 206 | echo "Running on WSL2" 207 | elif grep -q "microsoft" /proc/version; then 208 | echo "Running on WSL1" 209 | fi 210 | fi 211 | } 212 | 213 | # Check if a command exists 214 | available() { command -v "$1" >/dev/null; } 215 | 216 | # Revert the status, removing containers, volumes, network and folder 217 | cleanup() { 218 | if [ -d "./../$folder_to_clean" ]; then 219 | if [ -f "docker-compose.yml" ]; then 220 | $docker_clean >/dev/null 2>&1 221 | $docker_remove_volumes >/dev/null 2>&1 222 | fi 223 | cd .. 224 | rm -rf "${folder_to_clean}" 225 | fi 226 | } 227 | 228 | # Generate the error log 229 | # parameter 1: error message 230 | # parameter 2: the container names to retrieve, separated by comma 231 | generate_error_log() { 232 | msg="$1" 233 | docker_services="$2" 234 | error_file="$error_log" 235 | if [ -d "./../$folder_to_clean" ]; then 236 | error_file="./../$error_log" 237 | fi 238 | if [ -n "${msg}" ]; then 239 | echo "${msg}" > "$error_file" 240 | fi 241 | { 242 | echo "Start-local version: ${version}" 243 | echo "Docker engine: $(docker --version)" 244 | echo "Docker compose: ${docker_version}" 245 | get_os_info 246 | } >> "$error_file" 247 | for service in $docker_services; do 248 | echo "-- Logs of service ${service}:" >> "$error_file" 249 | docker logs "${service}" >> "$error_file" 2> /dev/null 250 | done 251 | echo "An error log has been generated in ${error_log} file." 252 | echo "If you need assistance, open an issue at https://github.com/elastic/start-local/issues" 253 | } 254 | 255 | # Compare versions 256 | # parameter 1: version to compare 257 | # parameter 2: version to compare 258 | compare_versions() { 259 | v1=$1 260 | v2=$2 261 | 262 | original_ifs="$IFS" 263 | IFS='.' 264 | # shellcheck disable=SC2086 265 | set -- $v1; v1_major=${1:-0}; v1_minor=${2:-0}; v1_patch=${3:-0} 266 | IFS='.' 267 | # shellcheck disable=SC2086 268 | set -- $v2; v2_major=${1:-0}; v2_minor=${2:-0}; v2_patch=${3:-0} 269 | IFS="$original_ifs" 270 | 271 | [ "$v1_major" -lt "$v2_major" ] && echo "lt" && return 0 272 | [ "$v1_major" -gt "$v2_major" ] && echo "gt" && return 0 273 | 274 | [ "$v1_minor" -lt "$v2_minor" ] && echo "lt" && return 0 275 | [ "$v1_minor" -gt "$v2_minor" ] && echo "gt" && return 0 276 | 277 | [ "$v1_patch" -lt "$v2_patch" ] && echo "lt" && return 0 278 | [ "$v1_patch" -gt "$v2_patch" ] && echo "gt" && return 0 279 | 280 | echo "eq" 281 | } 282 | 283 | # Wait for availability of Kibana 284 | # parameter: timeout in seconds 285 | wait_for_kibana() { 286 | timeout="${1:-60}" 287 | echo "- Waiting for Kibana to be ready" 288 | echo 289 | start_time="$(date +%s)" 290 | until curl -s -I http://localhost:5601 | grep -q 'HTTP/1.1 302 Found'; do 291 | elapsed_time="$(($(date +%s) - start_time))" 292 | if [ "$elapsed_time" -ge "$timeout" ]; then 293 | error_msg="Error: Kibana timeout of ${timeout} sec" 294 | echo "$error_msg" 295 | generate_error_log "${error_msg}" "${elasticsearch_container_name} ${kibana_container_name} kibana_settings" 296 | cleanup 297 | exit 1 298 | fi 299 | sleep 2 300 | done 301 | } 302 | 303 | # Generates a random password with letters and numbers 304 | # parameter: size of the password (default is 8 characters) 305 | random_password() { 306 | LENGTH="${1:-8}" 307 | LC_ALL=C tr -dc 'A-Za-z0-9' < /dev/urandom | head -c "${LENGTH}" 308 | } 309 | 310 | # Create an API key for Elasticsearch 311 | # parameter 1: the Elasticsearch password 312 | # parameter 2: name of the API key to generate 313 | create_api_key() { 314 | es_password=$1 315 | name=$2 316 | response="$(curl -s -u "elastic:${es_password}" -X POST http://localhost:9200/_security/api_key -d "{\"name\": \"${name}\"}" -H "Content-Type: application/json")" 317 | if [ -z "$response" ]; then 318 | echo "" 319 | else 320 | api_key="$(echo "$response" | grep -Eo '"encoded":"[A-Za-z0-9+/=]+' | grep -Eo '[A-Za-z0-9+/=]+' | tail -n 1)" 321 | echo "$api_key" 322 | fi 323 | } 324 | 325 | # Check if a container is runnning 326 | # parameter: the name of the container 327 | check_container_running() { 328 | container_name=$1 329 | containers="$(docker ps --format '{{.Names}}')" 330 | if echo "$containers" | grep -q "^${container_name}$"; then 331 | echo "The docker container '$container_name' is already running!" 332 | echo "You can have only one running at time." 333 | echo "To stop the container run the following command:" 334 | echo 335 | echo "docker stop $container_name" 336 | exit 1 337 | fi 338 | } 339 | 340 | # Check the available disk space in GB 341 | # parameter: required size in GB 342 | check_disk_space_gb() { 343 | required=$1 344 | available_gb=$(($(df -k / | awk 'NR==2 {print $4}') / 1024 / 1024)) 345 | if [ "$available_gb" -lt "$required" ]; then 346 | echo "Error: only ${available_gb} GB of disk space available; ${required} GB required for the installation" 347 | exit 1 348 | fi 349 | } 350 | 351 | check_requirements() { 352 | # Check the requirements 353 | check_disk_space_gb ${min_disk_space_required} 354 | if ! available "curl"; then 355 | echo "Error: curl command is required" 356 | echo "You can install it from https://curl.se/download.html." 357 | exit 1 358 | fi 359 | if ! available "grep"; then 360 | echo "Error: grep command is required" 361 | echo "You can install it from https://www.gnu.org/software/grep/." 362 | exit 1 363 | fi 364 | need_wait_for_kibana=true 365 | # Check for "docker compose" or "docker-compose" 366 | set +e 367 | if ! docker compose >/dev/null 2>&1; then 368 | if ! available "docker-compose"; then 369 | if ! available "docker"; then 370 | echo "Error: docker command is required" 371 | echo "You can install it from https://docs.docker.com/engine/install/." 372 | exit 1 373 | fi 374 | echo "Error: docker compose is required" 375 | echo "You can install it from https://docs.docker.com/compose/install/" 376 | exit 1 377 | fi 378 | docker="docker-compose up -d" 379 | docker_stop="docker-compose stop" 380 | docker_clean="docker-compose rm -fsv" 381 | docker_remove_volumes="docker-compose down -v" 382 | docker_version=$(docker-compose --version | head -n 1 | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+') 383 | if [ "$(compare_versions "$docker_version" "$min_docker_compose")" = "lt" ]; then 384 | echo "Unfortunately we don't support docker compose ${docker_version}. The minimum required version is $min_docker_compose." 385 | echo "You can migrate you docker compose from https://docs.docker.com/compose/migrate/" 386 | cleanup 387 | exit 1 388 | fi 389 | else 390 | docker_stop="docker compose stop" 391 | docker_clean="docker compose rm -fsv" 392 | docker_remove_volumes="docker compose down -v" 393 | docker_version=$(docker compose version | head -n 1 | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+') 394 | # --wait option has been introduced in 2.1.1+ 395 | if [ "$(compare_versions "$docker_version" "2.1.0")" = "gt" ]; then 396 | docker="docker compose up --wait" 397 | need_wait_for_kibana=false 398 | else 399 | docker="docker compose up -d" 400 | fi 401 | fi 402 | set -e 403 | } 404 | 405 | check_installation_folder() { 406 | # Check if $installation_folder exists 407 | folder=$installation_folder 408 | if [ -d "$folder" ]; then 409 | if [ -n "$(ls -A "$folder")" ]; then 410 | echo "It seems you have already a start-local installation in '${folder}'." 411 | if [ -f "$folder/uninstall.sh" ]; then 412 | echo "I cannot proceed unless you uninstall it, using the following command:" 413 | echo "cd $folder && ./uninstall.sh" 414 | else 415 | echo "I did not find the uninstall.sh file, you need to proceed manually." 416 | if [ -f "$folder/docker-compose.yml" ] && [ -f "$folder/.env" ]; then 417 | echo "Execute the following commands:" 418 | echo "cd $folder" 419 | echo "$docker_clean" 420 | echo "$docker_remove_volumes" 421 | echo "cd .." 422 | echo "rm -rf $folder" 423 | fi 424 | fi 425 | exit 1 426 | fi 427 | fi 428 | } 429 | 430 | check_docker_services() { 431 | # Check for docker containers running 432 | check_container_running "$elasticsearch_container_name" 433 | check_container_running "$kibana_container_name" 434 | check_container_running "kibana_settings" 435 | } 436 | 437 | create_installation_folder() { 438 | # If $folder already exists, it is empty, see above 439 | if [ ! -d "$folder" ]; then 440 | mkdir $folder 441 | fi 442 | cd $folder 443 | folder_to_clean=$folder 444 | } 445 | 446 | generate_passwords() { 447 | # Generate random passwords 448 | es_password="$(random_password)" 449 | if [ -z "${esonly:-}" ]; then 450 | kibana_password="$(random_password)" 451 | kibana_encryption_key="$(random_password 32)" 452 | fi 453 | } 454 | 455 | choose_es_version() { 456 | if [ -z "${es_version:-}" ]; then 457 | # Get the latest Elasticsearch version 458 | es_version="$(get_latest_version)" 459 | fi 460 | } 461 | 462 | create_env_file() { 463 | # Create the .env file 464 | cat > .env <<- EOM 465 | START_LOCAL_VERSION=$version 466 | ES_LOCAL_VERSION=$es_version 467 | ES_LOCAL_CONTAINER_NAME=$elasticsearch_container_name 468 | ES_LOCAL_PASSWORD=$es_password 469 | ES_LOCAL_PORT=9200 470 | ES_LOCAL_URL=http://localhost:\${ES_LOCAL_PORT} 471 | ES_LOCAL_HEAP_INIT=128m 472 | ES_LOCAL_HEAP_MAX=2g 473 | ES_LOCAL_DISK_SPACE_REQUIRED=1gb 474 | EOM 475 | 476 | if [ -z "${esonly:-}" ]; then 477 | cat >> .env <<- EOM 478 | KIBANA_LOCAL_CONTAINER_NAME=$kibana_container_name 479 | KIBANA_LOCAL_PORT=5601 480 | KIBANA_LOCAL_PASSWORD=$kibana_password 481 | KIBANA_ENCRYPTION_KEY=$kibana_encryption_key 482 | EOM 483 | fi 484 | } 485 | 486 | # Create the start script (start.sh) 487 | # including the license update if trial expired 488 | create_start_file() { 489 | today=$(date +%s) 490 | expire=$((today + 3600*24*30)) 491 | 492 | cat > start.sh <<-'EOM' 493 | #!/bin/sh 494 | # Start script for start-local 495 | # More information: https://github.com/elastic/start-local 496 | set -eu 497 | 498 | SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" 499 | cd "${SCRIPT_DIR}" 500 | today=$(date +%s) 501 | . ./.env 502 | # Check disk space 503 | available_gb=$(($(df -k / | awk 'NR==2 {print $4}') / 1024 / 1024)) 504 | required=$(echo "${ES_LOCAL_DISK_SPACE_REQUIRED}" | grep -Eo '[0-9]+') 505 | if [ "$available_gb" -lt "$required" ]; then 506 | echo "----------------------------------------------------------------------------" 507 | echo "WARNING: Disk space is below the ${required} GB limit. Elasticsearch will be" 508 | echo "executed in read-only mode. Please free up disk space to resolve this issue." 509 | echo "----------------------------------------------------------------------------" 510 | echo "Press ENTER to confirm." 511 | # shellcheck disable=SC2034 512 | read -r line 513 | fi 514 | EOM 515 | if [ "$need_wait_for_kibana" = true ]; then 516 | cat >> start.sh <<-'EOM' 517 | wait_for_kibana() { 518 | _timeout="${1:-60}" 519 | echo "- Waiting for Kibana to be ready" 520 | echo 521 | _start_time="$(date +%s)" 522 | until curl -s -I http://localhost:5601 | grep -q 'HTTP/1.1 302 Found'; do 523 | elapsed_time="$(($(date +%s) - _start_time))" 524 | if [ "$elapsed_time" -ge "$_timeout" ]; then 525 | echo "Error: Kibana timeout of ${_timeout} sec" 526 | exit 1 527 | fi 528 | sleep 2 529 | done 530 | } 531 | 532 | EOM 533 | fi 534 | 535 | cat >> start.sh <<- EOM 536 | if [ -z "\${ES_LOCAL_LICENSE:-}" ] && [ "\$today" -gt $expire ]; then 537 | echo "---------------------------------------------------------------------" 538 | echo "The one-month trial period has expired. You can continue using the" 539 | echo "Free and open Basic license or request to extend the trial for" 540 | echo "another 30 days using this form:" 541 | echo "https://www.elastic.co/trialextension" 542 | echo "---------------------------------------------------------------------" 543 | echo "For more info about the license: https://www.elastic.co/subscriptions" 544 | echo 545 | echo "Updating the license..." 546 | $docker elasticsearch >/dev/null 2>&1 547 | result=\$(curl -s -X POST "\${ES_LOCAL_URL}/_license/start_basic?acknowledge=true" -H "Authorization: ApiKey \${ES_LOCAL_API_KEY}" -o /dev/null -w '%{http_code}\n') 548 | if [ "\$result" = "200" ]; then 549 | echo "✅ Basic license successfully installed" 550 | echo "ES_LOCAL_LICENSE=basic" >> .env 551 | else 552 | echo "Error: I cannot update the license" 553 | result=\$(curl -s -X GET "\${ES_LOCAL_URL}" -H "Authorization: ApiKey \${ES_LOCAL_API_KEY}" -o /dev/null -w '%{http_code}\n') 554 | if [ "\$result" != "200" ]; then 555 | echo "Elasticsearch is not running." 556 | fi 557 | exit 1 558 | fi 559 | echo 560 | fi 561 | $docker 562 | EOM 563 | 564 | if [ "$need_wait_for_kibana" = true ]; then 565 | cat >> start.sh <<-'EOM' 566 | wait_for_kibana 120 567 | EOM 568 | fi 569 | chmod +x start.sh 570 | } 571 | 572 | # Create the stop script (stop.sh) 573 | create_stop_file() { 574 | cat > stop.sh <<-'EOM' 575 | #!/bin/sh 576 | # Stop script for start-local 577 | # More information: https://github.com/elastic/start-local 578 | set -eu 579 | 580 | SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" 581 | cd "${SCRIPT_DIR}" 582 | EOM 583 | 584 | cat >> stop.sh <<- EOM 585 | $docker_stop 586 | EOM 587 | chmod +x stop.sh 588 | } 589 | 590 | # Create the uninstall script (uninstall.sh) 591 | create_uninstall_file() { 592 | 593 | cat > uninstall.sh <<-'EOM' 594 | #!/bin/sh 595 | # Uninstall script for start-local 596 | # More information: https://github.com/elastic/start-local 597 | set -eu 598 | 599 | SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" 600 | 601 | ask_confirmation() { 602 | echo "Do you confirm? (yes/no)" 603 | read -r answer 604 | case "$answer" in 605 | yes|y|Y|Yes|YES) 606 | return 0 # true 607 | ;; 608 | no|n|N|No|NO) 609 | return 1 # false 610 | ;; 611 | *) 612 | echo "Please answer yes or no." 613 | ask_confirmation # Ask again if the input is invalid 614 | ;; 615 | esac 616 | } 617 | 618 | cd "${SCRIPT_DIR}" 619 | if [ ! -e "docker-compose.yml" ]; then 620 | echo "Error: I cannot find the docker-compose.yml file" 621 | echo "I cannot uninstall start-local." 622 | fi 623 | if [ ! -e ".env" ]; then 624 | echo "Error: I cannot find the .env file" 625 | echo "I cannot uninstall start-local." 626 | fi 627 | echo "This script will uninstall start-local." 628 | echo "All data will be deleted and cannot be recovered." 629 | if ask_confirmation; then 630 | EOM 631 | 632 | cat >> uninstall.sh <<- EOM 633 | $docker_clean 634 | $docker_remove_volumes 635 | rm docker-compose.yml .env uninstall.sh start.sh stop.sh config/telemetry.yml 636 | if [ -z "\$(ls -A config)" ]; then 637 | rm -d config 638 | fi 639 | echo 640 | echo "Do you want to remove the following Docker images?" 641 | echo "- docker.elastic.co/elasticsearch/elasticsearch:${es_version}" 642 | EOM 643 | 644 | if [ -z "${esonly:-}" ]; then 645 | cat >> uninstall.sh <<- EOM 646 | echo "- docker.elastic.co/kibana/kibana:${es_version}" 647 | EOM 648 | fi 649 | 650 | cat >> uninstall.sh <<- EOM 651 | if ask_confirmation; then 652 | if docker rmi "docker.elastic.co/elasticsearch/elasticsearch:${es_version}" >/dev/null 2>&1; then 653 | echo "Image docker.elastic.co/elasticsearch/elasticsearch:${es_version} removed successfully" 654 | else 655 | echo "Failed to remove image docker.elastic.co/elasticsearch/elasticsearch:${es_version}. It might be in use." 656 | fi 657 | EOM 658 | 659 | if [ -z "${esonly:-}" ]; then 660 | cat >> uninstall.sh <<- EOM 661 | if docker rmi docker.elastic.co/kibana/kibana:${es_version} >/dev/null 2>&1; then 662 | echo "Image docker.elastic.co/kibana/kibana:${es_version} removed successfully" 663 | else 664 | echo "Failed to remove image docker.elastic.co/kibana/kibana:${es_version}. It might be in use." 665 | fi 666 | EOM 667 | fi 668 | 669 | cat >> uninstall.sh <<- EOM 670 | fi 671 | echo "Start-local successfully removed" 672 | fi 673 | EOM 674 | chmod +x uninstall.sh 675 | } 676 | 677 | create_docker_compose_file() { 678 | # Create the docker-compose-yml file 679 | cat > docker-compose.yml <<-'EOM' 680 | services: 681 | elasticsearch: 682 | image: docker.elastic.co/elasticsearch/elasticsearch:${ES_LOCAL_VERSION} 683 | container_name: ${ES_LOCAL_CONTAINER_NAME} 684 | volumes: 685 | - dev-elasticsearch:/usr/share/elasticsearch/data 686 | ports: 687 | - 127.0.0.1:${ES_LOCAL_PORT}:9200 688 | environment: 689 | - discovery.type=single-node 690 | - ELASTIC_PASSWORD=${ES_LOCAL_PASSWORD} 691 | - xpack.security.enabled=true 692 | - xpack.security.http.ssl.enabled=false 693 | - xpack.license.self_generated.type=trial 694 | - xpack.ml.use_auto_machine_memory_percent=true 695 | - ES_JAVA_OPTS=-Xms${ES_LOCAL_HEAP_INIT} -Xmx${ES_LOCAL_HEAP_MAX} 696 | - cluster.routing.allocation.disk.watermark.low=${ES_LOCAL_DISK_SPACE_REQUIRED} 697 | - cluster.routing.allocation.disk.watermark.high=${ES_LOCAL_DISK_SPACE_REQUIRED} 698 | - cluster.routing.allocation.disk.watermark.flood_stage=${ES_LOCAL_DISK_SPACE_REQUIRED} 699 | EOM 700 | 701 | # Fix for JDK AArch64 issue, see https://bugs.openjdk.org/browse/JDK-8345296 702 | if is_arm64; then 703 | cat >> docker-compose.yml <<-'EOM' 704 | - "_JAVA_OPTIONS=-XX:UseSVE=0" 705 | EOM 706 | fi 707 | 708 | # Fix for OCI issue on LXC, see https://github.com/elastic/start-local/issues/27 709 | if ! detect_lxc; then 710 | cat >> docker-compose.yml <<-'EOM' 711 | ulimits: 712 | memlock: 713 | soft: -1 714 | hard: -1 715 | EOM 716 | fi 717 | 718 | cat >> docker-compose.yml <<-'EOM' 719 | healthcheck: 720 | test: 721 | [ 722 | "CMD-SHELL", 723 | "curl --output /dev/null --silent --head --fail -u elastic:${ES_LOCAL_PASSWORD} http://elasticsearch:9200", 724 | ] 725 | interval: 10s 726 | timeout: 10s 727 | retries: 30 728 | 729 | EOM 730 | 731 | if [ -z "${esonly:-}" ]; then 732 | cat >> docker-compose.yml <<-'EOM' 733 | kibana_settings: 734 | depends_on: 735 | elasticsearch: 736 | condition: service_healthy 737 | image: docker.elastic.co/elasticsearch/elasticsearch:${ES_LOCAL_VERSION} 738 | container_name: kibana_settings 739 | restart: 'no' 740 | command: > 741 | bash -c ' 742 | echo "Setup the kibana_system password"; 743 | start_time=$$(date +%s); 744 | timeout=60; 745 | until curl -s -u "elastic:${ES_LOCAL_PASSWORD}" -X POST http://elasticsearch:9200/_security/user/kibana_system/_password -d "{\"password\":\"${KIBANA_LOCAL_PASSWORD}\"}" -H "Content-Type: application/json" | grep -q "^{}"; do 746 | if [ $$(($$(date +%s) - $$start_time)) -ge $$timeout ]; then 747 | echo "Error: Elasticsearch timeout"; 748 | exit 1; 749 | fi; 750 | sleep 2; 751 | done; 752 | ' 753 | 754 | kibana: 755 | depends_on: 756 | kibana_settings: 757 | condition: service_completed_successfully 758 | image: docker.elastic.co/kibana/kibana:${ES_LOCAL_VERSION} 759 | container_name: ${KIBANA_LOCAL_CONTAINER_NAME} 760 | volumes: 761 | - dev-kibana:/usr/share/kibana/data 762 | - ./config/telemetry.yml:/usr/share/kibana/config/telemetry.yml 763 | ports: 764 | - 127.0.0.1:${KIBANA_LOCAL_PORT}:5601 765 | environment: 766 | - SERVER_NAME=kibana 767 | - ELASTICSEARCH_HOSTS=http://elasticsearch:9200 768 | - ELASTICSEARCH_USERNAME=kibana_system 769 | - ELASTICSEARCH_PASSWORD=${KIBANA_LOCAL_PASSWORD} 770 | - XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY=${KIBANA_ENCRYPTION_KEY} 771 | - ELASTICSEARCH_PUBLICBASEURL=http://localhost:${ES_LOCAL_PORT} 772 | healthcheck: 773 | test: 774 | [ 775 | "CMD-SHELL", 776 | "curl -s -I http://kibana:5601 | grep -q 'HTTP/1.1 302 Found'", 777 | ] 778 | interval: 10s 779 | timeout: 10s 780 | retries: 30 781 | 782 | EOM 783 | fi 784 | 785 | cat >> docker-compose.yml <<-'EOM' 786 | volumes: 787 | dev-elasticsearch: 788 | EOM 789 | 790 | if [ -z "${esonly:-}" ]; then 791 | cat >> docker-compose.yml <<-'EOM' 792 | dev-kibana: 793 | EOM 794 | fi 795 | 796 | create_kibana_config 797 | } 798 | 799 | create_kibana_config() { 800 | if [ ! -d "config" ]; then 801 | mkdir config 802 | fi 803 | # Create telemetry 804 | cat > config/telemetry.yml <<- EOM 805 | start-local: 806 | version: ${version} 807 | EOM 808 | } 809 | 810 | print_steps() { 811 | if [ -z "${esonly:-}" ]; then 812 | echo "⌛️ Setting up Elasticsearch and Kibana v${es_version}..." 813 | else 814 | echo "⌛️ Setting up Elasticsearch v${es_version}..." 815 | fi 816 | echo 817 | echo "- Generated random passwords" 818 | echo "- Created the ${folder} folder containing the files:" 819 | echo " - .env, with settings" 820 | echo " - docker-compose.yml, for Docker services" 821 | echo " - start/stop/uninstall commands" 822 | } 823 | 824 | running_docker_compose() { 825 | # Execute docker compose 826 | echo "- Running ${docker}" 827 | echo 828 | set +e 829 | if ! $docker; then 830 | error_msg="Error: ${docker} command failed!" 831 | echo "$error_msg" 832 | if [ -z "${esonly:-}" ]; then 833 | generate_error_log "${error_msg}" "${elasticsearch_container_name} ${kibana_container_name} kibana_settings" 834 | else 835 | generate_error_log "${error_msg}" "${elasticsearch_container_name}" 836 | fi 837 | cleanup 838 | exit 1 839 | fi 840 | set -e 841 | } 842 | 843 | api_key() { 844 | # Create an API key for Elasticsearch 845 | api_key=$(create_api_key "$es_password" "$api_key_name") 846 | if [ -n "$api_key" ]; then 847 | echo "ES_LOCAL_API_KEY=${api_key}" >> .env 848 | fi 849 | } 850 | 851 | kibana_wait() { 852 | if [ "$need_wait_for_kibana" = true ]; then 853 | wait_for_kibana 120 854 | fi 855 | } 856 | 857 | success() { 858 | echo 859 | if [ -z "${esonly:-}" ]; then 860 | echo "🎉 Congrats, Elasticsearch and Kibana are installed and running in Docker!" 861 | echo 862 | echo "🌐 Open your browser at http://localhost:5601" 863 | echo 864 | echo " Username: elastic" 865 | echo " Password: ${es_password}" 866 | echo 867 | else 868 | echo "🎉 Congrats, Elasticsearch is installed and running in Docker!" 869 | fi 870 | 871 | echo "🔌 Elasticsearch API endpoint: http://localhost:9200" 872 | if [ -n "$api_key" ]; then 873 | echo "🔑 API key: $api_key" 874 | echo 875 | else 876 | echo "🔑 Use basic auth or create an API key" 877 | echo "https://www.elastic.co/guide/en/kibana/current/api-keys.html" 878 | echo 879 | fi 880 | echo 881 | echo "Learn more at https://github.com/elastic/start-local" 882 | 883 | echo 884 | } 885 | 886 | main() { 887 | parse_args "$@" 888 | startup 889 | check_requirements 890 | check_installation_folder 891 | check_docker_services 892 | create_installation_folder 893 | generate_passwords 894 | choose_es_version 895 | create_start_file 896 | create_stop_file 897 | create_uninstall_file 898 | create_env_file 899 | create_docker_compose_file 900 | print_steps 901 | running_docker_compose 902 | api_key 903 | kibana_wait 904 | success 905 | } 906 | 907 | ctrl_c() { 908 | cleanup 909 | exit 1 910 | } 911 | 912 | # Trap ctrl-c 913 | trap ctrl_c INT 914 | 915 | # Execute the script 916 | main "$@" -------------------------------------------------------------------------------- /tests/basic_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one 3 | # or more contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright 5 | # ownership. Elasticsearch B.V. licenses this file to you under 6 | # the Apache License, Version 2.0 (the "License"); you may 7 | # not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | 19 | CURRENT_DIR=$(pwd) 20 | DEFAULT_DIR="${CURRENT_DIR}/elastic-start-local" 21 | ENV_PATH="${DEFAULT_DIR}/.env" 22 | DOCKER_COMPOSE_FILE="${DEFAULT_DIR}/docker-compose.yml" 23 | START_FILE="${DEFAULT_DIR}/start.sh" 24 | STOP_FILE="${DEFAULT_DIR}/stop.sh" 25 | UNINSTALL_FILE="${DEFAULT_DIR}/uninstall.sh" 26 | 27 | # include external scripts 28 | source "${CURRENT_DIR}/tests/utility.sh" 29 | 30 | function set_up_before_script() { 31 | sh "${CURRENT_DIR}/start-local.sh" 32 | # shellcheck disable=SC1090 33 | source "${ENV_PATH}" 34 | } 35 | 36 | function tear_down_after_script() { 37 | printf "yes\nno\n" | "${DEFAULT_DIR}/uninstall.sh" 38 | rm -rf "${DEFAULT_DIR}" 39 | } 40 | 41 | function test_docker_compose_file_exists() { 42 | assert_file_exists "${DOCKER_COMPOSE_FILE}" 43 | } 44 | 45 | function test_env_file_exists() { 46 | assert_file_exists "${ENV_PATH}" 47 | } 48 | 49 | function test_start_file_exists() { 50 | assert_file_exists "${START_FILE}" 51 | } 52 | 53 | function test_stop_file_exists() { 54 | assert_file_exists "${STOP_FILE}" 55 | } 56 | 57 | function test_uninstall_file_exists() { 58 | assert_file_exists "${UNINSTALL_FILE}" 59 | } 60 | 61 | function test_elasticsearch_is_running() { 62 | result=$(get_http_response_code "http://localhost:9200" "elastic" "${ES_LOCAL_PASSWORD}") 63 | assert_equals "200" "$result" 64 | } 65 | 66 | function test_kibana_is_running() { 67 | result=$(get_http_response_code "http://localhost:5601") 68 | assert_equals "200" "$result" 69 | } 70 | 71 | function test_login_to_kibana() { 72 | result=$(login_kibana "http://localhost:5601" "elastic" "${ES_LOCAL_PASSWORD}") 73 | assert_equals "200" "$result" 74 | } 75 | 76 | function test_connector_API_for_Kibana() { 77 | result=$(curl -X POST \ 78 | -u elastic:"${ES_LOCAL_PASSWORD}" \ 79 | -H 'Content-Type: application/json' \ 80 | -H 'kbn-xsrf: true' \ 81 | "localhost:5601/api/actions/connector" \ 82 | -d '{"name": "my-connector", "connector_type_id": ".index", "config": {"index": "test-index"}}' \ 83 | -o /dev/null \ 84 | -w '%{http_code}\n' -s) 85 | 86 | assert_equals "200" "$result" 87 | } 88 | 89 | function test_API_key_exists() { 90 | result=$(curl -X GET \ 91 | -u elastic:"${ES_LOCAL_PASSWORD}" \ 92 | -H 'Content-Type: application/json' \ 93 | "localhost:9200/_security/api_key" \ 94 | -d "{\"name\":\"${DEFAULT_DIR}\"}" \ 95 | -o /dev/null \ 96 | -w '%{http_code}\n' -s) 97 | } 98 | 99 | function test_telemetry_start-local() { 100 | result=$(curl -X POST \ 101 | -u "elastic:${ES_LOCAL_PASSWORD}" \ 102 | -H "kbn-xsrf: reporting" \ 103 | -H "Content-Type: application/json" \ 104 | -H "x-elastic-internal-origin: Kibana" \ 105 | "http://localhost:5601/internal/telemetry/clusters/_stats?apiVersion=2" \ 106 | -d "{\"unencrypted\":true,\"refreshCache\":true}" \ 107 | -s | jq '.[0].stats.stack_stats.kibana.plugins.static_telemetry."start-local".version') 108 | 109 | assert_equals "\"${START_LOCAL_VERSION}\"" "$result" 110 | } -------------------------------------------------------------------------------- /tests/expire_license_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one 3 | # or more contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright 5 | # ownership. Elasticsearch B.V. licenses this file to you under 6 | # the Apache License, Version 2.0 (the "License"); you may 7 | # not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | 19 | CURRENT_DIR=$(pwd) 20 | DEFAULT_DIR="${CURRENT_DIR}/elastic-start-local" 21 | ENV_PATH="${DEFAULT_DIR}/.env" 22 | 23 | # include external scripts 24 | source "${CURRENT_DIR}/tests/utility.sh" 25 | 26 | function set_up_before_script() { 27 | sh "${CURRENT_DIR}/start-local.sh" 28 | # shellcheck disable=SC1090 29 | source "${ENV_PATH}" 30 | } 31 | 32 | function tear_down_after_script() { 33 | printf "yes\nno\n" | "${DEFAULT_DIR}/uninstall.sh" 34 | rm -rf "${DEFAULT_DIR}" 35 | } 36 | 37 | function test_start_with_expired_license() { 38 | # Check license is trial 39 | license=$(get_elasticsearch_license) 40 | assert_equals "$license" "trial" 41 | 42 | # Change the expire date in start.sh 43 | sed -i -E 's/-gt [0-9]+/-gt 1/' "${DEFAULT_DIR}/start.sh" 44 | "${DEFAULT_DIR}/start.sh" 45 | 46 | # Check license is basic 47 | license=$(get_elasticsearch_license) 48 | assert_equals "$license" "basic" 49 | } 50 | 51 | function get_elasticsearch_license() { 52 | local response 53 | response=$(curl -X GET "$ES_LOCAL_URL/_license" -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}") 54 | echo "$response" | jq -r '.license.type' 55 | } -------------------------------------------------------------------------------- /tests/install_esonly_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one 3 | # or more contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright 5 | # ownership. Elasticsearch B.V. licenses this file to you under 6 | # the Apache License, Version 2.0 (the "License"); you may 7 | # not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | 19 | CURRENT_DIR=$(pwd) 20 | DEFAULT_DIR="${CURRENT_DIR}/elastic-start-local" 21 | ENV_PATH="${DEFAULT_DIR}/.env" 22 | UNINSTALL_FILE="${DEFAULT_DIR}/uninstall.sh" 23 | 24 | # include external scripts 25 | source "${CURRENT_DIR}/tests/utility.sh" 26 | 27 | function set_up_before_script() { 28 | sh "${CURRENT_DIR}/start-local.sh" "-esonly" 29 | # shellcheck disable=SC1090 30 | source "${ENV_PATH}" 31 | } 32 | 33 | function tear_down_after_script() { 34 | printf "yes\nno\n" | "${UNINSTALL_FILE}" 35 | rm -rf "${DEFAULT_DIR}" 36 | } 37 | 38 | function test_kibana_is_not_in_env() { 39 | assert_file_not_contains "${ENV_PATH}" "KIBANA_" 40 | } 41 | 42 | function test_kibana_docker_is_not_running() { 43 | containers="$(docker ps --format '{{.Names}}')" 44 | assert_not_contains "kibana" "$containers" 45 | } 46 | 47 | function test_kibana_is_not_running() { 48 | result=$(get_http_response_code "http://localhost:5601") 49 | assert_equals "000" "$result" 50 | } 51 | -------------------------------------------------------------------------------- /tests/install_from_curl_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one 3 | # or more contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright 5 | # ownership. Elasticsearch B.V. licenses this file to you under 6 | # the Apache License, Version 2.0 (the "License"); you may 7 | # not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | 19 | CURRENT_DIR=$(pwd) 20 | DEFAULT_DIR="${CURRENT_DIR}/elastic-start-local" 21 | ENV_PATH="${DEFAULT_DIR}/.env" 22 | DOCKER_COMPOSE_FILE="${DEFAULT_DIR}/docker-compose.yml" 23 | START_FILE="${DEFAULT_DIR}/start.sh" 24 | STOP_FILE="${DEFAULT_DIR}/stop.sh" 25 | UNINSTALL_FILE="${DEFAULT_DIR}/uninstall.sh" 26 | 27 | # include external scripts 28 | source "${CURRENT_DIR}/tests/utility.sh" 29 | 30 | function set_up_before_script() { 31 | php -S 0.0.0.0:8080 & 32 | PHP_SERVER_PID=$! 33 | sleep 2 34 | curl -fsSL http://localhost:8080/start-local.sh | sh 35 | # shellcheck disable=SC1090 36 | source "${ENV_PATH}" 37 | } 38 | 39 | function tear_down_after_script() { 40 | printf "yes\nno\n" | "${DEFAULT_DIR}/uninstall.sh" 41 | rm -rf "${DEFAULT_DIR}" 42 | kill -9 "$PHP_SERVER_PID" 43 | wait "$PHP_SERVER_PID" 2>/dev/null 44 | } 45 | 46 | function test_docker_compose_file_exists() { 47 | assert_file_exists "${DOCKER_COMPOSE_FILE}" 48 | } 49 | 50 | function test_env_file_exists() { 51 | assert_file_exists "${ENV_PATH}" 52 | } 53 | 54 | function test_start_file_exists() { 55 | assert_file_exists "${START_FILE}" 56 | } 57 | 58 | function test_stop_file_exists() { 59 | assert_file_exists "${STOP_FILE}" 60 | } 61 | 62 | function test_uninstall_file_exists() { 63 | assert_file_exists "${UNINSTALL_FILE}" 64 | } 65 | 66 | function test_elasticsearch_is_running() { 67 | result=$(get_http_response_code "http://localhost:9200" "elastic" "${ES_LOCAL_PASSWORD}") 68 | assert_equals "200" "$result" 69 | } 70 | 71 | function test_kibana_is_running() { 72 | result=$(get_http_response_code "http://localhost:5601") 73 | assert_equals "200" "$result" 74 | } 75 | 76 | function test_login_to_kibana() { 77 | result=$(login_kibana "http://localhost:5601" "elastic" "${ES_LOCAL_PASSWORD}") 78 | assert_equals "200" "$result" 79 | } -------------------------------------------------------------------------------- /tests/install_with_version_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one 3 | # or more contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright 5 | # ownership. Elasticsearch B.V. licenses this file to you under 6 | # the Apache License, Version 2.0 (the "License"); you may 7 | # not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | 19 | CURRENT_DIR=$(pwd) 20 | DEFAULT_DIR="${CURRENT_DIR}/elastic-start-local" 21 | ENV_PATH="${DEFAULT_DIR}/.env" 22 | UNINSTALL_FILE="${DEFAULT_DIR}/uninstall.sh" 23 | ES_VERSION="8.17.0" 24 | 25 | # include external scripts 26 | source "${CURRENT_DIR}/tests/utility.sh" 27 | 28 | function set_up_before_script() { 29 | sh "${CURRENT_DIR}/start-local.sh" "-v" "${ES_VERSION}" 30 | # shellcheck disable=SC1090 31 | source "${ENV_PATH}" 32 | } 33 | 34 | function tear_down_after_script() { 35 | printf "yes\nno\n" | "${UNINSTALL_FILE}" 36 | rm -rf "${DEFAULT_DIR}" 37 | } 38 | 39 | function test_es_version_in_env_is_correct() { 40 | assert_file_contains "${ENV_PATH}" "ES_LOCAL_VERSION=${ES_VERSION}" 41 | } 42 | 43 | function test_es_version_is_correct() { 44 | response=$(curl -s "$ES_LOCAL_URL" -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}") 45 | version=$(echo "$response" | jq -r '.version.number') 46 | assert_equals "${ES_VERSION}" "$version" 47 | } 48 | 49 | -------------------------------------------------------------------------------- /tests/start_stop_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one 3 | # or more contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright 5 | # ownership. Elasticsearch B.V. licenses this file to you under 6 | # the Apache License, Version 2.0 (the "License"); you may 7 | # not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | 19 | CURRENT_DIR=$(pwd) 20 | DEFAULT_DIR="${CURRENT_DIR}/elastic-start-local" 21 | ENV_PATH="${DEFAULT_DIR}/.env" 22 | UNINSTALL_FILE="${DEFAULT_DIR}/uninstall.sh" 23 | 24 | # include external scripts 25 | source "${CURRENT_DIR}/tests/utility.sh" 26 | 27 | function set_up_before_script() { 28 | sh "${CURRENT_DIR}/start-local.sh" 29 | # shellcheck disable=SC1090 30 | source "${ENV_PATH}" 31 | } 32 | 33 | function tear_down_after_script() { 34 | printf "yes\nno\n" | "${UNINSTALL_FILE}" 35 | rm -rf "${DEFAULT_DIR}" 36 | } 37 | 38 | function test_stop() { 39 | "${TEST_DIR}/${DEFAULT_DIR}/stop.sh" 40 | 41 | assert_exit_code "1" "$(check_docker_service_running es-local-dev)" 42 | assert_exit_code "1" "$(check_docker_service_running kibana-local-dev)" 43 | assert_exit_code "1" "$(check_docker_service_running kibana_settings)" 44 | } 45 | 46 | function test_start() { 47 | "${TEST_DIR}/${DEFAULT_DIR}/start.sh" 48 | 49 | assert_exit_code "0" "$(check_docker_service_running es-local-dev)" 50 | assert_exit_code "0" "$(check_docker_service_running kibana-local-dev)" 51 | } -------------------------------------------------------------------------------- /tests/uninstall_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one 3 | # or more contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright 5 | # ownership. Elasticsearch B.V. licenses this file to you under 6 | # the Apache License, Version 2.0 (the "License"); you may 7 | # not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | 19 | CURRENT_DIR=$(pwd) 20 | DEFAULT_DIR="${CURRENT_DIR}/elastic-start-local" 21 | ENV_PATH="${DEFAULT_DIR}/.env" 22 | UNINSTALL_FILE="${DEFAULT_DIR}/uninstall.sh" 23 | 24 | # include external scripts 25 | source "${CURRENT_DIR}/tests/utility.sh" 26 | 27 | function set_up() { 28 | sh "${CURRENT_DIR}/start-local.sh" 29 | # shellcheck disable=SC1090 30 | source "${ENV_PATH}" 31 | } 32 | 33 | function tear_down() { 34 | rm -rf "${DEFAULT_DIR}" 35 | } 36 | 37 | function test_uninstall_outside_installation_folder() { 38 | printf "yes\nno\n" | "${UNINSTALL_FILE}" 39 | assert_exit_code "1" "$(check_docker_service_running es-local-dev)" 40 | assert_exit_code "1" "$(check_docker_service_running kibana-local-dev)" 41 | assert_exit_code "1" "$(check_docker_service_running kibana_settings)" 42 | assert_is_directory_empty "${TEST_DIR}/${DEFAULT_DIR}" 43 | assert_exit_code "0" "$(check_docker_image_exists docker.elastic.co/elasticsearch/elasticsearch:"${ES_LOCAL_VERSION}")" 44 | assert_exit_code "0" "$(check_docker_image_exists docker.elastic.co/kibana/kibana:"${ES_LOCAL_VERSION}")" 45 | } 46 | 47 | function test_uninstall_in_installation_folder() { 48 | cd "${DEFAULT_DIR}" || exit 49 | printf "yes\nno\n" | ./uninstall.sh 50 | assert_exit_code "1" "$(check_docker_service_running es-local-dev)" 51 | assert_exit_code "1" "$(check_docker_service_running kibana-local-dev)" 52 | assert_exit_code "1" "$(check_docker_service_running kibana_settings)" 53 | assert_is_directory_empty "${DEFAULT_DIR}" 54 | assert_exit_code "0" "$(check_docker_image_exists docker.elastic.co/elasticsearch/elasticsearch:"${ES_LOCAL_VERSION}")" 55 | assert_exit_code "0" "$(check_docker_image_exists docker.elastic.co/kibana/kibana:"${ES_LOCAL_VERSION}")" 56 | } 57 | 58 | function test_uninstall_remove_images() { 59 | cd "${DEFAULT_DIR}" || exit 60 | printf "yes\nyes\n" | ./uninstall.sh 61 | assert_exit_code "1" "$(check_docker_service_running es-local-dev)" 62 | assert_exit_code "1" "$(check_docker_service_running kibana-local-dev)" 63 | assert_exit_code "1" "$(check_docker_service_running kibana_settings)" 64 | assert_is_directory_empty "${DEFAULT_DIR}" 65 | assert_exit_code "1" "$(check_docker_image_exists docker.elastic.co/elasticsearch/elasticsearch:"${ES_LOCAL_VERSION}")" 66 | assert_exit_code "1" "$(check_docker_image_exists docker.elastic.co/kibana/kibana:"${ES_LOCAL_VERSION}")" 67 | } -------------------------------------------------------------------------------- /tests/utility.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one 3 | # or more contributor license agreements. See the NOTICE file distributed with 4 | # this work for additional information regarding copyright 5 | # ownership. Elasticsearch B.V. licenses this file to you under 6 | # the Apache License, Version 2.0 (the "License"); you may 7 | # not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | 19 | # Returns the HTTP status code from a call 20 | # usage: get_http_response_code url username password 21 | function get_http_response_code() { 22 | url=$1 23 | if [ -z "$url" ]; then 24 | echo "Error: you need to specify the URL for get the HTTP response" 25 | exit 1 26 | fi 27 | username=$2 28 | password=$3 29 | 30 | if [ -z "$username" ] || [ -z "$password" ]; then 31 | result=$(curl -LI "$url" -o /dev/null -w '%{http_code}\n' -s) 32 | else 33 | result=$(curl -LI -u "$username":"$password" "$url" -o /dev/null -w '%{http_code}\n' -s) 34 | fi 35 | 36 | echo "$result" 37 | } 38 | 39 | # Login to Kibana using username and password 40 | # usage: login_kibana url username password 41 | function login_kibana() { 42 | url=$1 43 | if [ -z "$url" ]; then 44 | echo "Error: you need to specify the URL for login to Kibana" 45 | exit 1 46 | fi 47 | username=$2 48 | password=$3 49 | if [ -z "$username" ] || [ -z "$password" ]; then 50 | echo "Error: you need to specify username and password to login to Kibana" 51 | exit 1 52 | fi 53 | 54 | result=$(curl -X POST \ 55 | -H "Content-Type: application/json" \ 56 | -H "kbn-xsrf: reporting" \ 57 | -H "x-elastic-internal-origin: Kibana" \ 58 | -d '{"providerType":"basic","providerName":"basic","currentURL":"'"$url"'/login?next=%2F","params":{"username":"'"$username"'","password":"'"$password"'"}}' \ 59 | "${url}/internal/security/login" \ 60 | -o /dev/null \ 61 | -w '%{http_code}\n' -s) 62 | 63 | echo "$result" 64 | } 65 | 66 | # Tee the output in a file 67 | function cap () { tee "${1}/capture.out"; } 68 | 69 | # Return the previous output 70 | function ret () { cat "${1}/capture.out"; } 71 | 72 | # Check if a docker service is running 73 | check_docker_service_running() { 74 | local container_name=$1 75 | local containers 76 | containers=$(docker ps --format '{{.Names}}') 77 | if echo "$containers" | grep -q "^${container_name}$"; then 78 | return 0 # true 79 | else 80 | return 1 # false 81 | fi 82 | } 83 | 84 | # Check if a docker image exists 85 | check_docker_image_exists() { 86 | local image_name=$1 87 | if docker image inspect "$image_name" > /dev/null 2>&1; then 88 | return 0 # true 89 | else 90 | return 1 # false 91 | fi 92 | } --------------------------------------------------------------------------------