From c11393912777f499576e80241a536d0e571ede84 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Fri, 24 May 2024 10:56:08 +0200 Subject: [PATCH 01/23] Use new readiness.port as of 8.2.0 --- pkg/apis/elasticsearch/v1/fields.go | 8 +++++++ .../elasticsearch/configmap/configmap.go | 11 +++++----- .../elasticsearch/nodespec/podspec.go | 2 +- .../elasticsearch/nodespec/podspec_test.go | 2 +- .../elasticsearch/nodespec/readiness_probe.go | 22 +++++++++++++++++-- .../elasticsearch/settings/merged_config.go | 4 ++++ .../settings/merged_config_test.go | 18 +++++++++++++++ 7 files changed, 58 insertions(+), 9 deletions(-) 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/elasticsearch/configmap/configmap.go b/pkg/controller/elasticsearch/configmap/configmap.go index 792bcca04c..8eec52f0c8 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.ReadinessProbeScriptConfigKey: nodespec.ReadinessProbeScript, + 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/nodespec/podspec.go b/pkg/controller/elasticsearch/nodespec/podspec.go index 0a4b3d5599..8f830b4af4 100644 --- a/pkg/controller/elasticsearch/nodespec/podspec.go +++ b/pkg/controller/elasticsearch/nodespec/podspec.go @@ -127,7 +127,7 @@ func BuildPodTemplateSpec( WithResources(DefaultResources). WithTerminationGracePeriod(DefaultTerminationGracePeriodSeconds). WithPorts(defaultContainerPorts). - WithReadinessProbe(*NewReadinessProbe()). + WithReadinessProbe(*NewReadinessProbe(v)). WithAffinity(DefaultAffinity(es.Name)). WithEnv(DefaultEnvVars(es.Spec.HTTP, headlessServiceName)...). WithVolumes(volumes...). diff --git a/pkg/controller/elasticsearch/nodespec/podspec_test.go b/pkg/controller/elasticsearch/nodespec/podspec_test.go index 07e8cbd63c..f569016bab 100644 --- a/pkg/controller/elasticsearch/nodespec/podspec_test.go +++ b/pkg/controller/elasticsearch/nodespec/podspec_test.go @@ -362,7 +362,7 @@ func TestBuildPodTemplateSpec(t *testing.T) { DefaultEnvVars(sampleES.Spec.HTTP, HeadlessServiceName(esv1.StatefulSet(sampleES.Name, nodeSet.Name)))...), Resources: DefaultResources, VolumeMounts: volumeMounts, - ReadinessProbe: NewReadinessProbe(), + ReadinessProbe: NewReadinessProbe(ver), Lifecycle: &corev1.Lifecycle{ PreStop: NewPreStopHook(), }, diff --git a/pkg/controller/elasticsearch/nodespec/readiness_probe.go b/pkg/controller/elasticsearch/nodespec/readiness_probe.go index c29e393399..e1f6d1e3c4 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 = ReadinessProbeScriptConfigKey + } + return &corev1.Probe{ FailureThreshold: 3, InitialDelaySeconds: 10, @@ -23,7 +41,7 @@ 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)}, }, }, } 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", From 151df5a0e49bab54903c5382f897cd5104212719 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Fri, 24 May 2024 11:17:14 +0200 Subject: [PATCH 02/23] Script indentation --- pkg/controller/elasticsearch/nodespec/readiness_probe.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/controller/elasticsearch/nodespec/readiness_probe.go b/pkg/controller/elasticsearch/nodespec/readiness_probe.go index e1f6d1e3c4..e65bfafe31 100644 --- a/pkg/controller/elasticsearch/nodespec/readiness_probe.go +++ b/pkg/controller/elasticsearch/nodespec/readiness_probe.go @@ -23,8 +23,8 @@ 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 - ` +nc -z -v -w5 127.0.0.1 8080 +` ) func NewReadinessProbe(v version.Version) *corev1.Probe { From 2652e0ff461e848453668e42d39bf98d3a6a7bc1 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Fri, 24 May 2024 15:31:02 +0200 Subject: [PATCH 03/23] Add unit test for buildPodTemplateSpec --- go.mod | 11 + go.sum | 25 +- .../nodespec/__snapshots__/podspec_test.snap | 1852 +++++++++++++++++ .../elasticsearch/nodespec/podspec_test.go | 208 +- 4 files changed, 1958 insertions(+), 138 deletions(-) create mode 100755 pkg/controller/elasticsearch/nodespec/__snapshots__/podspec_test.snap diff --git a/go.mod b/go.mod index 108f4c20ef..4c82d5ba10 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,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.1 github.com/go-test/deep v1.1.0 github.com/gobuffalo/flect v1.0.2 @@ -66,6 +67,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 @@ -92,7 +95,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.0 // 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 @@ -109,6 +115,7 @@ require ( github.com/pelletier/go-toml/v2 v2.1.0 // 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 @@ -118,6 +125,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 f33f6ebd98..1ab6fc15ce 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.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= @@ -164,6 +171,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= @@ -208,6 +217,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.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +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= @@ -225,8 +235,9 @@ github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+a github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= 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= @@ -271,6 +282,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/controller/elasticsearch/nodespec/__snapshots__/podspec_test.snap b/pkg/controller/elasticsearch/nodespec/__snapshots__/podspec_test.snap new file mode 100755 index 0000000000..d32cf47229 --- /dev/null +++ b/pkg/controller/elasticsearch/nodespec/__snapshots__/podspec_test.snap @@ -0,0 +1,1852 @@ + +[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": "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: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": "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: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": "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: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": "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: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/podspec_test.go b/pkg/controller/elasticsearch/nodespec/podspec_test.go index f569016bab..68dfda01cc 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,74 @@ 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(ver), - 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, " ", " ") + if err != nil { + panic(err) + } + snaps.MatchJSON(t, gotJSON) + }) + } } func Test_buildAnnotations(t *testing.T) { From b3b2fa775fe16b78e7be070e2874d7fe451825c9 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Fri, 24 May 2024 15:35:04 +0200 Subject: [PATCH 04/23] don't panic --- pkg/controller/elasticsearch/nodespec/podspec_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pkg/controller/elasticsearch/nodespec/podspec_test.go b/pkg/controller/elasticsearch/nodespec/podspec_test.go index 68dfda01cc..082e0a278a 100644 --- a/pkg/controller/elasticsearch/nodespec/podspec_test.go +++ b/pkg/controller/elasticsearch/nodespec/podspec_test.go @@ -311,9 +311,7 @@ func TestBuildPodTemplateSpec(t *testing.T) { // render as JSON for easier diff debugging gotJSON, err := json.MarshalIndent(&actual, " ", " ") - if err != nil { - panic(err) - } + require.NoError(t, err) snaps.MatchJSON(t, gotJSON) }) } From 43fb42a3dd9c5e24ab1e2ff68193e5bbeff9f587 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Fri, 24 May 2024 15:45:38 +0200 Subject: [PATCH 05/23] regen NOTICE and dep docs --- NOTICE.txt | 430 ++++++++++++++++++++++++++- docs/reference/dependencies.asciidoc | 10 +- 2 files changed, 436 insertions(+), 4 deletions(-) diff --git a/NOTICE.txt b/NOTICE.txt index 036531221c..6d8721a04f 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.1 @@ -7691,6 +7722,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 @@ -13529,6 +13621,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 @@ -15428,11 +15731,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. @@ -15922,6 +16225,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 308969783c..5c2abda074 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.1 | 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 @@ -93,6 +94,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 @@ -126,6 +129,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 @@ -147,7 +151,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 @@ -157,6 +161,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 From 653da513d17a7a3037496ba0ae56ccbe972b7642 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Fri, 24 May 2024 15:55:42 +0200 Subject: [PATCH 06/23] Remove unnecessary env vars --- .../nodespec/__snapshots__/podspec_test.snap | 80 ------------------- .../elasticsearch/nodespec/defaults.go | 14 ++-- .../elasticsearch/nodespec/podspec.go | 2 +- 3 files changed, 10 insertions(+), 86 deletions(-) diff --git a/pkg/controller/elasticsearch/nodespec/__snapshots__/podspec_test.snap b/pkg/controller/elasticsearch/nodespec/__snapshots__/podspec_test.snap index d32cf47229..81cf222d66 100755 --- a/pkg/controller/elasticsearch/nodespec/__snapshots__/podspec_test.snap +++ b/pkg/controller/elasticsearch/nodespec/__snapshots__/podspec_test.snap @@ -1084,26 +1084,6 @@ "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:8.13.2", @@ -1285,26 +1265,6 @@ { "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:8.13.2", @@ -1450,26 +1410,6 @@ { "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:8.13.2", @@ -1608,26 +1548,6 @@ { "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:8.13.2", diff --git a/pkg/controller/elasticsearch/nodespec/defaults.go b/pkg/controller/elasticsearch/nodespec/defaults.go index 3ffd511190..f6667d88d7 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,10 @@ 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 { + var vars []corev1.EnvVar + 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 +65,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 8f830b4af4..97626bc496 100644 --- a/pkg/controller/elasticsearch/nodespec/podspec.go +++ b/pkg/controller/elasticsearch/nodespec/podspec.go @@ -129,7 +129,7 @@ func BuildPodTemplateSpec( WithPorts(defaultContainerPorts). 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...). From 147353db8c77113ed103a0b523bf44f9715d01f7 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Fri, 24 May 2024 17:22:15 +0200 Subject: [PATCH 07/23] Restore HEADLESS_SERVICE_NAME var --- .../nodespec/__snapshots__/podspec_test.snap | 16 ++++++++++++++++ .../elasticsearch/nodespec/defaults.go | 5 ++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/pkg/controller/elasticsearch/nodespec/__snapshots__/podspec_test.snap b/pkg/controller/elasticsearch/nodespec/__snapshots__/podspec_test.snap index 81cf222d66..176f142292 100755 --- a/pkg/controller/elasticsearch/nodespec/__snapshots__/podspec_test.snap +++ b/pkg/controller/elasticsearch/nodespec/__snapshots__/podspec_test.snap @@ -1084,6 +1084,10 @@ "fieldPath": "metadata.namespace" } } + }, + { + "name": "HEADLESS_SERVICE_NAME", + "value": "name-es-nodeset-1" } ], "image": "docker.elastic.co/elasticsearch/elasticsearch:8.13.2", @@ -1265,6 +1269,10 @@ { "name": "my-env", "value": "my-value" + }, + { + "name": "HEADLESS_SERVICE_NAME", + "value": "name-es-nodeset-1" } ], "image": "docker.elastic.co/elasticsearch/elasticsearch:8.13.2", @@ -1410,6 +1418,10 @@ { "name": "my-env", "value": "my-value" + }, + { + "name": "HEADLESS_SERVICE_NAME", + "value": "name-es-nodeset-1" } ], "image": "docker.elastic.co/elasticsearch/elasticsearch:8.13.2", @@ -1548,6 +1560,10 @@ { "name": "my-env", "value": "my-value" + }, + { + "name": "HEADLESS_SERVICE_NAME", + "value": "name-es-nodeset-1" } ], "image": "docker.elastic.co/elasticsearch/elasticsearch:8.13.2", diff --git a/pkg/controller/elasticsearch/nodespec/defaults.go b/pkg/controller/elasticsearch/nodespec/defaults.go index f6667d88d7..a21c8ad4a5 100644 --- a/pkg/controller/elasticsearch/nodespec/defaults.go +++ b/pkg/controller/elasticsearch/nodespec/defaults.go @@ -45,7 +45,10 @@ var ( // DefaultEnvVars are environment variables injected into Elasticsearch pods. func DefaultEnvVars(v version.Version, httpCfg commonv1.HTTPConfig, headlessServiceName string) []corev1.EnvVar { - var vars []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)}, From 23589474ba130ae6ade23a02abe70c7fbcc1d786 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Sat, 25 May 2024 17:21:00 +0200 Subject: [PATCH 08/23] Experiment: list unready pods in internal service --- pkg/controller/elasticsearch/services/services.go | 2 +- pkg/controller/elasticsearch/services/services_test.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/controller/elasticsearch/services/services.go b/pkg/controller/elasticsearch/services/services.go index 86652fbce6..bd5abf9ac3 100644 --- a/pkg/controller/elasticsearch/services/services.go +++ b/pkg/controller/elasticsearch/services/services.go @@ -135,7 +135,7 @@ func NewInternalService(es esv1.Elasticsearch) *corev1.Service { }, }, Selector: label.NewLabels(k8s.ExtractNamespacedName(&es)), - PublishNotReadyAddresses: false, + PublishNotReadyAddresses: true, }, } } diff --git a/pkg/controller/elasticsearch/services/services_test.go b/pkg/controller/elasticsearch/services/services_test.go index 12974abb35..9391617db2 100644 --- a/pkg/controller/elasticsearch/services/services_test.go +++ b/pkg/controller/elasticsearch/services/services_test.go @@ -243,6 +243,7 @@ func TestNewInternalService(t *testing.T) { svc.Spec.Type = corev1.ServiceTypeClusterIP svc.Spec.Ports[0].Name = "https" svc.Name = "elasticsearch-test-es-internal-http" + svc.Spec.PublishNotReadyAddresses = true return svc }, }, From 09a6fc4a891f2a781f68057b3e86c67a272ffd48 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Mon, 27 May 2024 11:07:07 +0200 Subject: [PATCH 09/23] Experiment: adjust e2e test to use internal svc --- test/e2e/es/forced_upgrade_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/es/forced_upgrade_test.go b/test/e2e/es/forced_upgrade_test.go index ccb40e6531..f9a771c080 100644 --- a/test/e2e/es/forced_upgrade_test.go +++ b/test/e2e/es/forced_upgrade_test.go @@ -98,7 +98,7 @@ func TestForceUpgradePendingPodsInOneStatefulSet(t *testing.T) { { Name: "Wait for the ES service to have endpoints and become technically reachable", Test: test.Eventually(func() error { - endpoints, err := k.GetEndpoints(initial.Elasticsearch.Namespace, esv1.HTTPService(initial.Elasticsearch.Name)) + endpoints, err := k.GetEndpoints(initial.Elasticsearch.Namespace, esv1.InternalHTTPService(initial.Elasticsearch.Name)) if err != nil { return err } From 130ae8dba8fc8a6a4d839f9be403d391e6cc333b Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Tue, 28 May 2024 20:22:42 +0200 Subject: [PATCH 10/23] Experiment 2: url provider --- pkg/controller/common/esclient/esclient.go | 10 +++- pkg/controller/elasticsearch/client/base.go | 26 +++++----- pkg/controller/elasticsearch/client/client.go | 16 +++---- .../elasticsearch/client/client_test.go | 9 ++-- pkg/controller/elasticsearch/client/mock.go | 4 +- pkg/controller/elasticsearch/client/url.go | 30 ++++++++++++ pkg/controller/elasticsearch/client/v6.go | 2 +- pkg/controller/elasticsearch/driver/driver.go | 3 +- .../elasticsearch/services/services.go | 48 ++++++++++++++++++- pkg/utils/k8s/k8sutils.go | 10 ++++ test/e2e/test/elasticsearch/checks_http.go | 2 +- test/e2e/test/elasticsearch/http_client.go | 4 +- 12 files changed, 124 insertions(+), 40 deletions(-) create mode 100644 pkg/controller/elasticsearch/client/url.go diff --git a/pkg/controller/common/esclient/esclient.go b/pkg/controller/common/esclient/esclient.go index e9af94cda0..3e0750cb76 100644 --- a/pkg/controller/common/esclient/esclient.go +++ b/pkg/controller/common/esclient/esclient.go @@ -16,6 +16,7 @@ import ( "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/tracing" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/version" esclient "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/services" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/user" "github.com/elastic/cloud-on-k8s/v2/pkg/dev" @@ -32,7 +33,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 +68,16 @@ func NewClient( if err != nil { return nil, err } + + // Get all running pods from the cache + allPods, err := k8s.PodsMatchingLabels(c, es.Namespace, label.NewLabelSelectorForElasticsearch(es)) + if err != nil { + return nil, err + } return esclient.NewElasticsearchClient( dialer, k8s.ExtractNamespacedName(&es), - url, + services.ElasticsearchURLProvider(es, k8s.RunningPods(allPods)), 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..e8362abf3a 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.ServiceURL() == c2.URLProvider.ServiceURL() && c.User == c2.User } @@ -128,7 +128,7 @@ func (c *baseClient) request( body = bytes.NewBuffer(outData) } - request, err := http.NewRequest(method, stringsutil.Concat(c.Endpoint, pathWithQuery), body) //nolint:noctx + request, err := http.NewRequest(method, stringsutil.Concat(c.URLProvider.PodURL(), pathWithQuery), body) //nolint:noctx if err != nil { return err } @@ -188,10 +188,6 @@ 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 { if len(c.caCerts) != len(caCerts) { return false @@ -201,5 +197,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.ServiceURL() == url } diff --git a/pkg/controller/elasticsearch/client/client.go b/pkg/controller/elasticsearch/client/client.go index 6f40c9a524..3d08d99766 100644 --- a/pkg/controller/elasticsearch/client/client.go +++ b/pkg/controller/elasticsearch/client/client.go @@ -127,8 +127,6 @@ 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 } @@ -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..48e634d71d 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, }, { @@ -836,11 +836,12 @@ func Test_HasProperties(t *testing.T) { defaultVersion := version.MustParse("8.6.1") defaultUser := BasicAuth{Name: "foo", Password: "bar"} defaultURL := "https://foo.bar" + defaultURLProvider := NewStaticURLProvider(defaultURL) defaultCaCerts := []*x509.Certificate{{Raw: []byte("foo")}} defaultEsClient := NewElasticsearchClient( nil, types.NamespacedName{Namespace: "ns", Name: "es"}, - defaultURL, + defaultURLProvider, defaultUser, defaultVersion, defaultCaCerts, 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..fd938fd89a --- /dev/null +++ b/pkg/controller/elasticsearch/client/url.go @@ -0,0 +1,30 @@ +package client + +type URLProvider interface { + // PodURL is a url for a random pod (falls back to ServiceURL). + PodURL() string + // ServiceURL is the url for the Kubernetes service related to the Pod URLs provided. + ServiceURL() string +} + +func NewStaticURLProvider(url string) URLProvider { + return &staticURLProvider{ + url: url, + } +} + +type staticURLProvider struct { + url string +} + +// PodURL implements URLProvider. +func (s *staticURLProvider) PodURL() string { + return s.url +} + +// ServiceURL implements URLProvider. +func (s *staticURLProvider) ServiceURL() string { + return s.url +} + +var _ URLProvider = &staticURLProvider{} diff --git a/pkg/controller/elasticsearch/client/v6.go b/pkg/controller/elasticsearch/client/v6.go index 377f87f4b3..f99607750b 100644 --- a/pkg/controller/elasticsearch/client/v6.go +++ b/pkg/controller/elasticsearch/client/v6.go @@ -216,7 +216,7 @@ 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())) + newURL, err := url.Parse(stringsutil.Concat(c.URLProvider.PodURL(), r.URL.String())) if err != nil { return nil, err } diff --git a/pkg/controller/elasticsearch/driver/driver.go b/pkg/controller/elasticsearch/driver/driver.go index d8b3a872de..a0d7b8df4e 100644 --- a/pkg/controller/elasticsearch/driver/driver.go +++ b/pkg/controller/elasticsearch/driver/driver.go @@ -383,11 +383,10 @@ func (d *defaultDriver) newElasticsearchClient( 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, + services.ElasticsearchURLProvider(d.ES, state.CurrentPodsByPhase[corev1.PodRunning]), user, v, caCerts, diff --git a/pkg/controller/elasticsearch/services/services.go b/pkg/controller/elasticsearch/services/services.go index bd5abf9ac3..338943668e 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,6 +182,50 @@ func getServiceByName(c k8s.Client, es esv1.Elasticsearch, serviceName string) ( return svc, nil } +type urlProvider struct { + readyPods []corev1.Pod + runningPods []corev1.Pod + svcURL string +} + +// PodURL implements client.URLProvider. +func (u *urlProvider) PodURL() string { + var url string + if len(u.readyPods) > 0 { + url = randomESPodURL(u.readyPods) + } else if len(u.runningPods) > 0 { + url = randomESPodURL(u.runningPods) + } + if len(url) == 0 { + url = u.ServiceURL() + } + return url +} + +// ServiceURL implements client.URLProvider. +func (u *urlProvider) ServiceURL() string { + return u.svcURL +} + +func ElasticsearchURLProvider(es esv1.Elasticsearch, pods []corev1.Pod) client.URLProvider { + var readyPods []corev1.Pod + for _, p := range pods { + if k8s.IsPodReady(p) { + readyPods = append(readyPods, p) + } + } + return &urlProvider{ + readyPods: readyPods, + runningPods: pods, + svcURL: InternalServiceURL(es), + } +} + +func randomESPodURL(pods []corev1.Pod) string { + randomPod := pods[rand.Intn(len(pods))] //nolint:gosec + return ElasticsearchPodURL(randomPod) +} + // 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. @@ -195,8 +240,7 @@ func ElasticsearchURL(es esv1.Elasticsearch, pods []corev1.Pod) string { } 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 != "" { + if podURL := randomESPodURL(pods); podURL != "" { return podURL } } diff --git a/pkg/utils/k8s/k8sutils.go b/pkg/utils/k8s/k8sutils.go index cb2d484a3c..83a9f72e5f 100644 --- a/pkg/utils/k8s/k8sutils.go +++ b/pkg/utils/k8s/k8sutils.go @@ -86,6 +86,16 @@ func TerminatingPods(pods []corev1.Pod) []corev1.Pod { return terminating } +func RunningPods(pods []corev1.Pod) []corev1.Pod { + var running []corev1.Pod //nolint:prealloc + for _, p := range pods { + if p.DeletionTimestamp.IsZero() && p.Status.Phase == corev1.PodRunning { + 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/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..c70de8a999 100644 --- a/test/e2e/test/elasticsearch/http_client.go +++ b/test/e2e/test/elasticsearch/http_client.go @@ -72,7 +72,7 @@ func NewElasticsearchClientWithUser(es esv1.Elasticsearch, k *test.K8sClient, us if err != nil { return nil, err } - inClusterURL := services.ElasticsearchURL(es, reconcile.AvailableElasticsearchNodes(pods)) + inClusterURLs := services.ElasticsearchURLProvider(es, reconcile.AvailableElasticsearchNodes(pods)) var dialer net.Dialer if test.Ctx().AutoPortForwarding { dialer = portforward.NewForwardingDialer() @@ -84,7 +84,7 @@ func NewElasticsearchClientWithUser(es esv1.Elasticsearch, k *test.K8sClient, us esClient := client.NewElasticsearchClient( dialer, k8s.ExtractNamespacedName(&es), - inClusterURL, + inClusterURLs, user, v, caCert, From 05adf9851134c59aa52fe8b5d12ebf65e286907c Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Tue, 28 May 2024 20:24:19 +0200 Subject: [PATCH 11/23] Revert "Experiment: list unready pods in internal service" This reverts commit 23589474ba130ae6ade23a02abe70c7fbcc1d786. --- pkg/controller/elasticsearch/services/services.go | 2 +- pkg/controller/elasticsearch/services/services_test.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/controller/elasticsearch/services/services.go b/pkg/controller/elasticsearch/services/services.go index 338943668e..af5ff56e2a 100644 --- a/pkg/controller/elasticsearch/services/services.go +++ b/pkg/controller/elasticsearch/services/services.go @@ -136,7 +136,7 @@ func NewInternalService(es esv1.Elasticsearch) *corev1.Service { }, }, Selector: label.NewLabels(k8s.ExtractNamespacedName(&es)), - PublishNotReadyAddresses: true, + PublishNotReadyAddresses: false, }, } } diff --git a/pkg/controller/elasticsearch/services/services_test.go b/pkg/controller/elasticsearch/services/services_test.go index 9391617db2..12974abb35 100644 --- a/pkg/controller/elasticsearch/services/services_test.go +++ b/pkg/controller/elasticsearch/services/services_test.go @@ -243,7 +243,6 @@ func TestNewInternalService(t *testing.T) { svc.Spec.Type = corev1.ServiceTypeClusterIP svc.Spec.Ports[0].Name = "https" svc.Name = "elasticsearch-test-es-internal-http" - svc.Spec.PublishNotReadyAddresses = true return svc }, }, From 5e1a4f4756de0b4a6e438fbdbef22fdf5c0a3557 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Tue, 28 May 2024 20:24:48 +0200 Subject: [PATCH 12/23] Revert "Experiment: adjust e2e test to use internal svc" This reverts commit 09a6fc4a891f2a781f68057b3e86c67a272ffd48. --- test/e2e/es/forced_upgrade_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/es/forced_upgrade_test.go b/test/e2e/es/forced_upgrade_test.go index f9a771c080..ccb40e6531 100644 --- a/test/e2e/es/forced_upgrade_test.go +++ b/test/e2e/es/forced_upgrade_test.go @@ -98,7 +98,7 @@ func TestForceUpgradePendingPodsInOneStatefulSet(t *testing.T) { { Name: "Wait for the ES service to have endpoints and become technically reachable", Test: test.Eventually(func() error { - endpoints, err := k.GetEndpoints(initial.Elasticsearch.Namespace, esv1.InternalHTTPService(initial.Elasticsearch.Name)) + endpoints, err := k.GetEndpoints(initial.Elasticsearch.Namespace, esv1.HTTPService(initial.Elasticsearch.Name)) if err != nil { return err } From dfac339847a8bbf01fbd50c0b821c720fd845948 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Tue, 28 May 2024 20:28:23 +0200 Subject: [PATCH 13/23] Adjust force-upgrade test to new behaviour --- test/e2e/es/forced_upgrade_test.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/e2e/es/forced_upgrade_test.go b/test/e2e/es/forced_upgrade_test.go index ccb40e6531..786eba9b19 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,15 @@ 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") + running := k8s.RunningPods(pods) + if len(running) == 0 { + return errors.New("Elasticsearch does not have any running Pods") } return nil }), From 66cde4f17ab99fbcabde4a568f6fe8b411a78b10 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Wed, 29 May 2024 18:08:44 +0200 Subject: [PATCH 14/23] Dynamically fetch pod list in url provider + tests --- pkg/controller/common/esclient/esclient.go | 8 +- pkg/controller/elasticsearch/client/url.go | 6 + pkg/controller/elasticsearch/driver/driver.go | 31 ++- .../elasticsearch/services/services.go | 48 +++-- .../elasticsearch/services/services_test.go | 193 +++++++++++++++++- pkg/utils/k8s/k8sutils.go | 4 + pkg/utils/log/log.go | 4 + test/e2e/test/elasticsearch/http_client.go | 9 +- 8 files changed, 248 insertions(+), 55 deletions(-) diff --git a/pkg/controller/common/esclient/esclient.go b/pkg/controller/common/esclient/esclient.go index 3e0750cb76..ece5fbe4b5 100644 --- a/pkg/controller/common/esclient/esclient.go +++ b/pkg/controller/common/esclient/esclient.go @@ -16,7 +16,6 @@ import ( "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/tracing" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/common/version" esclient "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/services" "github.com/elastic/cloud-on-k8s/v2/pkg/controller/elasticsearch/user" "github.com/elastic/cloud-on-k8s/v2/pkg/dev" @@ -69,15 +68,10 @@ func NewClient( return nil, err } - // Get all running pods from the cache - allPods, err := k8s.PodsMatchingLabels(c, es.Namespace, label.NewLabelSelectorForElasticsearch(es)) - if err != nil { - return nil, err - } return esclient.NewElasticsearchClient( dialer, k8s.ExtractNamespacedName(&es), - services.ElasticsearchURLProvider(es, k8s.RunningPods(allPods)), + services.NewElasticsearchURLProvider(ctx, es, c), esclient.BasicAuth{ Name: user.ControllerUserName, Password: string(password), diff --git a/pkg/controller/elasticsearch/client/url.go b/pkg/controller/elasticsearch/client/url.go index fd938fd89a..6e90fe7538 100644 --- a/pkg/controller/elasticsearch/client/url.go +++ b/pkg/controller/elasticsearch/client/url.go @@ -5,6 +5,8 @@ type URLProvider interface { PodURL() string // ServiceURL is the url for the Kubernetes service related to the Pod URLs provided. ServiceURL() string + + HasEndpoints() bool } func NewStaticURLProvider(url string) URLProvider { @@ -27,4 +29,8 @@ func (s *staticURLProvider) ServiceURL() string { return s.url } +func (s *staticURLProvider) HasEndpoints() bool { + return true +} + var _ URLProvider = &staticURLProvider{} diff --git a/pkg/controller/elasticsearch/driver/driver.go b/pkg/controller/elasticsearch/driver/driver.go index a0d7b8df4e..ea9fa14aa4 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(ctx, 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,7 +376,7 @@ 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, @@ -386,7 +384,7 @@ func (d *defaultDriver) newElasticsearchClient( return esclient.NewElasticsearchClient( d.OperatorParameters.Dialer, k8s.ExtractNamespacedName(&d.ES), - services.ElasticsearchURLProvider(d.ES, state.CurrentPodsByPhase[corev1.PodRunning]), + urlProvider, user, v, caCerts, @@ -397,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.ServiceURL(), 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/services/services.go b/pkg/controller/elasticsearch/services/services.go index af5ff56e2a..aec4f45d4f 100644 --- a/pkg/controller/elasticsearch/services/services.go +++ b/pkg/controller/elasticsearch/services/services.go @@ -20,6 +20,7 @@ import ( "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" + ulog "github.com/elastic/cloud-on-k8s/v2/pkg/utils/log" "github.com/elastic/cloud-on-k8s/v2/pkg/utils/stringsutil" ) @@ -183,18 +184,26 @@ func getServiceByName(c k8s.Client, es esv1.Elasticsearch, serviceName string) ( } type urlProvider struct { - readyPods []corev1.Pod - runningPods []corev1.Pod - svcURL string + pods func() []corev1.Pod + svcURL string } // PodURL implements client.URLProvider. func (u *urlProvider) PodURL() string { + var ready, running []corev1.Pod + for _, p := range u.pods() { + if k8s.IsPodReady(p) { + ready = append(ready, p) + } + if k8s.IsPodRunning(p) { + running = append(running, p) + } + } var url string - if len(u.readyPods) > 0 { - url = randomESPodURL(u.readyPods) - } else if len(u.runningPods) > 0 { - url = randomESPodURL(u.runningPods) + if len(ready) > 0 { + url = randomESPodURL(ready) + } else if len(running) > 0 { + url = randomESPodURL(running) } if len(url) == 0 { url = u.ServiceURL() @@ -207,17 +216,22 @@ func (u *urlProvider) ServiceURL() string { return u.svcURL } -func ElasticsearchURLProvider(es esv1.Elasticsearch, pods []corev1.Pod) client.URLProvider { - var readyPods []corev1.Pod - for _, p := range pods { - if k8s.IsPodReady(p) { - readyPods = append(readyPods, p) - } - } +// HasEndpoints implements client.URLProvider. +func (u *urlProvider) HasEndpoints() bool { + return len(k8s.RunningPods(u.pods())) > 0 +} + +func NewElasticsearchURLProvider(ctx context.Context, es esv1.Elasticsearch, client k8s.Client) client.URLProvider { return &urlProvider{ - readyPods: readyPods, - runningPods: pods, - svcURL: InternalServiceURL(es), + pods: func() []corev1.Pod { + log := ulog.FromContext(ctx) + pods, err := k8s.PodsMatchingLabels(client, es.Namespace, label.NewLabelSelectorForElasticsearch(es)) + if err != nil { + log.Error(err, "while fetching pods from cache in URL provider") + } + return pods + }, + svcURL: InternalServiceURL(es), } } diff --git a/pkg/controller/elasticsearch/services/services_test.go b/pkg/controller/elasticsearch/services/services_test.go index 12974abb35..0963b5a498 100644 --- a/pkg/controller/elasticsearch/services/services_test.go +++ b/pkg/controller/elasticsearch/services/services_test.go @@ -5,19 +5,24 @@ package services import ( + "context" + "errors" "testing" - "github.com/go-test/deep" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - 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/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" + ulog "github.com/elastic/cloud-on-k8s/v2/pkg/utils/log" + "github.com/go-logr/logr" + "github.com/go-test/deep" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" ) func TestExternalServiceURL(t *testing.T) { @@ -376,3 +381,179 @@ func TestNewTransportService(t *testing.T) { }) } } + +func Test_urlProvider_PodURL(t *testing.T) { + type fields struct { + pods func() []corev1.Pod + svcURL string + } + tests := []struct { + name string + fields fields + want []string + }{ + { + name: "no pods or error fetching pods: fall back to svc url", + fields: fields{ + pods: func() []corev1.Pod { + return nil + }, + svcURL: "svc.url", + }, + want: []string{"svc.url"}, + }, + { + name: "ready and running pods: prefer ready", + fields: fields{ + pods: func() []corev1.Pod { + return []corev1.Pod{ + // name running ready + mkPod("sset-0", true, true), + mkPod("sset-1", true, false), + mkPod("sset-2", true, true), + } + }, + }, + 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 { + return []corev1.Pod{ + // name running ready + mkPod("sset-0", true, false), + mkPod("sset-1", true, false)} + }, + }, + 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, + } + require.Contains(t, tt.want, u.PodURL(), "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, + }, + } +} + +type errorLogSink struct { + errorLogs []string +} + +// Enabled implements logr.LogSink. +func (l *errorLogSink) Enabled(level int) bool { + return true +} + +// Error implements logr.LogSink. +func (l *errorLogSink) Error(err error, msg string, keysAndValues ...any) { + l.errorLogs = append(l.errorLogs, msg) +} + +// Info implements logr.LogSink. +func (l *errorLogSink) Info(level int, msg string, keysAndValues ...any) { + // ignore +} + +// Init implements logr.LogSink. +func (l *errorLogSink) Init(info logr.RuntimeInfo) { + //noop +} + +// WithName implements logr.LogSink. +func (l *errorLogSink) WithName(name string) logr.LogSink { + return l +} + +// WithValues implements logr.LogSink. +func (l *errorLogSink) WithValues(keysAndValues ...any) logr.LogSink { + return l +} + +var _ logr.LogSink = &errorLogSink{} + +func TestNewElasticsearchURLProvider(t *testing.T) { + type args struct { + es esv1.Elasticsearch + client k8s.Client + } + tests := []struct { + name string + args args + wantPodNames []string + wantErr string + }{ + { + name: "cache failures are swallowed but logged", + args: args{ + es: mkElasticsearch(commonv1.HTTPConfig{}), + client: k8s.NewFailingClient(errors.New("boom")), + }, + wantErr: "while fetching pods from cache in URL provider", + }, + { + name: "list pods from cache", + args: args{ + es: mkElasticsearch(commonv1.HTTPConfig{}), + client: k8s.NewFakeClient(ptr.To(mkPod("sset-0", true, true))), + }, + wantPodNames: []string{"sset-0"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sink := errorLogSink{} + ctx := ulog.AddToContext(context.Background(), logr.New(&sink)) + provider := NewElasticsearchURLProvider(ctx, tt.args.es, tt.args.client) + + providerImpl, ok := provider.(*urlProvider) + require.True(t, ok, "must be the urlProvider impl") + + got := k8s.PodNames(providerImpl.pods()) + require.ElementsMatch(t, got, tt.wantPodNames) + if len(tt.wantErr) > 0 { + require.Contains(t, sink.errorLogs, tt.wantErr) + } else { + require.Empty(t, sink.errorLogs, "did not expect any error logs") + } + }) + } +} diff --git a/pkg/utils/k8s/k8sutils.go b/pkg/utils/k8s/k8sutils.go index 83a9f72e5f..84ddfe102a 100644 --- a/pkg/utils/k8s/k8sutils.go +++ b/pkg/utils/k8s/k8sutils.go @@ -74,6 +74,10 @@ func IsPodReady(pod corev1.Pod) bool { return conditionsTrue == 2 } +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 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/test/elasticsearch/http_client.go b/test/e2e/test/elasticsearch/http_client.go index c70de8a999..78c543c1a5 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 - } - inClusterURLs := services.ElasticsearchURLProvider(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), - inClusterURLs, + services.NewElasticsearchURLProvider(context.Background(), es, k.Client), user, v, caCert, From 71f81a439ac89c92be8584433d28189c1228ed0c Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Wed, 29 May 2024 18:16:20 +0200 Subject: [PATCH 15/23] missing license header --- pkg/controller/elasticsearch/client/url.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/controller/elasticsearch/client/url.go b/pkg/controller/elasticsearch/client/url.go index 6e90fe7538..cf46bb1277 100644 --- a/pkg/controller/elasticsearch/client/url.go +++ b/pkg/controller/elasticsearch/client/url.go @@ -1,3 +1,7 @@ +// 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 { From 0727a1be749bc8764906fc63d09f5a8daf44b5ff Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Wed, 29 May 2024 18:31:51 +0200 Subject: [PATCH 16/23] my local linter does not work --- .../elasticsearch/services/services_test.go | 17 +++++++++-------- pkg/utils/k8s/k8sutils.go | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/pkg/controller/elasticsearch/services/services_test.go b/pkg/controller/elasticsearch/services/services_test.go index 0963b5a498..7d868ba084 100644 --- a/pkg/controller/elasticsearch/services/services_test.go +++ b/pkg/controller/elasticsearch/services/services_test.go @@ -9,13 +9,6 @@ import ( "errors" "testing" - 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/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" - ulog "github.com/elastic/cloud-on-k8s/v2/pkg/utils/log" "github.com/go-logr/logr" "github.com/go-test/deep" "github.com/stretchr/testify/assert" @@ -23,6 +16,14 @@ import ( 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/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" + ulog "github.com/elastic/cloud-on-k8s/v2/pkg/utils/log" ) func TestExternalServiceURL(t *testing.T) { @@ -495,7 +496,7 @@ func (l *errorLogSink) Info(level int, msg string, keysAndValues ...any) { // Init implements logr.LogSink. func (l *errorLogSink) Init(info logr.RuntimeInfo) { - //noop + // noop } // WithName implements logr.LogSink. diff --git a/pkg/utils/k8s/k8sutils.go b/pkg/utils/k8s/k8sutils.go index 84ddfe102a..54b486f54e 100644 --- a/pkg/utils/k8s/k8sutils.go +++ b/pkg/utils/k8s/k8sutils.go @@ -91,7 +91,7 @@ func TerminatingPods(pods []corev1.Pod) []corev1.Pod { } func RunningPods(pods []corev1.Pod) []corev1.Pod { - var running []corev1.Pod //nolint:prealloc + var running []corev1.Pod for _, p := range pods { if p.DeletionTimestamp.IsZero() && p.Status.Phase == corev1.PodRunning { running = append(running, p) From f954193eac72cb4bd9fe850bd0044d7b38255e08 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Wed, 29 May 2024 21:43:58 +0200 Subject: [PATCH 17/23] cleanup --- .../elasticsearch/services/services.go | 21 ---- .../elasticsearch/services/services_test.go | 106 +----------------- pkg/utils/k8s/k8sutils.go | 2 +- 3 files changed, 5 insertions(+), 124 deletions(-) diff --git a/pkg/controller/elasticsearch/services/services.go b/pkg/controller/elasticsearch/services/services.go index aec4f45d4f..efd42a50c3 100644 --- a/pkg/controller/elasticsearch/services/services.go +++ b/pkg/controller/elasticsearch/services/services.go @@ -240,27 +240,6 @@ func randomESPodURL(pods []corev1.Pod) string { return ElasticsearchPodURL(randomPod) } -// 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 - 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 schemeChange { - // switch to sending requests directly to a random pod instead of going through the service - if podURL := randomESPodURL(pods); podURL != "" { - return podURL - } - } - return InternalServiceURL(es) -} - // ElasticsearchPodURL calculates the URL for the given Pod based on the Pods metadata. func ElasticsearchPodURL(pod corev1.Pod) string { scheme, hasSchemeLabel := pod.Labels[label.HTTPSchemeLabelName] diff --git a/pkg/controller/elasticsearch/services/services_test.go b/pkg/controller/elasticsearch/services/services_test.go index 7d868ba084..d6ed7ad33c 100644 --- a/pkg/controller/elasticsearch/services/services_test.go +++ b/pkg/controller/elasticsearch/services/services_test.go @@ -64,107 +64,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 @@ -412,6 +311,7 @@ func Test_urlProvider_PodURL(t *testing.T) { mkPod("sset-0", true, true), mkPod("sset-1", true, false), mkPod("sset-2", true, true), + mkPod("sset-3", false, false), } }, }, @@ -424,7 +324,9 @@ func Test_urlProvider_PodURL(t *testing.T) { return []corev1.Pod{ // name running ready mkPod("sset-0", true, false), - mkPod("sset-1", true, false)} + mkPod("sset-1", true, false), + mkPod("sset-2", false, false), + } }, }, want: []string{"http://sset-0.sset.test:9200", "http://sset-1.sset.test:9200"}, diff --git a/pkg/utils/k8s/k8sutils.go b/pkg/utils/k8s/k8sutils.go index 54b486f54e..42e82c4a05 100644 --- a/pkg/utils/k8s/k8sutils.go +++ b/pkg/utils/k8s/k8sutils.go @@ -93,7 +93,7 @@ func TerminatingPods(pods []corev1.Pod) []corev1.Pod { func RunningPods(pods []corev1.Pod) []corev1.Pod { var running []corev1.Pod for _, p := range pods { - if p.DeletionTimestamp.IsZero() && p.Status.Phase == corev1.PodRunning { + if IsPodRunning(p) { running = append(running, p) } } From 8c84518bafffc07937eaf1ed4a193910830c38d9 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Wed, 29 May 2024 22:44:16 +0200 Subject: [PATCH 18/23] tweaks to urlprovider --- .../elasticsearch/services/services.go | 16 +++++++-------- .../elasticsearch/services/services_test.go | 20 ++++++++++++++++--- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/pkg/controller/elasticsearch/services/services.go b/pkg/controller/elasticsearch/services/services.go index efd42a50c3..dfe61f208f 100644 --- a/pkg/controller/elasticsearch/services/services.go +++ b/pkg/controller/elasticsearch/services/services.go @@ -199,16 +199,14 @@ func (u *urlProvider) PodURL() string { running = append(running, p) } } - var url string - if len(ready) > 0 { - url = randomESPodURL(ready) - } else if len(running) > 0 { - url = randomESPodURL(running) + switch { + case len(ready) > 0: + return randomESPodURL(ready) + case len(running) > 0: + return randomESPodURL(running) + default: + return u.ServiceURL() } - if len(url) == 0 { - url = u.ServiceURL() - } - return url } // ServiceURL implements client.URLProvider. diff --git a/pkg/controller/elasticsearch/services/services_test.go b/pkg/controller/elasticsearch/services/services_test.go index d6ed7ad33c..723ef21208 100644 --- a/pkg/controller/elasticsearch/services/services_test.go +++ b/pkg/controller/elasticsearch/services/services_test.go @@ -435,10 +435,24 @@ func TestNewElasticsearchURLProvider(t *testing.T) { { name: "list pods from cache", args: args{ - es: mkElasticsearch(commonv1.HTTPConfig{}), - client: k8s.NewFakeClient(ptr.To(mkPod("sset-0", true, true))), + 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"}, + wantPodNames: []string{"sset-0", "sset-1"}, }, } for _, tt := range tests { From 1649df75c87bdd0033acd79e2530661fc3c48e7e Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Wed, 29 May 2024 22:46:30 +0200 Subject: [PATCH 19/23] Clarify naming of readines probe script --- pkg/controller/elasticsearch/configmap/configmap.go | 12 ++++++------ .../elasticsearch/nodespec/podspec_test.go | 2 +- .../elasticsearch/nodespec/readiness_probe.go | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pkg/controller/elasticsearch/configmap/configmap.go b/pkg/controller/elasticsearch/configmap/configmap.go index 8eec52f0c8..53043ed017 100644 --- a/pkg/controller/elasticsearch/configmap/configmap.go +++ b/pkg/controller/elasticsearch/configmap/configmap.go @@ -53,12 +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.ReadinessPortProbeScriptConfigKey: nodespec.ReadinessPortProbeScript, - 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/nodespec/podspec_test.go b/pkg/controller/elasticsearch/nodespec/podspec_test.go index 082e0a278a..38c82c6160 100644 --- a/pkg/controller/elasticsearch/nodespec/podspec_test.go +++ b/pkg/controller/elasticsearch/nodespec/podspec_test.go @@ -540,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 e65bfafe31..cf871b3639 100644 --- a/pkg/controller/elasticsearch/nodespec/readiness_probe.go +++ b/pkg/controller/elasticsearch/nodespec/readiness_probe.go @@ -30,7 +30,7 @@ nc -z -v -w5 127.0.0.1 8080 func NewReadinessProbe(v version.Version) *corev1.Probe { scriptKey := ReadinessPortProbeScriptConfigKey if v.LE(esv1.MinReadinessPortVersion) { - scriptKey = ReadinessProbeScriptConfigKey + scriptKey = LegacyReadinessProbeScriptConfigKey } return &corev1.Probe{ @@ -47,8 +47,8 @@ func NewReadinessProbe(v version.Version) *corev1.Probe { } } -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 { From 878169e137108353d15a9662c3ad0b6bd7d5b3eb Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Mon, 3 Jun 2024 19:47:03 +0200 Subject: [PATCH 20/23] review: interface structure, error handling --- pkg/controller/common/esclient/esclient.go | 2 +- pkg/controller/elasticsearch/client/base.go | 12 +- pkg/controller/elasticsearch/client/client.go | 2 +- .../elasticsearch/client/client_test.go | 15 +- pkg/controller/elasticsearch/client/url.go | 28 +-- pkg/controller/elasticsearch/client/v6.go | 6 +- pkg/controller/elasticsearch/driver/driver.go | 4 +- .../elasticsearch/services/services.go | 43 +++-- .../elasticsearch/services/services_test.go | 173 +++++++++++------- test/e2e/test/elasticsearch/http_client.go | 2 +- 10 files changed, 170 insertions(+), 117 deletions(-) diff --git a/pkg/controller/common/esclient/esclient.go b/pkg/controller/common/esclient/esclient.go index ece5fbe4b5..895b217519 100644 --- a/pkg/controller/common/esclient/esclient.go +++ b/pkg/controller/common/esclient/esclient.go @@ -71,7 +71,7 @@ func NewClient( return esclient.NewElasticsearchClient( dialer, k8s.ExtractNamespacedName(&es), - services.NewElasticsearchURLProvider(ctx, es, c), + 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 e8362abf3a..6a5e2da4be 100644 --- a/pkg/controller/elasticsearch/client/base.go +++ b/pkg/controller/elasticsearch/client/base.go @@ -58,7 +58,7 @@ func (c *baseClient) equal(c2 *baseClient) bool { } } // compare endpoint svc url and user creds. Service URL acts purely as an identifier here. - return c.URLProvider.ServiceURL() == c2.URLProvider.ServiceURL() && + 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.URLProvider.PodURL(), 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,7 +192,7 @@ func versioned(b *baseClient, v version.Version) Client { } } -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 } @@ -197,5 +201,5 @@ func (c *baseClient) HasProperties(version version.Version, user BasicAuth, url return false } } - return c.version.Equals(version) && c.User == user && c.URLProvider.ServiceURL() == 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 3d08d99766..a26254fb1e 100644 --- a/pkg/controller/elasticsearch/client/client.go +++ b/pkg/controller/elasticsearch/client/client.go @@ -128,7 +128,7 @@ type Client interface { // in the cluster. Version() version.Version // 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. diff --git a/pkg/controller/elasticsearch/client/client_test.go b/pkg/controller/elasticsearch/client/client_test.go index 48e634d71d..cae414c8d7 100644 --- a/pkg/controller/elasticsearch/client/client_test.go +++ b/pkg/controller/elasticsearch/client/client_test.go @@ -835,8 +835,7 @@ 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(defaultURL) + defaultURLProvider := NewStaticURLProvider("https://foo.bar") defaultCaCerts := []*x509.Certificate{{Raw: []byte("foo")}} defaultEsClient := NewElasticsearchClient( nil, @@ -853,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 }{ @@ -862,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, }, @@ -871,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, }, @@ -880,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, }, @@ -889,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, }, @@ -898,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/url.go b/pkg/controller/elasticsearch/client/url.go index cf46bb1277..c22ccfa946 100644 --- a/pkg/controller/elasticsearch/client/url.go +++ b/pkg/controller/elasticsearch/client/url.go @@ -5,14 +5,16 @@ package client type URLProvider interface { - // PodURL is a url for a random pod (falls back to ServiceURL). - PodURL() string - // ServiceURL is the url for the Kubernetes service related to the Pod URLs provided. - ServiceURL() string - + // URL return 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 return 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, @@ -23,14 +25,18 @@ type staticURLProvider struct { url string } -// PodURL implements URLProvider. -func (s *staticURLProvider) PodURL() string { - return s.url +// URL implements URLProvider. +func (s *staticURLProvider) URL() (string, error) { + return s.url, nil } -// ServiceURL implements URLProvider. -func (s *staticURLProvider) ServiceURL() string { - return s.url +// 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 { diff --git a/pkg/controller/elasticsearch/client/v6.go b/pkg/controller/elasticsearch/client/v6.go index f99607750b..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.URLProvider.PodURL(), 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/driver/driver.go b/pkg/controller/elasticsearch/driver/driver.go index ea9fa14aa4..cae7fae24c 100644 --- a/pkg/controller/elasticsearch/driver/driver.go +++ b/pkg/controller/elasticsearch/driver/driver.go @@ -184,7 +184,7 @@ func (d *defaultDriver) Reconcile(ctx context.Context) *reconciler.Results { minVersion = &d.Version } - urlProvider := services.NewElasticsearchURLProvider(ctx, d.ES, d.Client) + urlProvider := services.NewElasticsearchURLProvider(d.ES, d.Client) hasEndpoints := urlProvider.HasEndpoints() observedState := d.Observers.ObservedStateResolver( @@ -401,7 +401,7 @@ func (d *defaultDriver) elasticsearchClientProvider( caCerts []*x509.Certificate, ) func(existingEsClient esclient.Client) esclient.Client { return func(existingEsClient esclient.Client) esclient.Client { - if existingEsClient != nil && existingEsClient.HasProperties(v, user, urlProvider.ServiceURL(), caCerts) { + if existingEsClient != nil && existingEsClient.HasProperties(v, user, urlProvider, caCerts) { return existingEsClient } return d.newElasticsearchClient(ctx, urlProvider, user, v, caCerts) diff --git a/pkg/controller/elasticsearch/services/services.go b/pkg/controller/elasticsearch/services/services.go index dfe61f208f..bb5b996d79 100644 --- a/pkg/controller/elasticsearch/services/services.go +++ b/pkg/controller/elasticsearch/services/services.go @@ -20,7 +20,6 @@ import ( "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" - ulog "github.com/elastic/cloud-on-k8s/v2/pkg/utils/log" "github.com/elastic/cloud-on-k8s/v2/pkg/utils/stringsutil" ) @@ -184,14 +183,18 @@ func getServiceByName(c k8s.Client, es esv1.Elasticsearch, serviceName string) ( } type urlProvider struct { - pods func() []corev1.Pod + pods func() ([]corev1.Pod, error) svcURL string } -// PodURL implements client.URLProvider. -func (u *urlProvider) PodURL() string { +// URL implements client.URLProvider. +func (u *urlProvider) URL() (string, error) { var ready, running []corev1.Pod - for _, p := range u.pods() { + pods, err := u.pods() + if err != nil { + return "", err + } + for _, p := range pods { if k8s.IsPodReady(p) { ready = append(ready, p) } @@ -201,33 +204,33 @@ func (u *urlProvider) PodURL() string { } switch { case len(ready) > 0: - return randomESPodURL(ready) + return randomESPodURL(ready), nil case len(running) > 0: - return randomESPodURL(running) + return randomESPodURL(running), nil default: - return u.ServiceURL() + return u.svcURL, nil } } -// ServiceURL implements client.URLProvider. -func (u *urlProvider) ServiceURL() string { - return u.svcURL +// 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 { - return len(k8s.RunningPods(u.pods())) > 0 + pods, err := u.pods() + return err == nil && len(k8s.RunningPods(pods)) > 0 } -func NewElasticsearchURLProvider(ctx context.Context, es esv1.Elasticsearch, client k8s.Client) client.URLProvider { +func NewElasticsearchURLProvider(es esv1.Elasticsearch, client k8s.Client) client.URLProvider { return &urlProvider{ - pods: func() []corev1.Pod { - log := ulog.FromContext(ctx) - pods, err := k8s.PodsMatchingLabels(client, es.Namespace, label.NewLabelSelectorForElasticsearch(es)) - if err != nil { - log.Error(err, "while fetching pods from cache in URL provider") - } - return pods + pods: func() ([]corev1.Pod, error) { + return k8s.PodsMatchingLabels(client, es.Namespace, label.NewLabelSelectorForElasticsearch(es)) }, svcURL: InternalServiceURL(es), } diff --git a/pkg/controller/elasticsearch/services/services_test.go b/pkg/controller/elasticsearch/services/services_test.go index 723ef21208..94e6b33306 100644 --- a/pkg/controller/elasticsearch/services/services_test.go +++ b/pkg/controller/elasticsearch/services/services_test.go @@ -5,25 +5,22 @@ package services import ( - "context" "errors" "testing" - "github.com/go-logr/logr" - "github.com/go-test/deep" - "github.com/stretchr/testify/assert" - "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" - ulog "github.com/elastic/cloud-on-k8s/v2/pkg/utils/log" + "github.com/go-test/deep" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" ) func TestExternalServiceURL(t *testing.T) { @@ -284,19 +281,29 @@ func TestNewTransportService(t *testing.T) { func Test_urlProvider_PodURL(t *testing.T) { type fields struct { - pods func() []corev1.Pod + pods func() ([]corev1.Pod, error) svcURL string } tests := []struct { - name string - fields fields - want []string + 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 { - return nil + pods: func() ([]corev1.Pod, error) { + return nil, nil }, svcURL: "svc.url", }, @@ -305,14 +312,14 @@ func Test_urlProvider_PodURL(t *testing.T) { { name: "ready and running pods: prefer ready", fields: fields{ - pods: func() []corev1.Pod { + 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"}, @@ -320,13 +327,13 @@ func Test_urlProvider_PodURL(t *testing.T) { { name: "only running pods: allow running ", fields: fields{ - pods: func() []corev1.Pod { + 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"}, @@ -338,7 +345,13 @@ func Test_urlProvider_PodURL(t *testing.T) { pods: tt.fields.pods, svcURL: tt.fields.svcURL, } - require.Contains(t, tt.want, u.PodURL(), "must contain one of expected url") + 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") + } }) } } @@ -377,42 +390,6 @@ func mkPod(name string, running bool, ready bool) corev1.Pod { } } -type errorLogSink struct { - errorLogs []string -} - -// Enabled implements logr.LogSink. -func (l *errorLogSink) Enabled(level int) bool { - return true -} - -// Error implements logr.LogSink. -func (l *errorLogSink) Error(err error, msg string, keysAndValues ...any) { - l.errorLogs = append(l.errorLogs, msg) -} - -// Info implements logr.LogSink. -func (l *errorLogSink) Info(level int, msg string, keysAndValues ...any) { - // ignore -} - -// Init implements logr.LogSink. -func (l *errorLogSink) Init(info logr.RuntimeInfo) { - // noop -} - -// WithName implements logr.LogSink. -func (l *errorLogSink) WithName(name string) logr.LogSink { - return l -} - -// WithValues implements logr.LogSink. -func (l *errorLogSink) WithValues(keysAndValues ...any) logr.LogSink { - return l -} - -var _ logr.LogSink = &errorLogSink{} - func TestNewElasticsearchURLProvider(t *testing.T) { type args struct { es esv1.Elasticsearch @@ -422,7 +399,7 @@ func TestNewElasticsearchURLProvider(t *testing.T) { name string args args wantPodNames []string - wantErr string + wantErr bool }{ { name: "cache failures are swallowed but logged", @@ -430,7 +407,7 @@ func TestNewElasticsearchURLProvider(t *testing.T) { es: mkElasticsearch(commonv1.HTTPConfig{}), client: k8s.NewFailingClient(errors.New("boom")), }, - wantErr: "while fetching pods from cache in URL provider", + wantErr: true, }, { name: "list pods from cache", @@ -457,19 +434,79 @@ func TestNewElasticsearchURLProvider(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - sink := errorLogSink{} - ctx := ulog.AddToContext(context.Background(), logr.New(&sink)) - provider := NewElasticsearchURLProvider(ctx, tt.args.es, tt.args.client) + provider := NewElasticsearchURLProvider(tt.args.es, tt.args.client) providerImpl, ok := provider.(*urlProvider) require.True(t, ok, "must be the urlProvider impl") - got := k8s.PodNames(providerImpl.pods()) - require.ElementsMatch(t, got, tt.wantPodNames) - if len(tt.wantErr) > 0 { - require.Contains(t, sink.errorLogs, tt.wantErr) - } else { - require.Empty(t, sink.errorLogs, "did not expect any error logs") + 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/test/e2e/test/elasticsearch/http_client.go b/test/e2e/test/elasticsearch/http_client.go index 78c543c1a5..aee8674dca 100644 --- a/test/e2e/test/elasticsearch/http_client.go +++ b/test/e2e/test/elasticsearch/http_client.go @@ -77,7 +77,7 @@ func NewElasticsearchClientWithUser(es esv1.Elasticsearch, k *test.K8sClient, us esClient := client.NewElasticsearchClient( dialer, k8s.ExtractNamespacedName(&es), - services.NewElasticsearchURLProvider(context.Background(), es, k.Client), + services.NewElasticsearchURLProvider(es, k.Client), user, v, caCert, From 78aca2648e7ad5acd905ac5109e518fd52a5dd90 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Mon, 3 Jun 2024 20:13:44 +0200 Subject: [PATCH 21/23] review: autoscaling online detection --- .../elasticsearch/controller_test.go | 27 ++++++++---------- .../autoscaling/elasticsearch/driver.go | 28 +++---------------- .../elasticsearch/services/services_test.go | 13 +++++---- 3 files changed, 23 insertions(+), 45 deletions(-) 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/elasticsearch/services/services_test.go b/pkg/controller/elasticsearch/services/services_test.go index 94e6b33306..2d1350541c 100644 --- a/pkg/controller/elasticsearch/services/services_test.go +++ b/pkg/controller/elasticsearch/services/services_test.go @@ -8,6 +8,13 @@ import ( "errors" "testing" + "github.com/go-test/deep" + "github.com/stretchr/testify/assert" + "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" @@ -15,12 +22,6 @@ import ( "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" - "github.com/go-test/deep" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/ptr" ) func TestExternalServiceURL(t *testing.T) { From 8fbf987c5e875eaf4b7d1efe1823436f4d70c6c7 Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Mon, 3 Jun 2024 20:47:49 +0200 Subject: [PATCH 22/23] go docs and such --- pkg/controller/elasticsearch/services/services.go | 2 ++ pkg/controller/elasticsearch/services/services_test.go | 2 +- pkg/utils/k8s/k8sutils.go | 2 ++ test/e2e/es/forced_upgrade_test.go | 3 +-- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pkg/controller/elasticsearch/services/services.go b/pkg/controller/elasticsearch/services/services.go index bb5b996d79..71284a22bb 100644 --- a/pkg/controller/elasticsearch/services/services.go +++ b/pkg/controller/elasticsearch/services/services.go @@ -227,6 +227,8 @@ func (u *urlProvider) HasEndpoints() bool { 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) { diff --git a/pkg/controller/elasticsearch/services/services_test.go b/pkg/controller/elasticsearch/services/services_test.go index 2d1350541c..ccbde26fc2 100644 --- a/pkg/controller/elasticsearch/services/services_test.go +++ b/pkg/controller/elasticsearch/services/services_test.go @@ -403,7 +403,7 @@ func TestNewElasticsearchURLProvider(t *testing.T) { wantErr bool }{ { - name: "cache failures are swallowed but logged", + name: "cache failures are returned to the caller", args: args{ es: mkElasticsearch(commonv1.HTTPConfig{}), client: k8s.NewFailingClient(errors.New("boom")), diff --git a/pkg/utils/k8s/k8sutils.go b/pkg/utils/k8s/k8sutils.go index 42e82c4a05..bdd1c4e675 100644 --- a/pkg/utils/k8s/k8sutils.go +++ b/pkg/utils/k8s/k8sutils.go @@ -74,6 +74,7 @@ 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 } @@ -90,6 +91,7 @@ func TerminatingPods(pods []corev1.Pod) []corev1.Pod { return terminating } +// RunningPods filters pods for Pods are in running (and not terminating). func RunningPods(pods []corev1.Pod) []corev1.Pod { var running []corev1.Pod for _, p := range pods { diff --git a/test/e2e/es/forced_upgrade_test.go b/test/e2e/es/forced_upgrade_test.go index 786eba9b19..6ab0290872 100644 --- a/test/e2e/es/forced_upgrade_test.go +++ b/test/e2e/es/forced_upgrade_test.go @@ -103,8 +103,7 @@ func TestForceUpgradePendingPodsInOneStatefulSet(t *testing.T) { if err != nil { return err } - running := k8s.RunningPods(pods) - if len(running) == 0 { + if len(k8s.RunningPods(pods)) == 0 { return errors.New("Elasticsearch does not have any running Pods") } return nil From b78c28791d7c9ac9402b38d8791d70b831ee14de Mon Sep 17 00:00:00 2001 From: Peter Brachwitz Date: Tue, 4 Jun 2024 20:01:21 +0200 Subject: [PATCH 23/23] review: wording --- pkg/controller/elasticsearch/client/url.go | 4 ++-- pkg/utils/k8s/k8sutils.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/controller/elasticsearch/client/url.go b/pkg/controller/elasticsearch/client/url.go index c22ccfa946..e258c5b451 100644 --- a/pkg/controller/elasticsearch/client/url.go +++ b/pkg/controller/elasticsearch/client/url.go @@ -5,11 +5,11 @@ package client type URLProvider interface { - // URL return a URL to route traffic to (can fall back to a k8s service URL). + // 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 return true if the provider has currently any endpoints/URLs to return. + // 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 } diff --git a/pkg/utils/k8s/k8sutils.go b/pkg/utils/k8s/k8sutils.go index bdd1c4e675..a68c16d769 100644 --- a/pkg/utils/k8s/k8sutils.go +++ b/pkg/utils/k8s/k8sutils.go @@ -91,7 +91,7 @@ func TerminatingPods(pods []corev1.Pod) []corev1.Pod { return terminating } -// RunningPods filters pods for Pods are in running (and not 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 {