diff --git a/NOTICE.txt b/NOTICE.txt index d84590631f..7a273aa46c 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -403,6 +403,37 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- +Module : github.com/gkampitakis/go-snaps +Version : v0.5.4 +Time : 2024-04-23T16:04:50Z +Licence : MIT + +Contents of probable licence file $GOMODCACHE/github.com/gkampitakis/go-snaps@v0.5.4/LICENSE: + +MIT License + +Copyright (c) 2021 Georgios Kampitakis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + -------------------------------------------------------------------------------- Module : github.com/go-logr/logr Version : v1.4.2 @@ -7728,6 +7759,67 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- +Module : github.com/gkampitakis/ciinfo +Version : v0.3.0 +Time : 2023-10-30T18:42:40Z +Licence : MIT + +Contents of probable licence file $GOMODCACHE/github.com/gkampitakis/ciinfo@v0.3.0/LICENSE: + +MIT License + +Copyright (c) 2021 Georgios Kampitakis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +-------------------------------------------------------------------------------- +Module : github.com/gkampitakis/go-diff +Version : v1.3.2 +Time : 2023-02-18T11:30:13Z +Licence : MIT + +Contents of probable licence file $GOMODCACHE/github.com/gkampitakis/go-diff@v1.3.2/LICENSE: + +Copyright (c) 2012-2016 The go-diff Authors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + + -------------------------------------------------------------------------------- Module : github.com/go-jose/go-jose/v4 Version : v4.0.1 @@ -13568,6 +13660,217 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-------------------------------------------------------------------------------- +Module : github.com/maruel/natural +Version : v1.1.1 +Time : 2023-11-03T01:37:02Z +Licence : Apache-2.0 + +Contents of probable licence file $GOMODCACHE/github.com/maruel/natural@v1.1.1/LICENSE: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 Marc-Antoine Ruel + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + -------------------------------------------------------------------------------- Module : github.com/mattn/go-colorable Version : v0.1.13 @@ -15467,11 +15770,11 @@ Contents of probable licence file $GOMODCACHE/github.com/prometheus/procfs@v0.14 -------------------------------------------------------------------------------- Module : github.com/rogpeppe/go-internal -Version : v1.11.0 -Time : 2023-05-24T17:50:51Z +Version : v1.12.0 +Time : 2023-12-13T11:29:26Z Licence : BSD-3-Clause -Contents of probable licence file $GOMODCACHE/github.com/rogpeppe/go-internal@v1.11.0/LICENSE: +Contents of probable licence file $GOMODCACHE/github.com/rogpeppe/go-internal@v1.12.0/LICENSE: Copyright (c) 2018 The Go Authors. All rights reserved. @@ -15961,6 +16264,127 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-------------------------------------------------------------------------------- +Module : github.com/tidwall/gjson +Version : v1.17.0 +Time : 2023-09-22T17:13:56Z +Licence : MIT + +Contents of probable licence file $GOMODCACHE/github.com/tidwall/gjson@v1.17.0/LICENSE: + +The MIT License (MIT) + +Copyright (c) 2016 Josh Baker + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +-------------------------------------------------------------------------------- +Module : github.com/tidwall/match +Version : v1.1.1 +Time : 2021-10-08T14:36:13Z +Licence : MIT + +Contents of probable licence file $GOMODCACHE/github.com/tidwall/match@v1.1.1/LICENSE: + +The MIT License (MIT) + +Copyright (c) 2016 Josh Baker + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +-------------------------------------------------------------------------------- +Module : github.com/tidwall/pretty +Version : v1.2.1 +Time : 2022-10-01T20:21:24Z +Licence : MIT + +Contents of probable licence file $GOMODCACHE/github.com/tidwall/pretty@v1.2.1/LICENSE: + +The MIT License (MIT) + +Copyright (c) 2017 Josh Baker + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +-------------------------------------------------------------------------------- +Module : github.com/tidwall/sjson +Version : v1.2.5 +Time : 2022-08-05T01:15:59Z +Licence : MIT + +Contents of probable licence file $GOMODCACHE/github.com/tidwall/sjson@v1.2.5/LICENSE: + +The MIT License (MIT) + +Copyright (c) 2016 Josh Baker + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + -------------------------------------------------------------------------------- Module : github.com/vbatts/tar-split Version : v0.11.3 diff --git a/docs/reference/dependencies.asciidoc b/docs/reference/dependencies.asciidoc index 6021bfc626..650592d286 100644 --- a/docs/reference/dependencies.asciidoc +++ b/docs/reference/dependencies.asciidoc @@ -25,6 +25,7 @@ This page lists the third-party dependencies used to build {n}. | link:https://github.com/davecgh/go-spew[$$github.com/davecgh/go-spew$$] | v1.1.2-0.20180830191138-d8f796af33cc | ISC | link:https://github.com/elastic/go-ucfg[$$github.com/elastic/go-ucfg$$] | v0.8.8 | Apache-2.0 | link:https://github.com/ghodss/yaml[$$github.com/ghodss/yaml$$] | v1.0.0 | MIT +| link:https://github.com/gkampitakis/go-snaps[$$github.com/gkampitakis/go-snaps$$] | v0.5.4 | MIT | link:https://github.com/go-logr/logr[$$github.com/go-logr/logr$$] | v1.4.2 | Apache-2.0 | link:https://github.com/go-test/deep[$$github.com/go-test/deep$$] | v1.1.0 | MIT | link:https://github.com/gobuffalo/flect[$$github.com/gobuffalo/flect$$] | v1.0.2 | MIT @@ -94,6 +95,8 @@ This page lists the third-party dependencies used to build {n}. | link:https://github.com/fatih/color[$$github.com/fatih/color$$] | v1.16.0 | MIT | link:https://github.com/frankban/quicktest[$$github.com/frankban/quicktest$$] | v1.14.6 | MIT | link:https://github.com/fsnotify/fsnotify[$$github.com/fsnotify/fsnotify$$] | v1.7.0 | BSD-3-Clause +| link:https://github.com/gkampitakis/ciinfo[$$github.com/gkampitakis/ciinfo$$] | v0.3.0 | MIT +| link:https://github.com/gkampitakis/go-diff[$$github.com/gkampitakis/go-diff$$] | v1.3.2 | MIT | link:https://github.com/go-jose/go-jose[$$github.com/go-jose/go-jose/v4$$] | v4.0.1 | Apache-2.0 | link:https://github.com/go-logr/zapr[$$github.com/go-logr/zapr$$] | v1.3.0 | Apache-2.0 | link:https://github.com/go-openapi/jsonpointer[$$github.com/go-openapi/jsonpointer$$] | v0.21.0 | Apache-2.0 @@ -127,6 +130,7 @@ This page lists the third-party dependencies used to build {n}. | link:https://github.com/kr/pretty[$$github.com/kr/pretty$$] | v0.3.1 | MIT | link:https://github.com/kr/text[$$github.com/kr/text$$] | v0.2.0 | MIT | link:https://github.com/mailru/easyjson[$$github.com/mailru/easyjson$$] | v0.7.7 | MIT +| link:https://github.com/maruel/natural[$$github.com/maruel/natural$$] | v1.1.1 | Apache-2.0 | link:https://github.com/mattn/go-colorable[$$github.com/mattn/go-colorable$$] | v0.1.13 | MIT | link:https://github.com/mattn/go-isatty[$$github.com/mattn/go-isatty$$] | v0.0.20 | MIT | link:https://github.com/mitchellh/copystructure[$$github.com/mitchellh/copystructure$$] | v1.0.0 | MIT @@ -148,7 +152,7 @@ This page lists the third-party dependencies used to build {n}. | link:https://github.com/prashantv/gostub[$$github.com/prashantv/gostub$$] | v1.1.0 | MIT | link:https://github.com/prometheus/client_model[$$github.com/prometheus/client_model$$] | v0.6.1 | Apache-2.0 | link:https://github.com/prometheus/procfs[$$github.com/prometheus/procfs$$] | v0.14.0 | Apache-2.0 -| link:https://github.com/rogpeppe/go-internal[$$github.com/rogpeppe/go-internal$$] | v1.11.0 | BSD-3-Clause +| link:https://github.com/rogpeppe/go-internal[$$github.com/rogpeppe/go-internal$$] | v1.12.0 | BSD-3-Clause | link:https://github.com/ryanuber/go-glob[$$github.com/ryanuber/go-glob$$] | v1.0.0 | MIT | link:https://github.com/sagikazarmark/locafero[$$github.com/sagikazarmark/locafero$$] | v0.4.0 | MIT | link:https://github.com/sagikazarmark/slog-shim[$$github.com/sagikazarmark/slog-shim$$] | v0.1.0 | BSD-3-Clause @@ -158,6 +162,10 @@ This page lists the third-party dependencies used to build {n}. | link:https://github.com/spf13/afero[$$github.com/spf13/afero$$] | v1.11.0 | Apache-2.0 | link:https://github.com/spf13/cast[$$github.com/spf13/cast$$] | v1.6.0 | MIT | link:https://github.com/subosito/gotenv[$$github.com/subosito/gotenv$$] | v1.6.0 | MIT +| link:https://github.com/tidwall/gjson[$$github.com/tidwall/gjson$$] | v1.17.0 | MIT +| link:https://github.com/tidwall/match[$$github.com/tidwall/match$$] | v1.1.1 | MIT +| link:https://github.com/tidwall/pretty[$$github.com/tidwall/pretty$$] | v1.2.1 | MIT +| link:https://github.com/tidwall/sjson[$$github.com/tidwall/sjson$$] | v1.2.5 | MIT | link:https://github.com/vbatts/tar-split[$$github.com/vbatts/tar-split$$] | v0.11.3 | BSD-3-Clause | link:https://go.elastic.co/fastjson[$$go.elastic.co/fastjson$$] | v1.3.0 | MIT | link:https://go.uber.org/goleak[$$go.uber.org/goleak$$] | v1.3.0 | MIT diff --git a/go.mod b/go.mod index 06e824d3c1..7db13b4cec 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/elastic/go-ucfg v0.8.8 github.com/ghodss/yaml v1.0.0 + github.com/gkampitakis/go-snaps v0.5.4 github.com/go-logr/logr v1.4.2 github.com/go-test/deep v1.1.0 github.com/gobuffalo/flect v1.0.2 @@ -65,6 +66,8 @@ require ( github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/fatih/color v1.16.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/gkampitakis/ciinfo v0.3.0 // indirect + github.com/gkampitakis/go-diff v1.3.2 // indirect github.com/go-jose/go-jose/v4 v4.0.1 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect @@ -91,7 +94,10 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.2 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/maruel/natural v1.1.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect @@ -108,6 +114,7 @@ require ( github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/procfs v0.14.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect @@ -117,6 +124,10 @@ require ( github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/tidwall/gjson v1.17.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.1 // indirect + github.com/tidwall/sjson v1.2.5 // indirect github.com/vbatts/tar-split v0.11.3 // indirect go.elastic.co/fastjson v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect diff --git a/go.sum b/go.sum index 635ce77a0b..92db8ec928 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,7 @@ github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSk github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -64,6 +65,12 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gkampitakis/ciinfo v0.3.0 h1:gWZlOC2+RYYttL0hBqcoQhM7h1qNkVqvRCV1fOvpAv8= +github.com/gkampitakis/ciinfo v0.3.0/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= +github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= +github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= +github.com/gkampitakis/go-snaps v0.5.4 h1:GX+dkKmVsRenz7SoTbdIEL4KQARZctkMiZ8ZKprRwT8= +github.com/gkampitakis/go-snaps v0.5.4/go.mod h1:ZABkO14uCuVxBHAXAfKG+bqNz+aa1bGPAg8jkI0Nk8Y= github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= @@ -162,6 +169,8 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= +github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= @@ -206,6 +215,7 @@ github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0 github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -223,8 +233,9 @@ github.com/prometheus/common v0.54.0 h1:ZlZy0BgJhTwVZUn7dLOkwCZHUkrAqd3WYtcFCWnM github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ= github.com/prometheus/procfs v0.14.0 h1:Lw4VdGGoKEZilJsayHf0B+9YgLGREba2C6xr+Fdfq6s= github.com/prometheus/procfs v0.14.0/go.mod h1:XL+Iwz8k8ZabyZfMFHPiilCniixqQarAy5Mu67pHlNQ= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= @@ -270,6 +281,16 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= +github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck= github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY= diff --git a/pkg/apis/elasticsearch/v1/fields.go b/pkg/apis/elasticsearch/v1/fields.go index be031b3461..e5952994f5 100644 --- a/pkg/apis/elasticsearch/v1/fields.go +++ b/pkg/apis/elasticsearch/v1/fields.go @@ -4,6 +4,12 @@ package v1 +import "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/version" + +// as of 8.2.0 a simplified unauthenticated readiness port is available which takes cluster membership into account +// see https://www.elastic.co/guide/en/elasticsearch/reference/current/advanced-configuration.html#readiness-tcp-port +var MinReadinessPortVersion = version.MinFor(8, 2, 0) + const ( ClusterName = "cluster.name" @@ -14,6 +20,8 @@ const ( DiscoverySeedProviders = "discovery.seed_providers" // ES >= 7.X DiscoverySeedHosts = "discovery.seed_hosts" // ES >= 7.X + ReadinessPort = "readiness.port" // ES >= 8.2.0 + NetworkHost = "network.host" NetworkPublishHost = "network.publish_host" HTTPPublishHost = "http.publish_host" diff --git a/pkg/controller/autoscaling/elasticsearch/controller_test.go b/pkg/controller/autoscaling/elasticsearch/controller_test.go index 1fed418e5d..e4f4c88e9b 100644 --- a/pkg/controller/autoscaling/elasticsearch/controller_test.go +++ b/pkg/controller/autoscaling/elasticsearch/controller_test.go @@ -33,7 +33,7 @@ import ( "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/operator" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/watches" esclient "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/client" - "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/services" + eslabel "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/label" "github.com/elastic/cloud-on-k8s/v2/pkg/utils/k8s" "github.com/elastic/cloud-on-k8s/v2/pkg/utils/net" ) @@ -48,23 +48,20 @@ var ( return events } - fakeService = &corev1.Service{ + // fakePod is one running pod for online tests == ES considered reachable + fakePod = &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Namespace: "testns", - Name: services.InternalServiceName("testes"), + Name: "testes-es-master", + Labels: map[string]string{ + eslabel.HTTPSchemeLabelName: "http", + eslabel.StatefulSetNameLabelName: "sset", + eslabel.ClusterNameLabelName: "testes", + }, }, - } - fakeEndpoints = &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "testns", - Name: services.InternalServiceName("testes"), + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, }, - Subsets: []corev1.EndpointSubset{{ - Addresses: []corev1.EndpointAddress{{ - IP: "10.0.0.2", - }}, - Ports: []corev1.EndpointPort{}, - }}, } ) @@ -267,7 +264,7 @@ func TestReconcile(t *testing.T) { t.Fatalf("yaml.Unmarshal error = %v, wantErr %v", err, tt.wantErr) } if tt.args.isOnline { - k8sClient = k8s.NewFakeClient(es.DeepCopy(), esa.DeepCopy(), fakeService, fakeEndpoints) + k8sClient = k8s.NewFakeClient(es.DeepCopy(), esa.DeepCopy(), fakePod) } else { k8sClient = k8s.NewFakeClient(es.DeepCopy(), esa.DeepCopy()) } diff --git a/pkg/controller/autoscaling/elasticsearch/driver.go b/pkg/controller/autoscaling/elasticsearch/driver.go index b7d3b7eecc..68a96e1bdd 100644 --- a/pkg/controller/autoscaling/elasticsearch/driver.go +++ b/pkg/controller/autoscaling/elasticsearch/driver.go @@ -14,7 +14,6 @@ import ( "github.com/go-logr/logr" "go.elastic.co/apm/v2" - apierrors "k8s.io/apimachinery/pkg/api/errors" "github.com/elastic/cloud-on-k8s/v2/pkg/apis/common/v1alpha1" esv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/elasticsearch/v1" @@ -34,17 +33,9 @@ func (r *baseReconcileAutoscaling) reconcileInternal( ) (*esv1.Elasticsearch, error) { defer tracing.Span(&ctx)() log := logconf.FromContext(ctx) - if esReachable, err := r.isElasticsearchReachable(ctx, es); !esReachable || err != nil { + if !r.isElasticsearchReachable(ctx, es) { // Elasticsearch is not reachable, or we got an error while checking Elasticsearch availability, follow up with an offline reconciliation. - if err != nil { - log.V(1).Info( - "error while checking if Elasticsearch is available, attempting offline reconciliation", - "error.message", err.Error(), - ) - statusBuilder.SetOnline(false, fmt.Sprintf("Error while checking if Elasticsearch is available: %s", err.Error())) - } else { - statusBuilder.SetOnline(false, "Elasticsearch is not available") - } + statusBuilder.SetOnline(false, "Elasticsearch is not available") return r.doOfflineReconciliation(ctx, es, statusBuilder, autoscaledNodeSets, autoscalingResource) } statusBuilder.SetOnline(true, "Elasticsearch is available") @@ -98,20 +89,9 @@ func newStatusBuilder(log logr.Logger, autoscalingPolicies v1alpha1.AutoscalingP } // Check if the Service is available. -func (r *baseReconcileAutoscaling) isElasticsearchReachable(ctx context.Context, es esv1.Elasticsearch) (bool, error) { +func (r *baseReconcileAutoscaling) isElasticsearchReachable(ctx context.Context, es esv1.Elasticsearch) bool { defer tracing.Span(&ctx)() - internalService, err := services.GetInternalService(r.Client, es) - if apierrors.IsNotFound(err) { - return false, nil - } - if err != nil { - return false, tracing.CaptureError(ctx, err) - } - esReachable, err := services.IsServiceReady(r.Client, internalService) - if err != nil { - return false, tracing.CaptureError(ctx, err) - } - return esReachable, nil + return services.NewElasticsearchURLProvider(es, r.Client).HasEndpoints() } // attemptOnlineReconciliation attempts an online autoscaling reconciliation with a call to the Elasticsearch autoscaling API. diff --git a/pkg/controller/common/esclient/esclient.go b/pkg/controller/common/esclient/esclient.go index e9af94cda0..895b217519 100644 --- a/pkg/controller/common/esclient/esclient.go +++ b/pkg/controller/common/esclient/esclient.go @@ -32,7 +32,6 @@ func NewClient( es esv1.Elasticsearch, ) (esclient.Client, error) { defer tracing.Span(&ctx)() - url := services.ExternalServiceURL(es) v, err := version.Parse(es.Spec.Version) if err != nil { return nil, err @@ -68,10 +67,11 @@ func NewClient( if err != nil { return nil, err } + return esclient.NewElasticsearchClient( dialer, k8s.ExtractNamespacedName(&es), - url, + services.NewElasticsearchURLProvider(es, c), esclient.BasicAuth{ Name: user.ControllerUserName, Password: string(password), diff --git a/pkg/controller/elasticsearch/client/base.go b/pkg/controller/elasticsearch/client/base.go index eab30e867a..6a5e2da4be 100644 --- a/pkg/controller/elasticsearch/client/base.go +++ b/pkg/controller/elasticsearch/client/base.go @@ -22,13 +22,13 @@ import ( ) type baseClient struct { - User BasicAuth - HTTP *http.Client - Endpoint string - es types.NamespacedName - caCerts []*x509.Certificate - version version.Version - debug bool + User BasicAuth + HTTP *http.Client + URLProvider URLProvider + es types.NamespacedName + caCerts []*x509.Certificate + version version.Version + debug bool } // Close idle connections in the underlying http client. @@ -57,8 +57,8 @@ func (c *baseClient) equal(c2 *baseClient) bool { return false } } - // compare endpoint and user creds - return c.Endpoint == c2.Endpoint && + // compare endpoint svc url and user creds. Service URL acts purely as an identifier here. + return c.URLProvider.Equals(c2.URLProvider) && c.User == c2.User } @@ -128,7 +128,11 @@ func (c *baseClient) request( body = bytes.NewBuffer(outData) } - request, err := http.NewRequest(method, stringsutil.Concat(c.Endpoint, pathWithQuery), body) //nolint:noctx + url, err := c.URLProvider.URL() + if err != nil { + return err + } + request, err := http.NewRequest(method, stringsutil.Concat(url, pathWithQuery), body) //nolint:noctx if err != nil { return err } @@ -188,11 +192,7 @@ func versioned(b *baseClient, v version.Version) Client { } } -func (c *baseClient) URL() string { - return c.Endpoint -} - -func (c *baseClient) HasProperties(version version.Version, user BasicAuth, url string, caCerts []*x509.Certificate) bool { +func (c *baseClient) HasProperties(version version.Version, user BasicAuth, url URLProvider, caCerts []*x509.Certificate) bool { if len(c.caCerts) != len(caCerts) { return false } @@ -201,5 +201,5 @@ func (c *baseClient) HasProperties(version version.Version, user BasicAuth, url return false } } - return c.version.Equals(version) && c.User == user && c.Endpoint == url + return c.version.Equals(version) && c.User == user && c.URLProvider.Equals(url) } diff --git a/pkg/controller/elasticsearch/client/client.go b/pkg/controller/elasticsearch/client/client.go index 6f40c9a524..a26254fb1e 100644 --- a/pkg/controller/elasticsearch/client/client.go +++ b/pkg/controller/elasticsearch/client/client.go @@ -127,10 +127,8 @@ type Client interface { // Version returns the Elasticsearch version this client is constructed for which should equal the minimal version // in the cluster. Version() version.Version - // URL returns the Elasticsearch URL configured for this client - URL() string // HasProperties checks whether this client has the indicated properties. - HasProperties(version version.Version, user BasicAuth, url string, caCerts []*x509.Certificate) bool + HasProperties(version version.Version, user BasicAuth, url URLProvider, caCerts []*x509.Certificate) bool } // Timeout returns the Elasticsearch client timeout value for the given Elasticsearch resource. @@ -148,7 +146,7 @@ func formatAsSeconds(d time.Duration) string { func NewElasticsearchClient( dialer net.Dialer, es types.NamespacedName, - esURL string, + esURL URLProvider, esUser BasicAuth, v version.Version, caCerts []*x509.Certificate, @@ -158,12 +156,12 @@ func NewElasticsearchClient( client := commonhttp.Client(dialer, caCerts, timeout) client.Transport = apmelasticsearch.WrapRoundTripper(client.Transport) base := &baseClient{ - Endpoint: esURL, - User: esUser, - caCerts: caCerts, - HTTP: client, - es: es, - debug: debug, + URLProvider: esURL, + User: esUser, + caCerts: caCerts, + HTTP: client, + es: es, + debug: debug, } return versioned(base, v) } diff --git a/pkg/controller/elasticsearch/client/client_test.go b/pkg/controller/elasticsearch/client/client_test.go index eda42be9c1..cae414c8d7 100644 --- a/pkg/controller/elasticsearch/client/client_test.go +++ b/pkg/controller/elasticsearch/client/client_test.go @@ -237,7 +237,7 @@ func TestClient_request(t *testing.T) { assert.Equal(t, "cloud", req.Header.Get("x-elastic-product-origin")) }), }, - Endpoint: "http://example.com", + URLProvider: NewStaticURLProvider("http://example.com"), } requests := []func() (string, error){ func() (string, error) { @@ -361,7 +361,7 @@ func TestGetInfo(t *testing.T) { } func TestClient_Equal(t *testing.T) { - dummyEndpoint := "es-url" + dummyEndpoint := NewStaticURLProvider("es-url") dummyUser := BasicAuth{Name: "user", Password: "password"} dummyNamespaceName := types.NamespacedName{ Namespace: "ns", @@ -398,7 +398,7 @@ func TestClient_Equal(t *testing.T) { { name: "different endpoint", c1: NewElasticsearchClient(nil, dummyNamespaceName, dummyEndpoint, dummyUser, v6, dummyCACerts, timeout, false), - c2: NewElasticsearchClient(nil, dummyNamespaceName, "another-endpoint", dummyUser, v6, dummyCACerts, timeout, false), + c2: NewElasticsearchClient(nil, dummyNamespaceName, NewStaticURLProvider("another-endpoint"), dummyUser, v6, dummyCACerts, timeout, false), want: false, }, { @@ -835,12 +835,12 @@ func TestGetClusterHealthWaitForAllEvents(t *testing.T) { func Test_HasProperties(t *testing.T) { defaultVersion := version.MustParse("8.6.1") defaultUser := BasicAuth{Name: "foo", Password: "bar"} - defaultURL := "https://foo.bar" + defaultURLProvider := NewStaticURLProvider("https://foo.bar") defaultCaCerts := []*x509.Certificate{{Raw: []byte("foo")}} defaultEsClient := NewElasticsearchClient( nil, types.NamespacedName{Namespace: "ns", Name: "es"}, - defaultURL, + defaultURLProvider, defaultUser, defaultVersion, defaultCaCerts, @@ -852,7 +852,7 @@ func Test_HasProperties(t *testing.T) { esClient Client version version.Version user BasicAuth - url string + url URLProvider caCerts []*x509.Certificate want bool }{ @@ -861,7 +861,7 @@ func Test_HasProperties(t *testing.T) { esClient: defaultEsClient, version: version.MustParse("8.6.0"), user: defaultUser, - url: defaultURL, + url: defaultURLProvider, caCerts: defaultCaCerts, want: false, }, @@ -870,7 +870,7 @@ func Test_HasProperties(t *testing.T) { esClient: defaultEsClient, version: defaultVersion, user: BasicAuth{Name: "foo", Password: "changed"}, - url: defaultURL, + url: defaultURLProvider, caCerts: defaultCaCerts, want: false, }, @@ -879,7 +879,7 @@ func Test_HasProperties(t *testing.T) { esClient: defaultEsClient, version: defaultVersion, user: defaultUser, - url: "https://foo.com", + url: NewStaticURLProvider("https://foo.com"), caCerts: defaultCaCerts, want: false, }, @@ -888,7 +888,7 @@ func Test_HasProperties(t *testing.T) { esClient: defaultEsClient, version: defaultVersion, user: defaultUser, - url: defaultURL, + url: defaultURLProvider, caCerts: []*x509.Certificate{{Raw: []byte("bar")}}, want: false, }, @@ -897,7 +897,7 @@ func Test_HasProperties(t *testing.T) { esClient: defaultEsClient, version: defaultVersion, user: defaultUser, - url: defaultURL, + url: defaultURLProvider, caCerts: defaultCaCerts, want: true, }, diff --git a/pkg/controller/elasticsearch/client/mock.go b/pkg/controller/elasticsearch/client/mock.go index 8c7825532a..6de18d3fec 100644 --- a/pkg/controller/elasticsearch/client/mock.go +++ b/pkg/controller/elasticsearch/client/mock.go @@ -27,8 +27,8 @@ func NewMockClientWithUser(v version.Version, u BasicAuth, fn RoundTripFunc) Cli HTTP: &http.Client{ Transport: fn, }, - Endpoint: "http://example.com", - User: u, + URLProvider: NewStaticURLProvider("http://example.com"), + User: u, } return versioned(baseClient, v) } diff --git a/pkg/controller/elasticsearch/client/url.go b/pkg/controller/elasticsearch/client/url.go new file mode 100644 index 0000000000..e258c5b451 --- /dev/null +++ b/pkg/controller/elasticsearch/client/url.go @@ -0,0 +1,46 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License 2.0; +// you may not use this file except in compliance with the Elastic License 2.0. + +package client + +type URLProvider interface { + // URL returns a URL to route traffic to (can fall back to a k8s service URL). + URL() (string, error) + // Equals returns true if the other URLProvider is equal to the one in the receiver. + Equals(other URLProvider) bool + // HasEndpoints returns true if the provider has currently any endpoints/URLs to return. + // Makes sense for implementations that do not return a static URL. + HasEndpoints() bool +} + +// NewStaticURLProvider is a static implementation of the URL provider interface for testing purposes. +func NewStaticURLProvider(url string) URLProvider { + return &staticURLProvider{ + url: url, + } +} + +type staticURLProvider struct { + url string +} + +// URL implements URLProvider. +func (s *staticURLProvider) URL() (string, error) { + return s.url, nil +} + +// Equals implements URLProvider. +func (s *staticURLProvider) Equals(other URLProvider) bool { + otherStatic, ok := other.(*staticURLProvider) + if !ok { + return false + } + return s.url == otherStatic.url +} + +func (s *staticURLProvider) HasEndpoints() bool { + return true +} + +var _ URLProvider = &staticURLProvider{} diff --git a/pkg/controller/elasticsearch/client/v6.go b/pkg/controller/elasticsearch/client/v6.go index 377f87f4b3..08efdc760e 100644 --- a/pkg/controller/elasticsearch/client/v6.go +++ b/pkg/controller/elasticsearch/client/v6.go @@ -216,7 +216,11 @@ func (c *clientV6) GetClusterState(_ context.Context) (ClusterState, error) { } func (c *clientV6) Request(ctx context.Context, r *http.Request) (*http.Response, error) { - newURL, err := url.Parse(stringsutil.Concat(c.Endpoint, r.URL.String())) + baseURL, err := c.URLProvider.URL() + if err != nil { + return nil, err + } + newURL, err := url.Parse(stringsutil.Concat(baseURL, r.URL.String())) if err != nil { return nil, err } diff --git a/pkg/controller/elasticsearch/configmap/configmap.go b/pkg/controller/elasticsearch/configmap/configmap.go index 792bcca04c..53043ed017 100644 --- a/pkg/controller/elasticsearch/configmap/configmap.go +++ b/pkg/controller/elasticsearch/configmap/configmap.go @@ -53,11 +53,12 @@ func ReconcileScriptsConfigMap(ctx context.Context, c k8s.Client, es esv1.Elasti types.NamespacedName{Namespace: es.Namespace, Name: esv1.ScriptsConfigMap(es.Name)}, k8s.ExtractNamespacedName(&es), map[string]string{ - nodespec.ReadinessProbeScriptConfigKey: nodespec.ReadinessProbeScript, - nodespec.PreStopHookScriptConfigKey: preStopScript, - initcontainer.PrepareFsScriptConfigKey: fsScript, - initcontainer.SuspendScriptConfigKey: initcontainer.SuspendScript, - initcontainer.SuspendedHostsFile: initcontainer.RenderSuspendConfiguration(es), + nodespec.LegacyReadinessProbeScriptConfigKey: nodespec.LegacyReadinessProbeScript, + nodespec.ReadinessPortProbeScriptConfigKey: nodespec.ReadinessPortProbeScript, + nodespec.PreStopHookScriptConfigKey: preStopScript, + initcontainer.PrepareFsScriptConfigKey: fsScript, + initcontainer.SuspendScriptConfigKey: initcontainer.SuspendScript, + initcontainer.SuspendedHostsFile: initcontainer.RenderSuspendConfiguration(es), }, ) diff --git a/pkg/controller/elasticsearch/driver/driver.go b/pkg/controller/elasticsearch/driver/driver.go index d8b3a872de..cae7fae24c 100644 --- a/pkg/controller/elasticsearch/driver/driver.go +++ b/pkg/controller/elasticsearch/driver/driver.go @@ -184,22 +184,20 @@ func (d *defaultDriver) Reconcile(ctx context.Context) *reconciler.Results { minVersion = &d.Version } - isServiceReady, err := services.IsServiceReady(d.Client, *internalService) - if err != nil { - return results.WithError(err) - } + urlProvider := services.NewElasticsearchURLProvider(d.ES, d.Client) + hasEndpoints := urlProvider.HasEndpoints() observedState := d.Observers.ObservedStateResolver( ctx, d.ES, d.elasticsearchClientProvider( ctx, - resourcesState, + urlProvider, controllerUser, *minVersion, trustedHTTPCertificates, ), - isServiceReady, + hasEndpoints, ) // Always update the Elasticsearch state bits with the latest observed state. @@ -235,7 +233,7 @@ func (d *defaultDriver) Reconcile(ctx context.Context) *reconciler.Results { // TODO: support user-supplied certificate (non-ca) esClient := d.newElasticsearchClient( ctx, - resourcesState, + urlProvider, controllerUser, *minVersion, trustedHTTPCertificates, @@ -244,12 +242,12 @@ func (d *defaultDriver) Reconcile(ctx context.Context) *reconciler.Results { // use unknown health as a proxy for a cluster not responding to requests hasKnownHealthState := observedState() != esv1.ElasticsearchUnknownHealth - esReachable := isServiceReady && hasKnownHealthState + esReachable := hasEndpoints && hasKnownHealthState // report condition in Pod status if esReachable { - d.ReconcileState.ReportCondition(esv1.ElasticsearchIsReachable, corev1.ConditionTrue, esReachableConditionMessage(internalService, isServiceReady, hasKnownHealthState)) + d.ReconcileState.ReportCondition(esv1.ElasticsearchIsReachable, corev1.ConditionTrue, esReachableConditionMessage(internalService, hasEndpoints, hasKnownHealthState)) } else { - d.ReconcileState.ReportCondition(esv1.ElasticsearchIsReachable, corev1.ConditionFalse, esReachableConditionMessage(internalService, isServiceReady, hasKnownHealthState)) + d.ReconcileState.ReportCondition(esv1.ElasticsearchIsReachable, corev1.ConditionFalse, esReachableConditionMessage(internalService, hasEndpoints, hasKnownHealthState)) } var currentLicense esclient.License @@ -282,7 +280,7 @@ func (d *defaultDriver) Reconcile(ctx context.Context) *reconciler.Results { // reconcile the Elasticsearch license (even if we assume the cluster might not respond to requests to cover the case of // expired licenses where all health API responses are 403) - if isServiceReady { + if hasEndpoints { err = license.Reconcile(ctx, d.Client, d.ES, esClient, currentLicense) if err != nil { msg := "Could not reconcile cluster license, re-queuing" @@ -378,16 +376,15 @@ func (d *defaultDriver) Reconcile(ctx context.Context) *reconciler.Results { // newElasticsearchClient creates a new Elasticsearch HTTP client for this cluster using the provided user func (d *defaultDriver) newElasticsearchClient( ctx context.Context, - state *reconcile.ResourcesState, + urlProvider esclient.URLProvider, user esclient.BasicAuth, v version.Version, caCerts []*x509.Certificate, ) esclient.Client { - url := services.ElasticsearchURL(d.ES, state.CurrentPodsByPhase[corev1.PodRunning]) return esclient.NewElasticsearchClient( d.OperatorParameters.Dialer, k8s.ExtractNamespacedName(&d.ES), - url, + urlProvider, user, v, caCerts, @@ -398,17 +395,16 @@ func (d *defaultDriver) newElasticsearchClient( func (d *defaultDriver) elasticsearchClientProvider( ctx context.Context, - state *reconcile.ResourcesState, + urlProvider esclient.URLProvider, user esclient.BasicAuth, v version.Version, caCerts []*x509.Certificate, ) func(existingEsClient esclient.Client) esclient.Client { return func(existingEsClient esclient.Client) esclient.Client { - url := services.ElasticsearchURL(d.ES, state.CurrentPodsByPhase[corev1.PodRunning]) - if existingEsClient != nil && existingEsClient.HasProperties(v, user, url, caCerts) { + if existingEsClient != nil && existingEsClient.HasProperties(v, user, urlProvider, caCerts) { return existingEsClient } - return d.newElasticsearchClient(ctx, state, user, v, caCerts) + return d.newElasticsearchClient(ctx, urlProvider, user, v, caCerts) } } diff --git a/pkg/controller/elasticsearch/nodespec/__snapshots__/podspec_test.snap b/pkg/controller/elasticsearch/nodespec/__snapshots__/podspec_test.snap new file mode 100755 index 0000000000..176f142292 --- /dev/null +++ b/pkg/controller/elasticsearch/nodespec/__snapshots__/podspec_test.snap @@ -0,0 +1,1788 @@ + +[TestBuildPodTemplateSpec/v7.20 - 1] +{ + "metadata": { + "annotations": { + "co.elastic.logs/module": "elasticsearch", + "elasticsearch.k8s.elastic.co/config-hash": "267866193", + "pod-template-annotation-name": "pod-template-annotation-value", + "policy.k8s.elastic.co/elasticsearch-config-mounts-hash": "2095567618" + }, + "creationTimestamp": null, + "labels": { + "common.k8s.elastic.co/type": "elasticsearch", + "elasticsearch.k8s.elastic.co/cluster-name": "name", + "elasticsearch.k8s.elastic.co/http-scheme": "https", + "elasticsearch.k8s.elastic.co/node-data": "false", + "elasticsearch.k8s.elastic.co/node-ingest": "true", + "elasticsearch.k8s.elastic.co/node-master": "true", + "elasticsearch.k8s.elastic.co/node-ml": "true", + "elasticsearch.k8s.elastic.co/statefulset-name": "name-es-nodeset-1", + "elasticsearch.k8s.elastic.co/version": "7.2.0", + "pod-template-label-name": "pod-template-label-value" + } + }, + "spec": { + "affinity": { + "podAntiAffinity": { + "preferredDuringSchedulingIgnoredDuringExecution": [ + { + "podAffinityTerm": { + "labelSelector": { + "matchLabels": { + "elasticsearch.k8s.elastic.co/cluster-name": "name" + } + }, + "topologyKey": "kubernetes.io/hostname" + }, + "weight": 100 + } + ] + } + }, + "automountServiceAccountToken": false, + "containers": [ + { + "name": "additional-container", + "resources": {}, + "securityContext": { + "allowPrivilegeEscalation": false, + "privileged": false, + "readOnlyRootFilesystem": false + } + }, + { + "env": [ + { + "name": "my-env", + "value": "my-value" + }, + { + "name": "POD_IP", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "status.podIP" + } + } + }, + { + "name": "POD_NAME", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.name" + } + } + }, + { + "name": "NODE_NAME", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "spec.nodeName" + } + } + }, + { + "name": "NAMESPACE", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + } + } + }, + { + "name": "PROBE_PASSWORD_PATH", + "value": "/mnt/elastic-internal/pod-mounted-users/elastic-internal-probe" + }, + { + "name": "PROBE_USERNAME", + "value": "elastic-internal-probe" + }, + { + "name": "READINESS_PROBE_PROTOCOL", + "value": "https" + }, + { + "name": "HEADLESS_SERVICE_NAME", + "value": "name-es-nodeset-1" + }, + { + "name": "NSS_SDB_USE_CACHE", + "value": "no" + } + ], + "image": "docker.elastic.co/elasticsearch/elasticsearch:7.2.0", + "lifecycle": { + "preStop": { + "exec": { + "command": [ + "bash", + "-c", + "/mnt/elastic-internal/scripts/pre-stop-hook-script.sh" + ] + } + } + }, + "name": "elasticsearch", + "ports": [ + { + "containerPort": 9200, + "name": "https", + "protocol": "TCP" + }, + { + "containerPort": 9300, + "name": "transport", + "protocol": "TCP" + } + ], + "readinessProbe": { + "exec": { + "command": [ + "bash", + "-c", + "/mnt/elastic-internal/scripts/readiness-probe-script.sh" + ] + }, + "failureThreshold": 3, + "initialDelaySeconds": 10, + "periodSeconds": 5, + "successThreshold": 1, + "timeoutSeconds": 5 + }, + "resources": { + "limits": { + "memory": "2Gi" + }, + "requests": { + "memory": "2Gi" + } + }, + "securityContext": { + "allowPrivilegeEscalation": false, + "privileged": false, + "readOnlyRootFilesystem": false + }, + "volumeMounts": [ + { + "mountPath": "/mnt/elastic-internal/downward-api", + "name": "downward-api", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/bin", + "name": "elastic-internal-elasticsearch-bin-local" + }, + { + "mountPath": "/mnt/elastic-internal/elasticsearch-config", + "name": "elastic-internal-elasticsearch-config", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config", + "name": "elastic-internal-elasticsearch-config-local" + }, + { + "mountPath": "/usr/share/elasticsearch/plugins", + "name": "elastic-internal-elasticsearch-plugins-local" + }, + { + "mountPath": "/usr/share/elasticsearch/config/http-certs", + "name": "elastic-internal-http-certificates", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/pod-mounted-users", + "name": "elastic-internal-probe-user", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config/transport-remote-certs/", + "name": "elastic-internal-remote-certificate-authorities", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/scripts", + "name": "elastic-internal-scripts", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config/transport-certs", + "name": "elastic-internal-transport-certificates", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/unicast-hosts", + "name": "elastic-internal-unicast-hosts", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/xpack-file-realm", + "name": "elastic-internal-xpack-file-realm", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/logs", + "name": "elasticsearch-logs" + }, + { + "mountPath": "/usr/test", + "name": "test-es-secretname", + "readOnly": true + }, + { + "mountPath": "/tmp", + "name": "tmp-volume" + } + ] + } + ], + "initContainers": [ + { + "command": [ + "bash", + "-c", + "/mnt/elastic-internal/scripts/prepare-fs.sh" + ], + "env": [ + { + "name": "POD_IP", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "status.podIP" + } + } + }, + { + "name": "POD_NAME", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.name" + } + } + }, + { + "name": "NODE_NAME", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "spec.nodeName" + } + } + }, + { + "name": "NAMESPACE", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + } + } + }, + { + "name": "my-env", + "value": "my-value" + }, + { + "name": "PROBE_PASSWORD_PATH", + "value": "/mnt/elastic-internal/pod-mounted-users/elastic-internal-probe" + }, + { + "name": "PROBE_USERNAME", + "value": "elastic-internal-probe" + }, + { + "name": "READINESS_PROBE_PROTOCOL", + "value": "https" + }, + { + "name": "HEADLESS_SERVICE_NAME", + "value": "name-es-nodeset-1" + }, + { + "name": "NSS_SDB_USE_CACHE", + "value": "no" + } + ], + "image": "docker.elastic.co/elasticsearch/elasticsearch:7.2.0", + "imagePullPolicy": "IfNotPresent", + "name": "elastic-internal-init-filesystem", + "resources": { + "limits": { + "cpu": "100m", + "memory": "50Mi" + }, + "requests": { + "cpu": "100m", + "memory": "50Mi" + } + }, + "securityContext": { + "allowPrivilegeEscalation": false, + "privileged": false, + "readOnlyRootFilesystem": false + }, + "volumeMounts": [ + { + "mountPath": "/mnt/elastic-internal/downward-api", + "name": "downward-api", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/elasticsearch-bin-local", + "name": "elastic-internal-elasticsearch-bin-local" + }, + { + "mountPath": "/mnt/elastic-internal/elasticsearch-config", + "name": "elastic-internal-elasticsearch-config", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/elasticsearch-config-local", + "name": "elastic-internal-elasticsearch-config-local" + }, + { + "mountPath": "/mnt/elastic-internal/elasticsearch-plugins-local", + "name": "elastic-internal-elasticsearch-plugins-local" + }, + { + "mountPath": "/usr/share/elasticsearch/config/http-certs", + "name": "elastic-internal-http-certificates", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/pod-mounted-users", + "name": "elastic-internal-probe-user", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config/transport-remote-certs/", + "name": "elastic-internal-remote-certificate-authorities", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/scripts", + "name": "elastic-internal-scripts", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/transport-certificates", + "name": "elastic-internal-transport-certificates", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/unicast-hosts", + "name": "elastic-internal-unicast-hosts", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/xpack-file-realm", + "name": "elastic-internal-xpack-file-realm", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/logs", + "name": "elasticsearch-logs" + }, + { + "mountPath": "/usr/test", + "name": "test-es-secretname", + "readOnly": true + }, + { + "mountPath": "/tmp", + "name": "tmp-volume" + } + ] + }, + { + "env": [ + { + "name": "POD_IP", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "status.podIP" + } + } + }, + { + "name": "POD_NAME", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.name" + } + } + }, + { + "name": "NODE_NAME", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "spec.nodeName" + } + } + }, + { + "name": "NAMESPACE", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + } + } + }, + { + "name": "my-env", + "value": "my-value" + }, + { + "name": "PROBE_PASSWORD_PATH", + "value": "/mnt/elastic-internal/pod-mounted-users/elastic-internal-probe" + }, + { + "name": "PROBE_USERNAME", + "value": "elastic-internal-probe" + }, + { + "name": "READINESS_PROBE_PROTOCOL", + "value": "https" + }, + { + "name": "HEADLESS_SERVICE_NAME", + "value": "name-es-nodeset-1" + }, + { + "name": "NSS_SDB_USE_CACHE", + "value": "no" + } + ], + "image": "docker.elastic.co/elasticsearch/elasticsearch:7.2.0", + "name": "", + "resources": { + "limits": { + "memory": "2Gi" + }, + "requests": { + "memory": "2Gi" + } + }, + "securityContext": { + "allowPrivilegeEscalation": false, + "privileged": false, + "readOnlyRootFilesystem": false + }, + "volumeMounts": [ + { + "mountPath": "/mnt/elastic-internal/downward-api", + "name": "downward-api", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/bin", + "name": "elastic-internal-elasticsearch-bin-local" + }, + { + "mountPath": "/mnt/elastic-internal/elasticsearch-config", + "name": "elastic-internal-elasticsearch-config", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config", + "name": "elastic-internal-elasticsearch-config-local" + }, + { + "mountPath": "/usr/share/elasticsearch/plugins", + "name": "elastic-internal-elasticsearch-plugins-local" + }, + { + "mountPath": "/usr/share/elasticsearch/config/http-certs", + "name": "elastic-internal-http-certificates", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/pod-mounted-users", + "name": "elastic-internal-probe-user", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config/transport-remote-certs/", + "name": "elastic-internal-remote-certificate-authorities", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/scripts", + "name": "elastic-internal-scripts", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config/transport-certs", + "name": "elastic-internal-transport-certificates", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/unicast-hosts", + "name": "elastic-internal-unicast-hosts", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/xpack-file-realm", + "name": "elastic-internal-xpack-file-realm", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/logs", + "name": "elasticsearch-logs" + }, + { + "mountPath": "/usr/test", + "name": "test-es-secretname", + "readOnly": true + }, + { + "mountPath": "/tmp", + "name": "tmp-volume" + } + ] + }, + { + "command": [ + "bash", + "-c", + "/mnt/elastic-internal/scripts/suspend.sh" + ], + "env": [ + { + "name": "POD_IP", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "status.podIP" + } + } + }, + { + "name": "POD_NAME", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.name" + } + } + }, + { + "name": "NODE_NAME", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "spec.nodeName" + } + } + }, + { + "name": "NAMESPACE", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + } + } + }, + { + "name": "my-env", + "value": "my-value" + }, + { + "name": "PROBE_PASSWORD_PATH", + "value": "/mnt/elastic-internal/pod-mounted-users/elastic-internal-probe" + }, + { + "name": "PROBE_USERNAME", + "value": "elastic-internal-probe" + }, + { + "name": "READINESS_PROBE_PROTOCOL", + "value": "https" + }, + { + "name": "HEADLESS_SERVICE_NAME", + "value": "name-es-nodeset-1" + }, + { + "name": "NSS_SDB_USE_CACHE", + "value": "no" + } + ], + "image": "docker.elastic.co/elasticsearch/elasticsearch:7.2.0", + "imagePullPolicy": "IfNotPresent", + "name": "elastic-internal-suspend", + "resources": { + "limits": { + "memory": "2Gi" + }, + "requests": { + "memory": "2Gi" + } + }, + "securityContext": { + "allowPrivilegeEscalation": false, + "privileged": false, + "readOnlyRootFilesystem": false + }, + "volumeMounts": [ + { + "mountPath": "/mnt/elastic-internal/downward-api", + "name": "downward-api", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/bin", + "name": "elastic-internal-elasticsearch-bin-local" + }, + { + "mountPath": "/mnt/elastic-internal/elasticsearch-config", + "name": "elastic-internal-elasticsearch-config", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config", + "name": "elastic-internal-elasticsearch-config-local" + }, + { + "mountPath": "/usr/share/elasticsearch/plugins", + "name": "elastic-internal-elasticsearch-plugins-local" + }, + { + "mountPath": "/usr/share/elasticsearch/config/http-certs", + "name": "elastic-internal-http-certificates", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/pod-mounted-users", + "name": "elastic-internal-probe-user", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config/transport-remote-certs/", + "name": "elastic-internal-remote-certificate-authorities", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/scripts", + "name": "elastic-internal-scripts", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config/transport-certs", + "name": "elastic-internal-transport-certificates", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/unicast-hosts", + "name": "elastic-internal-unicast-hosts", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/xpack-file-realm", + "name": "elastic-internal-xpack-file-realm", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/logs", + "name": "elasticsearch-logs" + }, + { + "mountPath": "/usr/test", + "name": "test-es-secretname", + "readOnly": true + }, + { + "mountPath": "/tmp", + "name": "tmp-volume" + } + ] + }, + { + "env": [ + { + "name": "POD_IP", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "status.podIP" + } + } + }, + { + "name": "POD_NAME", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.name" + } + } + }, + { + "name": "NODE_NAME", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "spec.nodeName" + } + } + }, + { + "name": "NAMESPACE", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + } + } + }, + { + "name": "my-env", + "value": "my-value" + }, + { + "name": "PROBE_PASSWORD_PATH", + "value": "/mnt/elastic-internal/pod-mounted-users/elastic-internal-probe" + }, + { + "name": "PROBE_USERNAME", + "value": "elastic-internal-probe" + }, + { + "name": "READINESS_PROBE_PROTOCOL", + "value": "https" + }, + { + "name": "HEADLESS_SERVICE_NAME", + "value": "name-es-nodeset-1" + }, + { + "name": "NSS_SDB_USE_CACHE", + "value": "no" + } + ], + "image": "docker.elastic.co/elasticsearch/elasticsearch:7.2.0", + "name": "additional-init-container", + "resources": { + "limits": { + "memory": "2Gi" + }, + "requests": { + "memory": "2Gi" + } + }, + "securityContext": { + "allowPrivilegeEscalation": false, + "privileged": false, + "readOnlyRootFilesystem": false + }, + "volumeMounts": [ + { + "mountPath": "/mnt/elastic-internal/downward-api", + "name": "downward-api", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/bin", + "name": "elastic-internal-elasticsearch-bin-local" + }, + { + "mountPath": "/mnt/elastic-internal/elasticsearch-config", + "name": "elastic-internal-elasticsearch-config", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config", + "name": "elastic-internal-elasticsearch-config-local" + }, + { + "mountPath": "/usr/share/elasticsearch/plugins", + "name": "elastic-internal-elasticsearch-plugins-local" + }, + { + "mountPath": "/usr/share/elasticsearch/config/http-certs", + "name": "elastic-internal-http-certificates", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/pod-mounted-users", + "name": "elastic-internal-probe-user", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config/transport-remote-certs/", + "name": "elastic-internal-remote-certificate-authorities", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/scripts", + "name": "elastic-internal-scripts", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config/transport-certs", + "name": "elastic-internal-transport-certificates", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/unicast-hosts", + "name": "elastic-internal-unicast-hosts", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/xpack-file-realm", + "name": "elastic-internal-xpack-file-realm", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/logs", + "name": "elasticsearch-logs" + }, + { + "mountPath": "/usr/test", + "name": "test-es-secretname", + "readOnly": true + }, + { + "mountPath": "/tmp", + "name": "tmp-volume" + } + ] + } + ], + "terminationGracePeriodSeconds": 180, + "volumes": [ + { + "name": "" + }, + { + "downwardAPI": { + "items": [ + { + "fieldRef": { + "fieldPath": "metadata.labels" + }, + "path": "labels" + } + ] + }, + "name": "downward-api" + }, + { + "emptyDir": {}, + "name": "elastic-internal-elasticsearch-bin-local" + }, + { + "name": "elastic-internal-elasticsearch-config", + "secret": { + "optional": false, + "secretName": "name-es-nodeset-1-es-config" + } + }, + { + "emptyDir": {}, + "name": "elastic-internal-elasticsearch-config-local" + }, + { + "emptyDir": {}, + "name": "elastic-internal-elasticsearch-plugins-local" + }, + { + "name": "elastic-internal-http-certificates", + "secret": { + "optional": false, + "secretName": "name-es-http-certs-internal" + } + }, + { + "name": "elastic-internal-probe-user", + "secret": { + "items": [ + { + "key": "elastic-internal-probe", + "path": "elastic-internal-probe" + }, + { + "key": "elastic-internal-pre-stop", + "path": "elastic-internal-pre-stop" + } + ], + "optional": false, + "secretName": "name-es-internal-users" + } + }, + { + "name": "elastic-internal-remote-certificate-authorities", + "secret": { + "optional": false, + "secretName": "name-es-remote-ca" + } + }, + { + "configMap": { + "defaultMode": 493, + "name": "name-es-scripts", + "optional": false + }, + "name": "elastic-internal-scripts" + }, + { + "name": "elastic-internal-transport-certificates", + "secret": { + "optional": false, + "secretName": "name-es-nodeset-1-es-transport-certs" + } + }, + { + "configMap": { + "defaultMode": 420, + "name": "name-es-unicast-hosts", + "optional": false + }, + "name": "elastic-internal-unicast-hosts" + }, + { + "name": "elastic-internal-xpack-file-realm", + "secret": { + "optional": false, + "secretName": "name-es-xpack-file-realm" + } + }, + { + "emptyDir": {}, + "name": "elasticsearch-logs" + }, + { + "name": "test-es-secretname", + "secret": { + "optional": false, + "secretName": "test-es-secretname" + } + }, + { + "emptyDir": {}, + "name": "tmp-volume" + } + ] + } +} +--- + +[TestBuildPodTemplateSpec/v8.13.2 - 1] +{ + "metadata": { + "annotations": { + "co.elastic.logs/module": "elasticsearch", + "elasticsearch.k8s.elastic.co/config-hash": "1794839471", + "pod-template-annotation-name": "pod-template-annotation-value" + }, + "creationTimestamp": null, + "labels": { + "common.k8s.elastic.co/type": "elasticsearch", + "elasticsearch.k8s.elastic.co/cluster-name": "name", + "elasticsearch.k8s.elastic.co/http-scheme": "https", + "elasticsearch.k8s.elastic.co/node-data": "false", + "elasticsearch.k8s.elastic.co/node-data_cold": "false", + "elasticsearch.k8s.elastic.co/node-data_content": "false", + "elasticsearch.k8s.elastic.co/node-data_frozen": "false", + "elasticsearch.k8s.elastic.co/node-data_hot": "false", + "elasticsearch.k8s.elastic.co/node-data_warm": "false", + "elasticsearch.k8s.elastic.co/node-ingest": "true", + "elasticsearch.k8s.elastic.co/node-master": "true", + "elasticsearch.k8s.elastic.co/node-ml": "true", + "elasticsearch.k8s.elastic.co/node-remote_cluster_client": "true", + "elasticsearch.k8s.elastic.co/node-transform": "false", + "elasticsearch.k8s.elastic.co/node-voting_only": "false", + "elasticsearch.k8s.elastic.co/statefulset-name": "name-es-nodeset-1", + "elasticsearch.k8s.elastic.co/version": "8.13.2", + "pod-template-label-name": "pod-template-label-value" + } + }, + "spec": { + "affinity": { + "podAntiAffinity": { + "preferredDuringSchedulingIgnoredDuringExecution": [ + { + "podAffinityTerm": { + "labelSelector": { + "matchLabels": { + "elasticsearch.k8s.elastic.co/cluster-name": "name" + } + }, + "topologyKey": "kubernetes.io/hostname" + }, + "weight": 100 + } + ] + } + }, + "automountServiceAccountToken": false, + "containers": [ + { + "name": "additional-container", + "resources": {}, + "securityContext": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": [ + "ALL" + ] + }, + "privileged": false, + "readOnlyRootFilesystem": false, + "runAsNonRoot": true + } + }, + { + "env": [ + { + "name": "my-env", + "value": "my-value" + }, + { + "name": "POD_IP", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "status.podIP" + } + } + }, + { + "name": "POD_NAME", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.name" + } + } + }, + { + "name": "NODE_NAME", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "spec.nodeName" + } + } + }, + { + "name": "NAMESPACE", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + } + } + }, + { + "name": "HEADLESS_SERVICE_NAME", + "value": "name-es-nodeset-1" + } + ], + "image": "docker.elastic.co/elasticsearch/elasticsearch:8.13.2", + "lifecycle": { + "preStop": { + "exec": { + "command": [ + "bash", + "-c", + "/mnt/elastic-internal/scripts/pre-stop-hook-script.sh" + ] + } + } + }, + "name": "elasticsearch", + "ports": [ + { + "containerPort": 9200, + "name": "https", + "protocol": "TCP" + }, + { + "containerPort": 9300, + "name": "transport", + "protocol": "TCP" + } + ], + "readinessProbe": { + "exec": { + "command": [ + "bash", + "-c", + "/mnt/elastic-internal/scripts/readiness-port-script.sh" + ] + }, + "failureThreshold": 3, + "initialDelaySeconds": 10, + "periodSeconds": 5, + "successThreshold": 1, + "timeoutSeconds": 5 + }, + "resources": { + "limits": { + "memory": "2Gi" + }, + "requests": { + "memory": "2Gi" + } + }, + "securityContext": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": [ + "ALL" + ] + }, + "privileged": false, + "readOnlyRootFilesystem": false, + "runAsNonRoot": true + }, + "volumeMounts": [ + { + "mountPath": "/mnt/elastic-internal/downward-api", + "name": "downward-api", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/bin", + "name": "elastic-internal-elasticsearch-bin-local" + }, + { + "mountPath": "/mnt/elastic-internal/elasticsearch-config", + "name": "elastic-internal-elasticsearch-config", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config", + "name": "elastic-internal-elasticsearch-config-local" + }, + { + "mountPath": "/usr/share/elasticsearch/plugins", + "name": "elastic-internal-elasticsearch-plugins-local" + }, + { + "mountPath": "/usr/share/elasticsearch/config/http-certs", + "name": "elastic-internal-http-certificates", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/pod-mounted-users", + "name": "elastic-internal-probe-user", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config/transport-remote-certs/", + "name": "elastic-internal-remote-certificate-authorities", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/scripts", + "name": "elastic-internal-scripts", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config/transport-certs", + "name": "elastic-internal-transport-certificates", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/unicast-hosts", + "name": "elastic-internal-unicast-hosts", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/xpack-file-realm", + "name": "elastic-internal-xpack-file-realm", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/logs", + "name": "elasticsearch-logs" + }, + { + "mountPath": "/usr/share/elasticsearch/config/operator", + "name": "file-settings", + "readOnly": true + }, + { + "mountPath": "/tmp", + "name": "tmp-volume" + } + ] + } + ], + "initContainers": [ + { + "command": [ + "bash", + "-c", + "/mnt/elastic-internal/scripts/prepare-fs.sh" + ], + "env": [ + { + "name": "POD_IP", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "status.podIP" + } + } + }, + { + "name": "POD_NAME", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.name" + } + } + }, + { + "name": "NODE_NAME", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "spec.nodeName" + } + } + }, + { + "name": "NAMESPACE", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + } + } + }, + { + "name": "my-env", + "value": "my-value" + }, + { + "name": "HEADLESS_SERVICE_NAME", + "value": "name-es-nodeset-1" + } + ], + "image": "docker.elastic.co/elasticsearch/elasticsearch:8.13.2", + "imagePullPolicy": "IfNotPresent", + "name": "elastic-internal-init-filesystem", + "resources": { + "limits": { + "cpu": "100m", + "memory": "50Mi" + }, + "requests": { + "cpu": "100m", + "memory": "50Mi" + } + }, + "securityContext": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": [ + "ALL" + ] + }, + "privileged": false, + "readOnlyRootFilesystem": false, + "runAsNonRoot": true + }, + "volumeMounts": [ + { + "mountPath": "/mnt/elastic-internal/downward-api", + "name": "downward-api", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/elasticsearch-bin-local", + "name": "elastic-internal-elasticsearch-bin-local" + }, + { + "mountPath": "/mnt/elastic-internal/elasticsearch-config", + "name": "elastic-internal-elasticsearch-config", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/elasticsearch-config-local", + "name": "elastic-internal-elasticsearch-config-local" + }, + { + "mountPath": "/mnt/elastic-internal/elasticsearch-plugins-local", + "name": "elastic-internal-elasticsearch-plugins-local" + }, + { + "mountPath": "/usr/share/elasticsearch/config/http-certs", + "name": "elastic-internal-http-certificates", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/pod-mounted-users", + "name": "elastic-internal-probe-user", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config/transport-remote-certs/", + "name": "elastic-internal-remote-certificate-authorities", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/scripts", + "name": "elastic-internal-scripts", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/transport-certificates", + "name": "elastic-internal-transport-certificates", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/unicast-hosts", + "name": "elastic-internal-unicast-hosts", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/xpack-file-realm", + "name": "elastic-internal-xpack-file-realm", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/logs", + "name": "elasticsearch-logs" + }, + { + "mountPath": "/usr/share/elasticsearch/config/operator", + "name": "file-settings", + "readOnly": true + }, + { + "mountPath": "/tmp", + "name": "tmp-volume" + } + ] + }, + { + "command": [ + "bash", + "-c", + "/mnt/elastic-internal/scripts/suspend.sh" + ], + "env": [ + { + "name": "POD_IP", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "status.podIP" + } + } + }, + { + "name": "POD_NAME", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.name" + } + } + }, + { + "name": "NODE_NAME", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "spec.nodeName" + } + } + }, + { + "name": "NAMESPACE", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + } + } + }, + { + "name": "my-env", + "value": "my-value" + }, + { + "name": "HEADLESS_SERVICE_NAME", + "value": "name-es-nodeset-1" + } + ], + "image": "docker.elastic.co/elasticsearch/elasticsearch:8.13.2", + "imagePullPolicy": "IfNotPresent", + "name": "elastic-internal-suspend", + "resources": { + "limits": { + "memory": "2Gi" + }, + "requests": { + "memory": "2Gi" + } + }, + "securityContext": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": [ + "ALL" + ] + }, + "privileged": false, + "readOnlyRootFilesystem": false, + "runAsNonRoot": true + }, + "volumeMounts": [ + { + "mountPath": "/mnt/elastic-internal/downward-api", + "name": "downward-api", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/bin", + "name": "elastic-internal-elasticsearch-bin-local" + }, + { + "mountPath": "/mnt/elastic-internal/elasticsearch-config", + "name": "elastic-internal-elasticsearch-config", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config", + "name": "elastic-internal-elasticsearch-config-local" + }, + { + "mountPath": "/usr/share/elasticsearch/plugins", + "name": "elastic-internal-elasticsearch-plugins-local" + }, + { + "mountPath": "/usr/share/elasticsearch/config/http-certs", + "name": "elastic-internal-http-certificates", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/pod-mounted-users", + "name": "elastic-internal-probe-user", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config/transport-remote-certs/", + "name": "elastic-internal-remote-certificate-authorities", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/scripts", + "name": "elastic-internal-scripts", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config/transport-certs", + "name": "elastic-internal-transport-certificates", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/unicast-hosts", + "name": "elastic-internal-unicast-hosts", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/xpack-file-realm", + "name": "elastic-internal-xpack-file-realm", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/logs", + "name": "elasticsearch-logs" + }, + { + "mountPath": "/usr/share/elasticsearch/config/operator", + "name": "file-settings", + "readOnly": true + }, + { + "mountPath": "/tmp", + "name": "tmp-volume" + } + ] + }, + { + "env": [ + { + "name": "POD_IP", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "status.podIP" + } + } + }, + { + "name": "POD_NAME", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.name" + } + } + }, + { + "name": "NODE_NAME", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "spec.nodeName" + } + } + }, + { + "name": "NAMESPACE", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + } + } + }, + { + "name": "my-env", + "value": "my-value" + }, + { + "name": "HEADLESS_SERVICE_NAME", + "value": "name-es-nodeset-1" + } + ], + "image": "docker.elastic.co/elasticsearch/elasticsearch:8.13.2", + "name": "additional-init-container", + "resources": { + "limits": { + "memory": "2Gi" + }, + "requests": { + "memory": "2Gi" + } + }, + "securityContext": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": [ + "ALL" + ] + }, + "privileged": false, + "readOnlyRootFilesystem": false, + "runAsNonRoot": true + }, + "volumeMounts": [ + { + "mountPath": "/mnt/elastic-internal/downward-api", + "name": "downward-api", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/bin", + "name": "elastic-internal-elasticsearch-bin-local" + }, + { + "mountPath": "/mnt/elastic-internal/elasticsearch-config", + "name": "elastic-internal-elasticsearch-config", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config", + "name": "elastic-internal-elasticsearch-config-local" + }, + { + "mountPath": "/usr/share/elasticsearch/plugins", + "name": "elastic-internal-elasticsearch-plugins-local" + }, + { + "mountPath": "/usr/share/elasticsearch/config/http-certs", + "name": "elastic-internal-http-certificates", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/pod-mounted-users", + "name": "elastic-internal-probe-user", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config/transport-remote-certs/", + "name": "elastic-internal-remote-certificate-authorities", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/scripts", + "name": "elastic-internal-scripts", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/config/transport-certs", + "name": "elastic-internal-transport-certificates", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/unicast-hosts", + "name": "elastic-internal-unicast-hosts", + "readOnly": true + }, + { + "mountPath": "/mnt/elastic-internal/xpack-file-realm", + "name": "elastic-internal-xpack-file-realm", + "readOnly": true + }, + { + "mountPath": "/usr/share/elasticsearch/logs", + "name": "elasticsearch-logs" + }, + { + "mountPath": "/usr/share/elasticsearch/config/operator", + "name": "file-settings", + "readOnly": true + }, + { + "mountPath": "/tmp", + "name": "tmp-volume" + } + ] + } + ], + "terminationGracePeriodSeconds": 180, + "volumes": [ + { + "downwardAPI": { + "items": [ + { + "fieldRef": { + "fieldPath": "metadata.labels" + }, + "path": "labels" + } + ] + }, + "name": "downward-api" + }, + { + "emptyDir": {}, + "name": "elastic-internal-elasticsearch-bin-local" + }, + { + "name": "elastic-internal-elasticsearch-config", + "secret": { + "optional": false, + "secretName": "name-es-nodeset-1-es-config" + } + }, + { + "emptyDir": {}, + "name": "elastic-internal-elasticsearch-config-local" + }, + { + "emptyDir": {}, + "name": "elastic-internal-elasticsearch-plugins-local" + }, + { + "name": "elastic-internal-http-certificates", + "secret": { + "optional": false, + "secretName": "name-es-http-certs-internal" + } + }, + { + "name": "elastic-internal-probe-user", + "secret": { + "items": [ + { + "key": "elastic-internal-probe", + "path": "elastic-internal-probe" + }, + { + "key": "elastic-internal-pre-stop", + "path": "elastic-internal-pre-stop" + } + ], + "optional": false, + "secretName": "name-es-internal-users" + } + }, + { + "name": "elastic-internal-remote-certificate-authorities", + "secret": { + "optional": false, + "secretName": "name-es-remote-ca" + } + }, + { + "configMap": { + "defaultMode": 493, + "name": "name-es-scripts", + "optional": false + }, + "name": "elastic-internal-scripts" + }, + { + "name": "elastic-internal-transport-certificates", + "secret": { + "optional": false, + "secretName": "name-es-nodeset-1-es-transport-certs" + } + }, + { + "configMap": { + "defaultMode": 420, + "name": "name-es-unicast-hosts", + "optional": false + }, + "name": "elastic-internal-unicast-hosts" + }, + { + "name": "elastic-internal-xpack-file-realm", + "secret": { + "optional": false, + "secretName": "name-es-xpack-file-realm" + } + }, + { + "emptyDir": {}, + "name": "elasticsearch-logs" + }, + { + "name": "file-settings", + "secret": { + "optional": false, + "secretName": "name-es-file-settings" + } + }, + { + "emptyDir": {}, + "name": "tmp-volume" + } + ] + } +} +--- + +[TestBuildPodTemplateSpec/failing_client - 1] +{ + "metadata": { + "creationTimestamp": null + }, + "spec": { + "containers": null + } +} +--- diff --git a/pkg/controller/elasticsearch/nodespec/defaults.go b/pkg/controller/elasticsearch/nodespec/defaults.go index 3ffd511190..a21c8ad4a5 100644 --- a/pkg/controller/elasticsearch/nodespec/defaults.go +++ b/pkg/controller/elasticsearch/nodespec/defaults.go @@ -12,7 +12,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" commonv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/common/v1" + esv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/elasticsearch/v1" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/defaults" + "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/version" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/label" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/settings" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/user" @@ -42,9 +44,13 @@ var ( ) // DefaultEnvVars are environment variables injected into Elasticsearch pods. -func DefaultEnvVars(httpCfg commonv1.HTTPConfig, headlessServiceName string) []corev1.EnvVar { - return defaults.ExtendPodDownwardEnvVars( - []corev1.EnvVar{ +func DefaultEnvVars(v version.Version, httpCfg commonv1.HTTPConfig, headlessServiceName string) []corev1.EnvVar { + vars := []corev1.EnvVar{ + // needed in elasticsearch.yml + {Name: settings.HeadlessServiceName, Value: headlessServiceName}, + } + if v.LT(esv1.MinReadinessPortVersion) { + vars = []corev1.EnvVar{ {Name: settings.EnvProbePasswordPath, Value: path.Join(esvolume.PodMountedUsersSecretMountPath, user.ProbeUserName)}, {Name: settings.EnvProbeUsername, Value: user.ProbeUserName}, {Name: settings.EnvReadinessProbeProtocol, Value: httpCfg.Protocol()}, @@ -62,8 +68,9 @@ func DefaultEnvVars(httpCfg commonv1.HTTPConfig, headlessServiceName string) []c // https://github.com/elastic/cloud-on-k8s/issues/1635 // https://issuetracker.google.com/issues/140577001 {Name: "NSS_SDB_USE_CACHE", Value: "no"}, - }..., - ) + } + } + return defaults.ExtendPodDownwardEnvVars(vars...) } // DefaultAffinity returns the default affinity for pods in a cluster. diff --git a/pkg/controller/elasticsearch/nodespec/podspec.go b/pkg/controller/elasticsearch/nodespec/podspec.go index 197d911fc2..0f4add04ae 100644 --- a/pkg/controller/elasticsearch/nodespec/podspec.go +++ b/pkg/controller/elasticsearch/nodespec/podspec.go @@ -127,9 +127,9 @@ func BuildPodTemplateSpec( WithResources(DefaultResources). WithTerminationGracePeriod(DefaultTerminationGracePeriodSeconds). WithPorts(defaultContainerPorts). - WithReadinessProbe(*NewReadinessProbe()). + WithReadinessProbe(*NewReadinessProbe(v)). WithAffinity(DefaultAffinity(es.Name)). - WithEnv(DefaultEnvVars(es.Spec.HTTP, headlessServiceName)...). + WithEnv(DefaultEnvVars(v, es.Spec.HTTP, headlessServiceName)...). WithVolumes(volumes...). WithVolumeMounts(volumeMounts...). WithInitContainers(initContainers...). diff --git a/pkg/controller/elasticsearch/nodespec/podspec_test.go b/pkg/controller/elasticsearch/nodespec/podspec_test.go index 2b569ee563..65b7087a89 100644 --- a/pkg/controller/elasticsearch/nodespec/podspec_test.go +++ b/pkg/controller/elasticsearch/nodespec/podspec_test.go @@ -6,11 +6,11 @@ package nodespec import ( "context" - "path" - "sort" + "encoding/json" + "errors" "testing" - "github.com/go-test/deep" + "github.com/gkampitakis/go-snaps/snaps" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" @@ -21,7 +21,6 @@ import ( esv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/elasticsearch/v1" policyv1alpha1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/stackconfigpolicy/v1alpha1" commonannotation "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/annotation" - "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/defaults" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/hash" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/keystore" common "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/settings" @@ -29,8 +28,6 @@ import ( "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/volume" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/initcontainer" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/settings" - "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/user" - esvolume "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/volume" "github.com/elastic/cloud-on-k8s/v2/pkg/utils/k8s" ) @@ -38,6 +35,7 @@ type esSampleBuilder struct { userConfig map[string]interface{} esAdditionalAnnotations map[string]string keystoreResources *keystore.Resources + version string } func newEsSampleBuilder() *esSampleBuilder { @@ -52,9 +50,17 @@ func (esb *esSampleBuilder) build() esv1.Elasticsearch { if esb.userConfig != nil { es.Spec.NodeSets[0].Config = &commonv1.Config{Data: esb.userConfig} } + if esb.version != "" { + es.Spec.Version = esb.version + } return *es } +func (esb *esSampleBuilder) withVersion(version string) *esSampleBuilder { + esb.version = version + return esb +} + func (esb *esSampleBuilder) withUserConfig(userConfig map[string]interface{}) *esSampleBuilder { esb.userConfig = userConfig return esb @@ -224,10 +230,8 @@ func TestBuildPodTemplateSpecWithDefaultSecurityContext(t *testing.T) { } func TestBuildPodTemplateSpec(t *testing.T) { + // 7.20 fixtures sampleES := newEsSampleBuilder().build() - nodeSet := sampleES.Spec.NodeSets[0] - ver, err := version.Parse(sampleES.Spec.Version) - require.NoError(t, err) policyEsConfig := common.MustCanonicalConfig(map[string]interface{}{ "logger.org.elasticsearch.discovery": "DEBUG", }) @@ -235,9 +239,7 @@ func TestBuildPodTemplateSpec(t *testing.T) { SecretName: "test-es-secretname", MountPath: "/usr/test", }} - elasticsearchConfigAndMountsHash := hash.HashObject([]interface{}{policyEsConfig, secretMounts}) - policyConfig := PolicyConfig{ ElasticsearchConfig: policyEsConfig, AdditionalVolumes: []volume.VolumeLike{ @@ -247,140 +249,72 @@ func TestBuildPodTemplateSpec(t *testing.T) { commonannotation.ElasticsearchConfigAndSecretMountsHashAnnotation: elasticsearchConfigAndMountsHash, }, } + // shared fixture + scriptsConfigMap := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Namespace: sampleES.Namespace, Name: esv1.ScriptsConfigMap(sampleES.Name)}} - cfg, err := settings.NewMergedESConfig(sampleES.Name, ver, corev1.IPv4Protocol, sampleES.Spec.HTTP, *nodeSet.Config, policyConfig.ElasticsearchConfig) - require.NoError(t, err) - - client := k8s.NewFakeClient(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Namespace: sampleES.Namespace, Name: esv1.ScriptsConfigMap(sampleES.Name)}}) - actual, err := BuildPodTemplateSpec(context.Background(), client, sampleES, sampleES.Spec.NodeSets[0], cfg, nil, false, policyConfig) - require.NoError(t, err) - - // build expected PodTemplateSpec - - terminationGracePeriodSeconds := DefaultTerminationGracePeriodSeconds - varFalse := false - - volumes, volumeMounts := buildVolumes(sampleES.Name, ver, nodeSet, nil, volume.DownwardAPI{}, policyConfig.AdditionalVolumes) - // should be sorted - sort.Slice(volumes, func(i, j int) bool { return volumes[i].Name < volumes[j].Name }) - sort.Slice(volumeMounts, func(i, j int) bool { return volumeMounts[i].Name < volumeMounts[j].Name }) - - initContainers, err := initcontainer.NewInitContainers(transportCertificatesVolume(sampleES.Name), nil, nil) - require.NoError(t, err) - // init containers should be patched with volume and inherited env vars and image - // init container env vars come in a slightly different order than main container ones which is an artefact of how the pod template builder works - initContainerEnv := defaults.ExtendPodDownwardEnvVars( - []corev1.EnvVar{ - {Name: "my-env", Value: "my-value"}, - {Name: settings.EnvProbePasswordPath, Value: path.Join(esvolume.PodMountedUsersSecretMountPath, user.ProbeUserName)}, - {Name: settings.EnvProbeUsername, Value: user.ProbeUserName}, - {Name: settings.EnvReadinessProbeProtocol, Value: sampleES.Spec.HTTP.Protocol()}, - {Name: settings.HeadlessServiceName, Value: HeadlessServiceName(esv1.StatefulSet(sampleES.Name, nodeSet.Name))}, - {Name: "NSS_SDB_USE_CACHE", Value: "no"}, - }..., - ) - esDockerImage := "docker.elastic.co/elasticsearch/elasticsearch:7.2.0" - for i := range initContainers { - initContainers[i].Image = esDockerImage - initContainers[i].Env = initContainerEnv - initContainers[i].VolumeMounts = append(initContainers[i].VolumeMounts, volumeMounts...) - initContainers[i].Resources = DefaultResources - initContainers[i].SecurityContext = &corev1.SecurityContext{ - Privileged: ptr.To[bool](false), - ReadOnlyRootFilesystem: ptr.To[bool](false), - AllowPrivilegeEscalation: ptr.To[bool](false), - } - } - - // remove the prepare-fs init-container from comparison, it has its own volume mount logic - // that is harder to test - for i, c := range initContainers { - if c.Name == initcontainer.PrepareFilesystemContainerName { - initContainers = append(initContainers[:i], initContainers[i+1:]...) - } - } - for i, c := range actual.Spec.InitContainers { - if c.Name == initcontainer.PrepareFilesystemContainerName { - actual.Spec.InitContainers = append(actual.Spec.InitContainers[:i], actual.Spec.InitContainers[i+1:]...) - } + type args struct { + client k8s.Client + es esv1.Elasticsearch + keystoreResources *keystore.Resources + setDefaultSecurityContext bool + policyConfig PolicyConfig } - - expected := corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "common.k8s.elastic.co/type": "elasticsearch", - "elasticsearch.k8s.elastic.co/cluster-name": "name", - "elasticsearch.k8s.elastic.co/http-scheme": "https", - "elasticsearch.k8s.elastic.co/node-data": "false", - "elasticsearch.k8s.elastic.co/node-ingest": "true", - "elasticsearch.k8s.elastic.co/node-master": "true", - "elasticsearch.k8s.elastic.co/node-ml": "true", - "elasticsearch.k8s.elastic.co/statefulset-name": "name-es-nodeset-1", - "elasticsearch.k8s.elastic.co/version": "7.2.0", - "pod-template-label-name": "pod-template-label-value", + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "v7.20", + args: args{ + client: k8s.NewFakeClient(scriptsConfigMap), + es: sampleES, + keystoreResources: &keystore.Resources{}, + setDefaultSecurityContext: false, + policyConfig: policyConfig, }, - Annotations: map[string]string{ - "elasticsearch.k8s.elastic.co/config-hash": "267866193", - "pod-template-annotation-name": "pod-template-annotation-value", - "co.elastic.logs/module": "elasticsearch", - "policy.k8s.elastic.co/elasticsearch-config-mounts-hash": "2095567618", + wantErr: false, + }, + { + name: "v8.13.2", + args: args{ + client: k8s.NewFakeClient(scriptsConfigMap), + es: newEsSampleBuilder().withVersion("8.13.2").build(), }, + wantErr: false, }, - Spec: corev1.PodSpec{ - Volumes: volumes, - InitContainers: append(initContainers, corev1.Container{ - Name: "additional-init-container", - Image: esDockerImage, - Env: initContainerEnv, - VolumeMounts: volumeMounts, - Resources: DefaultResources, // inherited from main container - SecurityContext: &corev1.SecurityContext{ - Privileged: ptr.To[bool](false), - // ReadOnlyRootFilesystem is expected to be false in this test because there is no data volume. - ReadOnlyRootFilesystem: ptr.To[bool](false), - AllowPrivilegeEscalation: ptr.To[bool](false), - }, - }), - Containers: []corev1.Container{ - { - Name: "additional-container", - SecurityContext: &corev1.SecurityContext{ - Privileged: ptr.To[bool](false), - ReadOnlyRootFilesystem: ptr.To[bool](false), - AllowPrivilegeEscalation: ptr.To[bool](false), - }, - }, - { - Name: "elasticsearch", - Image: esDockerImage, - Ports: []corev1.ContainerPort{ - {Name: "https", HostPort: 0, ContainerPort: 9200, Protocol: "TCP", HostIP: ""}, - {Name: "transport", HostPort: 0, ContainerPort: 9300, Protocol: "TCP", HostIP: ""}, - }, - Env: append( - []corev1.EnvVar{{Name: "my-env", Value: "my-value"}}, - DefaultEnvVars(sampleES.Spec.HTTP, HeadlessServiceName(esv1.StatefulSet(sampleES.Name, nodeSet.Name)))...), - Resources: DefaultResources, - VolumeMounts: volumeMounts, - ReadinessProbe: NewReadinessProbe(), - Lifecycle: &corev1.Lifecycle{ - PreStop: NewPreStopHook(), - }, - SecurityContext: &corev1.SecurityContext{ - Privileged: ptr.To[bool](false), - ReadOnlyRootFilesystem: ptr.To[bool](false), - AllowPrivilegeEscalation: ptr.To[bool](false), - }, - }, + { + name: "failing client", + args: args{ + client: k8s.NewFailingClient(errors.New("should fail")), + es: newEsSampleBuilder().withVersion("8.13.2").build(), }, - TerminationGracePeriodSeconds: &terminationGracePeriodSeconds, - AutomountServiceAccountToken: &varFalse, - Affinity: DefaultAffinity(sampleES.Name), + wantErr: true, }, } - deep.MaxDepth = 25 - require.Nil(t, deep.Equal(expected, actual)) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + es := tt.args.es + nodeSet := es.Spec.NodeSets[0] + + ver, err := version.Parse(es.Spec.Version) + require.NoError(t, err) + + cfg, err := settings.NewMergedESConfig(es.Name, ver, corev1.IPv4Protocol, es.Spec.HTTP, *nodeSet.Config, tt.args.policyConfig.ElasticsearchConfig) + require.NoError(t, err) + + actual, err := BuildPodTemplateSpec(context.Background(), tt.args.client, es, es.Spec.NodeSets[0], cfg, tt.args.keystoreResources, tt.args.setDefaultSecurityContext, tt.args.policyConfig) + if (err != nil) != tt.wantErr { + t.Errorf("BuildPodTemplateSpec wantErr %v got %v", tt.wantErr, err) + } + + // render as JSON for easier diff debugging + gotJSON, err := json.MarshalIndent(&actual, " ", " ") + require.NoError(t, err) + snaps.MatchJSON(t, gotJSON) + }) + } } func Test_buildAnnotations(t *testing.T) { @@ -606,7 +540,7 @@ func Test_getScriptsConfigMapContent(t *testing.T) { Data: map[string]string{ PreStopHookScriptConfigKey: "value1#", initcontainer.PrepareFsScriptConfigKey: "value2#", - ReadinessProbeScriptConfigKey: "value3#", + LegacyReadinessProbeScriptConfigKey: "value3#", initcontainer.SuspendScriptConfigKey: "value4#", initcontainer.SuspendedHostsFile: "value5#", }, diff --git a/pkg/controller/elasticsearch/nodespec/readiness_probe.go b/pkg/controller/elasticsearch/nodespec/readiness_probe.go index c29e393399..cf871b3639 100644 --- a/pkg/controller/elasticsearch/nodespec/readiness_probe.go +++ b/pkg/controller/elasticsearch/nodespec/readiness_probe.go @@ -9,12 +9,30 @@ import ( corev1 "k8s.io/api/core/v1" + esv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/elasticsearch/v1" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/http" + "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/version" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/label" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/volume" ) -func NewReadinessProbe() *corev1.Probe { +// as of 8.2.0 a simplified unauthenticated readiness port is available which takes cluster membership into account +// see https://www.elastic.co/guide/en/elasticsearch/reference/current/advanced-configuration.html#readiness-tcp-port + +const ( + ReadinessPortProbeScriptConfigKey = "readiness-port-script.sh" + // ReadinessPortProbeScript is the simplified readiness check for ES >= 8.2.0 which supports a dedicated TCP check + ReadinessPortProbeScript = `#!/usr/bin/env bash +nc -z -v -w5 127.0.0.1 8080 +` +) + +func NewReadinessProbe(v version.Version) *corev1.Probe { + scriptKey := ReadinessPortProbeScriptConfigKey + if v.LE(esv1.MinReadinessPortVersion) { + scriptKey = LegacyReadinessProbeScriptConfigKey + } + return &corev1.Probe{ FailureThreshold: 3, InitialDelaySeconds: 10, @@ -23,14 +41,14 @@ func NewReadinessProbe() *corev1.Probe { TimeoutSeconds: 5, ProbeHandler: corev1.ProbeHandler{ Exec: &corev1.ExecAction{ - Command: []string{"bash", "-c", path.Join(volume.ScriptsVolumeMountPath, ReadinessProbeScriptConfigKey)}, + Command: []string{"bash", "-c", path.Join(volume.ScriptsVolumeMountPath, scriptKey)}, }, }, } } -const ReadinessProbeScriptConfigKey = "readiness-probe-script.sh" -const ReadinessProbeScript = `#!/usr/bin/env bash +const LegacyReadinessProbeScriptConfigKey = "readiness-probe-script.sh" +const LegacyReadinessProbeScript = `#!/usr/bin/env bash # fail should be called as a last resort to help the user to understand why the probe failed function fail { diff --git a/pkg/controller/elasticsearch/services/services.go b/pkg/controller/elasticsearch/services/services.go index 86652fbce6..71284a22bb 100644 --- a/pkg/controller/elasticsearch/services/services.go +++ b/pkg/controller/elasticsearch/services/services.go @@ -16,6 +16,7 @@ import ( esv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/elasticsearch/v1" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/defaults" + "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/client" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/label" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/network" "github.com/elastic/cloud-on-k8s/v2/pkg/utils/k8s" @@ -181,26 +182,65 @@ func getServiceByName(c k8s.Client, es esv1.Elasticsearch, serviceName string) ( return svc, nil } -// ElasticsearchURL calculates the base url for Elasticsearch, taking into account the currently running pods. -// If there is an HTTP scheme mismatch between spec and pods we switch to requesting individual pods directly -// otherwise this delegates to ExternalServiceURL. -func ElasticsearchURL(es esv1.Elasticsearch, pods []corev1.Pod) string { - var schemeChange bool +type urlProvider struct { + pods func() ([]corev1.Pod, error) + svcURL string +} + +// URL implements client.URLProvider. +func (u *urlProvider) URL() (string, error) { + var ready, running []corev1.Pod + pods, err := u.pods() + if err != nil { + return "", err + } for _, p := range pods { - scheme, exists := p.Labels[label.HTTPSchemeLabelName] - if exists && scheme != es.Spec.HTTP.Protocol() { - // scheme in existing pods does not match scheme in spec, user toggled HTTP(S) - schemeChange = true + if k8s.IsPodReady(p) { + ready = append(ready, p) } - } - if schemeChange { - // switch to sending requests directly to a random pod instead of going through the service - randomPod := pods[rand.Intn(len(pods))] //nolint:gosec - if podURL := ElasticsearchPodURL(randomPod); podURL != "" { - return podURL + if k8s.IsPodRunning(p) { + running = append(running, p) } } - return InternalServiceURL(es) + switch { + case len(ready) > 0: + return randomESPodURL(ready), nil + case len(running) > 0: + return randomESPodURL(running), nil + default: + return u.svcURL, nil + } +} + +// Equals implements client.URLProvider. +func (u *urlProvider) Equals(other client.URLProvider) bool { + otherImpl, ok := other.(*urlProvider) + if !ok { + return false + } + return u.svcURL == otherImpl.svcURL +} + +// HasEndpoints implements client.URLProvider. +func (u *urlProvider) HasEndpoints() bool { + pods, err := u.pods() + return err == nil && len(k8s.RunningPods(pods)) > 0 +} + +// NewElasticsearchURLProvider returns a client.URLProvider that dynamically tries to find Pod URLs among the +// currently running Pods. Preferring ready Pods over running ones. +func NewElasticsearchURLProvider(es esv1.Elasticsearch, client k8s.Client) client.URLProvider { + return &urlProvider{ + pods: func() ([]corev1.Pod, error) { + return k8s.PodsMatchingLabels(client, es.Namespace, label.NewLabelSelectorForElasticsearch(es)) + }, + svcURL: InternalServiceURL(es), + } +} + +func randomESPodURL(pods []corev1.Pod) string { + randomPod := pods[rand.Intn(len(pods))] //nolint:gosec + return ElasticsearchPodURL(randomPod) } // ElasticsearchPodURL calculates the URL for the given Pod based on the Pods metadata. diff --git a/pkg/controller/elasticsearch/services/services_test.go b/pkg/controller/elasticsearch/services/services_test.go index 12974abb35..ccbde26fc2 100644 --- a/pkg/controller/elasticsearch/services/services_test.go +++ b/pkg/controller/elasticsearch/services/services_test.go @@ -5,6 +5,7 @@ package services import ( + "errors" "testing" "github.com/go-test/deep" @@ -12,12 +13,15 @@ import ( "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" commonv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/common/v1" esv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/elasticsearch/v1" + "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/client" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/label" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/network" "github.com/elastic/cloud-on-k8s/v2/pkg/utils/compare" + "github.com/elastic/cloud-on-k8s/v2/pkg/utils/k8s" ) func TestExternalServiceURL(t *testing.T) { @@ -58,107 +62,6 @@ func TestExternalServiceURL(t *testing.T) { } } -func TestElasticsearchURL(t *testing.T) { - type args struct { - es esv1.Elasticsearch - pods []corev1.Pod - } - tests := []struct { - name string - args args - want string - }{ - { - name: "default: external service url", - args: args{ - es: esv1.Elasticsearch{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-cluster", - Namespace: "my-ns", - }, - }, - pods: []corev1.Pod{ - { - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - label.HTTPSchemeLabelName: "https", - }, - }, - }, - }, - }, - want: "https://my-cluster-es-internal-http.my-ns.svc:9200", - }, - { - name: "scheme change in progress: random pod address", - args: args{ - es: esv1.Elasticsearch{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-cluster", - Namespace: "my-ns", - }, - }, - pods: []corev1.Pod{ - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "my-ns", - Name: "my-sset-0", - Labels: map[string]string{ - label.HTTPSchemeLabelName: "http", - label.StatefulSetNameLabelName: "my-sset", - }, - }, - }, - }, - }, - want: "http://my-sset-0.my-sset.my-ns:9200", - }, - { - name: "unexpected: missing pod labels: fallback to service", - args: args{ - es: esv1.Elasticsearch{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-cluster", - Namespace: "my-ns", - }, - }, - pods: []corev1.Pod{ - {}, - }, - }, - want: "https://my-cluster-es-internal-http.my-ns.svc:9200", - }, - { - name: "unexpected: partially missing pod labels: fallback to service", - args: args{ - es: esv1.Elasticsearch{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-cluster", - Namespace: "my-ns", - }, - }, - pods: []corev1.Pod{ - { - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - label.HTTPSchemeLabelName: "http", - }, - }, - }, - }, - }, - want: "https://my-cluster-es-internal-http.my-ns.svc:9200", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := ElasticsearchURL(tt.args.es, tt.args.pods); got != tt.want { - t.Errorf("ElasticsearchURL() = %v, want %v", got, tt.want) - } - }) - } -} - func TestNewExternalService(t *testing.T) { testCases := []struct { name string @@ -376,3 +279,236 @@ func TestNewTransportService(t *testing.T) { }) } } + +func Test_urlProvider_PodURL(t *testing.T) { + type fields struct { + pods func() ([]corev1.Pod, error) + svcURL string + } + tests := []struct { + name string + fields fields + want []string + wantErr bool + }{ + { + name: "fetch failure", + fields: fields{ + pods: func() ([]corev1.Pod, error) { + return nil, errors.New("failed to fetch pods") + }, + }, + wantErr: true, + }, + { + name: "no pods or error fetching pods: fall back to svc url", + fields: fields{ + pods: func() ([]corev1.Pod, error) { + return nil, nil + }, + svcURL: "svc.url", + }, + want: []string{"svc.url"}, + }, + { + name: "ready and running pods: prefer ready", + fields: fields{ + pods: func() ([]corev1.Pod, error) { + return []corev1.Pod{ + // name running ready + mkPod("sset-0", true, true), + mkPod("sset-1", true, false), + mkPod("sset-2", true, true), + mkPod("sset-3", false, false), + }, nil + }, + }, + want: []string{"http://sset-0.sset.test:9200", "http://sset-2.sset.test:9200"}, + }, + { + name: "only running pods: allow running ", + fields: fields{ + pods: func() ([]corev1.Pod, error) { + return []corev1.Pod{ + // name running ready + mkPod("sset-0", true, false), + mkPod("sset-1", true, false), + mkPod("sset-2", false, false), + }, nil + }, + }, + want: []string{"http://sset-0.sset.test:9200", "http://sset-1.sset.test:9200"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + u := &urlProvider{ + pods: tt.fields.pods, + svcURL: tt.fields.svcURL, + } + url, err := u.URL() + if (err != nil) != tt.wantErr { + t.Errorf("urlProvider.URL() error = %v, wantErr %v", err, tt.wantErr) + } + if err == nil { + require.Contains(t, tt.want, url, "must contain one of expected url") + } + }) + } +} + +func mkPod(name string, running bool, ready bool) corev1.Pod { + phase := corev1.PodPending + if running { + phase = corev1.PodRunning + } + var conditions []corev1.PodCondition + if ready { + conditions = append(conditions, + corev1.PodCondition{ + Type: corev1.PodReady, + Status: corev1.ConditionTrue, + }, corev1.PodCondition{ + Type: corev1.ContainersReady, + Status: corev1.ConditionTrue, + }, + ) + } + return corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: name, + Labels: map[string]string{ + label.HTTPSchemeLabelName: "http", + label.StatefulSetNameLabelName: "sset", + label.ClusterNameLabelName: "elasticsearch-test", + }, + }, + Status: corev1.PodStatus{ + Phase: phase, + Conditions: conditions, + }, + } +} + +func TestNewElasticsearchURLProvider(t *testing.T) { + type args struct { + es esv1.Elasticsearch + client k8s.Client + } + tests := []struct { + name string + args args + wantPodNames []string + wantErr bool + }{ + { + name: "cache failures are returned to the caller", + args: args{ + es: mkElasticsearch(commonv1.HTTPConfig{}), + client: k8s.NewFailingClient(errors.New("boom")), + }, + wantErr: true, + }, + { + name: "list pods from cache", + args: args{ + es: mkElasticsearch(commonv1.HTTPConfig{}), + client: k8s.NewFakeClient( + ptr.To(mkPod("sset-0", true, true)), + ptr.To(mkPod("sset-1", true, false)), + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test", + Name: "unrelated-0", + Labels: map[string]string{ + label.HTTPSchemeLabelName: "http", + label.StatefulSetNameLabelName: "unrelated", + label.ClusterNameLabelName: "unrelated", + }, + }, + }, + ), + }, + wantPodNames: []string{"sset-0", "sset-1"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + provider := NewElasticsearchURLProvider(tt.args.es, tt.args.client) + + providerImpl, ok := provider.(*urlProvider) + require.True(t, ok, "must be the urlProvider impl") + + got, err := providerImpl.pods() + if (err != nil) != tt.wantErr { + t.Errorf("NewElasticsearchURLProvider.URL() error = %v, wantErr %v", err, tt.wantErr) + return + } + require.ElementsMatch(t, k8s.PodNames(got), tt.wantPodNames) + }) + } +} + +func Test_urlProvider_Equals(t *testing.T) { + type fields struct { + svcURL string + } + type args struct { + other client.URLProvider + } + tests := []struct { + name string + fields fields + args args + want bool + }{ + { + name: "svc url is used as the identity", + fields: fields{ + svcURL: "http://elastic.co", + }, + args: args{ + other: &urlProvider{ + svcURL: "http://elastic.co", + }, + }, + want: true, + }, + { + name: "different impl with same URL is not equal", + fields: fields{ + svcURL: "http://k8s.io", + }, + args: args{ + other: client.NewStaticURLProvider("http://k8s.io"), + }, + want: false, + }, + { + name: "different URLs are not equal", + fields: fields{ + svcURL: "http://a.com", + }, + args: args{ + other: &urlProvider{ + svcURL: "http://b.com", + }, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + u := &urlProvider{ + pods: func() ([]corev1.Pod, error) { + return nil, nil + }, + svcURL: tt.fields.svcURL, + } + if got := u.Equals(tt.args.other); got != tt.want { + t.Errorf("urlProvider.Equals() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/controller/elasticsearch/settings/merged_config.go b/pkg/controller/elasticsearch/settings/merged_config.go index 788e28fdbb..3e0f2b9823 100644 --- a/pkg/controller/elasticsearch/settings/merged_config.go +++ b/pkg/controller/elasticsearch/settings/merged_config.go @@ -83,6 +83,10 @@ func baseConfig(clusterName string, ver version.Version, ipFamily corev1.IPFamil cfg[esv1.DiscoverySeedHosts] = []string{} } + if ver.GTE(esv1.MinReadinessPortVersion) { + cfg[esv1.ReadinessPort] = "8080" + } + return &CanonicalConfig{common.MustCanonicalConfig(cfg)} } diff --git a/pkg/controller/elasticsearch/settings/merged_config_test.go b/pkg/controller/elasticsearch/settings/merged_config_test.go index d443aa349b..848d504074 100644 --- a/pkg/controller/elasticsearch/settings/merged_config_test.go +++ b/pkg/controller/elasticsearch/settings/merged_config_test.go @@ -181,6 +181,24 @@ func TestNewMergedESConfig(t *testing.T) { require.Equal(t, 1, len(cfg.HasKeys([]string{esv1.XPackLicenseUploadTypes}))) }, }, + { + name: "prior to 8.2.0 we should not enable the readiness.port", + version: "8.1.0", + ipFamily: corev1.IPv4Protocol, + cfgData: map[string]interface{}{}, + assert: func(cfg CanonicalConfig) { + require.Equal(t, 0, len(cfg.HasKeys([]string{esv1.ReadinessPort}))) + }, + }, + { + name: "starting 8.2.0 we should enable the readiness.port", + version: "8.2.0", + ipFamily: corev1.IPv4Protocol, + cfgData: map[string]interface{}{}, + assert: func(cfg CanonicalConfig) { + require.Equal(t, 1, len(cfg.HasKeys([]string{esv1.ReadinessPort}))) + }, + }, { name: "user-provided Elasticsearch config overrides should have precedence over ECK config", version: "7.6.0", diff --git a/pkg/utils/k8s/k8sutils.go b/pkg/utils/k8s/k8sutils.go index cb2d484a3c..a68c16d769 100644 --- a/pkg/utils/k8s/k8sutils.go +++ b/pkg/utils/k8s/k8sutils.go @@ -74,6 +74,11 @@ func IsPodReady(pod corev1.Pod) bool { return conditionsTrue == 2 } +// IsPodRunning returns true if the Pod is in phase running and not terminating. +func IsPodRunning(pod corev1.Pod) bool { + return pod.DeletionTimestamp.IsZero() && pod.Status.Phase == corev1.PodRunning +} + // TerminatingPods filters pods for Pods that are in the process of (graceful) termination. func TerminatingPods(pods []corev1.Pod) []corev1.Pod { var terminating []corev1.Pod //nolint:prealloc @@ -86,6 +91,17 @@ func TerminatingPods(pods []corev1.Pod) []corev1.Pod { return terminating } +// RunningPods filters pods for Pods that are running (and not terminating). +func RunningPods(pods []corev1.Pod) []corev1.Pod { + var running []corev1.Pod + for _, p := range pods { + if IsPodRunning(p) { + running = append(running, p) + } + } + return running +} + // PodsByName returns a map of pod names to pods func PodsByName(pods []corev1.Pod) map[string]corev1.Pod { podMap := make(map[string]corev1.Pod, len(pods)) diff --git a/pkg/utils/log/log.go b/pkg/utils/log/log.go index cf7748715a..6477df3b52 100644 --- a/pkg/utils/log/log.go +++ b/pkg/utils/log/log.go @@ -163,6 +163,10 @@ var loggerCtxKey = ctxKey{} // Returns a context containing the newly created logger. func InitInContext(ctx context.Context, loggerName string, keysAndValues ...interface{}) context.Context { logger := NewFromContext(ctx).WithName(loggerName).WithValues(keysAndValues...) + return AddToContext(ctx, logger) +} + +func AddToContext(ctx context.Context, logger logr.Logger) context.Context { return context.WithValue(ctx, loggerCtxKey, logger) } diff --git a/test/e2e/es/forced_upgrade_test.go b/test/e2e/es/forced_upgrade_test.go index ccb40e6531..6ab0290872 100644 --- a/test/e2e/es/forced_upgrade_test.go +++ b/test/e2e/es/forced_upgrade_test.go @@ -12,6 +12,7 @@ import ( "testing" esv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/elasticsearch/v1" + "github.com/elastic/cloud-on-k8s/v2/pkg/utils/k8s" corev1 "k8s.io/api/core/v1" @@ -96,14 +97,14 @@ func TestForceUpgradePendingPodsInOneStatefulSet(t *testing.T) { }), }, { - Name: "Wait for the ES service to have endpoints and become technically reachable", + Name: "Wait for at least one ES pod to become technically reachable", Test: test.Eventually(func() error { - endpoints, err := k.GetEndpoints(initial.Elasticsearch.Namespace, esv1.HTTPService(initial.Elasticsearch.Name)) + pods, err := k.GetPods(test.ESPodListOptions(initial.Elasticsearch.Namespace, initial.Elasticsearch.Name)...) if err != nil { return err } - if len(endpoints.Subsets) == 0 || len(endpoints.Subsets[0].Addresses) == 0 { - return errors.New("elasticsearch HTTP service does not have endpoint") + if len(k8s.RunningPods(pods)) == 0 { + return errors.New("Elasticsearch does not have any running Pods") } return nil }), diff --git a/test/e2e/test/elasticsearch/checks_http.go b/test/e2e/test/elasticsearch/checks_http.go index 95d2b65a21..de6722702a 100644 --- a/test/e2e/test/elasticsearch/checks_http.go +++ b/test/e2e/test/elasticsearch/checks_http.go @@ -46,7 +46,7 @@ func CheckHTTPConnectivityWithCA(es esv1.Elasticsearch, k *test.K8sClient, caCer esClient := client.NewElasticsearchClient( dialer, k8s.ExtractNamespacedName(&es), - url, + client.NewStaticURLProvider(url), user, v, caCert, diff --git a/test/e2e/test/elasticsearch/http_client.go b/test/e2e/test/elasticsearch/http_client.go index 4b3e01d0cb..aee8674dca 100644 --- a/test/e2e/test/elasticsearch/http_client.go +++ b/test/e2e/test/elasticsearch/http_client.go @@ -12,9 +12,7 @@ import ( esv1 "github.com/elastic/cloud-on-k8s/v2/pkg/apis/elasticsearch/v1" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/version" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/client" - "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/reconcile" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/services" - "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/sset" esuser "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/user" "github.com/elastic/cloud-on-k8s/v2/pkg/dev/portforward" "github.com/elastic/cloud-on-k8s/v2/pkg/utils/k8s" @@ -68,11 +66,6 @@ func NewElasticsearchClientWithUser(es esv1.Elasticsearch, k *test.K8sClient, us if err != nil { return nil, err } - pods, err := sset.GetActualPodsForCluster(k.Client, es) - if err != nil { - return nil, err - } - inClusterURL := services.ElasticsearchURL(es, reconcile.AvailableElasticsearchNodes(pods)) var dialer net.Dialer if test.Ctx().AutoPortForwarding { dialer = portforward.NewForwardingDialer() @@ -84,7 +77,7 @@ func NewElasticsearchClientWithUser(es esv1.Elasticsearch, k *test.K8sClient, us esClient := client.NewElasticsearchClient( dialer, k8s.ExtractNamespacedName(&es), - inClusterURL, + services.NewElasticsearchURLProvider(es, k.Client), user, v, caCert,