From 28a291b49f3c1cbf3a07d5434b7e74645ddeb5e9 Mon Sep 17 00:00:00 2001 From: Girish Chandrashekar Date: Fri, 4 Mar 2022 15:41:58 +0100 Subject: [PATCH 01/11] Refactor transient settings to strict types will nullable values - Extract strict types of ESSettings to better handle initalization of types instead of string for updating es settings - Introduce null library for rendering null json values Signed-off-by: Girish Chandrashekar --- go.mod | 1 + go.sum | 2 ++ operator/es_client.go | 68 +++++++++++++++++++++++++------------------ 3 files changed, 42 insertions(+), 29 deletions(-) diff --git a/go.mod b/go.mod index 7d503357..944e8f29 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/stretchr/testify v1.7.0 golang.org/x/tools v0.1.1 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 + gopkg.in/guregu/null.v4 v4.0.0 gopkg.in/resty.v1 v1.12.0 k8s.io/api v0.21.5 k8s.io/apiextensions-apiserver v0.21.5 // indirect diff --git a/go.sum b/go.sum index 2b3c45a3..9ae250de 100644 --- a/go.sum +++ b/go.sum @@ -861,6 +861,8 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/guregu/null.v4 v4.0.0 h1:1Wm3S1WEA2I26Kq+6vcW+w0gcDo44YKYD7YIEJNHDjg= +gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu/JrI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/operator/es_client.go b/operator/es_client.go index 49deaf8b..1894d3e4 100644 --- a/operator/es_client.go +++ b/operator/es_client.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "gopkg.in/guregu/null.v4" "net/http" "net/url" "sort" @@ -67,22 +68,33 @@ type ESHealth struct { Status string `json:"status"` } +type Exclude struct { + IP null.String `json:"_ip,omitempty"` +} + +type Allocation struct { + Exclude Exclude `json:"exclude,omitempty"` +} +type Rebalance struct { + Enable null.String `json:"enable,omitempty"` +} + +type Routing struct { + Allocation Allocation `json:"allocation,omitempty"` + Rebalance Rebalance `json:"rebalance,omitempty"` +} + +type Cluster struct { + Routing Routing `json:"routing,omitempty"` +} + +type Transient struct { + Cluster Cluster `json:"cluster"` +} + // ESSettings represent response from _cluster/settings type ESSettings struct { - Transient struct { - Cluster struct { - Routing struct { - Allocation struct { - Exclude struct { - IP string `json:"_ip"` - } `json:"exclude"` - } `json:"allocation"` - Rebalance struct { - Enable string `json:"enable"` - } `json:"rebalance"` - } `json:"routing"` - } `json:"cluster"` - } `json:"transient"` + Transient Transient `json:"transient,omitempty"` } func (c *ESClient) logger() *log.Entry { @@ -130,7 +142,7 @@ func (c *ESClient) Cleanup(ctx context.Context) error { } // 3. clean up exclude._ip settings based on known IPs from (1) - excludedIPsString := esSettings.Transient.Cluster.Routing.Allocation.Exclude.IP + excludedIPsString := esSettings.Transient.Cluster.Routing.Allocation.Exclude.IP.ValueOrZero() excludedIPs := strings.Split(excludedIPsString, ",") var newExcludedIPs []string for _, excludeIP := range excludedIPs { @@ -154,7 +166,7 @@ func (c *ESClient) Cleanup(ctx context.Context) error { } } - if esSettings.Transient.Cluster.Routing.Rebalance.Enable != "all" { + if esSettings.Transient.Cluster.Routing.Rebalance.Enable.ValueOrZero() != "all" { c.logger().Info("Enabling auto-rebalance") return c.updateAutoRebalance("all") } @@ -214,7 +226,7 @@ func (c *ESClient) excludePodIP(pod *v1.Pod) error { return err } - excludeString := esSettings.Transient.Cluster.Routing.Allocation.Exclude.IP + excludeString := esSettings.Transient.Cluster.Routing.Allocation.Exclude.IP.ValueOrZero() // add pod IP to exclude list ips := []string{} @@ -239,14 +251,12 @@ func (c *ESClient) excludePodIP(pod *v1.Pod) error { } func (c *ESClient) setExcludeIPs(ips string) error { + esSettings := ESSettings{ + Transient: Transient{Cluster: Cluster{Routing: Routing{Allocation: Allocation{Exclude: Exclude{IP: null.StringFromPtr(&ips)}}}}}, + } resp, err := resty.New().R(). SetHeader("Content-Type", "application/json"). - SetBody([]byte( - fmt.Sprintf( - `{"transient" : {"cluster.routing.allocation.exclude._ip" : "%s"}}`, - ips, - ), - )). + SetBody(esSettings). Put(c.Endpoint.String() + "/_cluster/settings") if err != nil { return err @@ -258,14 +268,14 @@ func (c *ESClient) setExcludeIPs(ips string) error { } func (c *ESClient) updateAutoRebalance(value string) error { + esSettings := ESSettings{ + Transient: Transient{ + Cluster: Cluster{Routing: Routing{Rebalance: Rebalance{Enable: null.StringFromPtr(&value)}}}, + }, + } resp, err := resty.New().R(). SetHeader("Content-Type", "application/json"). - SetBody([]byte( - fmt.Sprintf( - `{"transient" : {"cluster.routing.rebalance.enable" : "%s"}}`, - value, - ), - )). + SetBody(esSettings). Put(c.Endpoint.String() + "/_cluster/settings") if err != nil { return err From 3db39258a9f14f0bbbdfd0853086f26d21f06100 Mon Sep 17 00:00:00 2001 From: Girish Chandrashekar Date: Fri, 4 Mar 2022 15:48:39 +0100 Subject: [PATCH 02/11] Rename high ES settings type to ClusterSettings in order to duplicate the common cluster settings into transient and persistent Signed-off-by: Girish Chandrashekar --- operator/es_client.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/operator/es_client.go b/operator/es_client.go index 1894d3e4..2448c93f 100644 --- a/operator/es_client.go +++ b/operator/es_client.go @@ -88,13 +88,13 @@ type Cluster struct { Routing Routing `json:"routing,omitempty"` } -type Transient struct { +type ClusterSettings struct { Cluster Cluster `json:"cluster"` } // ESSettings represent response from _cluster/settings type ESSettings struct { - Transient Transient `json:"transient,omitempty"` + Transient ClusterSettings `json:"transient,omitempty"` } func (c *ESClient) logger() *log.Entry { @@ -252,7 +252,7 @@ func (c *ESClient) excludePodIP(pod *v1.Pod) error { func (c *ESClient) setExcludeIPs(ips string) error { esSettings := ESSettings{ - Transient: Transient{Cluster: Cluster{Routing: Routing{Allocation: Allocation{Exclude: Exclude{IP: null.StringFromPtr(&ips)}}}}}, + Transient: ClusterSettings{Cluster: Cluster{Routing: Routing{Allocation: Allocation{Exclude: Exclude{IP: null.StringFromPtr(&ips)}}}}}, } resp, err := resty.New().R(). SetHeader("Content-Type", "application/json"). @@ -269,7 +269,7 @@ func (c *ESClient) setExcludeIPs(ips string) error { func (c *ESClient) updateAutoRebalance(value string) error { esSettings := ESSettings{ - Transient: Transient{ + Transient: ClusterSettings{ Cluster: Cluster{Routing: Routing{Rebalance: Rebalance{Enable: null.StringFromPtr(&value)}}}, }, } From fe9d1a31b3d30a21d6f406f2f09bd20ab13efd83 Mon Sep 17 00:00:00 2001 From: Girish Chandrashekar Date: Fri, 4 Mar 2022 18:07:56 +0100 Subject: [PATCH 03/11] Operate on persistent cluster settings - copy non empty transient settings into persistent cluter settings and set transient settings to null - operate on persistent settings for modifying cluster rebalance and ip exclusion Signed-off-by: Girish Chandrashekar --- operator/es_client.go | 76 ++++++++++++++++++++++++++++---------- operator/es_client_test.go | 70 +++++++++++++++++++++++++++++++++-- 2 files changed, 123 insertions(+), 23 deletions(-) diff --git a/operator/es_client.go b/operator/es_client.go index 2448c93f..6fcd65cf 100644 --- a/operator/es_client.go +++ b/operator/es_client.go @@ -94,7 +94,33 @@ type ClusterSettings struct { // ESSettings represent response from _cluster/settings type ESSettings struct { - Transient ClusterSettings `json:"transient,omitempty"` + Transient ClusterSettings `json:"transient,omitempty"` + Persistent ClusterSettings `json:"persistent,omitempty"` +} + +func (esSettings *ESSettings) copyNonEmtpyTransientSettings() { + if value := esSettings.GetTransientRebalance().ValueOrZero(); value != "" { + esSettings.Persistent.Cluster.Routing.Rebalance.Enable = null.StringFromPtr(&value) + } + if value := esSettings.GetTransientExcludeIPs().ValueOrZero(); value != "" { + esSettings.Persistent.Cluster.Routing.Allocation.Exclude.IP = null.StringFromPtr(&value) + } +} + +func (esSettings *ESSettings) GetTransientExcludeIPs() null.String { + return esSettings.Transient.Cluster.Routing.Allocation.Exclude.IP +} + +func (esSettings *ESSettings) GetPersistentExcludeIPs() null.String { + return esSettings.Persistent.Cluster.Routing.Allocation.Exclude.IP +} + +func (esSettings *ESSettings) GetTransientRebalance() null.String { + return esSettings.Transient.Cluster.Routing.Rebalance.Enable +} + +func (esSettings *ESSettings) GetPersistentRebalance() null.String { + return esSettings.Persistent.Cluster.Routing.Rebalance.Enable } func (c *ESClient) logger() *log.Entry { @@ -113,7 +139,12 @@ func (c *ESClient) Drain(ctx context.Context, pod *v1.Pod) error { return err } c.logger().Info("Disabling auto-rebalance") - err = c.updateAutoRebalance("none") + esSettings, err := c.getClusterSettings() + if err != nil { + return err + } + + err = c.updateAutoRebalance("none", esSettings) if err != nil { return err } @@ -142,7 +173,7 @@ func (c *ESClient) Cleanup(ctx context.Context) error { } // 3. clean up exclude._ip settings based on known IPs from (1) - excludedIPsString := esSettings.Transient.Cluster.Routing.Allocation.Exclude.IP.ValueOrZero() + excludedIPsString := esSettings.GetPersistentExcludeIPs().ValueOrZero() excludedIPs := strings.Split(excludedIPsString, ",") var newExcludedIPs []string for _, excludeIP := range excludedIPs { @@ -160,15 +191,15 @@ func (c *ESClient) Cleanup(ctx context.Context) error { c.logger().Infof("Setting exclude list to '%s'", strings.Join(newExcludedIPs, ",")) // 4. update exclude._ip setting - err = c.setExcludeIPs(newExcludedIPsString) + err = c.setExcludeIPs(newExcludedIPsString, esSettings) if err != nil { return err } } - if esSettings.Transient.Cluster.Routing.Rebalance.Enable.ValueOrZero() != "all" { + if esSettings.GetPersistentRebalance().ValueOrZero() != "all" { c.logger().Info("Enabling auto-rebalance") - return c.updateAutoRebalance("all") + return c.updateAutoRebalance("all", esSettings) } return nil } @@ -210,6 +241,7 @@ func (c *ESClient) getClusterSettings() (*ESSettings, error) { if err != nil { return nil, err } + esSettings.copyNonEmtpyTransientSettings() return &esSettings, nil } @@ -226,7 +258,7 @@ func (c *ESClient) excludePodIP(pod *v1.Pod) error { return err } - excludeString := esSettings.Transient.Cluster.Routing.Allocation.Exclude.IP.ValueOrZero() + excludeString := esSettings.GetPersistentExcludeIPs().ValueOrZero() // add pod IP to exclude list ips := []string{} @@ -243,20 +275,18 @@ func (c *ESClient) excludePodIP(pod *v1.Pod) error { if !foundPodIP { ips = append(ips, podIP) sort.Strings(ips) - err = c.setExcludeIPs(strings.Join(ips, ",")) + err = c.setExcludeIPs(strings.Join(ips, ","), esSettings) } c.mux.Unlock() return err } -func (c *ESClient) setExcludeIPs(ips string) error { - esSettings := ESSettings{ - Transient: ClusterSettings{Cluster: Cluster{Routing: Routing{Allocation: Allocation{Exclude: Exclude{IP: null.StringFromPtr(&ips)}}}}}, - } +func (c *ESClient) setExcludeIPs(ips string, originalESSettings *ESSettings) error { + originalESSettings.updateExcludeIps(ips) resp, err := resty.New().R(). SetHeader("Content-Type", "application/json"). - SetBody(esSettings). + SetBody(originalESSettings). Put(c.Endpoint.String() + "/_cluster/settings") if err != nil { return err @@ -267,15 +297,16 @@ func (c *ESClient) setExcludeIPs(ips string) error { return nil } -func (c *ESClient) updateAutoRebalance(value string) error { - esSettings := ESSettings{ - Transient: ClusterSettings{ - Cluster: Cluster{Routing: Routing{Rebalance: Rebalance{Enable: null.StringFromPtr(&value)}}}, - }, - } +func (esSettings *ESSettings) updateExcludeIps(ips string) { + esSettings.Transient.Cluster.Routing.Allocation.Exclude.IP = null.StringFromPtr(nil) + esSettings.Persistent.Cluster.Routing.Allocation.Exclude.IP = null.StringFromPtr(&ips) +} + +func (c *ESClient) updateAutoRebalance(value string, originalESSettings *ESSettings) error { + originalESSettings.updateRebalance(value) resp, err := resty.New().R(). SetHeader("Content-Type", "application/json"). - SetBody(esSettings). + SetBody(originalESSettings). Put(c.Endpoint.String() + "/_cluster/settings") if err != nil { return err @@ -286,6 +317,11 @@ func (c *ESClient) updateAutoRebalance(value string) error { return nil } +func (esSettings *ESSettings) updateRebalance(value string) { + esSettings.Transient.Cluster.Routing.Rebalance.Enable = null.StringFromPtr(nil) + esSettings.Persistent.Cluster.Routing.Rebalance.Enable = null.StringFromPtr(&value) +} + // repeatedly query shard allocations to ensure success of drain operation. func (c *ESClient) waitForEmptyEsNode(ctx context.Context, pod *v1.Pod) error { // TODO: implement context handling diff --git a/operator/es_client_test.go b/operator/es_client_test.go index 708ea669..bde3008e 100644 --- a/operator/es_client_test.go +++ b/operator/es_client_test.go @@ -2,6 +2,9 @@ package operator import ( "context" + "encoding/json" + "io/ioutil" + "net/http" "net/url" "testing" @@ -16,7 +19,7 @@ func TestDrain(t *testing.T) { defer httpmock.DeactivateAndReset() httpmock.RegisterResponder("GET", "http://elasticsearch:9200/_cluster/settings", - httpmock.NewStringResponder(200, `{"transient":{"cluster":{"routing":{"rebalance":{"enable":"all"}}}}}`)) + httpmock.NewStringResponder(200, `{"persistent":{"cluster":{"routing":{"rebalance":{"enable":"all"}}}}}`)) httpmock.RegisterResponder("PUT", "http://elasticsearch:9200/_cluster/settings", httpmock.NewStringResponder(200, `{}`)) httpmock.RegisterResponder("GET", "http://elasticsearch:9200/_cluster/health", @@ -24,9 +27,9 @@ func TestDrain(t *testing.T) { httpmock.RegisterResponder("GET", "http://elasticsearch:9200/_cat/shards", httpmock.NewStringResponder(200, `[{"index":"a","ip":"10.2.19.5"},{"index":"b","ip":"10.2.10.2"},{"index":"c","ip":"10.2.16.2"}]`)) - url, _ := url.Parse("http://elasticsearch:9200") + esUrl, _ := url.Parse("http://elasticsearch:9200") systemUnderTest := &ESClient{ - Endpoint: url, + Endpoint: esUrl, } err := systemUnderTest.Drain(context.TODO(), &v1.Pod{ Status: v1.PodStatus{ @@ -43,6 +46,67 @@ func TestDrain(t *testing.T) { require.EqualValues(t, 1, info["GET http://elasticsearch:9200/_cat/shards"]) } +func TestDrainWithTransientSettings(t *testing.T) { + httpmock.Activate() + defer httpmock.DeactivateAndReset() + var intermediateClusterSettings []byte + expectedSequenceOfExcludeIPs := []string{"", "1.2.3.4"} + var numCalls = 0 + + httpmock.RegisterResponder("GET", "http://elasticsearch:9200/_cluster/settings", + func(request *http.Request) (*http.Response, error) { + if numCalls == 0 { + return httpmock.NewStringResponse(200, `{"transient":{"cluster":{"routing":{"rebalance":{"enable":"all"}}}}}`), nil + } else { + return httpmock.NewStringResponse(200, string(intermediateClusterSettings)), nil + } + }) + httpmock.RegisterResponder("PUT", "http://elasticsearch:9200/_cluster/settings", + func(request *http.Request) (*http.Response, error) { + var esSettings ESSettings + bodyReader, _ := request.GetBody() + _ = json.NewDecoder(bodyReader).Decode(&esSettings) + + if numCalls == 0 { + bodyReader, _ = request.GetBody() + intermediateClusterSettings, _ = ioutil.ReadAll(bodyReader) + } + + if esSettings.GetTransientExcludeIPs().ValueOrZero() != "" || esSettings.GetTransientRebalance().ValueOrZero() != "" { + return httpmock.NewStringResponse(400, ""), nil + } + + if esSettings.GetPersistentExcludeIPs().ValueOrZero() != expectedSequenceOfExcludeIPs[numCalls] || esSettings.GetPersistentRebalance().ValueOrZero() != "none" { + return httpmock.NewStringResponse(400, ""), nil + } + + numCalls = numCalls + 1 + return httpmock.NewStringResponse(200, `{}`), nil + }) + httpmock.RegisterResponder("GET", "http://elasticsearch:9200/_cluster/health", + httpmock.NewStringResponder(200, `{"status":"green"}`)) + httpmock.RegisterResponder("GET", "http://elasticsearch:9200/_cat/shards", + httpmock.NewStringResponder(200, `[{"index":"a","ip":"10.2.19.5"},{"index":"b","ip":"10.2.10.2"},{"index":"c","ip":"10.2.16.2"}]`)) + + esUrl, _ := url.Parse("http://elasticsearch:9200") + systemUnderTest := &ESClient{ + Endpoint: esUrl, + } + err := systemUnderTest.Drain(context.TODO(), &v1.Pod{ + Status: v1.PodStatus{ + PodIP: "1.2.3.4", + }, + }) + + assert.NoError(t, err) + + info := httpmock.GetCallCountInfo() + require.EqualValues(t, 1, info["GET http://elasticsearch:9200/_cluster/health"]) + require.EqualValues(t, 2, info["PUT http://elasticsearch:9200/_cluster/settings"]) + require.EqualValues(t, 2, info["GET http://elasticsearch:9200/_cluster/settings"]) + require.EqualValues(t, 1, info["GET http://elasticsearch:9200/_cat/shards"]) +} + func TestCleanup(t *testing.T) { httpmock.Activate() defer httpmock.DeactivateAndReset() From c7c6282c0efd4a1925fdfd70b839576b8af5c5a5 Mon Sep 17 00:00:00 2001 From: Girish Chandrashekar Date: Mon, 7 Mar 2022 10:03:49 +0100 Subject: [PATCH 04/11] Fix failing tests and regenerate files Signed-off-by: Girish Chandrashekar --- operator/es_client_test.go | 2 +- pkg/apis/zalando.org/v1/zz_generated.deepcopy.go | 3 +-- pkg/client/clientset/versioned/clientset.go | 2 +- pkg/client/clientset/versioned/doc.go | 2 +- pkg/client/clientset/versioned/fake/clientset_generated.go | 2 +- pkg/client/clientset/versioned/fake/doc.go | 2 +- pkg/client/clientset/versioned/fake/register.go | 2 +- pkg/client/clientset/versioned/scheme/doc.go | 2 +- pkg/client/clientset/versioned/scheme/register.go | 2 +- pkg/client/clientset/versioned/typed/zalando.org/v1/doc.go | 2 +- .../versioned/typed/zalando.org/v1/elasticsearchdataset.go | 2 +- .../versioned/typed/zalando.org/v1/elasticsearchmetricset.go | 2 +- .../clientset/versioned/typed/zalando.org/v1/fake/doc.go | 2 +- .../typed/zalando.org/v1/fake/fake_elasticsearchdataset.go | 2 +- .../typed/zalando.org/v1/fake/fake_elasticsearchmetricset.go | 2 +- .../typed/zalando.org/v1/fake/fake_zalando.org_client.go | 2 +- .../versioned/typed/zalando.org/v1/generated_expansion.go | 2 +- .../versioned/typed/zalando.org/v1/zalando.org_client.go | 2 +- pkg/client/informers/externalversions/factory.go | 2 +- pkg/client/informers/externalversions/generic.go | 2 +- .../externalversions/internalinterfaces/factory_interfaces.go | 2 +- pkg/client/informers/externalversions/zalando.org/interface.go | 2 +- .../externalversions/zalando.org/v1/elasticsearchdataset.go | 2 +- .../externalversions/zalando.org/v1/elasticsearchmetricset.go | 2 +- .../informers/externalversions/zalando.org/v1/interface.go | 2 +- pkg/client/listers/zalando.org/v1/elasticsearchdataset.go | 2 +- pkg/client/listers/zalando.org/v1/elasticsearchmetricset.go | 2 +- pkg/client/listers/zalando.org/v1/expansion_generated.go | 2 +- 28 files changed, 28 insertions(+), 29 deletions(-) diff --git a/operator/es_client_test.go b/operator/es_client_test.go index bde3008e..c2b3f02c 100644 --- a/operator/es_client_test.go +++ b/operator/es_client_test.go @@ -42,7 +42,7 @@ func TestDrain(t *testing.T) { info := httpmock.GetCallCountInfo() require.EqualValues(t, 1, info["GET http://elasticsearch:9200/_cluster/health"]) require.EqualValues(t, 2, info["PUT http://elasticsearch:9200/_cluster/settings"]) - require.EqualValues(t, 1, info["GET http://elasticsearch:9200/_cluster/settings"]) + require.EqualValues(t, 2, info["GET http://elasticsearch:9200/_cluster/settings"]) require.EqualValues(t, 1, info["GET http://elasticsearch:9200/_cat/shards"]) } diff --git a/pkg/apis/zalando.org/v1/zz_generated.deepcopy.go b/pkg/apis/zalando.org/v1/zz_generated.deepcopy.go index 57ab244c..b7b7a6c3 100644 --- a/pkg/apis/zalando.org/v1/zz_generated.deepcopy.go +++ b/pkg/apis/zalando.org/v1/zz_generated.deepcopy.go @@ -1,8 +1,7 @@ -//go:build !ignore_autogenerated // +build !ignore_autogenerated /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go index 10f5f3f5..47559862 100644 --- a/pkg/client/clientset/versioned/clientset.go +++ b/pkg/client/clientset/versioned/clientset.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/doc.go b/pkg/client/clientset/versioned/doc.go index 92576525..ab7539cb 100644 --- a/pkg/client/clientset/versioned/doc.go +++ b/pkg/client/clientset/versioned/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/fake/clientset_generated.go b/pkg/client/clientset/versioned/fake/clientset_generated.go index d79b5f09..ca336164 100644 --- a/pkg/client/clientset/versioned/fake/clientset_generated.go +++ b/pkg/client/clientset/versioned/fake/clientset_generated.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/fake/doc.go b/pkg/client/clientset/versioned/fake/doc.go index 4b91eb6a..7d98eabc 100644 --- a/pkg/client/clientset/versioned/fake/doc.go +++ b/pkg/client/clientset/versioned/fake/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/fake/register.go b/pkg/client/clientset/versioned/fake/register.go index a01199fa..cffcdffb 100644 --- a/pkg/client/clientset/versioned/fake/register.go +++ b/pkg/client/clientset/versioned/fake/register.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/scheme/doc.go b/pkg/client/clientset/versioned/scheme/doc.go index 6dc5e653..288d3794 100644 --- a/pkg/client/clientset/versioned/scheme/doc.go +++ b/pkg/client/clientset/versioned/scheme/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/scheme/register.go b/pkg/client/clientset/versioned/scheme/register.go index 5fe584d6..03818678 100644 --- a/pkg/client/clientset/versioned/scheme/register.go +++ b/pkg/client/clientset/versioned/scheme/register.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/zalando.org/v1/doc.go b/pkg/client/clientset/versioned/typed/zalando.org/v1/doc.go index d2b78a77..01fa5fd6 100644 --- a/pkg/client/clientset/versioned/typed/zalando.org/v1/doc.go +++ b/pkg/client/clientset/versioned/typed/zalando.org/v1/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/zalando.org/v1/elasticsearchdataset.go b/pkg/client/clientset/versioned/typed/zalando.org/v1/elasticsearchdataset.go index 8ceedb7e..7c8adad0 100644 --- a/pkg/client/clientset/versioned/typed/zalando.org/v1/elasticsearchdataset.go +++ b/pkg/client/clientset/versioned/typed/zalando.org/v1/elasticsearchdataset.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/zalando.org/v1/elasticsearchmetricset.go b/pkg/client/clientset/versioned/typed/zalando.org/v1/elasticsearchmetricset.go index 53921d24..90718589 100644 --- a/pkg/client/clientset/versioned/typed/zalando.org/v1/elasticsearchmetricset.go +++ b/pkg/client/clientset/versioned/typed/zalando.org/v1/elasticsearchmetricset.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/zalando.org/v1/fake/doc.go b/pkg/client/clientset/versioned/typed/zalando.org/v1/fake/doc.go index 43eec078..dd9e9e4c 100644 --- a/pkg/client/clientset/versioned/typed/zalando.org/v1/fake/doc.go +++ b/pkg/client/clientset/versioned/typed/zalando.org/v1/fake/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/zalando.org/v1/fake/fake_elasticsearchdataset.go b/pkg/client/clientset/versioned/typed/zalando.org/v1/fake/fake_elasticsearchdataset.go index e865858c..f953c908 100644 --- a/pkg/client/clientset/versioned/typed/zalando.org/v1/fake/fake_elasticsearchdataset.go +++ b/pkg/client/clientset/versioned/typed/zalando.org/v1/fake/fake_elasticsearchdataset.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/zalando.org/v1/fake/fake_elasticsearchmetricset.go b/pkg/client/clientset/versioned/typed/zalando.org/v1/fake/fake_elasticsearchmetricset.go index 0a9d7f75..a3d14571 100644 --- a/pkg/client/clientset/versioned/typed/zalando.org/v1/fake/fake_elasticsearchmetricset.go +++ b/pkg/client/clientset/versioned/typed/zalando.org/v1/fake/fake_elasticsearchmetricset.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/zalando.org/v1/fake/fake_zalando.org_client.go b/pkg/client/clientset/versioned/typed/zalando.org/v1/fake/fake_zalando.org_client.go index bdb4651c..450efc8c 100644 --- a/pkg/client/clientset/versioned/typed/zalando.org/v1/fake/fake_zalando.org_client.go +++ b/pkg/client/clientset/versioned/typed/zalando.org/v1/fake/fake_zalando.org_client.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/zalando.org/v1/generated_expansion.go b/pkg/client/clientset/versioned/typed/zalando.org/v1/generated_expansion.go index 30cac03b..e40ad6c1 100644 --- a/pkg/client/clientset/versioned/typed/zalando.org/v1/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/zalando.org/v1/generated_expansion.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go b/pkg/client/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go index c40c2e4f..8b516a33 100644 --- a/pkg/client/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go +++ b/pkg/client/clientset/versioned/typed/zalando.org/v1/zalando.org_client.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/informers/externalversions/factory.go b/pkg/client/informers/externalversions/factory.go index 5dbf29b4..6c0b2654 100644 --- a/pkg/client/informers/externalversions/factory.go +++ b/pkg/client/informers/externalversions/factory.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index c478bf4a..df3663e8 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go index 769da18d..b970175d 100644 --- a/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go +++ b/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/informers/externalversions/zalando.org/interface.go b/pkg/client/informers/externalversions/zalando.org/interface.go index 3ceb9490..1ba32753 100644 --- a/pkg/client/informers/externalversions/zalando.org/interface.go +++ b/pkg/client/informers/externalversions/zalando.org/interface.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/informers/externalversions/zalando.org/v1/elasticsearchdataset.go b/pkg/client/informers/externalversions/zalando.org/v1/elasticsearchdataset.go index 7f36c6bd..3d797280 100644 --- a/pkg/client/informers/externalversions/zalando.org/v1/elasticsearchdataset.go +++ b/pkg/client/informers/externalversions/zalando.org/v1/elasticsearchdataset.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/informers/externalversions/zalando.org/v1/elasticsearchmetricset.go b/pkg/client/informers/externalversions/zalando.org/v1/elasticsearchmetricset.go index e17dea43..deaaf985 100644 --- a/pkg/client/informers/externalversions/zalando.org/v1/elasticsearchmetricset.go +++ b/pkg/client/informers/externalversions/zalando.org/v1/elasticsearchmetricset.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/informers/externalversions/zalando.org/v1/interface.go b/pkg/client/informers/externalversions/zalando.org/v1/interface.go index 8cb6534f..7919d1ee 100644 --- a/pkg/client/informers/externalversions/zalando.org/v1/interface.go +++ b/pkg/client/informers/externalversions/zalando.org/v1/interface.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/listers/zalando.org/v1/elasticsearchdataset.go b/pkg/client/listers/zalando.org/v1/elasticsearchdataset.go index 1f622fd8..5015cf40 100644 --- a/pkg/client/listers/zalando.org/v1/elasticsearchdataset.go +++ b/pkg/client/listers/zalando.org/v1/elasticsearchdataset.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/listers/zalando.org/v1/elasticsearchmetricset.go b/pkg/client/listers/zalando.org/v1/elasticsearchmetricset.go index 00e76590..e2ee6466 100644 --- a/pkg/client/listers/zalando.org/v1/elasticsearchmetricset.go +++ b/pkg/client/listers/zalando.org/v1/elasticsearchmetricset.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/client/listers/zalando.org/v1/expansion_generated.go b/pkg/client/listers/zalando.org/v1/expansion_generated.go index 9ade5420..749f3cf2 100644 --- a/pkg/client/listers/zalando.org/v1/expansion_generated.go +++ b/pkg/client/listers/zalando.org/v1/expansion_generated.go @@ -1,5 +1,5 @@ /* -Copyright 2021 The Kubernetes Authors. +Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 15bc5856b5bdf3c8023834196683196bfcc1b546 Mon Sep 17 00:00:00 2001 From: Girish Chandrashekar Date: Mon, 7 Mar 2022 10:30:32 +0100 Subject: [PATCH 05/11] Fix lint issues Signed-off-by: Girish Chandrashekar --- operator/es_client.go | 2 +- operator/es_client_test.go | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/operator/es_client.go b/operator/es_client.go index 6fcd65cf..029aca72 100644 --- a/operator/es_client.go +++ b/operator/es_client.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "gopkg.in/guregu/null.v4" "net/http" "net/url" "sort" @@ -14,6 +13,7 @@ import ( "time" log "github.com/sirupsen/logrus" + "gopkg.in/guregu/null.v4" "gopkg.in/resty.v1" v1 "k8s.io/api/core/v1" ) diff --git a/operator/es_client_test.go b/operator/es_client_test.go index c2b3f02c..49510400 100644 --- a/operator/es_client_test.go +++ b/operator/es_client_test.go @@ -57,9 +57,8 @@ func TestDrainWithTransientSettings(t *testing.T) { func(request *http.Request) (*http.Response, error) { if numCalls == 0 { return httpmock.NewStringResponse(200, `{"transient":{"cluster":{"routing":{"rebalance":{"enable":"all"}}}}}`), nil - } else { - return httpmock.NewStringResponse(200, string(intermediateClusterSettings)), nil } + return httpmock.NewStringResponse(200, string(intermediateClusterSettings)), nil }) httpmock.RegisterResponder("PUT", "http://elasticsearch:9200/_cluster/settings", func(request *http.Request) (*http.Response, error) { From 065454a8886d95049cd559d26f7e3bae0a30ebde Mon Sep 17 00:00:00 2001 From: Girish Chandrashekar Date: Mon, 7 Mar 2022 12:30:42 +0100 Subject: [PATCH 06/11] Preserve exclude ip's set in transient cluster settings Signed-off-by: Girish Chandrashekar --- operator/es_client.go | 19 +++++++--- operator/es_client_test.go | 75 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 6 deletions(-) diff --git a/operator/es_client.go b/operator/es_client.go index 029aca72..25a78621 100644 --- a/operator/es_client.go +++ b/operator/es_client.go @@ -98,12 +98,21 @@ type ESSettings struct { Persistent ClusterSettings `json:"persistent,omitempty"` } -func (esSettings *ESSettings) copyNonEmtpyTransientSettings() { +func (esSettings *ESSettings) MergeNonEmtpyTransientSettings() { if value := esSettings.GetTransientRebalance().ValueOrZero(); value != "" { esSettings.Persistent.Cluster.Routing.Rebalance.Enable = null.StringFromPtr(&value) + esSettings.Transient.Cluster.Routing.Rebalance.Enable = null.StringFromPtr(nil) } - if value := esSettings.GetTransientExcludeIPs().ValueOrZero(); value != "" { - esSettings.Persistent.Cluster.Routing.Allocation.Exclude.IP = null.StringFromPtr(&value) + + if transientExcludeIps := esSettings.GetTransientExcludeIPs().ValueOrZero(); transientExcludeIps != "" { + persistentExcludeIps := esSettings.GetPersistentExcludeIPs().ValueOrZero() + if persistentExcludeIps == "" { + esSettings.Persistent.Cluster.Routing.Allocation.Exclude.IP = null.StringFromPtr(&transientExcludeIps) + } else { + mergedIps := null.StringFrom(transientExcludeIps + "," + persistentExcludeIps) + esSettings.Persistent.Cluster.Routing.Allocation.Exclude.IP = mergedIps + } + esSettings.Transient.Cluster.Routing.Allocation.Exclude.IP = null.StringFromPtr(nil) } } @@ -241,7 +250,7 @@ func (c *ESClient) getClusterSettings() (*ESSettings, error) { if err != nil { return nil, err } - esSettings.copyNonEmtpyTransientSettings() + esSettings.MergeNonEmtpyTransientSettings() return &esSettings, nil } @@ -298,7 +307,6 @@ func (c *ESClient) setExcludeIPs(ips string, originalESSettings *ESSettings) err } func (esSettings *ESSettings) updateExcludeIps(ips string) { - esSettings.Transient.Cluster.Routing.Allocation.Exclude.IP = null.StringFromPtr(nil) esSettings.Persistent.Cluster.Routing.Allocation.Exclude.IP = null.StringFromPtr(&ips) } @@ -318,7 +326,6 @@ func (c *ESClient) updateAutoRebalance(value string, originalESSettings *ESSetti } func (esSettings *ESSettings) updateRebalance(value string) { - esSettings.Transient.Cluster.Routing.Rebalance.Enable = null.StringFromPtr(nil) esSettings.Persistent.Cluster.Routing.Rebalance.Enable = null.StringFromPtr(&value) } diff --git a/operator/es_client_test.go b/operator/es_client_test.go index 49510400..1cad6366 100644 --- a/operator/es_client_test.go +++ b/operator/es_client_test.go @@ -11,6 +11,7 @@ import ( "github.com/jarcoal/httpmock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "gopkg.in/guregu/null.v4" v1 "k8s.io/api/core/v1" ) @@ -322,3 +323,77 @@ func TestExcludeSystemIndices(t *testing.T) { assert.Equal(t, "a", indices[0].Index, indices) } + +func TestESSettingsMergeNonEmtpyTransientSettings(t *testing.T) { + type fields struct { + Transient ClusterSettings + Persistent ClusterSettings + } + tests := []struct { + name string + fields fields + expected ESSettings + }{ + { + name: "null transient settings should remain as null persistent settings", + fields: fields{ + Transient: ClusterSettings{Cluster{Routing{ + Rebalance: Rebalance{Enable: null.StringFromPtr(nil)}, + Allocation: Allocation{Exclude{IP: null.StringFromPtr(nil)}}, + }}}, + }, + expected: ESSettings{ + Transient: ClusterSettings{Cluster{Routing{ + Rebalance: Rebalance{Enable: null.StringFromPtr(nil)}, + Allocation: Allocation{Exclude{IP: null.StringFromPtr(nil)}}, + }}}, + Persistent: ClusterSettings{Cluster{Routing{ + Rebalance: Rebalance{Enable: null.StringFromPtr(nil)}, + Allocation: Allocation{Exclude{IP: null.StringFromPtr(nil)}}, + }}}, + }, + }, + { + name: "copy over non empty transient cluster rebalance settings", + fields: fields{ + Transient: ClusterSettings{Cluster{Routing{Rebalance: Rebalance{Enable: null.StringFrom("none")}}}}, + }, + expected: ESSettings{ + Transient: ClusterSettings{Cluster{Routing{Rebalance: Rebalance{Enable: null.StringFromPtr(nil)}}}}, + Persistent: ClusterSettings{Cluster{Routing{Rebalance: Rebalance{Enable: null.StringFrom("none")}}}}, + }, + }, + { + name: "copy over non empty transient exclude ips string", + fields: fields{ + Transient: ClusterSettings{Cluster{Routing{Allocation: Allocation{Exclude{IP: null.StringFrom("1.2.3.4")}}}}}, + }, + expected: ESSettings{ + Transient: ClusterSettings{Cluster{Routing{Allocation: Allocation{Exclude{IP: null.StringFromPtr(nil)}}}}}, + Persistent: ClusterSettings{Cluster{Routing{Allocation: Allocation{Exclude{IP: null.StringFrom("1.2.3.4")}}}}}, + }, + }, + { + name: "merge existing persistent exclude ips with transient exclude ips", + fields: fields{ + Transient: ClusterSettings{Cluster{Routing{Allocation: Allocation{Exclude{IP: null.StringFrom("1.2.3.4")}}}}}, + Persistent: ClusterSettings{Cluster{Routing{Allocation: Allocation{Exclude{IP: null.StringFrom("11.21.31.41")}}}}}, + }, + expected: ESSettings{ + Transient: ClusterSettings{Cluster{Routing{Allocation: Allocation{Exclude{IP: null.StringFromPtr(nil)}}}}}, + Persistent: ClusterSettings{Cluster{Routing{Allocation: Allocation{Exclude{IP: null.StringFrom("1.2.3.4,11.21.31.41")}}}}}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + esSettings := &ESSettings{ + Transient: tt.fields.Transient, + Persistent: tt.fields.Persistent, + } + esSettings.MergeNonEmtpyTransientSettings() + assert.Equal(t, tt.expected.GetPersistentRebalance(), esSettings.GetPersistentRebalance()) + assert.Equal(t, tt.expected.GetPersistentExcludeIPs(), esSettings.GetPersistentExcludeIPs()) + }) + } +} From 847b9b31abc3e065d9905b7b8c2625d63929b74e Mon Sep 17 00:00:00 2001 From: Girish Chandrashekar Date: Mon, 7 Mar 2022 12:32:10 +0100 Subject: [PATCH 07/11] Document migration guide for migrating away from transient settings Signed-off-by: Girish Chandrashekar --- docs/migrating-from-transient-settings.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 docs/migrating-from-transient-settings.md diff --git a/docs/migrating-from-transient-settings.md b/docs/migrating-from-transient-settings.md new file mode 100644 index 00000000..94b47e6f --- /dev/null +++ b/docs/migrating-from-transient-settings.md @@ -0,0 +1,20 @@ +# Migrating away from Transient Cluster Settings + +The [transient cluster settings](https://www.elastic.co/guide/en/elasticsearch/reference/7.16/settings.html#cluster-setting-types) +are +being [deprecated](https://www.elastic.co/guide/en/elasticsearch/reference/7.16/migrating-7.16.html#breaking_716_settings_deprecations) +from ES 7.16.0. The es-operator was relying on the transient cluster settings for operating on the cluster because +transient settings have the highest priority. The es-operator controlled mainly the cluster rebalance and the exclude +ips list for the cluster scaling actions. Moving forward the es-operator will now exclusively operate only edit the +persistent cluster settings. Some teams might still rely on using the transient settings for manual cluster operations +which can inadvertently cause issues with the es-operator updates to the cluster settings causing an inconsistent +cluster state. To avoid this the es-operator is also copying any existing non empty transient settings related to the +cluster rebalance and the exclude ips list into the persistent settings before updating the new values for the +persistent settings. To avoid cluster inconsistencies with the new es-operator, we recommend the below migrations steps +before deploying the new es-operator. + +1. Follow the + official [Transient settings migration guide](https://docs.google.com/document/d/14SUV7Fi2FV2c1gSGP3VLIf-MJEMxlr7zCK_HmBCvB4U/edit?usp=sharing) + . +2. Update any custom scripts that are still operating on transient settings. +3. Deploy the new es-operator. \ No newline at end of file From b5ab444242062b5d6b7500b0489aac261a33a1a8 Mon Sep 17 00:00:00 2001 From: Girish Chandrashekar Date: Mon, 7 Mar 2022 12:35:49 +0100 Subject: [PATCH 08/11] Fix typo in method name Signed-off-by: Girish Chandrashekar Fix link Fix link to ES transient settings migration guide --- docs/migrating-from-transient-settings.md | 5 ++--- operator/es_client.go | 4 ++-- operator/es_client_test.go | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/migrating-from-transient-settings.md b/docs/migrating-from-transient-settings.md index 94b47e6f..0d073b03 100644 --- a/docs/migrating-from-transient-settings.md +++ b/docs/migrating-from-transient-settings.md @@ -14,7 +14,6 @@ persistent settings. To avoid cluster inconsistencies with the new es-operator, before deploying the new es-operator. 1. Follow the - official [Transient settings migration guide](https://docs.google.com/document/d/14SUV7Fi2FV2c1gSGP3VLIf-MJEMxlr7zCK_HmBCvB4U/edit?usp=sharing) - . + official [Transient settings migration guide](https://www.elastic.co/guide/en/elasticsearch/reference/8.1/transient-settings-migration-guide.html). 2. Update any custom scripts that are still operating on transient settings. -3. Deploy the new es-operator. \ No newline at end of file +3. Deploy the new es-operator. diff --git a/operator/es_client.go b/operator/es_client.go index 25a78621..eab72ef6 100644 --- a/operator/es_client.go +++ b/operator/es_client.go @@ -98,7 +98,7 @@ type ESSettings struct { Persistent ClusterSettings `json:"persistent,omitempty"` } -func (esSettings *ESSettings) MergeNonEmtpyTransientSettings() { +func (esSettings *ESSettings) MergeNonEmptyTransientSettings() { if value := esSettings.GetTransientRebalance().ValueOrZero(); value != "" { esSettings.Persistent.Cluster.Routing.Rebalance.Enable = null.StringFromPtr(&value) esSettings.Transient.Cluster.Routing.Rebalance.Enable = null.StringFromPtr(nil) @@ -250,7 +250,7 @@ func (c *ESClient) getClusterSettings() (*ESSettings, error) { if err != nil { return nil, err } - esSettings.MergeNonEmtpyTransientSettings() + esSettings.MergeNonEmptyTransientSettings() return &esSettings, nil } diff --git a/operator/es_client_test.go b/operator/es_client_test.go index 1cad6366..baf5abad 100644 --- a/operator/es_client_test.go +++ b/operator/es_client_test.go @@ -391,7 +391,7 @@ func TestESSettingsMergeNonEmtpyTransientSettings(t *testing.T) { Transient: tt.fields.Transient, Persistent: tt.fields.Persistent, } - esSettings.MergeNonEmtpyTransientSettings() + esSettings.MergeNonEmptyTransientSettings() assert.Equal(t, tt.expected.GetPersistentRebalance(), esSettings.GetPersistentRebalance()) assert.Equal(t, tt.expected.GetPersistentExcludeIPs(), esSettings.GetPersistentExcludeIPs()) }) From d583f8983ee82469789c7de782f9691af34bb6d1 Mon Sep 17 00:00:00 2001 From: Girish Chandrashekar Date: Mon, 14 Mar 2022 18:08:14 +0100 Subject: [PATCH 09/11] Deduplicate exclude ip's when merging from transient settings Signed-off-by: Girish Chandrashekar --- operator/es_client.go | 20 +++++++++++++++++++- operator/es_client_test.go | 11 +++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/operator/es_client.go b/operator/es_client.go index eab72ef6..1e220c1c 100644 --- a/operator/es_client.go +++ b/operator/es_client.go @@ -98,6 +98,24 @@ type ESSettings struct { Persistent ClusterSettings `json:"persistent,omitempty"` } +func deduplicateIPs(excludedIPsString string) string { + if excludedIPsString == "" { + return "" + } + + uniqueIPsMap := make(map[string]struct{}) + uniqueIPsList := []string{} + excludedIPs := strings.Split(excludedIPsString, ",") + for _, excludedIP := range excludedIPs { + if _, ok := uniqueIPsMap[excludedIP]; !ok { + uniqueIPsMap[excludedIP] = struct{}{} + uniqueIPsList = append(uniqueIPsList, excludedIP) + } + } + + return strings.Join(uniqueIPsList, ",") +} + func (esSettings *ESSettings) MergeNonEmptyTransientSettings() { if value := esSettings.GetTransientRebalance().ValueOrZero(); value != "" { esSettings.Persistent.Cluster.Routing.Rebalance.Enable = null.StringFromPtr(&value) @@ -109,7 +127,7 @@ func (esSettings *ESSettings) MergeNonEmptyTransientSettings() { if persistentExcludeIps == "" { esSettings.Persistent.Cluster.Routing.Allocation.Exclude.IP = null.StringFromPtr(&transientExcludeIps) } else { - mergedIps := null.StringFrom(transientExcludeIps + "," + persistentExcludeIps) + mergedIps := null.StringFrom(deduplicateIPs(transientExcludeIps + "," + persistentExcludeIps)) esSettings.Persistent.Cluster.Routing.Allocation.Exclude.IP = mergedIps } esSettings.Transient.Cluster.Routing.Allocation.Exclude.IP = null.StringFromPtr(nil) diff --git a/operator/es_client_test.go b/operator/es_client_test.go index baf5abad..1490ffa6 100644 --- a/operator/es_client_test.go +++ b/operator/es_client_test.go @@ -384,6 +384,17 @@ func TestESSettingsMergeNonEmtpyTransientSettings(t *testing.T) { Persistent: ClusterSettings{Cluster{Routing{Allocation: Allocation{Exclude{IP: null.StringFrom("1.2.3.4,11.21.31.41")}}}}}, }, }, + { + name: "deduplicate transient exclude ips", + fields: fields{ + Transient: ClusterSettings{Cluster{Routing{Allocation: Allocation{Exclude{IP: null.StringFrom("1.2.3.4,1.2.3.4")}}}}}, + Persistent: ClusterSettings{Cluster{Routing{Allocation: Allocation{Exclude{IP: null.StringFrom("11.21.31.41")}}}}}, + }, + expected: ESSettings{ + Transient: ClusterSettings{Cluster{Routing{Allocation: Allocation{Exclude{IP: null.StringFromPtr(nil)}}}}}, + Persistent: ClusterSettings{Cluster{Routing{Allocation: Allocation{Exclude{IP: null.StringFrom("1.2.3.4,11.21.31.41")}}}}}, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 0835bc081582e4c7e211fd25783bc25410d2274c Mon Sep 17 00:00:00 2001 From: Girish Chandrashekar Date: Thu, 24 Mar 2022 14:56:26 +0100 Subject: [PATCH 10/11] Replace null handling library with copied source code Signed-off-by: Girish Chandrashekar --- go.mod | 1 - operator/es_client.go | 2 +- operator/es_client_test.go | 2 +- operator/null/string.go | 95 +++++++++++++++++++ .../zalando.org/v1/zz_generated.deepcopy.go | 1 + 5 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 operator/null/string.go diff --git a/go.mod b/go.mod index 944e8f29..7d503357 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,6 @@ require ( github.com/stretchr/testify v1.7.0 golang.org/x/tools v0.1.1 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 - gopkg.in/guregu/null.v4 v4.0.0 gopkg.in/resty.v1 v1.12.0 k8s.io/api v0.21.5 k8s.io/apiextensions-apiserver v0.21.5 // indirect diff --git a/operator/es_client.go b/operator/es_client.go index 1e220c1c..683681af 100644 --- a/operator/es_client.go +++ b/operator/es_client.go @@ -13,7 +13,7 @@ import ( "time" log "github.com/sirupsen/logrus" - "gopkg.in/guregu/null.v4" + "github.com/zalando-incubator/es-operator/operator/null" "gopkg.in/resty.v1" v1 "k8s.io/api/core/v1" ) diff --git a/operator/es_client_test.go b/operator/es_client_test.go index 1490ffa6..7e67c91d 100644 --- a/operator/es_client_test.go +++ b/operator/es_client_test.go @@ -11,7 +11,7 @@ import ( "github.com/jarcoal/httpmock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/guregu/null.v4" + "github.com/zalando-incubator/es-operator/operator/null" v1 "k8s.io/api/core/v1" ) diff --git a/operator/null/string.go b/operator/null/string.go new file mode 100644 index 00000000..42eb466f --- /dev/null +++ b/operator/null/string.go @@ -0,0 +1,95 @@ +package null + +import ( + "bytes" + "database/sql" + "encoding/json" + "fmt" +) + +// nullBytes is a JSON null literal +var nullBytes = []byte("null") + +// String is a nullable string. It supports SQL and JSON serialization. +// It will marshal to null if null. Blank string input will be considered null. +type String struct { + sql.NullString +} + +// StringFrom creates a new String that will never be blank. +func StringFrom(s string) String { + return NewString(s, true) +} + +// StringFromPtr creates a new String that be null if s is nil. +func StringFromPtr(s *string) String { + if s == nil { + return NewString("", false) + } + return NewString(*s, true) +} + +// ValueOrZero returns the inner value if valid, otherwise zero. +func (s String) ValueOrZero() string { + if !s.Valid { + return "" + } + return s.String +} + +// NewString creates a new String +func NewString(s string, valid bool) String { + return String{ + NullString: sql.NullString{ + String: s, + Valid: valid, + }, + } +} + +// UnmarshalJSON implements json.Unmarshaler. +// It supports string and null input. Blank string input does not produce a null String. +func (s *String) UnmarshalJSON(data []byte) error { + if bytes.Equal(data, nullBytes) { + s.Valid = false + return nil + } + + if err := json.Unmarshal(data, &s.String); err != nil { + return fmt.Errorf("null: couldn't unmarshal JSON: %w", err) + } + + s.Valid = true + return nil +} + +// MarshalJSON implements json.Marshaler. +// It will encode null if this String is null. +func (s String) MarshalJSON() ([]byte, error) { + if !s.Valid { + return []byte("null"), nil + } + return json.Marshal(s.String) +} + +// MarshalText implements encoding.TextMarshaler. +// It will encode a blank string when this String is null. +func (s String) MarshalText() ([]byte, error) { + if !s.Valid { + return []byte{}, nil + } + return []byte(s.String), nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +// It will unmarshal to a null String if the input is a blank string. +func (s *String) UnmarshalText(text []byte) error { + s.String = string(text) + s.Valid = s.String != "" + return nil +} + +// Equal returns true if both strings have the same value or are both null. +func (s String) Equal(other String) bool { + return s.Valid == other.Valid && (!s.Valid || s.String == other.String) +} diff --git a/pkg/apis/zalando.org/v1/zz_generated.deepcopy.go b/pkg/apis/zalando.org/v1/zz_generated.deepcopy.go index b7b7a6c3..e39e67aa 100644 --- a/pkg/apis/zalando.org/v1/zz_generated.deepcopy.go +++ b/pkg/apis/zalando.org/v1/zz_generated.deepcopy.go @@ -1,3 +1,4 @@ +//go:build !ignore_autogenerated // +build !ignore_autogenerated /* From 1294fa20ab2561b3a72174aed9cc50f031dda0de Mon Sep 17 00:00:00 2001 From: Oliver Trosien Date: Thu, 24 Mar 2022 18:24:23 +0100 Subject: [PATCH 11/11] Revert obsolete go.sum change Signed-off-by: Oliver Trosien --- go.sum | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.sum b/go.sum index 9ae250de..2b3c45a3 100644 --- a/go.sum +++ b/go.sum @@ -861,8 +861,6 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/guregu/null.v4 v4.0.0 h1:1Wm3S1WEA2I26Kq+6vcW+w0gcDo44YKYD7YIEJNHDjg= -gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu/JrI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=