From 3ebcf3076b035894d6f6b1d82bc9c4064333a815 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=BD=95=E5=98=89=E4=BC=9F?=
<54312752+loon-hejw@users.noreply.github.com>
Date: Thu, 25 Apr 2024 14:20:08 +0800
Subject: [PATCH 01/27] feat: ServiceMonitor add label (#770)
### Description
ServiceMonitor generates labels based on crd configurationl
### Issues Resolved
solve issue #572
---------
Signed-off-by: = <13552656606@163.com>
Signed-off-by: loon-hejw <13552656606@163.com>
---
.../files/opensearch.opster.io_opensearchclusters.yaml | 4 ++++
docs/designs/crd.md | 6 ++++++
docs/userguide/main.md | 2 ++
opensearch-operator/api/v1/opensearch_types.go | 1 +
.../bases/opensearch.opster.io_opensearchclusters.yaml | 4 ++++
opensearch-operator/controllers/cluster_test.go | 10 ++++++++++
opensearch-operator/controllers/suite_test_helpers.go | 9 ++++++++-
opensearch-operator/go.sum | 9 +++++++++
opensearch-operator/pkg/builders/cluster.go | 9 ++++++++-
9 files changed, 52 insertions(+), 2 deletions(-)
diff --git a/charts/opensearch-operator/files/opensearch.opster.io_opensearchclusters.yaml b/charts/opensearch-operator/files/opensearch.opster.io_opensearchclusters.yaml
index c52e6c84..b257de52 100644
--- a/charts/opensearch-operator/files/opensearch.opster.io_opensearchclusters.yaml
+++ b/charts/opensearch-operator/files/opensearch.opster.io_opensearchclusters.yaml
@@ -2903,6 +2903,10 @@ spec:
properties:
enable:
type: boolean
+ labels:
+ additionalProperties:
+ type: string
+ type: object
monitoringUserSecret:
type: string
pluginUrl:
diff --git a/docs/designs/crd.md b/docs/designs/crd.md
index 6e5f189b..7b343d3d 100644
--- a/docs/designs/crd.md
+++ b/docs/designs/crd.md
@@ -607,6 +607,12 @@ Monitoring defines Opensearch monitoring configuration
Define if to enable monitoring for that cluster |
true |
- |
+
+ labels |
+ map[string]string |
+ Add LabelsSelector to ServiceMonitor |
+ false |
+ - |
monitoringUserSecret |
[]string |
diff --git a/docs/userguide/main.md b/docs/userguide/main.md
index eea812bd..01881ca4 100644
--- a/docs/userguide/main.md
+++ b/docs/userguide/main.md
@@ -1278,6 +1278,8 @@ spec:
version:
monitoring:
enable: true # Enable or disable the monitoring plugin
+ labels: # The labels add for ServiceMonitor
+ someLabelKey: someLabelValue
scrapeInterval: 30s # The scrape interval for Prometheus
monitoringUserSecret: monitoring-user-secret # Optional, name of a secret with username/password for prometheus to acces the plugin metrics endpoint with, defaults to the admin user
pluginUrl: https://github.com/aiven/prometheus-exporter-plugin-for-opensearch/releases/download/.0/prometheus-exporter-.0.zip # Optional, custom URL for the monitoring plugin
diff --git a/opensearch-operator/api/v1/opensearch_types.go b/opensearch-operator/api/v1/opensearch_types.go
index 8deee4ca..f3f36684 100644
--- a/opensearch-operator/api/v1/opensearch_types.go
+++ b/opensearch-operator/api/v1/opensearch_types.go
@@ -155,6 +155,7 @@ type MonitoringConfig struct {
ScrapeInterval string `json:"scrapeInterval,omitempty"`
PluginURL string `json:"pluginUrl,omitempty"`
TLSConfig *MonitoringConfigTLS `json:"tlsConfig,omitempty"`
+ Labels map[string]string `json:"labels,omitempty"`
}
type MonitoringConfigTLS struct {
diff --git a/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchclusters.yaml b/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchclusters.yaml
index c52e6c84..b257de52 100644
--- a/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchclusters.yaml
+++ b/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchclusters.yaml
@@ -2903,6 +2903,10 @@ spec:
properties:
enable:
type: boolean
+ labels:
+ additionalProperties:
+ type: string
+ type: object
monitoringUserSecret:
type: string
pluginUrl:
diff --git a/opensearch-operator/controllers/cluster_test.go b/opensearch-operator/controllers/cluster_test.go
index 56a607ad..1b8b71c0 100644
--- a/opensearch-operator/controllers/cluster_test.go
+++ b/opensearch-operator/controllers/cluster_test.go
@@ -126,6 +126,16 @@ var _ = Describe("Cluster Reconciler", func() {
// check if tlsConfig is not defined in the CRD declaration the ServiceMonitor not deploy that part of the config
// Expect(sm.Spec.Endpoints[0].TLSConfig).To(BeNil())
+
+ // check if the ServiceMonitor is using the General.Monitoring.label from the CRD declaration
+ Expect(func() bool {
+ for k, v := range OpensearchCluster.Spec.General.Monitoring.Labels {
+ if sm.Labels[k] != v {
+ return false
+ }
+ }
+ return true
+ }()).Should(BeTrue())
})
})
diff --git a/opensearch-operator/controllers/suite_test_helpers.go b/opensearch-operator/controllers/suite_test_helpers.go
index 5051378a..0972709e 100644
--- a/opensearch-operator/controllers/suite_test_helpers.go
+++ b/opensearch-operator/controllers/suite_test_helpers.go
@@ -106,7 +106,14 @@ func ComposeOpensearchCrd(clusterName string, namespace string) opsterv1.OpenSea
},
Spec: opsterv1.ClusterSpec{
General: opsterv1.GeneralConfig{
- Monitoring: opsterv1.MonitoringConfig{Enable: true, ScrapeInterval: "35s", TLSConfig: &opsterv1.MonitoringConfigTLS{InsecureSkipVerify: true, ServerName: "foo.bar"}},
+ Monitoring: opsterv1.MonitoringConfig{
+ Enable: true,
+ ScrapeInterval: "35s",
+ TLSConfig: &opsterv1.MonitoringConfigTLS{InsecureSkipVerify: true, ServerName: "foo.bar"},
+ Labels: map[string]string{
+ "foo": "bar",
+ },
+ },
HttpPort: 9200,
Vendor: "opensearch",
Version: "2.0.0",
diff --git a/opensearch-operator/go.sum b/opensearch-operator/go.sum
index f7aa1c8d..14868dd5 100644
--- a/opensearch-operator/go.sum
+++ b/opensearch-operator/go.sum
@@ -119,6 +119,7 @@ github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
+github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
@@ -161,6 +162,7 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg78
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
+github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@@ -303,6 +305,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -332,6 +335,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182aff
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/maxatome/go-testdeep v1.11.0 h1:Tgh5efyCYyJFGUYiT0qxBSIDeXw0F5zSoatlou685kk=
+github.com/maxatome/go-testdeep v1.11.0/go.mod h1:011SgQ6efzZYAen6fDn4BqQ+lUR72ysdyKe7Dyogw70=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@@ -369,6 +373,7 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
+github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
@@ -383,6 +388,7 @@ github.com/opensearch-project/opensearch-go v1.1.0 h1:eG5sh3843bbU1itPRjA9QXbxcg
github.com/opensearch-project/opensearch-go v1.1.0/go.mod h1:+6/XHCuTH+fwsMJikZEWsucZ4eZMma3zNSeLrTtVGbo=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
+github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
@@ -429,6 +435,7 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
+github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
@@ -500,6 +507,7 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
+go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
@@ -552,6 +560,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
+golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
diff --git a/opensearch-operator/pkg/builders/cluster.go b/opensearch-operator/pkg/builders/cluster.go
index e762b8fe..760a498c 100644
--- a/opensearch-operator/pkg/builders/cluster.go
+++ b/opensearch-operator/pkg/builders/cluster.go
@@ -1122,11 +1122,18 @@ func NewServiceMonitor(cr *opsterv1.OpenSearchCluster) *monitoring.ServiceMonito
tlsconfig = nil
}
+ monitorLabel := map[string]string{
+ helpers.ClusterLabel: cr.Name,
+ }
+ for k, v := range cr.Spec.General.Monitoring.Labels {
+ monitorLabel[k] = v
+ }
+
return &monitoring.ServiceMonitor{
ObjectMeta: metav1.ObjectMeta{
Name: cr.Name + "-monitor",
Namespace: cr.Namespace,
- Labels: labels,
+ Labels: monitorLabel,
},
Spec: monitoring.ServiceMonitorSpec{
JobLabel: cr.Name + "-monitor",
From 5fa7f0c85e421cc0ff3cbfd98f52ae344cd63949 Mon Sep 17 00:00:00 2001
From: Casper Thygesen
Date: Fri, 26 Apr 2024 17:28:54 +0200
Subject: [PATCH 02/27] Invalid CRD types for ISM actions and a nil pointer fix
(#788)
### Description
This fixes a few bugs found while testing the ISMPolicy custom
resources.
### Issues Resolved
_List any issues this PR will resolve, e.g. Closes [...]._
### Check List
- [x] Commits are signed per the DCO using --signoff
- [x] Unittest added for the new/changed functionality and all unit
tests are successful
- [x] Customer-visible features documented
- [x] No linter warnings (`make lint`)
If CRDs are changed:
- [x] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [x] Changes to CRDs documented
Please refer to the [PR
guidelines](https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/docs/developing.md#submitting-a-pr)
before submitting this pull request.
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
FYI @IshaGirdhar
---------
Signed-off-by: Casper Thygesen
---
...earch.opster.io_opensearchismpolicies.yaml | 7 ++--
.../api/v1/opensearchism_types.go | 10 ++++--
.../api/v1/zz_generated.deepcopy.go | 34 +++++++++++++++++--
...earch.opster.io_opensearchismpolicies.yaml | 7 ++--
.../opensearch-gateway/requests/IsmPolicy.go | 8 +++--
.../pkg/reconcilers/ismpolicy.go | 12 ++++---
6 files changed, 60 insertions(+), 18 deletions(-)
diff --git a/charts/opensearch-operator/files/opensearch.opster.io_opensearchismpolicies.yaml b/charts/opensearch-operator/files/opensearch.opster.io_opensearchismpolicies.yaml
index 42432eb8..8cdb5e1d 100644
--- a/charts/opensearch-operator/files/opensearch.opster.io_opensearchismpolicies.yaml
+++ b/charts/opensearch-operator/files/opensearch.opster.io_opensearchismpolicies.yaml
@@ -252,10 +252,10 @@ spec:
type: object
readOnly:
description: Sets a managed index to be read only.
- type: string
+ type: object
readWrite:
description: Sets a managed index to be writeable.
- type: string
+ type: object
replicaCount:
description: Sets the number of replicas to assign to
an index.
@@ -349,7 +349,8 @@ spec:
- snapshot
type: object
timeout:
- description: The timeout period for the action.
+ description: The timeout period for the action. Accepts
+ time units for minutes, hours, and days.
type: string
type: object
type: array
diff --git a/opensearch-operator/api/v1/opensearchism_types.go b/opensearch-operator/api/v1/opensearchism_types.go
index 03673dec..37cda37b 100644
--- a/opensearch-operator/api/v1/opensearchism_types.go
+++ b/opensearch-operator/api/v1/opensearchism_types.go
@@ -109,9 +109,9 @@ type Action struct {
// Opens a managed index.
Open *Open `json:"open,omitempty"`
// Sets a managed index to be read only.
- ReadOnly *string `json:"readOnly,omitempty"`
+ ReadOnly *ReadOnly `json:"readOnly,omitempty"`
// Sets a managed index to be writeable.
- ReadWrite *string `json:"readWrite,omitempty"`
+ ReadWrite *ReadWrite `json:"readWrite,omitempty"`
// Sets the number of replicas to assign to an index.
ReplicaCount *ReplicaCount `json:"replicaCount,omitempty"`
// The retry configuration for the action.
@@ -124,7 +124,7 @@ type Action struct {
Shrink *Shrink `json:"shrink,omitempty"`
// Back up your cluster’s indexes and state
Snapshot *Snapshot `json:"snapshot,omitempty"`
- // The timeout period for the action.
+ // The timeout period for the action. Accepts time units for minutes, hours, and days.
Timeout *string `json:"timeout,omitempty"`
}
@@ -161,6 +161,10 @@ type Allocation struct {
type Close struct{}
+type ReadOnly struct{}
+
+type ReadWrite struct{}
+
type Delete struct{}
type ForceMerge struct {
diff --git a/opensearch-operator/api/v1/zz_generated.deepcopy.go b/opensearch-operator/api/v1/zz_generated.deepcopy.go
index 9fe9637a..b87f794c 100644
--- a/opensearch-operator/api/v1/zz_generated.deepcopy.go
+++ b/opensearch-operator/api/v1/zz_generated.deepcopy.go
@@ -74,12 +74,12 @@ func (in *Action) DeepCopyInto(out *Action) {
}
if in.ReadOnly != nil {
in, out := &in.ReadOnly, &out.ReadOnly
- *out = new(string)
+ *out = new(ReadOnly)
**out = **in
}
if in.ReadWrite != nil {
in, out := &in.ReadWrite, &out.ReadWrite
- *out = new(string)
+ *out = new(ReadWrite)
**out = **in
}
if in.ReplicaCount != nil {
@@ -2295,6 +2295,36 @@ func (in *ProbesConfig) DeepCopy() *ProbesConfig {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ReadOnly) DeepCopyInto(out *ReadOnly) {
+ *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReadOnly.
+func (in *ReadOnly) DeepCopy() *ReadOnly {
+ if in == nil {
+ return nil
+ }
+ out := new(ReadOnly)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ReadWrite) DeepCopyInto(out *ReadWrite) {
+ *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReadWrite.
+func (in *ReadWrite) DeepCopy() *ReadWrite {
+ if in == nil {
+ return nil
+ }
+ out := new(ReadWrite)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ReadinessProbeConfig) DeepCopyInto(out *ReadinessProbeConfig) {
*out = *in
diff --git a/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchismpolicies.yaml b/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchismpolicies.yaml
index 42432eb8..8cdb5e1d 100644
--- a/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchismpolicies.yaml
+++ b/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchismpolicies.yaml
@@ -252,10 +252,10 @@ spec:
type: object
readOnly:
description: Sets a managed index to be read only.
- type: string
+ type: object
readWrite:
description: Sets a managed index to be writeable.
- type: string
+ type: object
replicaCount:
description: Sets the number of replicas to assign to
an index.
@@ -349,7 +349,8 @@ spec:
- snapshot
type: object
timeout:
- description: The timeout period for the action.
+ description: The timeout period for the action. Accepts
+ time units for minutes, hours, and days.
type: string
type: object
type: array
diff --git a/opensearch-operator/opensearch-gateway/requests/IsmPolicy.go b/opensearch-operator/opensearch-gateway/requests/IsmPolicy.go
index 2e5adc4f..5d292a6e 100644
--- a/opensearch-operator/opensearch-gateway/requests/IsmPolicy.go
+++ b/opensearch-operator/opensearch-gateway/requests/IsmPolicy.go
@@ -78,9 +78,9 @@ type Action struct {
// Opens a managed index.
Open *Open `json:"open,omitempty"`
// Sets a managed index to be read only.
- ReadOnly *string `json:"read_only,omitempty"`
+ ReadOnly *ReadOnly `json:"read_only,omitempty"`
// Sets a managed index to be writeable.
- ReadWrite *string `json:"read_write,omitempty"`
+ ReadWrite *ReadWrite `json:"read_write,omitempty"`
// Sets the number of replicas to assign to an index.
ReplicaCount *ReplicaCount `json:"replica_count,omitempty"`
// The retry configuration for the action.
@@ -130,6 +130,10 @@ type Allocation struct {
type Close struct{}
+type ReadOnly struct{}
+
+type ReadWrite struct{}
+
type Delete struct{}
type ForceMerge struct {
diff --git a/opensearch-operator/pkg/reconcilers/ismpolicy.go b/opensearch-operator/pkg/reconcilers/ismpolicy.go
index 53e5cea7..06dcb42a 100644
--- a/opensearch-operator/pkg/reconcilers/ismpolicy.go
+++ b/opensearch-operator/pkg/reconcilers/ismpolicy.go
@@ -427,7 +427,9 @@ func (r *IsmPolicyReconciler) CreateISMPolicyRequest() (*requests.Policy, error)
}
var indexPri *requests.IndexPriority
if action.IndexPriority != nil {
- indexPri.Priority = action.IndexPriority.Priority
+ indexPri = &requests.IndexPriority{
+ Priority: action.IndexPriority.Priority,
+ }
}
var snapshot *requests.Snapshot
if action.Snapshot != nil {
@@ -455,13 +457,13 @@ func (r *IsmPolicyReconciler) CreateISMPolicyRequest() (*requests.Policy, error)
if action.Timeout != nil {
timeOut = action.Timeout
}
- var readWrite *string
+ var readWrite *requests.ReadWrite
if action.ReadWrite != nil {
- readWrite = action.ReadWrite
+ readWrite = &requests.ReadWrite{}
}
- var readOnly *string
+ var readOnly *requests.ReadOnly
if action.ReadOnly != nil {
- readOnly = action.ReadOnly
+ readOnly = &requests.ReadOnly{}
}
actions = append(actions, requests.Action{
ReplicaCount: replicaCount,
From ea46394e62ea63f7d997f4ab88d5eae5badf1e3b Mon Sep 17 00:00:00 2001
From: Nilushan Costa
Date: Mon, 13 May 2024 17:20:40 +0530
Subject: [PATCH 03/27] Add retry for opensearch client creation in ISM Policy
reconciler (#805)
### Description
Add retry for opensearch client creation in ISM policy reconciler to fix
panic
Minor change - Remove extra whitespace in developing.md file
### Issues Resolved
Resolves
https://github.com/opensearch-project/opensearch-k8s-operator/issues/801
### Check List
- [x] Commits are signed per the DCO using --signoff
- [ ] Unittest added for the new/changed functionality and all unit
tests are successful
- [ ] Customer-visible features documented
- [x] No linter warnings (`make lint`)
If CRDs are changed:
- [ ] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [ ] Changes to CRDs documented
Please refer to the [PR
guidelines](https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/docs/developing.md#submitting-a-pr)
before submitting this pull request.
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
---------
Signed-off-by: Nilushan Costa
---
docs/developing.md | 2 +-
opensearch-operator/pkg/reconcilers/ismpolicy.go | 6 ++++++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/docs/developing.md b/docs/developing.md
index 253aa35e..c1414758 100644
--- a/docs/developing.md
+++ b/docs/developing.md
@@ -118,7 +118,7 @@ All PRs must conform to the following rules:
* If you make changes to the CRD the CRD YAMLs must be updated (via `make manifests`) and also copied into the helm chart:
```bash
- cp opensearch-operator/config/crd/bases/opensearch.opster.io_*.yaml charts/opensearch-operator/files/
+ cp opensearch-operator/config/crd/bases/opensearch.opster.io_*.yaml charts/opensearch-operator/files/
```
* Changes to the CRD must be documented in the [CRD reference](./designs/crd.md)
diff --git a/opensearch-operator/pkg/reconcilers/ismpolicy.go b/opensearch-operator/pkg/reconcilers/ismpolicy.go
index 06dcb42a..32343f3a 100644
--- a/opensearch-operator/pkg/reconcilers/ismpolicy.go
+++ b/opensearch-operator/pkg/reconcilers/ismpolicy.go
@@ -146,6 +146,12 @@ func (r *IsmPolicyReconciler) Reconcile() (retResult ctrl.Result, retErr error)
if err != nil {
reason := "error creating opensearch client"
r.recorder.Event(r.instance, "Warning", opensearchError, reason)
+ retResult = ctrl.Result{
+ Requeue: true,
+ RequeueAfter: 30 * time.Second,
+ }
+ retErr = err
+ return
}
// If PolicyID not provided explicitly, use metadata.name by default
From f38eb26bea8330964259d247dc347df114b6cdda Mon Sep 17 00:00:00 2001
From: Zack Brenton
Date: Mon, 13 May 2024 11:10:26 -0300
Subject: [PATCH 04/27] Sort component template fields before compare to avoid
false positives (#809)
### Description
This fixes the same issue that previously existed with
`OpenSearchIndexTemplate` resources (#731), which was fixed in #727 but
did not apply the fix to `OpenSearchComponentTemplate` resources.
### Issues Resolved
Fixes #799
### Check List
- [x] Commits are signed per the DCO using --signoff
- [ ] Unittest added for the new/changed functionality and all unit
tests are successful
- [ ] Customer-visible features documented
- [x] No linter warnings (`make lint`)
If CRDs are changed:
- [ ] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [ ] Changes to CRDs documented
Please refer to the [PR
guidelines](https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/docs/developing.md#submitting-a-pr)
before submitting this pull request.
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
Signed-off-by: Zack Brenton
---
.../opensearch-gateway/services/os_data_service.go | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/opensearch-operator/opensearch-gateway/services/os_data_service.go b/opensearch-operator/opensearch-gateway/services/os_data_service.go
index da8ef708..2c01762d 100644
--- a/opensearch-operator/opensearch-gateway/services/os_data_service.go
+++ b/opensearch-operator/opensearch-gateway/services/os_data_service.go
@@ -476,6 +476,20 @@ func ShouldUpdateComponentTemplate(
return false, fmt.Errorf("returned component template named '%s' does not equal the requested name '%s'", componentTemplateResponse.Name, componentTemplateName)
}
+ if componentTemplateResponse.ComponentTemplate.Template.Settings != nil {
+ componentTemplateResponse.ComponentTemplate.Template.Settings, err = helpers.SortedJsonKeys(componentTemplateResponse.ComponentTemplate.Template.Settings)
+ if err != nil {
+ return false, err
+ }
+ }
+
+ if componentTemplateResponse.ComponentTemplate.Template.Mappings != nil {
+ componentTemplateResponse.ComponentTemplate.Template.Mappings, err = helpers.SortedJsonKeys(componentTemplateResponse.ComponentTemplate.Template.Mappings)
+ if err != nil {
+ return false, err
+ }
+ }
+
if cmp.Equal(componentTemplate, componentTemplateResponse.ComponentTemplate, cmpopts.EquateEmpty()) {
return false, nil
}
From dfed662032042037c19a54979d9f64db4df58141 Mon Sep 17 00:00:00 2001
From: Zack Brenton
Date: Mon, 13 May 2024 11:18:25 -0300
Subject: [PATCH 05/27] Support projected volumes as additionalVolumes (#808)
### Description
This allows using a [projected
volume](https://kubernetes.io/docs/concepts/storage/projected-volumes/)
under `spec.general.additionalVolumes`.
My specific use case is to allow using a `serviceAccountToken` mount to
enable [AWS IAM Roles for service
accounts](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)
on EKS (and EC2) clusters, though I'm sure there are others.
### Issues Resolved
_List any issues this PR will resolve, e.g. Closes [...]._
Fixes #459
### Check List
- [x] Commits are signed per the DCO using --signoff
- [x] Unittest added for the new/changed functionality and all unit
tests are successful
- [x] Customer-visible features documented
- [x] No linter warnings (`make lint`)
If CRDs are changed:
- [x] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [x] Changes to CRDs documented
Please refer to the [PR
guidelines](https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/docs/developing.md#submitting-a-pr)
before submitting this pull request.
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
---------
Signed-off-by: Zack Brenton
---
...ensearch.opster.io_opensearchclusters.yaml | 518 +++++++++++++++++-
docs/designs/crd.md | 7 +
docs/userguide/main.md | 10 +-
.../api/v1/opensearch_types.go | 2 +
.../api/v1/zz_generated.deepcopy.go | 13 +-
...ensearch.opster.io_opensearchclusters.yaml | 518 +++++++++++++++++-
.../controllers/cluster_test.go | 5 +
.../controllers/suite_test_helpers.go | 23 +-
.../pkg/reconcilers/util/util.go | 10 +-
.../pkg/reconcilers/util/util_test.go | 47 ++
10 files changed, 1108 insertions(+), 45 deletions(-)
diff --git a/charts/opensearch-operator/files/opensearch.opster.io_opensearchclusters.yaml b/charts/opensearch-operator/files/opensearch.opster.io_opensearchclusters.yaml
index b257de52..1ea3615c 100644
--- a/charts/opensearch-operator/files/opensearch.opster.io_opensearchclusters.yaml
+++ b/charts/opensearch-operator/files/opensearch.opster.io_opensearchclusters.yaml
@@ -1098,6 +1098,245 @@ spec:
description: Path in the container to mount the volume at.
Required.
type: string
+ projected:
+ description: Projected object to use to populate the volume
+ properties:
+ defaultMode:
+ description: |-
+ defaultMode are the mode bits used to set permissions on created files by default.
+ Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+ YAML accepts both octal and decimal values, JSON requires decimal values for mode bits.
+ Directories within the path are not affected by this setting.
+ This might be in conflict with other options that affect the file
+ mode, like fsGroup, and the result can be other mode bits set.
+ format: int32
+ type: integer
+ sources:
+ description: sources is the list of volume projections
+ items:
+ description: Projection that may be projected along
+ with other supported volume types
+ properties:
+ configMap:
+ description: configMap information about the configMap
+ data to project
+ properties:
+ items:
+ description: |-
+ items if unspecified, each key-value pair in the Data field of the referenced
+ ConfigMap will be projected into the volume as a file whose name is the
+ key and content is the value. If specified, the listed keys will be
+ projected into the specified paths, and unlisted keys will not be
+ present. If a key is specified which is not present in the ConfigMap,
+ the volume setup will error unless it is marked optional. Paths must be
+ relative and may not contain the '..' path or start with '..'.
+ items:
+ description: Maps a string key to a path
+ within a volume.
+ properties:
+ key:
+ description: key is the key to project.
+ type: string
+ mode:
+ description: |-
+ mode is Optional: mode bits used to set permissions on this file.
+ Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+ YAML accepts both octal and decimal values, JSON requires decimal values for mode bits.
+ If not specified, the volume defaultMode will be used.
+ This might be in conflict with other options that affect the file
+ mode, like fsGroup, and the result can be other mode bits set.
+ format: int32
+ type: integer
+ path:
+ description: |-
+ path is the relative path of the file to map the key to.
+ May not be an absolute path.
+ May not contain the path element '..'.
+ May not start with the string '..'.
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ name:
+ description: |-
+ Name of the referent.
+ More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind, uid?
+ type: string
+ optional:
+ description: optional specify whether the
+ ConfigMap or its keys must be defined
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ downwardAPI:
+ description: downwardAPI information about the
+ downwardAPI data to project
+ properties:
+ items:
+ description: Items is a list of DownwardAPIVolume
+ file
+ items:
+ description: DownwardAPIVolumeFile represents
+ information to create the file containing
+ the pod field
+ properties:
+ fieldRef:
+ description: 'Required: Selects a field
+ of the pod: only annotations, labels,
+ name and namespace are supported.'
+ properties:
+ apiVersion:
+ description: Version of the schema
+ the FieldPath is written in terms
+ of, defaults to "v1".
+ type: string
+ fieldPath:
+ description: Path of the field to
+ select in the specified API version.
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ mode:
+ description: |-
+ Optional: mode bits used to set permissions on this file, must be an octal value
+ between 0000 and 0777 or a decimal value between 0 and 511.
+ YAML accepts both octal and decimal values, JSON requires decimal values for mode bits.
+ If not specified, the volume defaultMode will be used.
+ This might be in conflict with other options that affect the file
+ mode, like fsGroup, and the result can be other mode bits set.
+ format: int32
+ type: integer
+ path:
+ description: 'Required: Path is the
+ relative path name of the file to
+ be created. Must not be absolute or
+ contain the ''..'' path. Must be utf-8
+ encoded. The first item of the relative
+ path must not start with ''..'''
+ type: string
+ resourceFieldRef:
+ description: |-
+ Selects a resource of the container: only resources limits and requests
+ (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.
+ properties:
+ containerName:
+ description: 'Container name: required
+ for volumes, optional for env
+ vars'
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ description: Specifies the output
+ format of the exposed resources,
+ defaults to "1"
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ description: 'Required: resource
+ to select'
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - path
+ type: object
+ type: array
+ type: object
+ secret:
+ description: secret information about the secret
+ data to project
+ properties:
+ items:
+ description: |-
+ items if unspecified, each key-value pair in the Data field of the referenced
+ Secret will be projected into the volume as a file whose name is the
+ key and content is the value. If specified, the listed keys will be
+ projected into the specified paths, and unlisted keys will not be
+ present. If a key is specified which is not present in the Secret,
+ the volume setup will error unless it is marked optional. Paths must be
+ relative and may not contain the '..' path or start with '..'.
+ items:
+ description: Maps a string key to a path
+ within a volume.
+ properties:
+ key:
+ description: key is the key to project.
+ type: string
+ mode:
+ description: |-
+ mode is Optional: mode bits used to set permissions on this file.
+ Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+ YAML accepts both octal and decimal values, JSON requires decimal values for mode bits.
+ If not specified, the volume defaultMode will be used.
+ This might be in conflict with other options that affect the file
+ mode, like fsGroup, and the result can be other mode bits set.
+ format: int32
+ type: integer
+ path:
+ description: |-
+ path is the relative path of the file to map the key to.
+ May not be an absolute path.
+ May not contain the path element '..'.
+ May not start with the string '..'.
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ name:
+ description: |-
+ Name of the referent.
+ More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind, uid?
+ type: string
+ optional:
+ description: optional field specify whether
+ the Secret or its key must be defined
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ serviceAccountToken:
+ description: serviceAccountToken is information
+ about the serviceAccountToken data to project
+ properties:
+ audience:
+ description: |-
+ audience is the intended audience of the token. A recipient of a token
+ must identify itself with an identifier specified in the audience of the
+ token, and otherwise should reject the token. The audience defaults to the
+ identifier of the apiserver.
+ type: string
+ expirationSeconds:
+ description: |-
+ expirationSeconds is the requested duration of validity of the service
+ account token. As the token approaches expiration, the kubelet volume
+ plugin will proactively rotate the service account token. The kubelet will
+ start trying to rotate the token if the token is older than 80 percent of
+ its time to live or if the token is older than 24 hours.Defaults to 1 hour
+ and must be at least 10 minutes.
+ format: int64
+ type: integer
+ path:
+ description: |-
+ path is the path relative to the mount point of the file to project the
+ token into.
+ type: string
+ required:
+ - path
+ type: object
+ type: object
+ type: array
+ type: object
restartPods:
description: Whether to restart the pods on content change
type: boolean
@@ -2766,6 +3005,245 @@ spec:
description: Path in the container to mount the volume at.
Required.
type: string
+ projected:
+ description: Projected object to use to populate the volume
+ properties:
+ defaultMode:
+ description: |-
+ defaultMode are the mode bits used to set permissions on created files by default.
+ Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+ YAML accepts both octal and decimal values, JSON requires decimal values for mode bits.
+ Directories within the path are not affected by this setting.
+ This might be in conflict with other options that affect the file
+ mode, like fsGroup, and the result can be other mode bits set.
+ format: int32
+ type: integer
+ sources:
+ description: sources is the list of volume projections
+ items:
+ description: Projection that may be projected along
+ with other supported volume types
+ properties:
+ configMap:
+ description: configMap information about the configMap
+ data to project
+ properties:
+ items:
+ description: |-
+ items if unspecified, each key-value pair in the Data field of the referenced
+ ConfigMap will be projected into the volume as a file whose name is the
+ key and content is the value. If specified, the listed keys will be
+ projected into the specified paths, and unlisted keys will not be
+ present. If a key is specified which is not present in the ConfigMap,
+ the volume setup will error unless it is marked optional. Paths must be
+ relative and may not contain the '..' path or start with '..'.
+ items:
+ description: Maps a string key to a path
+ within a volume.
+ properties:
+ key:
+ description: key is the key to project.
+ type: string
+ mode:
+ description: |-
+ mode is Optional: mode bits used to set permissions on this file.
+ Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+ YAML accepts both octal and decimal values, JSON requires decimal values for mode bits.
+ If not specified, the volume defaultMode will be used.
+ This might be in conflict with other options that affect the file
+ mode, like fsGroup, and the result can be other mode bits set.
+ format: int32
+ type: integer
+ path:
+ description: |-
+ path is the relative path of the file to map the key to.
+ May not be an absolute path.
+ May not contain the path element '..'.
+ May not start with the string '..'.
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ name:
+ description: |-
+ Name of the referent.
+ More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind, uid?
+ type: string
+ optional:
+ description: optional specify whether the
+ ConfigMap or its keys must be defined
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ downwardAPI:
+ description: downwardAPI information about the
+ downwardAPI data to project
+ properties:
+ items:
+ description: Items is a list of DownwardAPIVolume
+ file
+ items:
+ description: DownwardAPIVolumeFile represents
+ information to create the file containing
+ the pod field
+ properties:
+ fieldRef:
+ description: 'Required: Selects a field
+ of the pod: only annotations, labels,
+ name and namespace are supported.'
+ properties:
+ apiVersion:
+ description: Version of the schema
+ the FieldPath is written in terms
+ of, defaults to "v1".
+ type: string
+ fieldPath:
+ description: Path of the field to
+ select in the specified API version.
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ mode:
+ description: |-
+ Optional: mode bits used to set permissions on this file, must be an octal value
+ between 0000 and 0777 or a decimal value between 0 and 511.
+ YAML accepts both octal and decimal values, JSON requires decimal values for mode bits.
+ If not specified, the volume defaultMode will be used.
+ This might be in conflict with other options that affect the file
+ mode, like fsGroup, and the result can be other mode bits set.
+ format: int32
+ type: integer
+ path:
+ description: 'Required: Path is the
+ relative path name of the file to
+ be created. Must not be absolute or
+ contain the ''..'' path. Must be utf-8
+ encoded. The first item of the relative
+ path must not start with ''..'''
+ type: string
+ resourceFieldRef:
+ description: |-
+ Selects a resource of the container: only resources limits and requests
+ (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.
+ properties:
+ containerName:
+ description: 'Container name: required
+ for volumes, optional for env
+ vars'
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ description: Specifies the output
+ format of the exposed resources,
+ defaults to "1"
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ description: 'Required: resource
+ to select'
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - path
+ type: object
+ type: array
+ type: object
+ secret:
+ description: secret information about the secret
+ data to project
+ properties:
+ items:
+ description: |-
+ items if unspecified, each key-value pair in the Data field of the referenced
+ Secret will be projected into the volume as a file whose name is the
+ key and content is the value. If specified, the listed keys will be
+ projected into the specified paths, and unlisted keys will not be
+ present. If a key is specified which is not present in the Secret,
+ the volume setup will error unless it is marked optional. Paths must be
+ relative and may not contain the '..' path or start with '..'.
+ items:
+ description: Maps a string key to a path
+ within a volume.
+ properties:
+ key:
+ description: key is the key to project.
+ type: string
+ mode:
+ description: |-
+ mode is Optional: mode bits used to set permissions on this file.
+ Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+ YAML accepts both octal and decimal values, JSON requires decimal values for mode bits.
+ If not specified, the volume defaultMode will be used.
+ This might be in conflict with other options that affect the file
+ mode, like fsGroup, and the result can be other mode bits set.
+ format: int32
+ type: integer
+ path:
+ description: |-
+ path is the relative path of the file to map the key to.
+ May not be an absolute path.
+ May not contain the path element '..'.
+ May not start with the string '..'.
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ name:
+ description: |-
+ Name of the referent.
+ More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind, uid?
+ type: string
+ optional:
+ description: optional field specify whether
+ the Secret or its key must be defined
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ serviceAccountToken:
+ description: serviceAccountToken is information
+ about the serviceAccountToken data to project
+ properties:
+ audience:
+ description: |-
+ audience is the intended audience of the token. A recipient of a token
+ must identify itself with an identifier specified in the audience of the
+ token, and otherwise should reject the token. The audience defaults to the
+ identifier of the apiserver.
+ type: string
+ expirationSeconds:
+ description: |-
+ expirationSeconds is the requested duration of validity of the service
+ account token. As the token approaches expiration, the kubelet volume
+ plugin will proactively rotate the service account token. The kubelet will
+ start trying to rotate the token if the token is older than 80 percent of
+ its time to live or if the token is older than 24 hours.Defaults to 1 hour
+ and must be at least 10 minutes.
+ format: int64
+ type: integer
+ path:
+ description: |-
+ path is the path relative to the mount point of the file to project the
+ token into.
+ type: string
+ required:
+ - path
+ type: object
+ type: object
+ type: array
+ type: object
restartPods:
description: Whether to restart the pods on content change
type: boolean
@@ -4770,21 +5248,25 @@ spec:
resource requirements.
properties:
claims:
- description: "Claims lists the names of resources,
- defined in spec.resourceClaims, that are used by
- this container. \n This is an alpha field and requires
- enabling the DynamicResourceAllocation feature gate.
- \n This field is immutable. It can only be set for
- containers."
+ description: |-
+ Claims lists the names of resources, defined in spec.resourceClaims,
+ that are used by this container.
+
+
+ This is an alpha field and requires enabling the
+ DynamicResourceAllocation feature gate.
+
+
+ This field is immutable. It can only be set for containers.
items:
description: ResourceClaim references one entry
in PodSpec.ResourceClaims.
properties:
name:
- description: Name must match the name of one
- entry in pod.spec.resourceClaims of the Pod
- where this field is used. It makes that resource
- available inside a container.
+ description: |-
+ Name must match the name of one entry in pod.spec.resourceClaims of
+ the Pod where this field is used. It makes that resource available
+ inside a container.
type: string
required:
- name
@@ -4800,8 +5282,9 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Limits describes the maximum amount
- of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+ description: |-
+ Limits describes the maximum amount of compute resources allowed.
+ More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
type: object
requests:
additionalProperties:
@@ -4810,12 +5293,11 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Requests describes the minimum amount
- of compute resources required. If Requests is omitted
- for a container, it defaults to Limits if that is
- explicitly specified, otherwise to an implementation-defined
- value. Requests cannot exceed Limits. More info:
- https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+ description: |-
+ Requests describes the minimum amount of compute resources required.
+ If Requests is omitted for a container, it defaults to Limits if that is explicitly specified,
+ otherwise to an implementation-defined value. Requests cannot exceed Limits.
+ More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
type: object
type: object
type: object
diff --git a/docs/designs/crd.md b/docs/designs/crd.md
index 7b343d3d..ceb49c0e 100644
--- a/docs/designs/crd.md
+++ b/docs/designs/crd.md
@@ -770,6 +770,13 @@ AdditionalVolume object define additional volume and volumeMount
false |
- |
+
+ projected |
+ corev1.ProjectedVolumeSource |
+ Defines the Projected object to be mounted |
+ false |
+ - |
+
diff --git a/docs/userguide/main.md b/docs/userguide/main.md
index 01881ca4..e554abc4 100644
--- a/docs/userguide/main.md
+++ b/docs/userguide/main.md
@@ -698,7 +698,7 @@ spec:
### Additional Volumes
-Sometimes it is neccessary to mount ConfigMaps, Secrets, emptyDir or CSI volumes into the Opensearch pods as volumes to provide additional configuration (e.g. plugin config files). This can be achieved by providing an array of additional volumes to mount to the custom resource. This option is located in either `spec.general.additionalVolumes` or `spec.dashboards.additionalVolumes`. The format is as follows:
+Sometimes it is neccessary to mount ConfigMaps, Secrets, emptyDir, projected volumes, or CSI volumes into the Opensearch pods as volumes to provide additional configuration (e.g. plugin config files). This can be achieved by providing an array of additional volumes to mount to the custom resource. This option is located in either `spec.general.additionalVolumes` or `spec.dashboards.additionalVolumes`. The format is as follows:
```yaml
spec:
@@ -720,7 +720,13 @@ spec:
driver: csi-driver-name
readOnly: true
volumeAttributes:
- secretProviderClass: example-secret-provider-class
+ secretProviderClass: example-secret-provider-class
+ - name: example-projected-volume
+ path: /path/to/mount/volume
+ projected:
+ sources:
+ serviceAccountToken:
+ path: "token"
dashboards:
additionalVolumes:
- name: example-secret
diff --git a/opensearch-operator/api/v1/opensearch_types.go b/opensearch-operator/api/v1/opensearch_types.go
index f3f36684..75e822ab 100644
--- a/opensearch-operator/api/v1/opensearch_types.go
+++ b/opensearch-operator/api/v1/opensearch_types.go
@@ -296,6 +296,8 @@ type AdditionalVolume struct {
EmptyDir *corev1.EmptyDirVolumeSource `json:"emptyDir,omitempty"`
// CSI object to use to populate the volume
CSI *corev1.CSIVolumeSource `json:"csi,omitempty"`
+ // Projected object to use to populate the volume
+ Projected *corev1.ProjectedVolumeSource `json:"projected,omitempty"`
// Whether to restart the pods on content change
RestartPods bool `json:"restartPods,omitempty"`
}
diff --git a/opensearch-operator/api/v1/zz_generated.deepcopy.go b/opensearch-operator/api/v1/zz_generated.deepcopy.go
index b87f794c..da195040 100644
--- a/opensearch-operator/api/v1/zz_generated.deepcopy.go
+++ b/opensearch-operator/api/v1/zz_generated.deepcopy.go
@@ -1,5 +1,4 @@
//go:build !ignore_autogenerated
-// +build !ignore_autogenerated
/*
Copyright 2021.
@@ -152,6 +151,11 @@ func (in *AdditionalVolume) DeepCopyInto(out *AdditionalVolume) {
*out = new(corev1.CSIVolumeSource)
(*in).DeepCopyInto(*out)
}
+ if in.Projected != nil {
+ in, out := &in.Projected, &out.Projected
+ *out = new(corev1.ProjectedVolumeSource)
+ (*in).DeepCopyInto(*out)
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdditionalVolume.
@@ -948,6 +952,13 @@ func (in *MonitoringConfig) DeepCopyInto(out *MonitoringConfig) {
*out = new(MonitoringConfigTLS)
**out = **in
}
+ if in.Labels != nil {
+ in, out := &in.Labels, &out.Labels
+ *out = make(map[string]string, len(*in))
+ for key, val := range *in {
+ (*out)[key] = val
+ }
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonitoringConfig.
diff --git a/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchclusters.yaml b/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchclusters.yaml
index b257de52..1ea3615c 100644
--- a/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchclusters.yaml
+++ b/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchclusters.yaml
@@ -1098,6 +1098,245 @@ spec:
description: Path in the container to mount the volume at.
Required.
type: string
+ projected:
+ description: Projected object to use to populate the volume
+ properties:
+ defaultMode:
+ description: |-
+ defaultMode are the mode bits used to set permissions on created files by default.
+ Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+ YAML accepts both octal and decimal values, JSON requires decimal values for mode bits.
+ Directories within the path are not affected by this setting.
+ This might be in conflict with other options that affect the file
+ mode, like fsGroup, and the result can be other mode bits set.
+ format: int32
+ type: integer
+ sources:
+ description: sources is the list of volume projections
+ items:
+ description: Projection that may be projected along
+ with other supported volume types
+ properties:
+ configMap:
+ description: configMap information about the configMap
+ data to project
+ properties:
+ items:
+ description: |-
+ items if unspecified, each key-value pair in the Data field of the referenced
+ ConfigMap will be projected into the volume as a file whose name is the
+ key and content is the value. If specified, the listed keys will be
+ projected into the specified paths, and unlisted keys will not be
+ present. If a key is specified which is not present in the ConfigMap,
+ the volume setup will error unless it is marked optional. Paths must be
+ relative and may not contain the '..' path or start with '..'.
+ items:
+ description: Maps a string key to a path
+ within a volume.
+ properties:
+ key:
+ description: key is the key to project.
+ type: string
+ mode:
+ description: |-
+ mode is Optional: mode bits used to set permissions on this file.
+ Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+ YAML accepts both octal and decimal values, JSON requires decimal values for mode bits.
+ If not specified, the volume defaultMode will be used.
+ This might be in conflict with other options that affect the file
+ mode, like fsGroup, and the result can be other mode bits set.
+ format: int32
+ type: integer
+ path:
+ description: |-
+ path is the relative path of the file to map the key to.
+ May not be an absolute path.
+ May not contain the path element '..'.
+ May not start with the string '..'.
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ name:
+ description: |-
+ Name of the referent.
+ More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind, uid?
+ type: string
+ optional:
+ description: optional specify whether the
+ ConfigMap or its keys must be defined
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ downwardAPI:
+ description: downwardAPI information about the
+ downwardAPI data to project
+ properties:
+ items:
+ description: Items is a list of DownwardAPIVolume
+ file
+ items:
+ description: DownwardAPIVolumeFile represents
+ information to create the file containing
+ the pod field
+ properties:
+ fieldRef:
+ description: 'Required: Selects a field
+ of the pod: only annotations, labels,
+ name and namespace are supported.'
+ properties:
+ apiVersion:
+ description: Version of the schema
+ the FieldPath is written in terms
+ of, defaults to "v1".
+ type: string
+ fieldPath:
+ description: Path of the field to
+ select in the specified API version.
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ mode:
+ description: |-
+ Optional: mode bits used to set permissions on this file, must be an octal value
+ between 0000 and 0777 or a decimal value between 0 and 511.
+ YAML accepts both octal and decimal values, JSON requires decimal values for mode bits.
+ If not specified, the volume defaultMode will be used.
+ This might be in conflict with other options that affect the file
+ mode, like fsGroup, and the result can be other mode bits set.
+ format: int32
+ type: integer
+ path:
+ description: 'Required: Path is the
+ relative path name of the file to
+ be created. Must not be absolute or
+ contain the ''..'' path. Must be utf-8
+ encoded. The first item of the relative
+ path must not start with ''..'''
+ type: string
+ resourceFieldRef:
+ description: |-
+ Selects a resource of the container: only resources limits and requests
+ (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.
+ properties:
+ containerName:
+ description: 'Container name: required
+ for volumes, optional for env
+ vars'
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ description: Specifies the output
+ format of the exposed resources,
+ defaults to "1"
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ description: 'Required: resource
+ to select'
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - path
+ type: object
+ type: array
+ type: object
+ secret:
+ description: secret information about the secret
+ data to project
+ properties:
+ items:
+ description: |-
+ items if unspecified, each key-value pair in the Data field of the referenced
+ Secret will be projected into the volume as a file whose name is the
+ key and content is the value. If specified, the listed keys will be
+ projected into the specified paths, and unlisted keys will not be
+ present. If a key is specified which is not present in the Secret,
+ the volume setup will error unless it is marked optional. Paths must be
+ relative and may not contain the '..' path or start with '..'.
+ items:
+ description: Maps a string key to a path
+ within a volume.
+ properties:
+ key:
+ description: key is the key to project.
+ type: string
+ mode:
+ description: |-
+ mode is Optional: mode bits used to set permissions on this file.
+ Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+ YAML accepts both octal and decimal values, JSON requires decimal values for mode bits.
+ If not specified, the volume defaultMode will be used.
+ This might be in conflict with other options that affect the file
+ mode, like fsGroup, and the result can be other mode bits set.
+ format: int32
+ type: integer
+ path:
+ description: |-
+ path is the relative path of the file to map the key to.
+ May not be an absolute path.
+ May not contain the path element '..'.
+ May not start with the string '..'.
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ name:
+ description: |-
+ Name of the referent.
+ More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind, uid?
+ type: string
+ optional:
+ description: optional field specify whether
+ the Secret or its key must be defined
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ serviceAccountToken:
+ description: serviceAccountToken is information
+ about the serviceAccountToken data to project
+ properties:
+ audience:
+ description: |-
+ audience is the intended audience of the token. A recipient of a token
+ must identify itself with an identifier specified in the audience of the
+ token, and otherwise should reject the token. The audience defaults to the
+ identifier of the apiserver.
+ type: string
+ expirationSeconds:
+ description: |-
+ expirationSeconds is the requested duration of validity of the service
+ account token. As the token approaches expiration, the kubelet volume
+ plugin will proactively rotate the service account token. The kubelet will
+ start trying to rotate the token if the token is older than 80 percent of
+ its time to live or if the token is older than 24 hours.Defaults to 1 hour
+ and must be at least 10 minutes.
+ format: int64
+ type: integer
+ path:
+ description: |-
+ path is the path relative to the mount point of the file to project the
+ token into.
+ type: string
+ required:
+ - path
+ type: object
+ type: object
+ type: array
+ type: object
restartPods:
description: Whether to restart the pods on content change
type: boolean
@@ -2766,6 +3005,245 @@ spec:
description: Path in the container to mount the volume at.
Required.
type: string
+ projected:
+ description: Projected object to use to populate the volume
+ properties:
+ defaultMode:
+ description: |-
+ defaultMode are the mode bits used to set permissions on created files by default.
+ Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+ YAML accepts both octal and decimal values, JSON requires decimal values for mode bits.
+ Directories within the path are not affected by this setting.
+ This might be in conflict with other options that affect the file
+ mode, like fsGroup, and the result can be other mode bits set.
+ format: int32
+ type: integer
+ sources:
+ description: sources is the list of volume projections
+ items:
+ description: Projection that may be projected along
+ with other supported volume types
+ properties:
+ configMap:
+ description: configMap information about the configMap
+ data to project
+ properties:
+ items:
+ description: |-
+ items if unspecified, each key-value pair in the Data field of the referenced
+ ConfigMap will be projected into the volume as a file whose name is the
+ key and content is the value. If specified, the listed keys will be
+ projected into the specified paths, and unlisted keys will not be
+ present. If a key is specified which is not present in the ConfigMap,
+ the volume setup will error unless it is marked optional. Paths must be
+ relative and may not contain the '..' path or start with '..'.
+ items:
+ description: Maps a string key to a path
+ within a volume.
+ properties:
+ key:
+ description: key is the key to project.
+ type: string
+ mode:
+ description: |-
+ mode is Optional: mode bits used to set permissions on this file.
+ Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+ YAML accepts both octal and decimal values, JSON requires decimal values for mode bits.
+ If not specified, the volume defaultMode will be used.
+ This might be in conflict with other options that affect the file
+ mode, like fsGroup, and the result can be other mode bits set.
+ format: int32
+ type: integer
+ path:
+ description: |-
+ path is the relative path of the file to map the key to.
+ May not be an absolute path.
+ May not contain the path element '..'.
+ May not start with the string '..'.
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ name:
+ description: |-
+ Name of the referent.
+ More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind, uid?
+ type: string
+ optional:
+ description: optional specify whether the
+ ConfigMap or its keys must be defined
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ downwardAPI:
+ description: downwardAPI information about the
+ downwardAPI data to project
+ properties:
+ items:
+ description: Items is a list of DownwardAPIVolume
+ file
+ items:
+ description: DownwardAPIVolumeFile represents
+ information to create the file containing
+ the pod field
+ properties:
+ fieldRef:
+ description: 'Required: Selects a field
+ of the pod: only annotations, labels,
+ name and namespace are supported.'
+ properties:
+ apiVersion:
+ description: Version of the schema
+ the FieldPath is written in terms
+ of, defaults to "v1".
+ type: string
+ fieldPath:
+ description: Path of the field to
+ select in the specified API version.
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ mode:
+ description: |-
+ Optional: mode bits used to set permissions on this file, must be an octal value
+ between 0000 and 0777 or a decimal value between 0 and 511.
+ YAML accepts both octal and decimal values, JSON requires decimal values for mode bits.
+ If not specified, the volume defaultMode will be used.
+ This might be in conflict with other options that affect the file
+ mode, like fsGroup, and the result can be other mode bits set.
+ format: int32
+ type: integer
+ path:
+ description: 'Required: Path is the
+ relative path name of the file to
+ be created. Must not be absolute or
+ contain the ''..'' path. Must be utf-8
+ encoded. The first item of the relative
+ path must not start with ''..'''
+ type: string
+ resourceFieldRef:
+ description: |-
+ Selects a resource of the container: only resources limits and requests
+ (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.
+ properties:
+ containerName:
+ description: 'Container name: required
+ for volumes, optional for env
+ vars'
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ description: Specifies the output
+ format of the exposed resources,
+ defaults to "1"
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ description: 'Required: resource
+ to select'
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ required:
+ - path
+ type: object
+ type: array
+ type: object
+ secret:
+ description: secret information about the secret
+ data to project
+ properties:
+ items:
+ description: |-
+ items if unspecified, each key-value pair in the Data field of the referenced
+ Secret will be projected into the volume as a file whose name is the
+ key and content is the value. If specified, the listed keys will be
+ projected into the specified paths, and unlisted keys will not be
+ present. If a key is specified which is not present in the Secret,
+ the volume setup will error unless it is marked optional. Paths must be
+ relative and may not contain the '..' path or start with '..'.
+ items:
+ description: Maps a string key to a path
+ within a volume.
+ properties:
+ key:
+ description: key is the key to project.
+ type: string
+ mode:
+ description: |-
+ mode is Optional: mode bits used to set permissions on this file.
+ Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+ YAML accepts both octal and decimal values, JSON requires decimal values for mode bits.
+ If not specified, the volume defaultMode will be used.
+ This might be in conflict with other options that affect the file
+ mode, like fsGroup, and the result can be other mode bits set.
+ format: int32
+ type: integer
+ path:
+ description: |-
+ path is the relative path of the file to map the key to.
+ May not be an absolute path.
+ May not contain the path element '..'.
+ May not start with the string '..'.
+ type: string
+ required:
+ - key
+ - path
+ type: object
+ type: array
+ name:
+ description: |-
+ Name of the referent.
+ More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind, uid?
+ type: string
+ optional:
+ description: optional field specify whether
+ the Secret or its key must be defined
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ serviceAccountToken:
+ description: serviceAccountToken is information
+ about the serviceAccountToken data to project
+ properties:
+ audience:
+ description: |-
+ audience is the intended audience of the token. A recipient of a token
+ must identify itself with an identifier specified in the audience of the
+ token, and otherwise should reject the token. The audience defaults to the
+ identifier of the apiserver.
+ type: string
+ expirationSeconds:
+ description: |-
+ expirationSeconds is the requested duration of validity of the service
+ account token. As the token approaches expiration, the kubelet volume
+ plugin will proactively rotate the service account token. The kubelet will
+ start trying to rotate the token if the token is older than 80 percent of
+ its time to live or if the token is older than 24 hours.Defaults to 1 hour
+ and must be at least 10 minutes.
+ format: int64
+ type: integer
+ path:
+ description: |-
+ path is the path relative to the mount point of the file to project the
+ token into.
+ type: string
+ required:
+ - path
+ type: object
+ type: object
+ type: array
+ type: object
restartPods:
description: Whether to restart the pods on content change
type: boolean
@@ -4770,21 +5248,25 @@ spec:
resource requirements.
properties:
claims:
- description: "Claims lists the names of resources,
- defined in spec.resourceClaims, that are used by
- this container. \n This is an alpha field and requires
- enabling the DynamicResourceAllocation feature gate.
- \n This field is immutable. It can only be set for
- containers."
+ description: |-
+ Claims lists the names of resources, defined in spec.resourceClaims,
+ that are used by this container.
+
+
+ This is an alpha field and requires enabling the
+ DynamicResourceAllocation feature gate.
+
+
+ This field is immutable. It can only be set for containers.
items:
description: ResourceClaim references one entry
in PodSpec.ResourceClaims.
properties:
name:
- description: Name must match the name of one
- entry in pod.spec.resourceClaims of the Pod
- where this field is used. It makes that resource
- available inside a container.
+ description: |-
+ Name must match the name of one entry in pod.spec.resourceClaims of
+ the Pod where this field is used. It makes that resource available
+ inside a container.
type: string
required:
- name
@@ -4800,8 +5282,9 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Limits describes the maximum amount
- of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+ description: |-
+ Limits describes the maximum amount of compute resources allowed.
+ More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
type: object
requests:
additionalProperties:
@@ -4810,12 +5293,11 @@ spec:
- type: string
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
- description: 'Requests describes the minimum amount
- of compute resources required. If Requests is omitted
- for a container, it defaults to Limits if that is
- explicitly specified, otherwise to an implementation-defined
- value. Requests cannot exceed Limits. More info:
- https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
+ description: |-
+ Requests describes the minimum amount of compute resources required.
+ If Requests is omitted for a container, it defaults to Limits if that is explicitly specified,
+ otherwise to an implementation-defined value. Requests cannot exceed Limits.
+ More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
type: object
type: object
type: object
diff --git a/opensearch-operator/controllers/cluster_test.go b/opensearch-operator/controllers/cluster_test.go
index 1b8b71c0..08d7e2ec 100644
--- a/opensearch-operator/controllers/cluster_test.go
+++ b/opensearch-operator/controllers/cluster_test.go
@@ -185,6 +185,7 @@ var _ = Describe("Cluster Reconciler", func() {
"test-secret",
"test-cm",
"test-emptydir",
+ "test-projected-token",
),
)),
HaveMatchingVolume(And(
@@ -199,6 +200,10 @@ var _ = Describe("Cluster Reconciler", func() {
HaveName("test-emptydir"),
HaveVolumeSource("EmptyDir"),
)),
+ HaveMatchingVolume(And(
+ HaveName("test-projected-token"),
+ HaveVolumeSource("Projected"),
+ )),
))
}(nodePool)
}
diff --git a/opensearch-operator/controllers/suite_test_helpers.go b/opensearch-operator/controllers/suite_test_helpers.go
index 0972709e..63789285 100644
--- a/opensearch-operator/controllers/suite_test_helpers.go
+++ b/opensearch-operator/controllers/suite_test_helpers.go
@@ -114,11 +114,12 @@ func ComposeOpensearchCrd(clusterName string, namespace string) opsterv1.OpenSea
"foo": "bar",
},
},
- HttpPort: 9200,
- Vendor: "opensearch",
- Version: "2.0.0",
- ServiceName: "es-svc",
- PluginsList: []string{"http://foo-plugin-1.0.0"},
+ HttpPort: 9200,
+ Vendor: "opensearch",
+ Version: "2.0.0",
+ ServiceName: "es-svc",
+ ServiceAccount: "default",
+ PluginsList: []string{"http://foo-plugin-1.0.0"},
AdditionalConfig: map[string]string{
"foo": "bar",
},
@@ -147,6 +148,18 @@ func ComposeOpensearchCrd(clusterName string, namespace string) opsterv1.OpenSea
EmptyDir: &corev1.EmptyDirVolumeSource{},
RestartPods: false,
},
+ {
+ Name: "test-projected-token",
+ Path: "/opt/test-projected",
+ Projected: &corev1.ProjectedVolumeSource{
+ Sources: []corev1.VolumeProjection{{
+ ServiceAccountToken: &corev1.ServiceAccountTokenProjection{
+ Path: "token",
+ },
+ }},
+ },
+ RestartPods: false,
+ },
},
},
ConfMgmt: opsterv1.ConfMgmt{
diff --git a/opensearch-operator/pkg/reconcilers/util/util.go b/opensearch-operator/pkg/reconcilers/util/util.go
index cde53a22..345e937d 100644
--- a/opensearch-operator/pkg/reconcilers/util/util.go
+++ b/opensearch-operator/pkg/reconcilers/util/util.go
@@ -135,6 +135,14 @@ func CreateAdditionalVolumes(
},
})
}
+ if volumeConfig.Projected != nil {
+ retVolumes = append(retVolumes, corev1.Volume{
+ Name: volumeConfig.Name,
+ VolumeSource: corev1.VolumeSource{
+ Projected: volumeConfig.Projected,
+ },
+ })
+ }
if volumeConfig.RestartPods {
namesIndex[volumeConfig.Name] = i
names = append(names, volumeConfig.Name)
@@ -142,7 +150,7 @@ func CreateAdditionalVolumes(
subPath := ""
// SubPaths are only supported for ConfigMaps, Secrets and CSI volumes
- if volumeConfig.ConfigMap != nil || volumeConfig.Secret != nil || volumeConfig.CSI != nil {
+ if volumeConfig.ConfigMap != nil || volumeConfig.Secret != nil || volumeConfig.CSI != nil || volumeConfig.Projected != nil {
subPath = strings.TrimSpace(volumeConfig.SubPath)
}
diff --git a/opensearch-operator/pkg/reconcilers/util/util_test.go b/opensearch-operator/pkg/reconcilers/util/util_test.go
index fb5f7756..ace524f5 100644
--- a/opensearch-operator/pkg/reconcilers/util/util_test.go
+++ b/opensearch-operator/pkg/reconcilers/util/util_test.go
@@ -131,4 +131,51 @@ var _ = Describe("Additional volumes", func() {
Expect(volumeMount[0].SubPath).To(BeEmpty())
})
})
+
+ When("Projected volume is added", func() {
+ It("Should have ProjectedVolumeSource fields", func() {
+ volumeConfigs[0].Projected = &v1.ProjectedVolumeSource{
+ Sources: []v1.VolumeProjection{},
+ }
+
+ volume, _, _, _ := CreateAdditionalVolumes(mockClient, namespace, volumeConfigs)
+ Expect(volume[0].Projected.Sources).To(BeEmpty())
+ })
+ })
+
+ When("Projected volume is added with a ServiceAccountToken source", func() {
+ It("Should have Path set on the volume source", func() {
+ volumeConfigs[0].Projected = &v1.ProjectedVolumeSource{
+ Sources: []v1.VolumeProjection{{
+ ServiceAccountToken: &v1.ServiceAccountTokenProjection{
+ Path: "token",
+ },
+ }},
+ }
+
+ volume, _, _, _ := CreateAdditionalVolumes(mockClient, namespace, volumeConfigs)
+ Expect(volume[0].Projected.Sources[0].ServiceAccountToken.Path).To(Equal("token"))
+ })
+ })
+
+ When("Projected volume is added with subPath", func() {
+ It("Should have the subPath", func() {
+ volumeConfigs[0].Projected = &v1.ProjectedVolumeSource{}
+ volumeConfigs[0].SubPath = "c"
+
+ _, volumeMount, _, _ := CreateAdditionalVolumes(mockClient, namespace, volumeConfigs)
+ Expect(volumeMount[0].MountPath).To(Equal("myPath/a/b"))
+ Expect(volumeMount[0].SubPath).To(Equal("c"))
+ })
+ })
+
+ When("Projected volume is added without subPath", func() {
+ It("Should not have the subPath", func() {
+ volumeConfigs[0].Projected = &v1.ProjectedVolumeSource{}
+
+ _, volumeMount, _, _ := CreateAdditionalVolumes(mockClient, namespace, volumeConfigs)
+ Expect(volumeMount[0].MountPath).To(Equal("myPath/a/b"))
+ Expect(volumeMount[0].SubPath).To(BeEmpty())
+ })
+ })
})
From 6677c6192ccbf9222f8955f85b58e6a55d558537 Mon Sep 17 00:00:00 2001
From: Prudhvi Godithi
Date: Mon, 13 May 2024 10:44:49 -0700
Subject: [PATCH 06/27] Update Operator compatibility matrix and honor
functional tests compatibility (#792)
### Description
Update Operator compatibility matrix and honor compatibility for
functional tests
### Issues Resolved
Coming from
https://github.com/opensearch-project/opensearch-k8s-operator/issues/776#issuecomment-2072921621.
This should also fix
https://github.com/opensearch-project/opensearch-k8s-operator/issues/760.
### Check List
- [x] Commits are signed per the DCO using --signoff
- [x] Unittest added for the new/changed functionality and all unit
tests are successful
- [ ] Customer-visible features documented
- [ ] No linter warnings (`make lint`)
If CRDs are changed:
- [ ] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [ ] Changes to CRDs documented
Please refer to the [PR
guidelines](https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/docs/developing.md#submitting-a-pr)
before submitting this pull request.
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
Signed-off-by: Prudhvi Godithi
---
.github/workflows/functional-tests.yaml | 8 +++++++-
README.md | 13 +++++--------
2 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/.github/workflows/functional-tests.yaml b/.github/workflows/functional-tests.yaml
index 4cae2e6a..1e770ed1 100644
--- a/.github/workflows/functional-tests.yaml
+++ b/.github/workflows/functional-tests.yaml
@@ -44,6 +44,12 @@ jobs:
cluster-helm-chart:
runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ version:
+ - 2.13.0
+ - 1.3.16
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -77,7 +83,7 @@ jobs:
## Install helm chart
helm install opensearch-operator ../charts/opensearch-operator --set manager.image.repository=controller --set manager.image.tag=latest --set manager.image.pullPolicy=IfNotPresent --namespace default --wait
- helm install opensearch-cluster ../charts/opensearch-cluster --set OpenSearchClusterSpec.enabled=true --wait
+ helm install opensearch-cluster ../charts/opensearch-cluster --set opensearchCluster.general.version=${{ matrix.version }} --set opensearchCluster.dashboards.version=${{ matrix.version }} --wait
cd functionaltests
## Run tests
diff --git a/README.md b/README.md
index 27ccdc36..fc285e9d 100644
--- a/README.md
+++ b/README.md
@@ -49,14 +49,11 @@ The Operator can be easily installed using Helm:
The opensearch k8s operator aims to be compatible to all supported opensearch versions. Please check the table below for details:
-| Operator Version | Min Supported Opensearch Version | Max supported Opensearch version | Comment |
-|------------------|----------------------------------|----------------------------------|---------|
-| 2.3 | 1.0 | 2.8 | |
-| 2.2 | 1.0 | 2.5 | |
-| 2.1 | 1.0 | 2.3 | |
-| 2.0 | 1.0 | 2.3 | |
-| 1.x | 1.0 | 1.x | |
-| 0.x | 1.0 | 1.x | Beta |
+
+| Operator Version | Min Supported Opensearch Version | Max Supported Opensearch Version | Comment |
+| ----------------------------------------------------------- | -------------------------------- | -------------------------------- | ------------------------------------------- |
+| 2.6.0
2.5.1
2.5.0 | 1.3.x | latest 2.x | Supports the latest OpenSearch 2.x version. |
+
This table only lists versions that have been explicitly tested with the operator, the operator will not prevent you from using other versions. Newer minor versions (2.x) not listed here generally also work but you should proceed with caution and test it our in a non-production environment first.
From f3455f9517c44e6419905be3097a8c7f1a8732bd Mon Sep 17 00:00:00 2001
From: Sebastian Woehrl
Date: Tue, 14 May 2024 09:27:16 +0200
Subject: [PATCH 07/27] Rename timestampField back to how it was released
(#810)
### Description
Due to some oversight the timestampField from
https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/opensearch-operator/api/v1/opensearch_index_types.go#L14
was released as `timestamp_field` in the helm chart
(https://github.com/opensearch-project/opensearch-k8s-operator/blob/v2.6.0/charts/opensearch-operator/files/opensearch.opster.io_opensearchindextemplates.yaml#L57).
To prevent an incompatible renaming in the released CRDs this PR renames
the field in the code to be consistent.
### Issues Resolved
Originally discovered in #805
### Check List
- [x] Commits are signed per the DCO using --signoff
- [x] Unittest added for the new/changed functionality and all unit
tests are successful
- [x] Customer-visible features documented
- [x] No linter warnings (`make lint`)
If CRDs are changed:
- [-] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [-] Changes to CRDs documented
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
Signed-off-by: Sebastian Woehrl
---
opensearch-operator/api/v1/opensearch_index_types.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/opensearch-operator/api/v1/opensearch_index_types.go b/opensearch-operator/api/v1/opensearch_index_types.go
index 37c9b94b..c2f2838d 100644
--- a/opensearch-operator/api/v1/opensearch_index_types.go
+++ b/opensearch-operator/api/v1/opensearch_index_types.go
@@ -11,7 +11,7 @@ type OpensearchDatastreamTimestampFieldSpec struct {
type OpensearchDatastreamSpec struct {
// TimestampField for dataStream
- TimestampField OpensearchDatastreamTimestampFieldSpec `json:"timestampField,omitempty"`
+ TimestampField OpensearchDatastreamTimestampFieldSpec `json:"timestamp_field,omitempty"`
}
// Describes the specs of an index
From 5a2d2ae837dffdf67bf7d020b641f2bdddfaf29a Mon Sep 17 00:00:00 2001
From: Sebastian Woehrl
Date: Wed, 15 May 2024 08:39:28 +0200
Subject: [PATCH 08/27] Add option to enable pprof endpoints (#813)
### Description
There have been reports of memory leaks in the operator (#700). This PR
adds an option to enable the [go
pprof](https://pkg.go.dev/net/http/pprof) endpoints. With them users can
get heap and allocation profiles that hopefully help in tracking down
the leak.
### Issues Resolved
Fixes #626
### Check List
- [x] Commits are signed per the DCO using --signoff
- [-] Unittest added for the new/changed functionality and all unit
tests are successful
- [x] Customer-visible features documented
- [x] No linter warnings (`make lint`)
If CRDs are changed:
- [-] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [-] Changes to CRDs documented
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
Signed-off-by: Sebastian Woehrl
---
...ch-operator-controller-manager-deployment.yaml | 2 ++
charts/opensearch-operator/values.yaml | 3 +++
docs/userguide/main.md | 11 +++++++++++
opensearch-operator/main.go | 15 +++++++++++++++
4 files changed, 31 insertions(+)
diff --git a/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml b/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml
index 2d1fafbd..08f089f4 100755
--- a/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml
+++ b/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml
@@ -63,6 +63,8 @@ spec:
value: {{ .Values.manager.dnsBase }}
- name: PARALLEL_RECOVERY_ENABLED
value: "{{ .Values.manager.parallelRecoveryEnabled }}"
+ - name: PPROF_ENDPOINTS_ENABLED
+ value: "{{ .Values.manager.pprofEndpointsEnabled }}"
{{- if .Values.manager.extraEnv }}
{{- toYaml .Values.manager.extraEnv | nindent 8 }}
{{- end }}
diff --git a/charts/opensearch-operator/values.yaml b/charts/opensearch-operator/values.yaml
index 4afa24a5..96bdc268 100644
--- a/charts/opensearch-operator/values.yaml
+++ b/charts/opensearch-operator/values.yaml
@@ -39,6 +39,9 @@ manager:
# Set this to false to disable the experimental parallel recovery in case you are experiencing problems
parallelRecoveryEnabled: true
+ # Set this to true to enable the standard go pprof endpoints on port 6060 (https://pkg.go.dev/net/http/pprof)
+ # Should only be used for debugging purposes
+ pprofEndpointsEnabled: false
image:
repository: opensearchproject/opensearch-operator
diff --git a/docs/userguide/main.md b/docs/userguide/main.md
index e554abc4..7f55780e 100644
--- a/docs/userguide/main.md
+++ b/docs/userguide/main.md
@@ -100,6 +100,17 @@ manager:
# value: somevalue
```
+### Pprof endpoints
+
+There have been situations reported where the operator is leaking memory. To help diagnose these situations the standard go [pprof](https://pkg.go.dev/net/http/pprof) endpoints can be enabled by adding the following to your `values.yaml`:
+
+```yaml
+manager:
+ pprofEndpointsEnabled: true
+```
+
+The access the endpoints you will need to use a port-forward as for security reasons the endpoints are only exposed on localhost inside the pod: `kubectl port-forward deployment/opensearch-operator-controller-manager 6060`. Then from another terminal you can use the [go pprof tool](https://pkg.go.dev/net/http/pprof#hdr-Usage_examples), e.g.: `go tool pprof http://localhost:6060/debug/pprof/heap`.
+
## Configuring OpenSearch
The main job of the operator is to deploy and manage OpenSearch clusters. As such it offers a wide range of options to configure clusters.
diff --git a/opensearch-operator/main.go b/opensearch-operator/main.go
index 3c898fbe..0b81ace3 100644
--- a/opensearch-operator/main.go
+++ b/opensearch-operator/main.go
@@ -37,6 +37,9 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
+
+ "net/http"
+ _ "net/http/pprof"
)
var (
@@ -87,6 +90,11 @@ func main() {
setupLog.Info("Enabled debug logging via environment variable OPERATOR_DEV_LOGGING")
}
}
+ pprofEndpoints, err := strconv.ParseBool(os.Getenv("PPROF_ENDPOINTS_ENABLED"))
+ if err != nil {
+ // by default to not enable endpoints
+ pprofEndpoints = false
+ }
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
@@ -188,6 +196,13 @@ func main() {
os.Exit(1)
}
+ if pprofEndpoints {
+ go func() {
+ listenError := http.ListenAndServe("localhost:6060", nil)
+ setupLog.Error(listenError, "Failed to start pprof endpoint listener")
+ }()
+ }
+
setupLog.Info("Starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
From 4f59766a066fd9f35a686d419b9360275ad7852b Mon Sep 17 00:00:00 2001
From: Sebastian Woehrl
Date: Tue, 21 May 2024 08:46:17 +0200
Subject: [PATCH 09/27] Fix upgrade detection during parallel recovery (#789)
### Description
Fixes a bug where parallel recovery did not engage if the cluster had
gone through a version upgrade beforehand.
Reason was that the upgrade logic added the status for each nodepool
twice leading to the recovery logic incorrectly detecting if an upgrade
was in progress.
Also did a small refactoring of names and constants and fixed a warning
by my IDE about unused parameters.
No changes to the CRDs or functionality, just internal logic fixes.
### Issues Resolved
Fixes #730
### Check List
- [x] Commits are signed per the DCO using --signoff
- [x] Unittest added for the new/changed functionality and all unit
tests are successful
- [x] Customer-visible features documented
- [x] No linter warnings (`make lint`)
If CRDs are changed:
- [-] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [-] Changes to CRDs documented
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
Signed-off-by: Sebastian Woehrl
---
.../pkg/builders/cluster_test.go | 2 +-
opensearch-operator/pkg/helpers/helpers.go | 36 ++++++++--
.../pkg/reconcilers/cluster.go | 4 +-
.../pkg/reconcilers/configuration_test.go | 5 --
.../pkg/reconcilers/rollingRestart.go | 3 +-
opensearch-operator/pkg/reconcilers/scaler.go | 2 +-
.../pkg/reconcilers/securityconfig.go | 2 +-
.../pkg/reconcilers/upgrade.go | 72 ++++++++++---------
8 files changed, 77 insertions(+), 49 deletions(-)
diff --git a/opensearch-operator/pkg/builders/cluster_test.go b/opensearch-operator/pkg/builders/cluster_test.go
index c7f22c53..7b0e2580 100644
--- a/opensearch-operator/pkg/builders/cluster_test.go
+++ b/opensearch-operator/pkg/builders/cluster_test.go
@@ -777,7 +777,7 @@ var _ = Describe("Builders", func() {
Expect(result.Spec.Template.Spec.Containers[0].ReadinessProbe.FailureThreshold).To(Equal(int32(9)))
})
})
-
+
When("Configuring InitHelper Resources", func() {
It("should propagate Resources to all init containers", func() {
clusterObject := ClusterDescWithVersion("2.2.1")
diff --git a/opensearch-operator/pkg/helpers/helpers.go b/opensearch-operator/pkg/helpers/helpers.go
index 7795f260..aa6de1d5 100644
--- a/opensearch-operator/pkg/helpers/helpers.go
+++ b/opensearch-operator/pkg/helpers/helpers.go
@@ -4,11 +4,12 @@ import (
"encoding/json"
"errors"
"fmt"
- apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"reflect"
"sort"
"time"
+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+
policyv1 "k8s.io/api/policy/v1"
opsterv1 "github.com/Opster/opensearch-k8s-operator/opensearch-operator/api/v1"
@@ -79,6 +80,22 @@ func FindFirstPartial(
return item, false
}
+func FindAllPartial(
+ arr []opsterv1.ComponentStatus,
+ item opsterv1.ComponentStatus,
+ predicator func(opsterv1.ComponentStatus, opsterv1.ComponentStatus) (opsterv1.ComponentStatus, bool),
+) []opsterv1.ComponentStatus {
+ var result []opsterv1.ComponentStatus
+
+ for i := 0; i < len(arr); i++ {
+ itemInArr, found := predicator(arr[i], item)
+ if found {
+ result = append(result, itemInArr)
+ }
+ }
+ return result
+}
+
func FindByPath(obj interface{}, keys []string) (interface{}, bool) {
mobj, ok := obj.(map[string]interface{})
if !ok {
@@ -116,7 +133,7 @@ func UsernameAndPassword(k8sClient k8s.K8sClient, cr *opsterv1.OpenSearchCluster
}
}
-func GetByDescriptionAndGroup(left opsterv1.ComponentStatus, right opsterv1.ComponentStatus) (opsterv1.ComponentStatus, bool) {
+func GetByDescriptionAndComponent(left opsterv1.ComponentStatus, right opsterv1.ComponentStatus) (opsterv1.ComponentStatus, bool) {
if left.Description == right.Description && left.Component == right.Component {
return left, true
}
@@ -430,12 +447,21 @@ func CalculateJvmHeapSize(nodePool *opsterv1.NodePool) string {
return nodePool.Jvm
}
-func UpgradeInProgress(status opsterv1.ClusterStatus) bool {
+func IsUpgradeInProgress(status opsterv1.ClusterStatus) bool {
componentStatus := opsterv1.ComponentStatus{
Component: "Upgrader",
}
- _, found := FindFirstPartial(status.ComponentsStatus, componentStatus, GetByComponent)
- return found
+ foundStatus := FindAllPartial(status.ComponentsStatus, componentStatus, GetByComponent)
+ inProgress := false
+
+ // check all statuses if any of the nodepools are still in progress or pending
+ for i := 0; i < len(foundStatus); i++ {
+ if foundStatus[i].Status != "Upgraded" && foundStatus[i].Status != "Finished" {
+ inProgress = true
+ }
+ }
+
+ return inProgress
}
func ReplicaHostName(currentSts appsv1.StatefulSet, repNum int32) string {
diff --git a/opensearch-operator/pkg/reconcilers/cluster.go b/opensearch-operator/pkg/reconcilers/cluster.go
index 6cea13bf..0a4a3249 100644
--- a/opensearch-operator/pkg/reconcilers/cluster.go
+++ b/opensearch-operator/pkg/reconcilers/cluster.go
@@ -200,7 +200,7 @@ func (r *ClusterReconciler) reconcileNodeStatefulSet(nodePool opsterv1.NodePool,
} else {
// A failure is assumed if n PVCs exist but less than n-1 pods (one missing pod is allowed for rolling restart purposes)
// We can assume the cluster is in a failure state and cannot recover on its own
- if !helpers.UpgradeInProgress(r.instance.Status) &&
+ if !helpers.IsUpgradeInProgress(r.instance.Status) &&
pvcCount >= int(nodePool.Replicas) && existing.Status.ReadyReplicas < nodePool.Replicas-1 {
r.logger.Info(fmt.Sprintf("Detected recovery situation for nodepool %s: PVC count: %d, replicas: %d. Recreating STS with parallel mode", nodePool.Component, pvcCount, existing.Status.Replicas))
if existing.Spec.PodManagementPolicy != appsv1.ParallelPodManagement {
@@ -301,7 +301,7 @@ func (r *ClusterReconciler) checkForEmptyDirRecovery() (*ctrl.Result, error) {
Description: nodePool.Component,
}
comp := r.instance.Status.ComponentsStatus
- _, found := helpers.FindFirstPartial(comp, componentStatus, helpers.GetByDescriptionAndGroup)
+ _, found := helpers.FindFirstPartial(comp, componentStatus, helpers.GetByDescriptionAndComponent)
if found {
return &ctrl.Result{}, nil
diff --git a/opensearch-operator/pkg/reconcilers/configuration_test.go b/opensearch-operator/pkg/reconcilers/configuration_test.go
index df697962..efab81bf 100644
--- a/opensearch-operator/pkg/reconcilers/configuration_test.go
+++ b/opensearch-operator/pkg/reconcilers/configuration_test.go
@@ -13,7 +13,6 @@ import (
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
- "github.com/cisco-open/operator-tools/pkg/reconciler"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/mock"
@@ -21,11 +20,9 @@ import (
func newConfigurationReconciler(
client *k8s.MockK8sClient,
- ctx context.Context,
recorder record.EventRecorder,
reconcilerContext *ReconcilerContext,
instance *opsterv1.OpenSearchCluster,
- opts ...reconciler.ResourceReconcilerOption,
) *ConfigurationReconciler {
return &ConfigurationReconciler{
client: client,
@@ -69,7 +66,6 @@ var _ = Describe("Configuration Controller", func() {
underTest := newConfigurationReconciler(
mockClient,
- context.Background(),
&helpers.MockEventRecorder{},
&reconcilerContext,
&spec,
@@ -116,7 +112,6 @@ var _ = Describe("Configuration Controller", func() {
underTest := newConfigurationReconciler(
mockClient,
- context.Background(),
&helpers.MockEventRecorder{},
&reconcilerContext,
&spec,
diff --git a/opensearch-operator/pkg/reconcilers/rollingRestart.go b/opensearch-operator/pkg/reconcilers/rollingRestart.go
index 748c06d8..483654a4 100644
--- a/opensearch-operator/pkg/reconcilers/rollingRestart.go
+++ b/opensearch-operator/pkg/reconcilers/rollingRestart.go
@@ -110,6 +110,7 @@ func (r *RollingRestartReconciler) Reconcile() (ctrl.Result, error) {
lg.V(1).Info("Restart complete. Reactivating shard allocation")
return ctrl.Result{Requeue: true}, err
}
+ r.recorder.AnnotatedEventf(r.instance, map[string]string{"cluster-name": r.instance.GetName()}, "Normal", "RollingRestart", "Rolling restart completed")
if err = r.updateStatus(statusFinished); err != nil {
return ctrl.Result{Requeue: true}, err
}
@@ -129,7 +130,7 @@ func (r *RollingRestartReconciler) Reconcile() (ctrl.Result, error) {
if err := r.updateStatus(statusInProgress); err != nil {
return ctrl.Result{Requeue: true}, err
}
- r.recorder.AnnotatedEventf(r.instance, map[string]string{"cluster-name": r.instance.GetName()}, "Normal", "RollingRestart", "Starting to rolling restart")
+ r.recorder.AnnotatedEventf(r.instance, map[string]string{"cluster-name": r.instance.GetName()}, "Normal", "RollingRestart", "Starting rolling restart")
// If there is work to do create an Opensearch Client
var err error
diff --git a/opensearch-operator/pkg/reconcilers/scaler.go b/opensearch-operator/pkg/reconcilers/scaler.go
index 8e5d5bde..297e53d0 100644
--- a/opensearch-operator/pkg/reconcilers/scaler.go
+++ b/opensearch-operator/pkg/reconcilers/scaler.go
@@ -78,7 +78,7 @@ func (r *ScalerReconciler) reconcileNodePool(nodePool *opsterv1.NodePool) (bool,
Description: nodePool.Component,
}
comp := r.instance.Status.ComponentsStatus
- currentStatus, found := helpers.FindFirstPartial(comp, componentStatus, helpers.GetByDescriptionAndGroup)
+ currentStatus, found := helpers.FindFirstPartial(comp, componentStatus, helpers.GetByDescriptionAndComponent)
desireReplicaDiff := *currentSts.Spec.Replicas - nodePool.Replicas
if desireReplicaDiff == 0 {
diff --git a/opensearch-operator/pkg/reconcilers/securityconfig.go b/opensearch-operator/pkg/reconcilers/securityconfig.go
index 36e5cca2..bc042ee4 100644
--- a/opensearch-operator/pkg/reconcilers/securityconfig.go
+++ b/opensearch-operator/pkg/reconcilers/securityconfig.go
@@ -162,7 +162,7 @@ func (r *SecurityconfigReconciler) Reconcile() (ctrl.Result, error) {
}
r.logger.Info("Starting securityconfig update job")
- r.recorder.AnnotatedEventf(r.instance, annotations, "Normal", "Security", "Starting to securityconfig update job")
+ r.recorder.AnnotatedEventf(r.instance, annotations, "Normal", "Security", "Starting securityconfig update job")
job = builders.NewSecurityconfigUpdateJob(
r.instance,
diff --git a/opensearch-operator/pkg/reconcilers/upgrade.go b/opensearch-operator/pkg/reconcilers/upgrade.go
index 9b9d8599..73a3acfc 100644
--- a/opensearch-operator/pkg/reconcilers/upgrade.go
+++ b/opensearch-operator/pkg/reconcilers/upgrade.go
@@ -30,6 +30,12 @@ var (
ErrUnexpectedStatus = errors.New("unexpected upgrade status")
)
+const (
+ componentNameUpgrader = "Upgrader"
+ upgradeStatusPending = "Pending"
+ upgradeStatusInProgress = "Upgrading"
+)
+
type UpgradeReconciler struct {
client k8s.K8sClient
ctx context.Context
@@ -93,10 +99,10 @@ func (r *UpgradeReconciler) Reconcile() (ctrl.Result, error) {
// Work on the current nodepool as appropriate
switch currentStatus.Status {
- case "Pending":
+ case upgradeStatusPending:
// Set it to upgrading and requeue
err := r.client.UpdateOpenSearchClusterStatus(client.ObjectKeyFromObject(r.instance), func(instance *opsterv1.OpenSearchCluster) {
- currentStatus.Status = "Upgrading"
+ currentStatus.Status = upgradeStatusInProgress
instance.Status.ComponentsStatus = append(instance.Status.ComponentsStatus, currentStatus)
})
r.recorder.AnnotatedEventf(r.instance, annotations, "Normal", "Upgrade", "Starting upgrade of node pool '%s'", currentStatus.Description)
@@ -104,7 +110,7 @@ func (r *UpgradeReconciler) Reconcile() (ctrl.Result, error) {
Requeue: true,
RequeueAfter: 15 * time.Second,
}, err
- case "Upgrading":
+ case upgradeStatusInProgress:
err := r.doNodePoolUpgrade(nodePool)
return ctrl.Result{
Requeue: true,
@@ -116,16 +122,16 @@ func (r *UpgradeReconciler) Reconcile() (ctrl.Result, error) {
instance.Status.Version = instance.Spec.General.Version
for _, pool := range instance.Spec.NodePools {
componentStatus := opsterv1.ComponentStatus{
- Component: "Upgrader",
+ Component: componentNameUpgrader,
Description: pool.Component,
}
- currentStatus, found := helpers.FindFirstPartial(instance.Status.ComponentsStatus, componentStatus, helpers.GetByDescriptionAndGroup)
+ currentStatus, found := helpers.FindFirstPartial(instance.Status.ComponentsStatus, componentStatus, helpers.GetByDescriptionAndComponent)
if found {
instance.Status.ComponentsStatus = helpers.RemoveIt(currentStatus, instance.Status.ComponentsStatus)
}
}
})
- r.recorder.AnnotatedEventf(r.instance, annotations, "Normal", "Upgrade", "Finished upgrade - NewVersion: %s", r.instance.Status.Version)
+ r.recorder.AnnotatedEventf(r.instance, annotations, "Normal", "Upgrade", "Finished upgrade - NewVersion: %s", r.instance.Spec.General.Version)
return ctrl.Result{}, err
default:
// We should never get here so return an error
@@ -190,35 +196,35 @@ func (r *UpgradeReconciler) findNextNodePoolForUpgrade() (opsterv1.NodePool, ops
pool, found := r.findInProgress(dataNodes)
if found {
return pool, opsterv1.ComponentStatus{
- Component: "Upgrader",
+ Component: componentNameUpgrader,
Description: pool.Component,
- Status: "Upgrading",
+ Status: upgradeStatusInProgress,
}
}
// Pick the first unworked on node next
pool, found = r.findNextPool(dataNodes)
if found {
return pool, opsterv1.ComponentStatus{
- Component: "Upgrader",
+ Component: componentNameUpgrader,
Description: pool.Component,
- Status: "Pending",
+ Status: upgradeStatusPending,
}
}
// Next do the same for any nodes that are data and master
pool, found = r.findInProgress(dataAndMasterNodes)
if found {
return pool, opsterv1.ComponentStatus{
- Component: "Upgrader",
+ Component: componentNameUpgrader,
Description: pool.Component,
- Status: "Upgrading",
+ Status: upgradeStatusInProgress,
}
}
pool, found = r.findNextPool(dataAndMasterNodes)
if found {
return pool, opsterv1.ComponentStatus{
- Component: "Upgrader",
+ Component: componentNameUpgrader,
Description: pool.Component,
- Status: "Pending",
+ Status: upgradeStatusPending,
}
}
@@ -226,23 +232,23 @@ func (r *UpgradeReconciler) findNextNodePoolForUpgrade() (opsterv1.NodePool, ops
pool, found = r.findInProgress(otherNodes)
if found {
return pool, opsterv1.ComponentStatus{
- Component: "Upgrader",
+ Component: componentNameUpgrader,
Description: pool.Component,
- Status: "Upgrading",
+ Status: upgradeStatusInProgress,
}
}
pool, found = r.findNextPool(otherNodes)
if found {
return pool, opsterv1.ComponentStatus{
- Component: "Upgrader",
+ Component: componentNameUpgrader,
Description: pool.Component,
- Status: "Pending",
+ Status: upgradeStatusPending,
}
}
// If we get here all nodes should be upgraded
return opsterv1.NodePool{}, opsterv1.ComponentStatus{
- Component: "Upgrade",
+ Component: componentNameUpgrader,
Status: "Finished",
}
}
@@ -250,11 +256,11 @@ func (r *UpgradeReconciler) findNextNodePoolForUpgrade() (opsterv1.NodePool, ops
func (r *UpgradeReconciler) findInProgress(pools []opsterv1.NodePool) (opsterv1.NodePool, bool) {
for _, nodePool := range pools {
componentStatus := opsterv1.ComponentStatus{
- Component: "Upgrader",
+ Component: componentNameUpgrader,
Description: nodePool.Component,
}
- currentStatus, found := helpers.FindFirstPartial(r.instance.Status.ComponentsStatus, componentStatus, helpers.GetByDescriptionAndGroup)
- if found && currentStatus.Status == "Upgrading" {
+ currentStatus, found := helpers.FindFirstPartial(r.instance.Status.ComponentsStatus, componentStatus, helpers.GetByDescriptionAndComponent)
+ if found && currentStatus.Status == upgradeStatusInProgress {
return nodePool, true
}
}
@@ -264,10 +270,10 @@ func (r *UpgradeReconciler) findInProgress(pools []opsterv1.NodePool) (opsterv1.
func (r *UpgradeReconciler) findNextPool(pools []opsterv1.NodePool) (opsterv1.NodePool, bool) {
for _, nodePool := range pools {
componentStatus := opsterv1.ComponentStatus{
- Component: "Upgrader",
+ Component: componentNameUpgrader,
Description: nodePool.Component,
}
- _, found := helpers.FindFirstPartial(r.instance.Status.ComponentsStatus, componentStatus, helpers.GetByDescriptionAndGroup)
+ _, found := helpers.FindFirstPartial(r.instance.Status.ComponentsStatus, componentStatus, helpers.GetByDescriptionAndComponent)
if !found {
return nodePool, true
}
@@ -323,12 +329,12 @@ func (r *UpgradeReconciler) doNodePoolUpgrade(pool opsterv1.NodePool) error {
return r.client.UpdateOpenSearchClusterStatus(client.ObjectKeyFromObject(r.instance), func(instance *opsterv1.OpenSearchCluster) {
currentStatus := opsterv1.ComponentStatus{
- Component: "Upgrader",
- Status: "Upgrading",
+ Component: componentNameUpgrader,
+ Status: upgradeStatusInProgress,
Description: pool.Component,
}
componentStatus := opsterv1.ComponentStatus{
- Component: "Upgrader",
+ Component: componentNameUpgrader,
Status: "Upgraded",
Description: pool.Component,
}
@@ -383,14 +389,14 @@ func (r *UpgradeReconciler) setComponentConditions(conditions []string, componen
err := r.client.UpdateOpenSearchClusterStatus(client.ObjectKeyFromObject(r.instance), func(instance *opsterv1.OpenSearchCluster) {
currentStatus := opsterv1.ComponentStatus{
- Component: "Upgrader",
- Status: "Upgrading",
+ Component: componentNameUpgrader,
+ Status: upgradeStatusInProgress,
Description: component,
}
- componentStatus, found := helpers.FindFirstPartial(instance.Status.ComponentsStatus, currentStatus, helpers.GetByDescriptionAndGroup)
+ componentStatus, found := helpers.FindFirstPartial(instance.Status.ComponentsStatus, currentStatus, helpers.GetByDescriptionAndComponent)
newStatus := opsterv1.ComponentStatus{
- Component: "Upgrader",
- Status: "Upgrading",
+ Component: componentNameUpgrader,
+ Status: upgradeStatusInProgress,
Description: component,
Conditions: conditions,
}
@@ -398,7 +404,7 @@ func (r *UpgradeReconciler) setComponentConditions(conditions []string, componen
conditions = append(componentStatus.Conditions, conditions...)
}
- instance.Status.ComponentsStatus = helpers.Replace(currentStatus, newStatus, instance.Status.ComponentsStatus)
+ instance.Status.ComponentsStatus = helpers.Replace(componentStatus, newStatus, instance.Status.ComponentsStatus)
})
if err != nil {
r.logger.Error(err, "Could not update status")
From f58948e203142a9f7b8f9c2c1033ed30344d0460 Mon Sep 17 00:00:00 2001
From: Casper Thygesen
Date: Tue, 28 May 2024 08:51:04 +0200
Subject: [PATCH 10/27] Add securitycontext to keystore container (#820)
### Description
This fixes the issue found in this kubernetes event log:
```log
create Pod opensearch-data-0 in StatefulSet opensearch-data failed error: pods "opensearch-data-0" is forbidden: violates PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "keystore" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "keystore" must set securityContext.capabilities.drop=["ALL"])
```
When we are running
```yaml
labels:
pod-security.kubernetes.io/enforce: restricted
```
### Issues Resolved
_List any issues this PR will resolve, e.g. Closes [...]._
### Check List
- [x] Commits are signed per the DCO using --signoff
- [ ] Unittest added for the new/changed functionality and all unit
tests are successful
- [x] Customer-visible features documented
- [x] No linter warnings (`make lint`)
If CRDs are changed:
- [ ] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [ ] Changes to CRDs documented
Please refer to the [PR
guidelines](https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/docs/developing.md#submitting-a-pr)
before submitting this pull request.
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
Signed-off-by: Casper Thygesen
---
opensearch-operator/pkg/builders/cluster.go | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/opensearch-operator/pkg/builders/cluster.go b/opensearch-operator/pkg/builders/cluster.go
index 760a498c..60938e9f 100644
--- a/opensearch-operator/pkg/builders/cluster.go
+++ b/opensearch-operator/pkg/builders/cluster.go
@@ -436,7 +436,8 @@ func NewSTSForNodePool(
cp -a /usr/share/opensearch/config/opensearch.keystore /tmp/keystore/
`,
},
- VolumeMounts: initContainerVolumeMounts,
+ VolumeMounts: initContainerVolumeMounts,
+ SecurityContext: securityContext,
}
initContainers = append(initContainers, keystoreInitContainer)
From 583e251bed0285ca91b161abeb81b27edb80934e Mon Sep 17 00:00:00 2001
From: David Tippett
Date: Wed, 5 Jun 2024 06:28:28 -0400
Subject: [PATCH 11/27] Updating and organizing demos (#823)
### Description
For some time we have had a large volume of demo deployments for
OpenSearch. These were scattered across various versions with some mixed
standards. I have taken and organized them into a 1.3.x and 2.x folders
that represent the current major versions.
I have yet been able to test these. My current cluster is armv7 based so
compatibility has been a challenge.
### Issues Resolved
#800 - While not closing this issue it's a good step.
### Check List
- [X] Commits are signed per the DCO using --signoff
If CRDs are changed:
N/A
Please refer to the [PR
guidelines](https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/docs/developing.md#submitting-a-pr)
before submitting this pull request.
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
Signed-off-by: David Tippett
---
.../opensearch-cluster-initcontainer-resources.yaml | 6 +++---
.../{ => 1.3.x}/opensearch-cluster-securityconfig.yaml | 4 ++--
.../examples/{ => 1.3.x}/opensearch-cluster.yaml | 4 ++--
.../{ => 2.x}/opensearch-cluster-custom-admin-user.yaml | 4 ++--
.../examples/{ => 2.x}/opensearch-cluster-pdb.yaml | 4 ++--
.../opensearch-cluster.yaml} | 4 ++--
.../examples/{ => 2.x}/opensearch-hot-warm-cold.yaml | 4 ++--
.../{ => templates}/opensearch-component-template.yaml | 0
.../examples/{ => templates}/opensearch-index-template.yaml | 0
9 files changed, 15 insertions(+), 15 deletions(-)
rename opensearch-operator/examples/{ => 1.3.x}/opensearch-cluster-initcontainer-resources.yaml (91%)
rename opensearch-operator/examples/{ => 1.3.x}/opensearch-cluster-securityconfig.yaml (97%)
rename opensearch-operator/examples/{ => 1.3.x}/opensearch-cluster.yaml (96%)
rename opensearch-operator/examples/{ => 2.x}/opensearch-cluster-custom-admin-user.yaml (96%)
rename opensearch-operator/examples/{ => 2.x}/opensearch-cluster-pdb.yaml (96%)
rename opensearch-operator/examples/{opensearch-v2-cluster.yaml => 2.x/opensearch-cluster.yaml} (96%)
rename opensearch-operator/examples/{ => 2.x}/opensearch-hot-warm-cold.yaml (98%)
rename opensearch-operator/examples/{ => templates}/opensearch-component-template.yaml (100%)
rename opensearch-operator/examples/{ => templates}/opensearch-index-template.yaml (100%)
diff --git a/opensearch-operator/examples/opensearch-cluster-initcontainer-resources.yaml b/opensearch-operator/examples/1.3.x/opensearch-cluster-initcontainer-resources.yaml
similarity index 91%
rename from opensearch-operator/examples/opensearch-cluster-initcontainer-resources.yaml
rename to opensearch-operator/examples/1.3.x/opensearch-cluster-initcontainer-resources.yaml
index fa2aa6fb..1ff3f0fe 100644
--- a/opensearch-operator/examples/opensearch-cluster-initcontainer-resources.yaml
+++ b/opensearch-operator/examples/1.3.x/opensearch-cluster-initcontainer-resources.yaml
@@ -5,11 +5,11 @@ metadata:
namespace: default
spec:
general:
- version: 1.3.0
+ version: 1.3.13
httpPort: 9200
vendor: opensearch
serviceName: my-cluster
- pluginsList: ["repository-s3","https://github.com/aiven/prometheus-exporter-plugin-for-opensearch/releases/download/1.3.0.0/prometheus-exporter-1.3.0.0.zip"]
+ pluginsList: ["repository-s3","https://github.com/aiven/prometheus-exporter-plugin-for-opensearch/releases/download/1.3.13.0/prometheus-exporter-1.3.13.0.zip"]
initHelper:
resources:
requests:
@@ -19,7 +19,7 @@ spec:
memory: "512Mi"
cpu: "500m"
dashboards:
- version: 1.3.0
+ version: 1.3.13
enable: true
replicas: 1
resources:
diff --git a/opensearch-operator/examples/opensearch-cluster-securityconfig.yaml b/opensearch-operator/examples/1.3.x/opensearch-cluster-securityconfig.yaml
similarity index 97%
rename from opensearch-operator/examples/opensearch-cluster-securityconfig.yaml
rename to opensearch-operator/examples/1.3.x/opensearch-cluster-securityconfig.yaml
index 856f1cca..20108967 100644
--- a/opensearch-operator/examples/opensearch-cluster-securityconfig.yaml
+++ b/opensearch-operator/examples/1.3.x/opensearch-cluster-securityconfig.yaml
@@ -5,12 +5,12 @@ metadata:
namespace: default
spec:
general:
- version: 1.3.2
+ version: 1.3.16
httpPort: 9200
vendor: opensearch
serviceName: my-cluster
dashboards:
- version: 1.3.2
+ version: 1.3.16
enable: true
replicas: 2
resources:
diff --git a/opensearch-operator/examples/opensearch-cluster.yaml b/opensearch-operator/examples/1.3.x/opensearch-cluster.yaml
similarity index 96%
rename from opensearch-operator/examples/opensearch-cluster.yaml
rename to opensearch-operator/examples/1.3.x/opensearch-cluster.yaml
index 8aa9f228..67cd1284 100644
--- a/opensearch-operator/examples/opensearch-cluster.yaml
+++ b/opensearch-operator/examples/1.3.x/opensearch-cluster.yaml
@@ -5,7 +5,7 @@ metadata:
namespace: default
spec:
general:
- version: "1.3.0"
+ version: 1.3.16
httpPort: 9200
vendor: opensearch
serviceName: my-cluster
@@ -13,7 +13,7 @@ spec:
enable: true
pluginsList: ["repository-s3"]
dashboards:
- version: "1.3.0"
+ version: 1.3.16
enable: true
replicas: 2
resources:
diff --git a/opensearch-operator/examples/opensearch-cluster-custom-admin-user.yaml b/opensearch-operator/examples/2.x/opensearch-cluster-custom-admin-user.yaml
similarity index 96%
rename from opensearch-operator/examples/opensearch-cluster-custom-admin-user.yaml
rename to opensearch-operator/examples/2.x/opensearch-cluster-custom-admin-user.yaml
index de2279db..b7c386ad 100644
--- a/opensearch-operator/examples/opensearch-cluster-custom-admin-user.yaml
+++ b/opensearch-operator/examples/2.x/opensearch-cluster-custom-admin-user.yaml
@@ -7,7 +7,7 @@ metadata:
namespace: default
spec:
general:
- version: "2.2.1"
+ version: 2.14.0
httpPort: 9200
vendor: opensearch
serviceName: my-cluster
@@ -26,7 +26,7 @@ spec:
dashboards:
opensearchCredentialsSecret:
name: admin-credentials-secret
- version: "2.2.1"
+ version: 2.14.0
enable: true
replicas: 2
resources:
diff --git a/opensearch-operator/examples/opensearch-cluster-pdb.yaml b/opensearch-operator/examples/2.x/opensearch-cluster-pdb.yaml
similarity index 96%
rename from opensearch-operator/examples/opensearch-cluster-pdb.yaml
rename to opensearch-operator/examples/2.x/opensearch-cluster-pdb.yaml
index 988e5ed8..7c13bbf0 100644
--- a/opensearch-operator/examples/opensearch-cluster-pdb.yaml
+++ b/opensearch-operator/examples/2.x/opensearch-cluster-pdb.yaml
@@ -5,14 +5,14 @@ metadata:
namespace: default
spec:
general:
- version: "2.3.0"
+ version: 2.14.0
httpPort: 9200
vendor: opensearch
serviceName: my-cluster
monitoring:
enable: true
dashboards:
- version: "2.3.0"
+ version: 2.14.0
enable: true
replicas: 1
resources:
diff --git a/opensearch-operator/examples/opensearch-v2-cluster.yaml b/opensearch-operator/examples/2.x/opensearch-cluster.yaml
similarity index 96%
rename from opensearch-operator/examples/opensearch-v2-cluster.yaml
rename to opensearch-operator/examples/2.x/opensearch-cluster.yaml
index 48613a17..5da24c94 100644
--- a/opensearch-operator/examples/opensearch-v2-cluster.yaml
+++ b/opensearch-operator/examples/2.x/opensearch-cluster.yaml
@@ -17,14 +17,14 @@ spec:
general:
httpPort: 9200
serviceName: my-first-cluster
- version: 2.3.0
+ version: 2.14.0
pluginsList: ["repository-s3"]
drainDataNodes: true
dashboards:
tls:
enable: true
generate: true
- version: 2.3.0
+ version: 2.14.0
enable: true
replicas: 1
resources:
diff --git a/opensearch-operator/examples/opensearch-hot-warm-cold.yaml b/opensearch-operator/examples/2.x/opensearch-hot-warm-cold.yaml
similarity index 98%
rename from opensearch-operator/examples/opensearch-hot-warm-cold.yaml
rename to opensearch-operator/examples/2.x/opensearch-hot-warm-cold.yaml
index 3e8f7269..a0da7568 100644
--- a/opensearch-operator/examples/opensearch-hot-warm-cold.yaml
+++ b/opensearch-operator/examples/2.x/opensearch-hot-warm-cold.yaml
@@ -19,13 +19,13 @@ spec:
httpPort: 9200
setVMMaxMapCount: true
serviceName: my-first-hot-warm-cold-cluster
- version: 2.6.0
+ version: 2.14.0
drainDataNodes: true
dashboards:
tls:
enable: true
generate: true
- version: 2.6.0
+ version: 2.14.0
enable: true
replicas: 1
resources:
diff --git a/opensearch-operator/examples/opensearch-component-template.yaml b/opensearch-operator/examples/templates/opensearch-component-template.yaml
similarity index 100%
rename from opensearch-operator/examples/opensearch-component-template.yaml
rename to opensearch-operator/examples/templates/opensearch-component-template.yaml
diff --git a/opensearch-operator/examples/opensearch-index-template.yaml b/opensearch-operator/examples/templates/opensearch-index-template.yaml
similarity index 100%
rename from opensearch-operator/examples/opensearch-index-template.yaml
rename to opensearch-operator/examples/templates/opensearch-index-template.yaml
From 6b033fccb3757361bef66dff1377e72a019d0fac Mon Sep 17 00:00:00 2001
From: JustInVTime
Date: Wed, 5 Jun 2024 12:38:09 +0200
Subject: [PATCH 12/27] Added missing affinity and tolerations options (#821)
### Description
Added missing affinity and tolerations options for nodepools in cluster
chart
Signed-off-by: JustInVTime
---
.../templates/opensearch-cluster-cr.yaml | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/charts/opensearch-cluster/templates/opensearch-cluster-cr.yaml b/charts/opensearch-cluster/templates/opensearch-cluster-cr.yaml
index db806d92..d8a5ede5 100644
--- a/charts/opensearch-cluster/templates/opensearch-cluster-cr.yaml
+++ b/charts/opensearch-cluster/templates/opensearch-cluster-cr.yaml
@@ -197,6 +197,14 @@ spec:
nodeSelector:
{{- toYaml $val.nodeSelector | nindent 8 }}
{{- end }}
+ {{- if $val.affinity }}
+ affinity:
+ {{- toYaml $val.affinity | nindent 8 }}
+ {{- end }}
+ {{- if $val.tolerations }}
+ tolerations:
+ {{- toYaml $val.tolerations | nindent 8 }}
+ {{- end }}
{{- if $val.labels }}
labels: # Add any extra labels as key-value pairs here
{{ toYaml $val.labels | nindent 8 }}
From 4b6b204c26840a96d36ecec1ec6b3c4e62df64a4 Mon Sep 17 00:00:00 2001
From: Kanzal Qalandri <128204757+kanzalqalandri@users.noreply.github.com>
Date: Wed, 5 Jun 2024 15:39:11 +0500
Subject: [PATCH 13/27] Added missing snapshotRepositories option in helm chart
(#826)
### Description
Added missing snapshotRepositories option in opensearch-cluster helm
chart
### Issues Resolved
#824
### Check List
- [x] Commits are signed per the DCO using --signoff
If CRDs are changed:
N/A
Please refer to the [PR
guidelines](https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/docs/developing.md#submitting-a-pr)
before submitting this pull request.
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
Signed-off-by: Kanzal Qalandri
---
.../opensearch-cluster/templates/opensearch-cluster-cr.yaml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/charts/opensearch-cluster/templates/opensearch-cluster-cr.yaml b/charts/opensearch-cluster/templates/opensearch-cluster-cr.yaml
index d8a5ede5..e08132ca 100644
--- a/charts/opensearch-cluster/templates/opensearch-cluster-cr.yaml
+++ b/charts/opensearch-cluster/templates/opensearch-cluster-cr.yaml
@@ -82,6 +82,10 @@ spec:
monitoring:
{{ toYaml .Values.opensearchCluster.general.monitoring | nindent 6 }}
{{- end }}
+ {{- if .Values.opensearchCluster.general.snapshotRepositories }}
+ snapshotRepositories:
+ {{- toYaml .Values.opensearchCluster.general.snapshotRepositories | nindent 6 }}
+ {{- end }}
{{- if .Values.opensearchCluster.dashboards }}
dashboards:
{{- if .Values.opensearchCluster.dashboards.image }}
From 418af82d5b9b78010b3b8e30795739653f1e62a2 Mon Sep 17 00:00:00 2001
From: rkthtrifork <131661717+rkthtrifork@users.noreply.github.com>
Date: Fri, 7 Jun 2024 11:44:11 +0200
Subject: [PATCH 14/27] Changed the structure of the cron condition for ISM
policy CRD (#838)
### Description
The cron condition for ISM policy CRDs was not working since it had the
wrong structure. The structure in the operator was defined as
```
...
"conditions": {
"cron": {
"expression": "* 17 * * SAT",
"timezone": "America/Los_Angeles"
}
}
...
```
while the OpenSearch API expects
```
...
"conditions": {
"cron": {
"cron": {
"expression": "* 17 * * SAT",
"timezone": "America/Los_Angeles"
}
}
}
...
```
This PR should resolve the issue.
I also ran into a few odd details in the code where we checked if an
error was nil when it would always be nil so I have moved those lines
out of the if-statement. A few error messages were capital first letter
which was different from the rest of the codebase so I fixed those.
Lastly, my linter (default Go extension for VSCode) swapped some import
orders. Let me know if those changes are alright.
### Issues Resolved
I don't think this PR solves any current issues
### Check List
- [x] Commits are signed per the DCO using --signoff
- [ ] Unittest added for the new/changed functionality and all unit
tests are successful
- [ ] Customer-visible features documented
- [x] No linter warnings (`make lint`)
If CRDs are changed:
- [x] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [x] Changes to CRDs documented - **(there was no documentation for
this part of the CRD, but I am happy to make one if you would like)**
Please refer to the [PR
guidelines](https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/docs/developing.md#submitting-a-pr)
before submitting this pull request.
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
Signed-off-by: rkthtrifork
---
...earch.opster.io_opensearchismpolicies.yaml | 28 +++++++++++++------
.../api/v1/opensearchism_types.go | 5 ++++
...earch.opster.io_opensearchismpolicies.yaml | 28 +++++++++++++------
.../opensearch-gateway/requests/IsmPolicy.go | 5 ++++
.../services/http_request.go | 5 ++--
.../services/os_data_service.go | 11 +++-----
.../services/os_ism_service.go | 7 +++--
.../pkg/reconcilers/ismpolicy.go | 11 ++++----
8 files changed, 65 insertions(+), 35 deletions(-)
diff --git a/charts/opensearch-operator/files/opensearch.opster.io_opensearchismpolicies.yaml b/charts/opensearch-operator/files/opensearch.opster.io_opensearchismpolicies.yaml
index 8cdb5e1d..1578f26b 100644
--- a/charts/opensearch-operator/files/opensearch.opster.io_opensearchismpolicies.yaml
+++ b/charts/opensearch-operator/files/opensearch.opster.io_opensearchismpolicies.yaml
@@ -370,16 +370,26 @@ spec:
description: The cron job that triggers the transition
if no other transition happens first.
properties:
- expression:
- description: The cron expression that triggers
- the transition.
- type: string
- timezone:
- description: The timezone that triggers the transition.
- type: string
+ cron:
+ description: A wrapper for the cron job that triggers
+ the transition if no other transition happens
+ first. This wrapper is here to adhere to the
+ OpenSearch API.
+ properties:
+ expression:
+ description: The cron expression that triggers
+ the transition.
+ type: string
+ timezone:
+ description: The timezone that triggers the
+ transition.
+ type: string
+ required:
+ - expression
+ - timezone
+ type: object
required:
- - expression
- - timezone
+ - cron
type: object
minDocCount:
description: The minimum document count of the index
diff --git a/opensearch-operator/api/v1/opensearchism_types.go b/opensearch-operator/api/v1/opensearchism_types.go
index 37cda37b..c187ae5c 100644
--- a/opensearch-operator/api/v1/opensearchism_types.go
+++ b/opensearch-operator/api/v1/opensearchism_types.go
@@ -251,6 +251,11 @@ type Condition struct {
}
type Cron struct {
+ // A wrapper for the cron job that triggers the transition if no other transition happens first. This wrapper is here to adhere to the OpenSearch API.
+ CronDetails *CronDetails `json:"cron"`
+}
+
+type CronDetails struct {
// The cron expression that triggers the transition.
Expression string `json:"expression"`
// The timezone that triggers the transition.
diff --git a/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchismpolicies.yaml b/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchismpolicies.yaml
index 8cdb5e1d..1578f26b 100644
--- a/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchismpolicies.yaml
+++ b/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchismpolicies.yaml
@@ -370,16 +370,26 @@ spec:
description: The cron job that triggers the transition
if no other transition happens first.
properties:
- expression:
- description: The cron expression that triggers
- the transition.
- type: string
- timezone:
- description: The timezone that triggers the transition.
- type: string
+ cron:
+ description: A wrapper for the cron job that triggers
+ the transition if no other transition happens
+ first. This wrapper is here to adhere to the
+ OpenSearch API.
+ properties:
+ expression:
+ description: The cron expression that triggers
+ the transition.
+ type: string
+ timezone:
+ description: The timezone that triggers the
+ transition.
+ type: string
+ required:
+ - expression
+ - timezone
+ type: object
required:
- - expression
- - timezone
+ - cron
type: object
minDocCount:
description: The minimum document count of the index
diff --git a/opensearch-operator/opensearch-gateway/requests/IsmPolicy.go b/opensearch-operator/opensearch-gateway/requests/IsmPolicy.go
index 5d292a6e..2f820a0a 100644
--- a/opensearch-operator/opensearch-gateway/requests/IsmPolicy.go
+++ b/opensearch-operator/opensearch-gateway/requests/IsmPolicy.go
@@ -218,7 +218,12 @@ type Condition struct {
// The minimum size of the total primary shard storage (not counting replicas) required to transition.
MinSize *string `json:"min_size,omitempty"`
}
+
type Cron struct {
+ CronDetails *CronDetails `json:"cron"`
+}
+
+type CronDetails struct {
// The cron expression that triggers the transition.
Expression string `json:"expression"`
// The timezone that triggers the transition.
diff --git a/opensearch-operator/opensearch-gateway/services/http_request.go b/opensearch-operator/opensearch-gateway/services/http_request.go
index 0033ca49..c99579de 100644
--- a/opensearch-operator/opensearch-gateway/services/http_request.go
+++ b/opensearch-operator/opensearch-gateway/services/http_request.go
@@ -2,11 +2,12 @@ package services
import (
"context"
- "github.com/opensearch-project/opensearch-go"
- "github.com/opensearch-project/opensearch-go/opensearchapi"
"io"
"net/http"
"strings"
+
+ "github.com/opensearch-project/opensearch-go"
+ "github.com/opensearch-project/opensearch-go/opensearchapi"
)
// doHTTPGet performs a HTTP GET request
diff --git a/opensearch-operator/opensearch-gateway/services/os_data_service.go b/opensearch-operator/opensearch-gateway/services/os_data_service.go
index 2c01762d..a709e385 100644
--- a/opensearch-operator/opensearch-gateway/services/os_data_service.go
+++ b/opensearch-operator/opensearch-gateway/services/os_data_service.go
@@ -4,9 +4,10 @@ import (
"context"
"encoding/json"
"fmt"
+ "strings"
+
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
- "strings"
"github.com/Opster/opensearch-k8s-operator/opensearch-operator/opensearch-gateway/requests"
"github.com/Opster/opensearch-k8s-operator/opensearch-operator/opensearch-gateway/responses"
@@ -96,9 +97,7 @@ func AppendExcludeNodeHost(service *OsClusterClient, nodeNameToExclude string) (
valAsString = strings.Join(valArr, ",")
}
settings := createClusterSettingsResponseWithExcludeName(valAsString)
- if err == nil {
- _, err = service.PutClusterSettings(settings)
- }
+ _, err = service.PutClusterSettings(settings)
return err == nil, err
}
@@ -114,9 +113,7 @@ func RemoveExcludeNodeHost(service *OsClusterClient, nodeNameToExclude string) (
valAsString := strings.ReplaceAll(val.(string), nodeNameToExclude, "")
valAsString = strings.ReplaceAll(valAsString, ",,", ",")
settings := createClusterSettingsResponseWithExcludeName(valAsString)
- if err == nil {
- _, err = service.PutClusterSettings(settings)
- }
+ _, err = service.PutClusterSettings(settings)
return err == nil, err
}
diff --git a/opensearch-operator/opensearch-gateway/services/os_ism_service.go b/opensearch-operator/opensearch-gateway/services/os_ism_service.go
index b2e7c5f2..d82c0e90 100644
--- a/opensearch-operator/opensearch-gateway/services/os_ism_service.go
+++ b/opensearch-operator/opensearch-gateway/services/os_ism_service.go
@@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
+
"github.com/Opster/opensearch-k8s-operator/opensearch-operator/opensearch-gateway/requests"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
@@ -12,7 +13,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log"
)
-var ErrNotFound = errors.New("Policy not found")
+var ErrNotFound = errors.New("policy not found")
// ShouldUpdateISMPolicy checks if the passed policy is same as existing or needs update
func ShouldUpdateISMPolicy(ctx context.Context, newPolicy, existingPolicy requests.Policy) (bool, error) {
@@ -87,7 +88,7 @@ func UpdateISMPolicy(ctx context.Context, service *OsClusterClient, ismpolicy re
}
defer resp.Body.Close()
if resp.IsError() {
- return fmt.Errorf("Failed to create ism policy: %s", resp.String())
+ return fmt.Errorf("failed to create ism policy: %s", resp.String())
}
return nil
}
@@ -100,7 +101,7 @@ func DeleteISMPolicy(ctx context.Context, service *OsClusterClient, policyName s
}
defer resp.Body.Close()
if resp.IsError() {
- return fmt.Errorf("Failed to delete ism policy: %s", resp.String())
+ return fmt.Errorf("failed to delete ism policy: %s", resp.String())
}
return nil
}
diff --git a/opensearch-operator/pkg/reconcilers/ismpolicy.go b/opensearch-operator/pkg/reconcilers/ismpolicy.go
index 32343f3a..f2954d45 100644
--- a/opensearch-operator/pkg/reconcilers/ismpolicy.go
+++ b/opensearch-operator/pkg/reconcilers/ismpolicy.go
@@ -219,9 +219,6 @@ func (r *IsmPolicyReconciler) Reconcile() (retResult ctrl.Result, retErr error)
r.recorder.Event(r.instance, "Normal", opensearchAPIUpdated, "policy created in opensearch")
return ctrl.Result{Requeue: true, RequeueAfter: 30 * time.Second}, retErr
}
- if err != nil {
- return
- }
priterm := existingPolicy.PrimaryTerm
seqno := existingPolicy.SequenceNumber
// Reset
@@ -505,8 +502,12 @@ func (r *IsmPolicyReconciler) CreateISMPolicyRequest() (*requests.Policy, error)
conditions.MinRolloverAge = transition.Conditions.MinRolloverAge
}
if transition.Conditions.Cron != nil {
- conditions.Cron.Expression = transition.Conditions.Cron.Expression
- conditions.Cron.Timezone = transition.Conditions.Cron.Timezone
+ conditions.Cron = &requests.Cron{
+ CronDetails: &requests.CronDetails{
+ Expression: transition.Conditions.Cron.CronDetails.Expression,
+ Timezone: transition.Conditions.Cron.CronDetails.Timezone,
+ },
+ }
}
statename := transition.StateName
transitions = append(transitions, requests.Transition{Conditions: conditions, StateName: statename})
From 56c9c8f16e79647e0a9b627f188f8486854a086e Mon Sep 17 00:00:00 2001
From: Sebastian Woehrl
Date: Thu, 13 Jun 2024 08:54:33 +0200
Subject: [PATCH 15/27] Disable http client connection reuse to prevent memory
leak (#842)
### Description
The operator pod is suffering from memory leaks. After some analysis I
think I have narrowed it down to connections for the http client being
kept for reuse but never being used due to a new client being created in
every reconcile run.
This PR disables the connection keepalive/reuse and (at least in my
experiments) prevents the memory leak.
### Issues Resolved
Fixes #700
### Check List
- [x] Commits are signed per the DCO using --signoff
- [-] Unittest added for the new/changed functionality and all unit
tests are successful
- [-] Customer-visible features documented
- [x] No linter warnings (`make lint`)
If CRDs are changed:
- [-] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [-] Changes to CRDs documented
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
Signed-off-by: Sebastian Woehrl
---
.../api/v1/zz_generated.deepcopy.go | 22 ++++++++++++-
.../opensearch-gateway/services/os_client.go | 4 +++
opensearch-operator/pkg/reconcilers/scaler.go | 33 +++++++------------
3 files changed, 36 insertions(+), 23 deletions(-)
diff --git a/opensearch-operator/api/v1/zz_generated.deepcopy.go b/opensearch-operator/api/v1/zz_generated.deepcopy.go
index da195040..055616a0 100644
--- a/opensearch-operator/api/v1/zz_generated.deepcopy.go
+++ b/opensearch-operator/api/v1/zz_generated.deepcopy.go
@@ -402,7 +402,7 @@ func (in *Condition) DeepCopyInto(out *Condition) {
if in.Cron != nil {
in, out := &in.Cron, &out.Cron
*out = new(Cron)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
if in.MinDocCount != nil {
in, out := &in.MinDocCount, &out.MinDocCount
@@ -454,6 +454,11 @@ func (in *ConfMgmt) DeepCopy() *ConfMgmt {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Cron) DeepCopyInto(out *Cron) {
*out = *in
+ if in.CronDetails != nil {
+ in, out := &in.CronDetails, &out.CronDetails
+ *out = new(CronDetails)
+ **out = **in
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Cron.
@@ -466,6 +471,21 @@ func (in *Cron) DeepCopy() *Cron {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *CronDetails) DeepCopyInto(out *CronDetails) {
+ *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CronDetails.
+func (in *CronDetails) DeepCopy() *CronDetails {
+ if in == nil {
+ return nil
+ }
+ out := new(CronDetails)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DashboardsConfig) DeepCopyInto(out *DashboardsConfig) {
*out = *in
diff --git a/opensearch-operator/opensearch-gateway/services/os_client.go b/opensearch-operator/opensearch-gateway/services/os_client.go
index b12729f9..081c6fa2 100644
--- a/opensearch-operator/opensearch-gateway/services/os_client.go
+++ b/opensearch-operator/opensearch-gateway/services/os_client.go
@@ -73,6 +73,10 @@ func NewOsClusterClient(clusterUrl string, username string, password string, opt
}
return &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+ // These options are needed as otherwise connections would be kept and leak memory
+ // Connection reuse is not really possible due to each reconcile run being independent
+ DisableKeepAlives: true,
+ MaxIdleConns: 1,
}
}(),
Addresses: []string{clusterUrl},
diff --git a/opensearch-operator/pkg/reconcilers/scaler.go b/opensearch-operator/pkg/reconcilers/scaler.go
index 297e53d0..3223a80a 100644
--- a/opensearch-operator/pkg/reconcilers/scaler.go
+++ b/opensearch-operator/pkg/reconcilers/scaler.go
@@ -10,6 +10,7 @@ import (
"github.com/Opster/opensearch-k8s-operator/opensearch-operator/pkg/builders"
"github.com/Opster/opensearch-k8s-operator/opensearch-operator/pkg/helpers"
"github.com/Opster/opensearch-k8s-operator/opensearch-operator/pkg/reconcilers/k8s"
+ "github.com/Opster/opensearch-k8s-operator/opensearch-operator/pkg/reconcilers/util"
"github.com/cisco-open/operator-tools/pkg/reconciler"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/client-go/tools/record"
@@ -25,6 +26,7 @@ type ScalerReconciler struct {
recorder record.EventRecorder
reconcilerContext *ReconcilerContext
instance *opsterv1.OpenSearchCluster
+ ReconcilerOptions
}
func NewScalerReconciler(
@@ -33,14 +35,17 @@ func NewScalerReconciler(
recorder record.EventRecorder,
reconcilerContext *ReconcilerContext,
instance *opsterv1.OpenSearchCluster,
- opts ...reconciler.ResourceReconcilerOption,
+ opts ...ReconcilerOption,
) *ScalerReconciler {
+ options := ReconcilerOptions{}
+ options.apply(opts...)
return &ScalerReconciler{
- client: k8s.NewK8sClient(client, ctx, append(opts, reconciler.WithLog(log.FromContext(ctx).WithValues("reconciler", "scaler")))...),
+ client: k8s.NewK8sClient(client, ctx, reconciler.WithLog(log.FromContext(ctx).WithValues("reconciler", "scaler"))),
ctx: ctx,
recorder: recorder,
reconcilerContext: reconcilerContext,
instance: instance,
+ ReconcilerOptions: options,
}
}
@@ -187,11 +192,7 @@ func (r *ScalerReconciler) decreaseOneNode(currentStatus opsterv1.ComponentStatu
if !smartDecrease {
return false, err
}
- username, password, err := helpers.UsernameAndPassword(r.client, r.instance)
- if err != nil {
- return true, err
- }
- clusterClient, err := services.NewOsClusterClient(builders.URLForCluster(r.instance), username, password)
+ clusterClient, err := util.CreateClientForCluster(r.client, r.ctx, r.instance, r.osClientTransport)
if err != nil {
lg.Error(err, "failed to create os client")
r.recorder.AnnotatedEventf(r.instance, annotations, "WARN", "failed to remove node exclude", "Group-%s . failed to remove node exclude %s", nodePoolGroupName, lastReplicaNodeName)
@@ -209,13 +210,9 @@ func (r *ScalerReconciler) decreaseOneNode(currentStatus opsterv1.ComponentStatu
func (r *ScalerReconciler) excludeNode(currentStatus opsterv1.ComponentStatus, currentSts appsv1.StatefulSet, nodePoolGroupName string) error {
lg := log.FromContext(r.ctx)
- username, password, err := helpers.UsernameAndPassword(r.client, r.instance)
annotations := map[string]string{"cluster-name": r.instance.GetName()}
- if err != nil {
- return err
- }
- clusterClient, err := services.NewOsClusterClient(builders.URLForCluster(r.instance), username, password)
+ clusterClient, err := util.CreateClientForCluster(r.client, r.ctx, r.instance, r.osClientTransport)
if err != nil {
lg.Error(err, "failed to create os client")
r.recorder.AnnotatedEventf(r.instance, annotations, "Warning", "Scaler", "Failed to create os client for scaling")
@@ -272,12 +269,8 @@ func (r *ScalerReconciler) drainNode(currentStatus opsterv1.ComponentStatus, cur
lg := log.FromContext(r.ctx)
annotations := map[string]string{"cluster-name": r.instance.GetName()}
lastReplicaNodeName := helpers.ReplicaHostName(currentSts, *currentSts.Spec.Replicas-1)
- username, password, err := helpers.UsernameAndPassword(r.client, r.instance)
- if err != nil {
- return err
- }
- clusterClient, err := services.NewOsClusterClient(builders.URLForCluster(r.instance), username, password)
+ clusterClient, err := util.CreateClientForCluster(r.client, r.ctx, r.instance, r.osClientTransport)
if err != nil {
return err
}
@@ -328,12 +321,8 @@ func (r *ScalerReconciler) removeStatefulSet(sts appsv1.StatefulSet) (*ctrl.Resu
}
// Gracefully remove nodes
- username, password, err := helpers.UsernameAndPassword(r.client, r.instance)
- if err != nil {
- return nil, err
- }
annotations := map[string]string{"cluster-name": r.instance.GetName()}
- clusterClient, err := services.NewOsClusterClient(builders.URLForCluster(r.instance), username, password)
+ clusterClient, err := util.CreateClientForCluster(r.client, r.ctx, r.instance, r.osClientTransport)
if err != nil {
lg.Error(err, "failed to create os client")
r.recorder.AnnotatedEventf(r.instance, annotations, "Warning", "Scaler", "Failed to create os client")
From ce25fccd734cddcf8b1441632cc80caac329057e Mon Sep 17 00:00:00 2001
From: Casper Thygesen
Date: Fri, 2 Aug 2024 12:50:05 +0200
Subject: [PATCH 16/27] Missing securityContext attributes on container
kube-rbac-proxy (#848)
### Description
Allows the following setup
```yaml
kubeRbacProxy:
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- all
```
### Issues Resolved
Fixes
[#745](https://github.com/opensearch-project/opensearch-k8s-operator/issues/745)
### Check List
- [x] Commits are signed per the DCO using --signoff
- [ ] Unittest added for the new/changed functionality and all unit
tests are successful
- [ ] Customer-visible features documented
- [ ] No linter warnings (`make lint`)
If CRDs are changed:
- [ ] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [ ] Changes to CRDs documented
Please refer to the [PR
guidelines](https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/docs/developing.md#submitting-a-pr)
before submitting this pull request.
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
Signed-off-by: Casper Thygesen
---
charts/opensearch-operator/values.yaml | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/charts/opensearch-operator/values.yaml b/charts/opensearch-operator/values.yaml
index 96bdc268..0629f261 100644
--- a/charts/opensearch-operator/values.yaml
+++ b/charts/opensearch-operator/values.yaml
@@ -74,7 +74,11 @@ serviceAccount:
kubeRbacProxy:
enable: true
securityContext:
- # allowPrivilegeEscalation: false
+ allowPrivilegeEscalation: false
+ readOnlyRootFilesystem: true
+ capabilities:
+ drop:
+ - ALL
resources:
limits:
cpu: 50m
From 231075ce87a7ccac999c66083b071f4ec6094d94 Mon Sep 17 00:00:00 2001
From: Alex <49783005+OlegVanHorst@users.noreply.github.com>
Date: Wed, 7 Aug 2024 14:05:38 +0200
Subject: [PATCH 17/27] Fix rollingRestart reconcile for multiple same named
clusters (#836)
### Description
Fixes a bug where reconcile breaks, if there are multiple same named
OpensearchClusters in different namespaces.
The rollingRestart reconciler selects pods in all namespaces when
evaluating if the Pods are healthy and matches them with the desired
replicas. Only pods in the own namespace should be checked.
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
---------
Signed-off-by: Alex Engel
---
opensearch-operator/pkg/helpers/helpers.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/opensearch-operator/pkg/helpers/helpers.go b/opensearch-operator/pkg/helpers/helpers.go
index aa6de1d5..82672731 100644
--- a/opensearch-operator/pkg/helpers/helpers.go
+++ b/opensearch-operator/pkg/helpers/helpers.go
@@ -247,7 +247,7 @@ func CountRunningPodsForNodePool(k8sClient k8s.K8sClient, cr *opsterv1.OpenSearc
selector := labels.NewSelector()
selector = selector.Add(*clusterReq, *componentReq)
// List pods matching selector
- list, err := k8sClient.ListPods(&client.ListOptions{LabelSelector: selector})
+ list, err := k8sClient.ListPods(&client.ListOptions{Namespace: cr.Namespace, LabelSelector: selector})
if err != nil {
return 0, err
}
@@ -281,7 +281,7 @@ func CountPVCsForNodePool(k8sClient k8s.K8sClient, cr *opsterv1.OpenSearchCluste
}
selector := labels.NewSelector()
selector = selector.Add(*clusterReq, *componentReq)
- list, err := k8sClient.ListPVCs(&client.ListOptions{LabelSelector: selector})
+ list, err := k8sClient.ListPVCs(&client.ListOptions{Namespace: cr.Namespace, LabelSelector: selector})
if err != nil {
return 0, err
}
From 610222aed69e483c14e9cf9e93205547079df35f Mon Sep 17 00:00:00 2001
From: Guilherme Oki
Date: Tue, 13 Aug 2024 10:21:21 -0300
Subject: [PATCH 18/27] feat(helm): add option to set resources for initHelper
(#865)
### Description
This PR adds an option to define resources for initHelper container.
### Issues Resolved
Closes this issue
https://github.com/opensearch-project/opensearch-k8s-operator/issues/866
### Check List
- [X] Commits are signed per the DCO using --signoff
- [X] Unittest added for the new/changed functionality and all unit
tests are successful
- [X] Customer-visible features documented
- [X] No linter warnings (`make lint`)
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
---------
Signed-off-by: Guilherme Oki
---
.../templates/opensearch-cluster-cr.yaml | 4 ++++
charts/opensearch-cluster/values.yaml | 11 +++++++++++
2 files changed, 15 insertions(+)
diff --git a/charts/opensearch-cluster/templates/opensearch-cluster-cr.yaml b/charts/opensearch-cluster/templates/opensearch-cluster-cr.yaml
index e08132ca..79f3b70b 100644
--- a/charts/opensearch-cluster/templates/opensearch-cluster-cr.yaml
+++ b/charts/opensearch-cluster/templates/opensearch-cluster-cr.yaml
@@ -27,6 +27,10 @@ spec:
imagePullSecrets:
{{ toYaml .Values.opensearchCluster.initHelper.imagePullSecrets | nindent 6 }}
{{- end }}
+ {{- if .Values.opensearchCluster.initHelper.resources }}
+ resources:
+ {{- toYaml .Values.opensearchCluster.initHelper.resources | nindent 6 }}
+ {{- end }}
{{- end }}
general:
{{- if .Values.opensearchCluster.general.version }}
diff --git a/charts/opensearch-cluster/values.yaml b/charts/opensearch-cluster/values.yaml
index c0cc622f..2841ea81 100644
--- a/charts/opensearch-cluster/values.yaml
+++ b/charts/opensearch-cluster/values.yaml
@@ -27,6 +27,17 @@ opensearchCluster:
limits:
memory: "1Gi"
cpu: "500m"
+ initHelper:
+ imagePullSecrets: []
+ # - registryKeySecretName
+ imagePullPolicy: IfNotPresent
+ resources: {}
+ # requests:
+ # memory: "1Gi"
+ # cpu: "500m"
+ # limits:
+ # memory: "1Gi"
+ # cpu: "500m"
nodePools:
- component: masters
diskSize: "30Gi"
From ec6428d4310da13087ac0546885c7a31e29a1356 Mon Sep 17 00:00:00 2001
From: rkthtrifork <131661717+rkthtrifork@users.noreply.github.com>
Date: Thu, 22 Aug 2024 15:18:17 +0200
Subject: [PATCH 19/27] Rewrote ISM Policy reconciler (#846)
### Description
The ISM Policy reconciler was constantly trying to update the ISM Policy
and it was not handling reconciliation requeue in some cases. There were
possibly other issues as well. Below I have described what caused the
different issues I encountered
- The ISM Policy request was different from the response, but they were
both made with the same struct. This caused the reconciler to always see
the existing ISM Policy and the ISM Policy from the CR as different and
try to update it. I have created a separate struct model for each to
separate the logic and in the code I now compare the existing policy
with the policy from the CR by comparing both the Policy IDs and the
policy spec
- There were some very complex cases in the code that were very
difficult to understand so I have attempted to make the code more
concise and easy to read and understand
- I have added reconciliation requeuing to all cases so the operator
doesn't just stop reconciling the ISM Policy in some cases
One thing I am wondering is that I am not sure why we would want to
create a CR without specifying the cluster ID and then the operator
automatically links it to that cluster ID so it breaks if the OpenSearch
CR is deleted. Is this intended and why? I'm talking about the section
with the comment "Check cluster ref has not changed"
Tested cases:
- A new ISM Policy is created through a CR and the operator creates it
in the OpenSearch Cluster
- The CR for an ISM Policy that is created by the operator is removed
and the operator removes it in the OpenSearch Cluster
- An ISM Policy that already exists in the OpenSearch Cluster is created
through a CR and the operator ignores it and marks it as existing
- The CR for an ISM Policy that was pre-existing and therefore was not
created by the operator is removed and the operator does not remove the
ISM Policy from the OpenSearch Cluster
- An ISM Policy that already exists in the OpenSearch Cluster is created
through a CR and the operator ignores it and marks it as existing. The
ISM Policy is then manually removed from the OpenSearch Cluster and the
operator now applies the ISM Policy from the CR
The test for ISM Policies is currently failing miserably, but I decided
to create the PR to get feedback before I dive into fixing it.
### Issues Resolved
https://github.com/opensearch-project/opensearch-k8s-operator/issues/833
https://github.com/opensearch-project/opensearch-k8s-operator/issues/732
Possibly other issues
### Check List
- [x] Commits are signed per the DCO using --signoff
- [x] Unittest added for the new/changed functionality and all unit
tests are successful
- [x] Customer-visible features documented
- [x] No linter warnings (`make lint`)
If CRDs are changed:
- [ ] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [ ] Changes to CRDs documented
Please refer to the [PR
guidelines](https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/docs/developing.md#submitting-a-pr)
before submitting this pull request.
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
Signed-off-by: rkthtrifork
---
.../opensearch-gateway/requests/IsmPolicy.go | 9 +-
.../responses/ISMPolicyResponse.go | 5 -
.../opensearch-gateway/responses/IsmPolicy.go | 10 +
.../services/os_ism_service.go | 33 +-
.../pkg/reconcilers/ismpolicy.go | 285 +++++++------
.../pkg/reconcilers/ismpolicy_test.go | 398 +++++++++---------
.../pkg/reconcilers/reconcilers.go | 16 +-
7 files changed, 383 insertions(+), 373 deletions(-)
delete mode 100644 opensearch-operator/opensearch-gateway/responses/ISMPolicyResponse.go
create mode 100644 opensearch-operator/opensearch-gateway/responses/IsmPolicy.go
diff --git a/opensearch-operator/opensearch-gateway/requests/IsmPolicy.go b/opensearch-operator/opensearch-gateway/requests/IsmPolicy.go
index 2f820a0a..023fc79e 100644
--- a/opensearch-operator/opensearch-gateway/requests/IsmPolicy.go
+++ b/opensearch-operator/opensearch-gateway/requests/IsmPolicy.go
@@ -1,14 +1,11 @@
package requests
-type Policy struct {
- PolicyID string `json:"_id,omitempty"`
- PrimaryTerm *int `json:"_primary_term,omitempty"`
- SequenceNumber *int `json:"_seq_no,omitempty"`
- Policy ISMPolicy `json:"policy"`
+type ISMPolicy struct {
+ Policy ISMPolicySpec `json:"policy"`
}
// ISMPolicySpec is the specification for the ISM policy for OS.
-type ISMPolicy struct {
+type ISMPolicySpec struct {
// The default starting state for each index that uses this policy.
DefaultState string `json:"default_state"`
// A human-readable description of the policy.
diff --git a/opensearch-operator/opensearch-gateway/responses/ISMPolicyResponse.go b/opensearch-operator/opensearch-gateway/responses/ISMPolicyResponse.go
deleted file mode 100644
index 753314cc..00000000
--- a/opensearch-operator/opensearch-gateway/responses/ISMPolicyResponse.go
+++ /dev/null
@@ -1,5 +0,0 @@
-package responses
-
-import "github.com/Opster/opensearch-k8s-operator/opensearch-operator/opensearch-gateway/requests"
-
-type GetISMPoliciesResponse requests.Policy
diff --git a/opensearch-operator/opensearch-gateway/responses/IsmPolicy.go b/opensearch-operator/opensearch-gateway/responses/IsmPolicy.go
new file mode 100644
index 00000000..29174466
--- /dev/null
+++ b/opensearch-operator/opensearch-gateway/responses/IsmPolicy.go
@@ -0,0 +1,10 @@
+package responses
+
+import "github.com/Opster/opensearch-k8s-operator/opensearch-operator/opensearch-gateway/requests"
+
+type GetISMPolicyResponse struct {
+ PolicyID string `json:"_id"`
+ PrimaryTerm int `json:"_primary_term"`
+ SequenceNumber int `json:"_seq_no"`
+ Policy requests.ISMPolicySpec
+}
diff --git a/opensearch-operator/opensearch-gateway/services/os_ism_service.go b/opensearch-operator/opensearch-gateway/services/os_ism_service.go
index d82c0e90..040e2e26 100644
--- a/opensearch-operator/opensearch-gateway/services/os_ism_service.go
+++ b/opensearch-operator/opensearch-gateway/services/os_ism_service.go
@@ -7,6 +7,7 @@ import (
"fmt"
"github.com/Opster/opensearch-k8s-operator/opensearch-operator/opensearch-gateway/requests"
+ "github.com/Opster/opensearch-k8s-operator/opensearch-operator/opensearch-gateway/responses"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/opensearch-project/opensearch-go/opensearchutil"
@@ -16,7 +17,7 @@ import (
var ErrNotFound = errors.New("policy not found")
// ShouldUpdateISMPolicy checks if the passed policy is same as existing or needs update
-func ShouldUpdateISMPolicy(ctx context.Context, newPolicy, existingPolicy requests.Policy) (bool, error) {
+func ShouldUpdateISMPolicy(ctx context.Context, newPolicy, existingPolicy requests.ISMPolicy) (bool, error) {
if cmp.Equal(newPolicy, existingPolicy, cmpopts.EquateEmpty()) {
return false, nil
}
@@ -27,23 +28,8 @@ func ShouldUpdateISMPolicy(ctx context.Context, newPolicy, existingPolicy reques
return true, nil
}
-// PolicyExists checks if the passed policy already exists or not
-func PolicyExists(ctx context.Context, service *OsClusterClient, policyName string) (bool, error) {
- resp, err := service.GetISMConfig(ctx, policyName)
- if err != nil {
- return false, err
- }
- defer resp.Body.Close()
- if resp.StatusCode == 404 {
- return false, nil
- } else if resp.IsError() {
- return false, fmt.Errorf("response from API is %s", resp.Status())
- }
- return true, nil
-}
-
// GetPolicy fetches the passed policy
-func GetPolicy(ctx context.Context, service *OsClusterClient, policyName string) (*requests.Policy, error) {
+func GetPolicy(ctx context.Context, service *OsClusterClient, policyName string) (*responses.GetISMPolicyResponse, error) {
resp, err := service.GetISMConfig(ctx, policyName)
if err != nil {
return nil, err
@@ -51,10 +37,11 @@ func GetPolicy(ctx context.Context, service *OsClusterClient, policyName string)
defer resp.Body.Close()
if resp.StatusCode == 404 {
return nil, ErrNotFound
- } else if resp.IsError() {
+ }
+ if resp.IsError() {
return nil, fmt.Errorf("response from API is %s", resp.Status())
}
- ismResponse := requests.Policy{}
+ ismResponse := responses.GetISMPolicyResponse{}
if resp != nil && resp.Body != nil {
err := json.NewDecoder(resp.Body).Decode(&ismResponse)
if err != nil {
@@ -66,7 +53,7 @@ func GetPolicy(ctx context.Context, service *OsClusterClient, policyName string)
}
// CreateISMPolicy creates the passed policy
-func CreateISMPolicy(ctx context.Context, service *OsClusterClient, ismpolicy requests.Policy, policyId string) error {
+func CreateISMPolicy(ctx context.Context, service *OsClusterClient, ismpolicy requests.ISMPolicy, policyId string) error {
spec := opensearchutil.NewJSONReader(ismpolicy)
resp, err := service.PutISMConfig(ctx, policyId, spec)
if err != nil {
@@ -80,15 +67,15 @@ func CreateISMPolicy(ctx context.Context, service *OsClusterClient, ismpolicy re
}
// UpdateISMPolicy updates the given policy
-func UpdateISMPolicy(ctx context.Context, service *OsClusterClient, ismpolicy requests.Policy, seqno, primterm *int, policyName string) error {
+func UpdateISMPolicy(ctx context.Context, service *OsClusterClient, ismpolicy requests.ISMPolicy, seqno, primterm *int, policyId string) error {
spec := opensearchutil.NewJSONReader(ismpolicy)
- resp, err := service.UpdateISMConfig(ctx, policyName, *seqno, *primterm, spec)
+ resp, err := service.UpdateISMConfig(ctx, policyId, *seqno, *primterm, spec)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.IsError() {
- return fmt.Errorf("failed to create ism policy: %s", resp.String())
+ return fmt.Errorf("failed to update ism policy: %s", resp.String())
}
return nil
}
diff --git a/opensearch-operator/pkg/reconcilers/ismpolicy.go b/opensearch-operator/pkg/reconcilers/ismpolicy.go
index f2954d45..8b757422 100644
--- a/opensearch-operator/pkg/reconcilers/ismpolicy.go
+++ b/opensearch-operator/pkg/reconcilers/ismpolicy.go
@@ -13,6 +13,8 @@ import (
"github.com/Opster/opensearch-k8s-operator/opensearch-operator/pkg/reconcilers/util"
"github.com/cisco-open/operator-tools/pkg/reconciler"
"github.com/go-logr/logr"
+ "github.com/google/go-cmp/cmp"
+ "github.com/google/go-cmp/cmp/cmpopts"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/record"
"k8s.io/utils/pointer"
@@ -22,7 +24,10 @@ import (
)
const (
- ismPolicyExists = "ism policy already exists in Opensearch"
+ opensearchIsmPolicyExists = "ISM Policy already exists in Opensearch"
+ opensearchIsmPolicyNameMismatch = "OpensearchISMPolicyNameMismatch"
+ opensearchClusterRequeueAfter = 10 * time.Second
+ defaultRequeueAfter = 30 * time.Second
)
type IsmPolicyReconciler struct {
@@ -58,6 +63,7 @@ func NewIsmReconciler(
func (r *IsmPolicyReconciler) Reconcile() (retResult ctrl.Result, retErr error) {
var reason string
var policyId string
+
defer func() {
if !pointer.BoolDeref(r.updateStatus, true) {
return
@@ -71,24 +77,23 @@ func (r *IsmPolicyReconciler) Reconcile() (retResult ctrl.Result, retErr error)
instance.Status.State = opsterv1.OpensearchISMPolicyError
}
// Requeue after is 10 seconds if waiting for OpenSearch cluster
- if retResult.Requeue && retResult.RequeueAfter == 10*time.Second {
+ if retResult.Requeue && retResult.RequeueAfter == opensearchClusterRequeueAfter {
instance.Status.State = opsterv1.OpensearchISMPolicyPending
}
- // Requeue is after 30 seconds for normal reconciliation after creation/update
- if retErr == nil && retResult.RequeueAfter == 30*time.Second {
+ if retErr == nil && retResult.Requeue {
instance.Status.State = opsterv1.OpensearchISMPolicyCreated
instance.Status.PolicyId = policyId
}
- if reason == ismPolicyExists {
+ if reason == opensearchIsmPolicyExists {
instance.Status.State = opsterv1.OpensearchISMPolicyIgnored
}
})
+
if err != nil {
r.logger.Error(err, "failed to update status")
}
}()
- var err error
r.cluster, retErr = util.FetchOpensearchCluster(r.client, r.ctx, types.NamespacedName{
Name: r.instance.Spec.OpensearchRef.Name,
Namespace: r.instance.Namespace,
@@ -97,167 +102,184 @@ func (r *IsmPolicyReconciler) Reconcile() (retResult ctrl.Result, retErr error)
reason = "error fetching opensearch cluster"
r.logger.Error(retErr, "failed to fetch opensearch cluster")
r.recorder.Event(r.instance, "Warning", opensearchError, reason)
- return
+ return ctrl.Result{
+ Requeue: true,
+ RequeueAfter: opensearchClusterRequeueAfter,
+ }, retErr
}
if r.cluster == nil {
r.logger.Info("opensearch cluster does not exist, requeueing")
reason = "waiting for opensearch cluster to exist"
r.recorder.Event(r.instance, "Normal", opensearchPending, reason)
- retResult = ctrl.Result{
+ return ctrl.Result{
Requeue: true,
- RequeueAfter: 10 * time.Second,
- }
- return
+ RequeueAfter: opensearchClusterRequeueAfter,
+ }, nil
}
+
// Check cluster ref has not changed
- if r.instance.Status.ManagedCluster != nil {
- if *r.instance.Status.ManagedCluster != r.cluster.UID {
- reason = "cannot change the cluster a role refers to"
- retErr = fmt.Errorf("%s", reason)
- r.recorder.Event(r.instance, "Warning", opensearchRefMismatch, reason)
- return
- }
- } else {
- if pointer.BoolDeref(r.updateStatus, true) {
- retErr = r.client.UdateObjectStatus(r.instance, func(object client.Object) {
- instance := object.(*opsterv1.OpenSearchISMPolicy)
- instance.Status.ManagedCluster = &r.cluster.UID
- })
- if retErr != nil {
- reason = fmt.Sprintf("failed to update status: %s", retErr)
- r.recorder.Event(r.instance, "Warning", statusError, reason)
- return
- }
+ managedCluster := r.instance.Status.ManagedCluster
+ if managedCluster != nil && *managedCluster != r.cluster.UID {
+ reason = "cannot change the cluster a resource refers to"
+ retErr = fmt.Errorf("%s", reason)
+ r.recorder.Event(r.instance, "Warning", opensearchRefMismatch, reason)
+ return ctrl.Result{
+ Requeue: false,
+ }, retErr
+ }
+
+ if pointer.BoolDeref(r.updateStatus, true) {
+ retErr = r.client.UdateObjectStatus(r.instance, func(object client.Object) {
+ object.(*opsterv1.OpenSearchISMPolicy).Status.ManagedCluster = &r.cluster.UID
+ })
+ if retErr != nil {
+ reason = fmt.Sprintf("failed to update status: %s", retErr)
+ r.recorder.Event(r.instance, "Warning", statusError, reason)
+ return ctrl.Result{
+ Requeue: true,
+ RequeueAfter: opensearchClusterRequeueAfter,
+ }, retErr
}
}
+
// Check cluster is ready
if r.cluster.Status.Phase != opsterv1.PhaseRunning {
r.logger.Info("opensearch cluster is not running, requeueing")
reason = "waiting for opensearch cluster status to be running"
r.recorder.Event(r.instance, "Normal", opensearchPending, reason)
- retResult = ctrl.Result{
+ return ctrl.Result{
Requeue: true,
- RequeueAfter: 10 * time.Second,
- }
- return
+ RequeueAfter: opensearchClusterRequeueAfter,
+ }, nil
}
- r.osClient, err = util.CreateClientForCluster(r.client, r.ctx, r.cluster, r.osClientTransport)
- if err != nil {
- reason := "error creating opensearch client"
+ r.osClient, retErr = util.CreateClientForCluster(r.client, r.ctx, r.cluster, r.osClientTransport)
+ if retErr != nil {
+ reason = "error creating opensearch client"
r.recorder.Event(r.instance, "Warning", opensearchError, reason)
- retResult = ctrl.Result{
+ return ctrl.Result{
Requeue: true,
- RequeueAfter: 30 * time.Second,
- }
- retErr = err
- return
- }
-
- // If PolicyID not provided explicitly, use metadata.name by default
- policyId = r.instance.Spec.PolicyID
- if r.instance.Spec.PolicyID == "" {
- policyId = r.instance.Name
- }
- // Check ism policy state to make sure we don't touch preexisting ism policy
- if r.instance.Status.ExistingISMPolicy == nil {
- var exists bool
- exists, retErr = services.PolicyExists(r.ctx, r.osClient, policyId)
- if retErr != nil {
- reason = "failed to get policy status from Opensearch API"
- r.logger.Error(retErr, reason)
- r.recorder.Event(r.instance, "Warning", opensearchAPIError, reason)
- return
- }
- if pointer.BoolDeref(r.updateStatus, true) {
- retErr = r.client.UdateObjectStatus(r.instance, func(object client.Object) {
- instance := object.(*opsterv1.OpenSearchISMPolicy)
- instance.Status.ExistingISMPolicy = &exists
- })
- if retErr != nil {
- reason = fmt.Sprintf("failed to update status: %s", retErr)
- r.recorder.Event(r.instance, "Warning", statusError, reason)
- return
- }
- } else {
- // Emit an event for unit testing assertion
- r.recorder.Event(r.instance, "Normal", "UnitTest", fmt.Sprintf("exists is %t", exists))
- return
- }
+ RequeueAfter: opensearchClusterRequeueAfter,
+ }, retErr
}
- // If ism policy is existing do nothing
- if *r.instance.Status.ExistingISMPolicy {
- reason = ismPolicyExists
- return
+ // If PolicyID is not provided explicitly, use metadata.name by default
+ policyId = r.instance.Name
+ if r.instance.Spec.PolicyID != "" {
+ policyId = r.instance.Spec.PolicyID
}
- ismpolicy, retErr := r.CreateISMPolicyRequest()
+ newPolicy, retErr := r.CreateISMPolicy()
if retErr != nil {
- reason = "failed to get create the ism policy request"
r.logger.Error(retErr, reason)
- r.recorder.Event(r.instance, "Warning", opensearchAPIError, reason)
- return
+ return ctrl.Result{
+ Requeue: true,
+ RequeueAfter: defaultRequeueAfter,
+ }, retErr
}
existingPolicy, retErr := services.GetPolicy(r.ctx, r.osClient, policyId)
- if retErr != nil && retErr != services.ErrNotFound {
- reason = "failed to get policy from Opensearch API"
- r.logger.Error(retErr, reason)
- r.recorder.Event(r.instance, "Warning", opensearchAPIError, reason)
- return
- }
+ // If not exists, create
if errors.Is(retErr, services.ErrNotFound) {
- r.logger.V(1).Info(fmt.Sprintf("policy %s not found, creating.", r.instance.Spec.PolicyID))
- retErr = services.CreateISMPolicy(r.ctx, r.osClient, *ismpolicy, policyId)
+ request := requests.ISMPolicy{
+ Policy: *newPolicy,
+ }
+ retErr = services.CreateISMPolicy(r.ctx, r.osClient, request, policyId)
if retErr != nil {
reason = "failed to create ism policy"
r.logger.Error(retErr, reason)
r.recorder.Event(r.instance, "Warning", opensearchAPIError, reason)
- return
+ return ctrl.Result{
+ Requeue: true,
+ RequeueAfter: defaultRequeueAfter,
+ }, retErr
}
- r.recorder.Event(r.instance, "Normal", opensearchAPIUpdated, "policy created in opensearch")
- return ctrl.Result{Requeue: true, RequeueAfter: 30 * time.Second}, retErr
+ // Mark the ISM Policy as not pre-existing (created by the operator)
+ retErr = r.client.UdateObjectStatus(r.instance, func(object client.Object) {
+ object.(*opsterv1.OpenSearchISMPolicy).Status.ExistingISMPolicy = pointer.Bool(false)
+ })
+ if retErr != nil {
+ reason = "failed to update custom resource object"
+ r.logger.Error(retErr, reason)
+ return ctrl.Result{
+ Requeue: true,
+ RequeueAfter: defaultRequeueAfter,
+ }, retErr
+ }
+
+ r.recorder.Event(r.instance, "Normal", opensearchAPIUpdated, "policy successfully created in OpenSearch Cluster")
+ return ctrl.Result{
+ Requeue: true,
+ RequeueAfter: defaultRequeueAfter,
+ }, nil
}
- priterm := existingPolicy.PrimaryTerm
- seqno := existingPolicy.SequenceNumber
- // Reset
- existingPolicy.PrimaryTerm = nil
- existingPolicy.SequenceNumber = nil
- shouldUpdate, retErr := services.ShouldUpdateISMPolicy(r.ctx, *ismpolicy, *existingPolicy)
+
+ // If other error, report
if retErr != nil {
- reason = "failed to compare the policies"
+ reason = "failed to get the ism policy from Opensearch API"
r.logger.Error(retErr, reason)
r.recorder.Event(r.instance, "Warning", opensearchAPIError, reason)
- return
+ return ctrl.Result{
+ Requeue: true,
+ RequeueAfter: defaultRequeueAfter,
+ }, retErr
}
- if !shouldUpdate {
- r.logger.V(1).Info(fmt.Sprintf("policy %s is in sync", r.instance.Spec.PolicyID))
- return ctrl.Result{Requeue: true, RequeueAfter: 30 * time.Second}, retErr
+ // If the ISM policy exists in OpenSearch cluster and was not created by the operator, update the status and return
+ if r.instance.Status.ExistingISMPolicy == nil || *r.instance.Status.ExistingISMPolicy {
+ retErr = r.client.UdateObjectStatus(r.instance, func(object client.Object) {
+ object.(*opsterv1.OpenSearchISMPolicy).Status.ExistingISMPolicy = pointer.Bool(true)
+ })
+ if retErr != nil {
+ reason = "failed to update custom resource object"
+ r.logger.Error(retErr, reason)
+ return ctrl.Result{
+ Requeue: true,
+ RequeueAfter: defaultRequeueAfter,
+ }, retErr
+ }
+ reason = "the ISM policy already exists in the OpenSearch cluster"
+ r.logger.Error(errors.New(opensearchIsmPolicyExists), reason)
+ r.recorder.Event(r.instance, "Warning", opensearchIsmPolicyExists, reason)
+ return ctrl.Result{
+ Requeue: true,
+ RequeueAfter: defaultRequeueAfter,
+ }, nil
}
- // the policyId is immutable, so check the old name (r.instance.Status.PolicyId) against the new
- if r.instance.Status.PolicyId != "" && policyId != r.instance.Status.PolicyId {
- reason = "can't change PolicyID"
- r.recorder.Event(r.instance, "Warning", opensearchError, reason)
- return
+ // Return if there are no changes
+ if r.instance.Spec.PolicyID == existingPolicy.PolicyID && cmp.Equal(*newPolicy, existingPolicy.Policy, cmpopts.EquateEmpty()) {
+ r.logger.V(1).Info(fmt.Sprintf("user %s is in sync", r.instance.Name))
+ r.recorder.Event(r.instance, "Normal", opensearchAPIUnchanged, "policy is in sync")
+ return ctrl.Result{
+ Requeue: true,
+ RequeueAfter: defaultRequeueAfter,
+ }, nil
+ }
+ request := requests.ISMPolicy{
+ Policy: *newPolicy,
}
- retErr = services.UpdateISMPolicy(r.ctx, r.osClient, *ismpolicy, seqno, priterm, policyId)
+ retErr = services.UpdateISMPolicy(r.ctx, r.osClient, request, &existingPolicy.SequenceNumber, &existingPolicy.PrimaryTerm, existingPolicy.PolicyID)
if retErr != nil {
reason = "failed to update ism policy with Opensearch API"
r.logger.Error(retErr, reason)
r.recorder.Event(r.instance, "Warning", opensearchAPIError, reason)
+ return ctrl.Result{
+ Requeue: true,
+ RequeueAfter: defaultRequeueAfter,
+ }, retErr
}
r.recorder.Event(r.instance, "Normal", opensearchAPIUpdated, "policy updated in opensearch")
-
- return ctrl.Result{Requeue: true, RequeueAfter: 30 * time.Second}, retErr
+ return ctrl.Result{
+ Requeue: true,
+ RequeueAfter: defaultRequeueAfter,
+ }, nil
}
-func (r *IsmPolicyReconciler) CreateISMPolicyRequest() (*requests.Policy, error) {
- policy := requests.ISMPolicy{
+func (r *IsmPolicyReconciler) CreateISMPolicy() (*requests.ISMPolicySpec, error) {
+ policy := requests.ISMPolicySpec{
DefaultState: r.instance.Spec.DefaultState,
Description: r.instance.Spec.Description,
}
@@ -378,35 +400,35 @@ func (r *IsmPolicyReconciler) CreateISMPolicyRequest() (*requests.Policy, error)
shrink.ForceUnsafe = action.Shrink.ForceUnsafe
}
if action.Shrink.MaxShardSize == nil && action.Shrink.NumNewShards == nil && action.Shrink.PercentageOfSourceShards == nil {
- reason := "Either of MaxShardSize or NumNewShards or PercentageOfSourceShards is required"
- r.recorder.Event(r.instance, "Error", opensearchError, reason)
- return nil, nil
+ reason := "either of MaxShardSize or NumNewShards or PercentageOfSourceShards is required"
+ r.recorder.Event(r.instance, "Error", opensearchCustomResourceError, reason)
+ return nil, errors.New(reason)
}
if action.Shrink.MaxShardSize != nil {
if action.Shrink.NumNewShards == nil && action.Shrink.PercentageOfSourceShards == nil {
shrink.MaxShardSize = action.Shrink.MaxShardSize
} else {
- reason := "MaxShardSize can't exist with NumNewShards or PercentageOfSourceShards. Keep one of these."
- r.recorder.Event(r.instance, "Error", opensearchError, reason)
- return nil, nil
+ reason := "maxShardSize can't exist with NumNewShards or PercentageOfSourceShards. Keep one of these"
+ r.recorder.Event(r.instance, "Error", opensearchCustomResourceError, reason)
+ return nil, errors.New(reason)
}
if action.Shrink.NumNewShards != nil {
if action.Shrink.MaxShardSize == nil && action.Shrink.PercentageOfSourceShards == nil {
shrink.NumNewShards = action.Shrink.NumNewShards
} else {
- reason := "NumNewShards can't exist with MaxShardSize or PercentageOfSourceShards. Keep one of these."
- r.recorder.Event(r.instance, "Error", opensearchError, reason)
- return nil, nil
+ reason := "numNewShards can't exist with MaxShardSize or PercentageOfSourceShards. Keep one of these"
+ r.recorder.Event(r.instance, "Error", opensearchCustomResourceError, reason)
+ return nil, errors.New(reason)
}
}
if action.Shrink.PercentageOfSourceShards != nil {
if action.Shrink.NumNewShards == nil && action.Shrink.MaxShardSize == nil {
shrink.PercentageOfSourceShards = action.Shrink.PercentageOfSourceShards
} else {
- reason := "PercentageOfSourceShards can't exist with MaxShardSize or NumNewShards. Keep one of these."
- r.recorder.Event(r.instance, "Error", opensearchError, reason)
- return nil, nil
+ reason := "percentageOfSourceShards can't exist with MaxShardSize or NumNewShards. Keep one of these"
+ r.recorder.Event(r.instance, "Error", opensearchCustomResourceError, reason)
+ return nil, errors.New(reason)
}
}
if action.Shrink.TargetIndexNameTemplate != nil {
@@ -515,10 +537,8 @@ func (r *IsmPolicyReconciler) CreateISMPolicyRequest() (*requests.Policy, error)
policy.States = append(policy.States, requests.State{Actions: actions, Name: state.Name, Transitions: transitions})
}
}
- ismPolicy := requests.Policy{
- Policy: policy,
- }
- return &ismPolicy, nil
+
+ return &policy, nil
}
// Delete ISM policy from the OS cluster
@@ -527,10 +547,12 @@ func (r *IsmPolicyReconciler) Delete() error {
if r.instance.Status.ExistingISMPolicy == nil {
return nil
}
+
if *r.instance.Status.ExistingISMPolicy {
r.logger.Info("policy was pre-existing; not deleting")
return nil
}
+
var err error
r.cluster, err = util.FetchOpensearchCluster(r.client, r.ctx, types.NamespacedName{
Name: r.instance.Spec.OpensearchRef.Name,
@@ -544,15 +566,18 @@ func (r *IsmPolicyReconciler) Delete() error {
// If the opensearch cluster doesn't exist, we don't need to delete anything
return nil
}
+
r.osClient, err = util.CreateClientForCluster(r.client, r.ctx, r.cluster, r.osClientTransport)
if err != nil {
return err
}
+
// If PolicyID not provided explicitly, use metadata.name by default
policyId := r.instance.Spec.PolicyID
- if r.instance.Spec.PolicyID == "" {
+ if policyId == "" {
policyId = r.instance.Name
}
+
err = services.DeleteISMPolicy(r.ctx, r.osClient, policyId)
if err != nil {
return err
diff --git a/opensearch-operator/pkg/reconcilers/ismpolicy_test.go b/opensearch-operator/pkg/reconcilers/ismpolicy_test.go
index 22351613..a7b9d290 100644
--- a/opensearch-operator/pkg/reconcilers/ismpolicy_test.go
+++ b/opensearch-operator/pkg/reconcilers/ismpolicy_test.go
@@ -21,7 +21,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log"
)
-var seqno *int = new(int)
var _ = Describe("ism policy reconciler", func() {
var (
transport *httpmock.MockTransport
@@ -117,7 +116,7 @@ var _ = Describe("ism policy reconciler", func() {
recorder = record.NewFakeRecorder(1)
mockClient.EXPECT().GetOpenSearchCluster(mock.Anything, mock.Anything).Return(*cluster, nil)
})
- It("should wait for the cluster to be running", func() {
+ It("should emit a unit test event and requeue", func() {
go func() {
defer GinkgoRecover()
defer close(recorder.Events)
@@ -140,6 +139,7 @@ var _ = Describe("ism policy reconciler", func() {
cluster.Status.Phase = opsterv1.PhaseRunning
cluster.Status.ComponentsStatus = []opsterv1.ComponentStatus{}
mockClient.EXPECT().GetOpenSearchCluster(mock.Anything, mock.Anything).Return(*cluster, nil)
+ recorder = record.NewFakeRecorder(1)
transport.RegisterResponder(
http.MethodGet,
@@ -162,44 +162,73 @@ var _ = Describe("ism policy reconciler", func() {
)
})
- When("existing status is true", func() {
+ When("cluster reference mismatch", func() {
BeforeEach(func() {
- instance.Status.ExistingISMPolicy = pointer.Bool(true)
+ managedCluster := types.UID("different-uid")
+ instance.Status.ManagedCluster = &managedCluster
})
- It("should do nothing", func() {
- _, err := reconciler.Reconcile()
- Expect(err).ToNot(HaveOccurred())
+ It("should emit a unit test event and not requeue", func() {
+ go func() {
+ defer GinkgoRecover()
+ defer close(recorder.Events)
+ result, err := reconciler.Reconcile()
+ Expect(err).To(HaveOccurred())
+ Expect(result.Requeue).To(BeFalse())
+ }()
+ var events []string
+ for msg := range recorder.Events {
+ events = append(events, msg)
+ }
+ Expect(len(events)).To(Equal(1))
+ Expect(events[0]).To(Equal(fmt.Sprintf("Warning %s cannot change the cluster a resource refers to", opensearchRefMismatch)))
})
})
- When("existing status is nil", func() {
- var localExtraCalls = 4
+ When("policy does not exist in opensearch", func() {
BeforeEach(func() {
- policyRequest := requests.ISMPolicy{
- DefaultState: "abc",
- Description: "test",
- }
+ mockClient.EXPECT().UdateObjectStatus(mock.Anything, mock.Anything).Return(nil)
- recorder = record.NewFakeRecorder(1)
transport.RegisterResponder(
http.MethodGet,
fmt.Sprintf(
- "https://%s.%s.svc.cluster.local:9200/",
+ "https://%s.%s.svc.cluster.local:9200/_plugins/_ism/policies/%s",
cluster.Spec.General.ServiceName,
cluster.Namespace,
+ instance.Name,
),
- httpmock.NewStringResponder(200, "OK").Times(4, failMessage),
+ httpmock.NewStringResponder(404, "Not Found").Once(),
)
transport.RegisterResponder(
- http.MethodHead,
+ http.MethodPut,
fmt.Sprintf(
- "https://%s.%s.svc.cluster.local:9200/",
+ "https://%s.%s.svc.cluster.local:9200/_plugins/_ism/policies/%s",
cluster.Spec.General.ServiceName,
cluster.Namespace,
+ instance.Name,
),
- httpmock.NewStringResponder(200, "OK").Times(2, failMessage),
+ httpmock.NewStringResponder(200, "OK").Once(),
)
+ })
+ It("should create the policy, emit a unit test event, and requeue", func() {
+ go func() {
+ defer GinkgoRecover()
+ defer close(recorder.Events)
+ result, err := reconciler.Reconcile()
+ Expect(err).ToNot(HaveOccurred())
+ Expect(result.Requeue).To(BeTrue())
+ }()
+ var events []string
+ for msg := range recorder.Events {
+ events = append(events, msg)
+ }
+ Expect(len(events)).To(Equal(1))
+ Expect(events[0]).To(Equal(fmt.Sprintf("Normal %s policy successfully created in OpenSearch Cluster", opensearchAPIUpdated)))
+ })
+ })
+
+ When("failed to get policy from opensearch api", func() {
+ BeforeEach(func() {
transport.RegisterResponder(
http.MethodGet,
fmt.Sprintf(
@@ -208,118 +237,61 @@ var _ = Describe("ism policy reconciler", func() {
cluster.Namespace,
instance.Name,
),
- httpmock.NewJsonResponderOrPanic(200, responses.GetISMPoliciesResponse{
- Policy: policyRequest,
- }).Then(
- httpmock.NewStringResponder(404, "does not exist"),
- ).Then(
- httpmock.NewNotFoundResponder(failMessage),
- ),
+ httpmock.NewErrorResponder(fmt.Errorf("failed to get policy")).Once(),
)
})
-
- It("should do nothing and emit a unit test event", func() {
+ It("should emit a unit test event, requeue, and return an error", func() {
go func() {
defer GinkgoRecover()
defer close(recorder.Events)
- _, err := reconciler.Reconcile()
- Expect(err).ToNot(HaveOccurred())
- _, err = reconciler.Reconcile()
- Expect(err).ToNot(HaveOccurred())
- // Confirm all responders have been called
- Expect(transport.GetTotalCallCount()).To(Equal(transport.NumResponders() + extraContextCalls + localExtraCalls))
+ result, err := reconciler.Reconcile()
+ Expect(err).To(HaveOccurred())
+ Expect(result.Requeue).To(BeTrue())
}()
var events []string
for msg := range recorder.Events {
events = append(events, msg)
}
- Expect(len(events)).To(Equal(2))
- Expect(events[0]).To(Equal("Normal UnitTest exists is true"))
- Expect(events[1]).To(Equal("Normal UnitTest exists is false"))
+ Expect(len(events)).To(Equal(1))
+ Expect(events[0]).To(Equal(fmt.Sprintf("Warning %s failed to get the ism policy from Opensearch API", opensearchAPIError)))
})
})
- When("existing status is true", func() {
+ Context("policy exists in opensearch", func() {
BeforeEach(func() {
- instance.Status.ExistingISMPolicy = pointer.Bool(true)
- })
- It("should do nothing", func() {
- _, err := reconciler.Reconcile()
- Expect(err).ToNot(HaveOccurred())
- })
- })
+ instance.Spec.PolicyID = "test-policy-id"
- When("existing status is false", func() {
- BeforeEach(func() {
- instance.Status.ExistingISMPolicy = pointer.Bool(false)
+ transport.RegisterResponder(
+ http.MethodGet,
+ fmt.Sprintf(
+ "https://%s.%s.svc.cluster.local:9200/_plugins/_ism/policies/%s",
+ cluster.Spec.General.ServiceName,
+ cluster.Namespace,
+ instance.Spec.PolicyID,
+ ),
+ httpmock.NewJsonResponderOrPanic(200, responses.GetISMPolicyResponse{
+ PolicyID: "test-policy-id",
+ Policy: requests.ISMPolicySpec{
+ DefaultState: "test-state",
+ Description: "test-policy",
+ },
+ }).Once(),
+ )
})
- When("policy exists in opensearch and is the same", func() {
+ When("existing status is nil", func() {
BeforeEach(func() {
- policyRequest := requests.ISMPolicy{
- DefaultState: "",
- Description: "",
- }
- transport.RegisterResponder(
- http.MethodGet,
- fmt.Sprintf(
- "https://%s.%s.svc.cluster.local:9200/_plugins/_ism/policies/%s",
- cluster.Spec.General.ServiceName,
- cluster.Namespace,
- instance.Name,
- ),
- httpmock.NewJsonResponderOrPanic(200, responses.GetISMPoliciesResponse{
- Policy: policyRequest,
- SequenceNumber: seqno,
- PrimaryTerm: seqno,
- }).Once(failMessage),
- )
- })
- It("should do nothing", func() {
- _, err := reconciler.Reconcile()
- Expect(err).ToNot(HaveOccurred())
- Expect(transport.GetTotalCallCount()).To(Equal(transport.NumResponders() + extraContextCalls))
+ mockClient.EXPECT().UdateObjectStatus(mock.Anything, mock.Anything).Return(nil)
+ instance.Status.ExistingISMPolicy = nil
})
- })
- When("policy exists in opensearch and is not the same", func() {
- BeforeEach(func() {
- recorder = record.NewFakeRecorder(1)
- policyRequest := requests.ISMPolicy{
- DefaultState: "policy",
- Description: "test-policy",
- }
- transport.RegisterResponder(
- http.MethodGet,
- fmt.Sprintf(
- "https://%s.%s.svc.cluster.local:9200/_plugins/_ism/policies/%s",
- cluster.Spec.General.ServiceName,
- cluster.Namespace,
- instance.Name,
- ),
- httpmock.NewJsonResponderOrPanic(200, responses.GetISMPoliciesResponse{
- Policy: policyRequest,
- SequenceNumber: seqno,
- PrimaryTerm: seqno,
- PolicyID: "test-policy",
- }).Once(failMessage),
- )
- transport.RegisterResponder(
- http.MethodPut,
- fmt.Sprintf(
- "https://%s.%s.svc.cluster.local:9200/_plugins/_ism/policies/%s?if_seq_no=0&if_primary_term=0",
- cluster.Spec.General.ServiceName,
- cluster.Namespace,
- instance.Name,
- ),
- httpmock.NewStringResponder(200, "OK").Once(failMessage),
- )
- })
- It("should update the policy", func() {
+
+ It("should emit a unit test event and requeue", func() {
go func() {
defer GinkgoRecover()
defer close(recorder.Events)
- _, err := reconciler.Reconcile()
+ result, err := reconciler.Reconcile()
Expect(err).ToNot(HaveOccurred())
+ Expect(result.Requeue).To(BeTrue())
// Confirm all responders have been called
Expect(transport.GetTotalCallCount()).To(Equal(transport.NumResponders() + extraContextCalls))
}()
@@ -328,39 +300,23 @@ var _ = Describe("ism policy reconciler", func() {
events = append(events, msg)
}
Expect(len(events)).To(Equal(1))
- Expect(events[0]).To(Equal(fmt.Sprintf("Normal %s policy updated in opensearch", opensearchAPIUpdated)))
+ Expect(events[0]).To(Equal(fmt.Sprintf("Warning %s the ISM policy already exists in the OpenSearch cluster", opensearchIsmPolicyExists)))
})
})
- When("policy doesn't exist in opensearch", func() {
+
+ When("existing status is true", func() {
BeforeEach(func() {
- recorder = record.NewFakeRecorder(1)
- transport.RegisterResponder(
- http.MethodGet,
- fmt.Sprintf(
- "https://%s.%s.svc.cluster.local:9200/_plugins/_ism/policies/%s",
- cluster.Spec.General.ServiceName,
- cluster.Namespace,
- instance.Name,
- ),
- httpmock.NewStringResponder(404, "OK").Once(failMessage),
- )
- transport.RegisterResponder(
- http.MethodPut,
- fmt.Sprintf(
- "https://%s.%s.svc.cluster.local:9200/_plugins/_ism/policies/%s",
- cluster.Spec.General.ServiceName,
- cluster.Namespace,
- instance.Name,
- ),
- httpmock.NewStringResponder(200, "OK").Once(failMessage),
- )
+ mockClient.EXPECT().UdateObjectStatus(mock.Anything, mock.Anything).Return(nil)
+ instance.Status.ExistingISMPolicy = pointer.Bool(true)
})
- It("should create the policy", func() {
+
+ It("should emit a unit test event and requeue", func() {
go func() {
defer GinkgoRecover()
defer close(recorder.Events)
- _, err := reconciler.Reconcile()
+ result, err := reconciler.Reconcile()
Expect(err).ToNot(HaveOccurred())
+ Expect(result.Requeue).To(BeTrue())
// Confirm all responders have been called
Expect(transport.GetTotalCallCount()).To(Equal(transport.NumResponders() + extraContextCalls))
}()
@@ -369,7 +325,72 @@ var _ = Describe("ism policy reconciler", func() {
events = append(events, msg)
}
Expect(len(events)).To(Equal(1))
- Expect(events[0]).To(Equal(fmt.Sprintf("Normal %s policy created in opensearch", opensearchAPIUpdated)))
+ Expect(events[0]).To(Equal(fmt.Sprintf("Warning %s the ISM policy already exists in the OpenSearch cluster", opensearchIsmPolicyExists)))
+ })
+ })
+
+ Context("existing status is false", func() {
+ BeforeEach(func() {
+ instance.Status.ExistingISMPolicy = pointer.Bool(false)
+ })
+
+ When("policy is the same", func() {
+ BeforeEach(func() {
+ instance.Spec.DefaultState = "test-state"
+ instance.Spec.Description = "test-policy"
+ })
+ It("should emit a unit test event and requeue", func() {
+ go func() {
+ defer GinkgoRecover()
+ defer close(recorder.Events)
+ result, err := reconciler.Reconcile()
+ Expect(err).ToNot(HaveOccurred())
+ Expect(result.Requeue).To(BeTrue())
+ // Confirm all responders have been called
+ Expect(transport.GetTotalCallCount()).To(Equal(transport.NumResponders() + extraContextCalls))
+ }()
+ var events []string
+ for msg := range recorder.Events {
+ events = append(events, msg)
+ }
+ Expect(len(events)).To(Equal(1))
+ Expect(events[0]).To(Equal(fmt.Sprintf("Normal %s policy is in sync", opensearchAPIUnchanged)))
+ })
+ })
+
+ When("policy is not the same", func() {
+ BeforeEach(func() {
+ instance.Spec.DefaultState = "test-state2"
+ instance.Spec.Description = "test-policy2"
+
+ transport.RegisterResponder(
+ http.MethodPut,
+ fmt.Sprintf(
+ "https://%s.%s.svc.cluster.local:9200/_plugins/_ism/policies/%s",
+ cluster.Spec.General.ServiceName,
+ cluster.Namespace,
+ instance.Spec.PolicyID,
+ ),
+ httpmock.NewStringResponder(200, "OK").Once(),
+ )
+ })
+ It("should update ism policy, emit a unit test event, and requeue", func() {
+ go func() {
+ defer GinkgoRecover()
+ defer close(recorder.Events)
+ result, err := reconciler.Reconcile()
+ Expect(err).ToNot(HaveOccurred())
+ Expect(result.Requeue).To(BeTrue())
+ // Confirm all responders have been called
+ Expect(transport.GetTotalCallCount()).To(Equal(transport.NumResponders() + extraContextCalls))
+ }()
+ var events []string
+ for msg := range recorder.Events {
+ events = append(events, msg)
+ }
+ Expect(len(events)).To(Equal(1))
+ Expect(events[0]).To(Equal(fmt.Sprintf("Normal %s policy updated in opensearch", opensearchAPIUpdated)))
+ })
})
})
})
@@ -406,9 +427,14 @@ var _ = Describe("ism policy reconciler", func() {
})
})
- When("policy does not exist", func() {
+ Context("cluster is ready", func() {
+ // extraContextCalls := 1
BeforeEach(func() {
+ cluster.Status.Phase = opsterv1.PhaseRunning
+ cluster.Status.ComponentsStatus = []opsterv1.ComponentStatus{}
mockClient.EXPECT().GetOpenSearchCluster(mock.Anything, mock.Anything).Return(*cluster, nil)
+ recorder = record.NewFakeRecorder(1)
+
transport.RegisterResponder(
http.MethodGet,
fmt.Sprintf(
@@ -418,6 +444,7 @@ var _ = Describe("ism policy reconciler", func() {
),
httpmock.NewStringResponder(200, "OK").Times(2, failMessage),
)
+
transport.RegisterResponder(
http.MethodHead,
fmt.Sprintf(
@@ -427,75 +454,42 @@ var _ = Describe("ism policy reconciler", func() {
),
httpmock.NewStringResponder(200, "OK").Once(failMessage),
)
- transport.RegisterResponder(
- http.MethodGet,
- fmt.Sprintf(
- "https://%s.%s.svc.cluster.local:9200/_plugins/_ism/policies/%s",
- cluster.Spec.General.ServiceName,
- cluster.Namespace,
- instance.Name,
- ),
- httpmock.NewStringResponder(404, "does not exist").Once(failMessage),
- )
- transport.RegisterResponder(
- http.MethodDelete,
- fmt.Sprintf(
- "https://%s.%s.svc.cluster.local:9200/_plugins/_ism/policies/%s",
- cluster.Spec.General.ServiceName,
- cluster.Namespace,
- instance.Name,
- ),
- httpmock.NewStringResponder(200, "OK").Once(failMessage),
- )
})
- It("should do nothing and exit", func() {
- Expect(reconciler.Delete()).To(Succeed())
- })
- })
- When("policy does exist", func() {
- BeforeEach(func() {
- mockClient.EXPECT().GetOpenSearchCluster(mock.Anything, mock.Anything).Return(*cluster, nil)
- transport.RegisterResponder(
- http.MethodGet,
- fmt.Sprintf(
- "https://%s.%s.svc.cluster.local:9200/",
- cluster.Spec.General.ServiceName,
- cluster.Namespace,
- ),
- httpmock.NewStringResponder(200, "OK").Times(2, failMessage),
- )
- transport.RegisterResponder(
- http.MethodHead,
- fmt.Sprintf(
- "https://%s.%s.svc.cluster.local:9200/",
- cluster.Spec.General.ServiceName,
- cluster.Namespace,
- ),
- httpmock.NewStringResponder(200, "OK").Once(failMessage),
- )
- transport.RegisterResponder(
- http.MethodGet,
- fmt.Sprintf(
- "https://%s.%s.svc.cluster.local:9200/_plugins/_ism/policies/%s",
- cluster.Spec.General.ServiceName,
- cluster.Namespace,
- instance.Name,
- ),
- httpmock.NewStringResponder(200, "OK").Once(failMessage),
- )
- transport.RegisterResponder(
- http.MethodDelete,
- fmt.Sprintf(
- "https://%s.%s.svc.cluster.local:9200/_plugins/_ism/policies/%s",
- cluster.Spec.General.ServiceName,
- cluster.Namespace,
- instance.Name,
- ),
- httpmock.NewStringResponder(200, "OK").Once(failMessage),
- )
+
+ When("policy does not exist", func() {
+ BeforeEach(func() {
+ transport.RegisterResponder(
+ http.MethodDelete,
+ fmt.Sprintf(
+ "https://%s.%s.svc.cluster.local:9200/_plugins/_ism/policies/%s",
+ cluster.Spec.General.ServiceName,
+ cluster.Namespace,
+ instance.Name,
+ ),
+ httpmock.NewStringResponder(404, "does not exist").Once(failMessage),
+ )
+ })
+ It("should do nothing and exit", func() {
+ Expect(reconciler.Delete()).NotTo(Succeed())
+ })
})
- It("should delete the policy", func() {
- Expect(reconciler.Delete()).To(Succeed())
+
+ When("policy does exist", func() {
+ BeforeEach(func() {
+ transport.RegisterResponder(
+ http.MethodDelete,
+ fmt.Sprintf(
+ "https://%s.%s.svc.cluster.local:9200/_plugins/_ism/policies/%s",
+ cluster.Spec.General.ServiceName,
+ cluster.Namespace,
+ instance.Name,
+ ),
+ httpmock.NewStringResponder(200, "OK").Once(failMessage),
+ )
+ })
+ It("should delete the policy", func() {
+ Expect(reconciler.Delete()).To(Succeed())
+ })
})
})
})
diff --git a/opensearch-operator/pkg/reconcilers/reconcilers.go b/opensearch-operator/pkg/reconcilers/reconcilers.go
index 6531bc6f..c0e644ee 100644
--- a/opensearch-operator/pkg/reconcilers/reconcilers.go
+++ b/opensearch-operator/pkg/reconcilers/reconcilers.go
@@ -14,13 +14,15 @@ import (
)
const (
- opensearchPending = "OpensearchPending"
- opensearchError = "OpensearchError"
- opensearchAPIError = "OpensearchAPIError"
- opensearchRefMismatch = "OpensearchRefMismatch"
- opensearchAPIUpdated = "OpensearchAPIUpdated"
- passwordError = "PasswordError"
- statusError = "StatusUpdateError"
+ opensearchPending = "OpensearchPending"
+ opensearchError = "OpensearchError"
+ opensearchAPIError = "OpensearchAPIError"
+ opensearchRefMismatch = "OpensearchRefMismatch"
+ opensearchAPIUpdated = "OpensearchAPIUpdated"
+ opensearchAPIUnchanged = "OpensearchAPIUnchanged"
+ opensearchCustomResourceError = "OpensearchCustomResourceError"
+ passwordError = "PasswordError"
+ statusError = "StatusUpdateError"
)
type ComponentReconciler func() (reconcile.Result, error)
From fb3812d58795838607bbfaeefd6670d3762a71f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gergely=20Szab=C3=B3?=
Date: Thu, 19 Sep 2024 15:12:59 +0200
Subject: [PATCH 20/27] Adding pluginsList and keystore fields to
BootstrapConfig (#862)
### Description
This change adds the `bootstrap.keystore` and `bootstrap.pluginsList`
fields to the OpenSearchCluster custom resource.
This changes how the bootstrap pod is generated. The behavior is the
same as with the `general.keystore` and `general.pluginsList` fields.
### Issues Resolved
Closes
https://github.com/opensearch-project/opensearch-k8s-operator/issues/430
and
https://github.com/opensearch-project/opensearch-k8s-operator/issues/639.
### Check List
- [x] Commits are signed per the DCO using --signoff
- [x] Unittest added for the new/changed functionality and all unit
tests are successful
- [x] Customer-visible features documented
- [x] No linter warnings (`make lint`)
If CRDs are changed:
- [x] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [x] Changes to CRDs documented
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
---------
Signed-off-by: Gergely Szabo
---
...ensearch.opster.io_opensearchclusters.yaml | 25 +++++
docs/designs/crd.md | 24 +++--
docs/userguide/main.md | 34 +++++--
.../api/v1/opensearch_types.go | 2 +
.../api/v1/zz_generated.deepcopy.go | 12 +++
...ensearch.opster.io_opensearchclusters.yaml | 25 +++++
opensearch-operator/pkg/builders/cluster.go | 93 +++++++++++++++++++
.../pkg/builders/cluster_test.go | 88 ++++++++++++++++++
8 files changed, 290 insertions(+), 13 deletions(-)
diff --git a/charts/opensearch-operator/files/opensearch.opster.io_opensearchclusters.yaml b/charts/opensearch-operator/files/opensearch.opster.io_opensearchclusters.yaml
index 1ea3615c..b329b1bc 100644
--- a/charts/opensearch-operator/files/opensearch.opster.io_opensearchclusters.yaml
+++ b/charts/opensearch-operator/files/opensearch.opster.io_opensearchclusters.yaml
@@ -839,10 +839,35 @@ spec:
type: object
jvm:
type: string
+ keystore:
+ items:
+ properties:
+ keyMappings:
+ additionalProperties:
+ type: string
+ description: Key mappings from secret to keystore keys
+ type: object
+ secret:
+ description: Secret containing key value pairs
+ properties:
+ name:
+ description: |-
+ Name of the referent.
+ More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind, uid?
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
nodeSelector:
additionalProperties:
type: string
type: object
+ pluginsList:
+ items:
+ type: string
+ type: array
resources:
description: ResourceRequirements describes the compute resource
requirements.
diff --git a/docs/designs/crd.md b/docs/designs/crd.md
index ceb49c0e..52bb82b1 100644
--- a/docs/designs/crd.md
+++ b/docs/designs/crd.md
@@ -6,7 +6,7 @@ A resource is an endpoint in the Kubernetes API that stores a collection of API
A [Custom Resource](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) is an extension of the Kubernetes API, many core Kubernetes functions are now built using custom resources, making Kubernetes more modular.
Cluster admins can update custom resources independently of the cluster itself. Once a custom resource is installed, users can create and access its objects using kubectl, just as they do for built-in resources like Pods.
-The CustomResourceDefinition API resource allows you to define custom resources. Defining a CRD object creates a new custom resource with a name and schema that you specify. The Kubernetes API serves and handles the storage of your custom resource. Every resource is build from `KGV` that stands for Group Version Resource and this is what drives the Kubernetes API Server structure.
+The CustomResourceDefinition API resource allows you to define custom resources. Defining a CRD object creates a new custom resource with a name and schema that you specify. The Kubernetes API serves and handles the storage of your custom resource. Every resource is build from `KGV` that stands for Group Version Resource and this is what drives the Kubernetes API Server structure.
The `OpensearchCLuster` CRD is representing an Opensearch cluster.
@@ -115,7 +115,7 @@ ClusterSpec defines the desired state of OpensearchCluster
GeneralConfig
-GeneralConfig defines global Opensearch cluster configuration
+GeneralConfig defines global Opensearch cluster configuration
@@ -290,6 +290,18 @@ Bootstrap defines Opensearch bootstrap pod configuration
Added extra items to opensearch.yml in the bootstrap pod |
map[string]string |
general.additionalConfig |
+
+ keystore |
+ []opsterv1.KeystoreValue |
+ List of objects that define secret values that will populate the opensearch keystore in the bootstrap pod |
+ false |
+ - |
+
+ pluginsList |
+ []string |
+ List of plugins that should be installed for OpenSearch at startup in the boostrap pod |
+ false |
+ [] |
@@ -432,7 +444,7 @@ Dashboards defines Opensearch-Dashboard configuration and deployment
NodePools
-Every NodePool is defining different Opensearch Nodes StatefulSet
+Every NodePool is defining different Opensearch Nodes StatefulSet
@@ -581,8 +593,8 @@ InitHelperConfig defines global Opensearch InitHelper image configuration
string |
Version of InitHelper (busybox) image to deploy |
false |
- 1.27.2-buildx |
-
+ 1.27.2-buildx |
+
@@ -676,7 +688,7 @@ Monitoring TLS configuration options
Keystore
-Every Keystore Value defines a secret to pull secrets from.
+Every Keystore Value defines a secret to pull secrets from.
diff --git a/docs/userguide/main.md b/docs/userguide/main.md
index 7f55780e..227c3f71 100644
--- a/docs/userguide/main.md
+++ b/docs/userguide/main.md
@@ -124,7 +124,7 @@ spec:
nodePools:
- component: masters
replicas: 3 # The number of replicas
- diskSize: "30Gi" # The disk size to use
+ diskSize: "30Gi" # The disk size to use
resources: # The resource requests and limits for that nodepool
requests:
memory: "2Gi"
@@ -221,7 +221,7 @@ If you provide your own node certificates you must also provide an admin cert th
spec:
security:
config:
- adminSecret:
+ adminSecret:
name: my-first-cluster-admin-cert # The secret must have keys tls.crt and tls.key
```
@@ -278,6 +278,14 @@ To install a plugin for opensearch dashboards add it to the list under `dashboar
- sample-plugin-name
```
+To install a plugin for the bootstrap pod add it to the list under `bootstrap.pluginsList`:
+
+```yaml
+ bootstrap:
+ pluginsList: ["repository-s3"]
+```
+
+
Please note:
* [Bundled plugins](https://opensearch.org/docs/latest/install-and-configure/install-opensearch/plugins/#bundled-plugins) do not have to be added to the list, they are installed automatically
@@ -323,6 +331,18 @@ If you only want to load some keys from a secret or rename the existing keys, yo
Note that only provided keys will be loaded from the secret! Any keys not specified will be ignored.
+To populate the keystore of the boostrap pod add the secrets under the `bootstrap.keystore` section:
+
+```yaml
+ bootstrap:
+ # ...
+ keystore:
+ - secret:
+ name: credentials
+ - secret:
+ name: some-other-secret
+```
+
### SmartScaler
What is SmartScaler?
@@ -382,7 +402,7 @@ You can configure the snapshot repositories for the OpenSearch cluster through t
```yaml
spec:
general:
- snapshotRepositories:
+ snapshotRepositories:
- name: my_s3_repository_1
type: s3
settings:
@@ -737,7 +757,7 @@ spec:
projected:
sources:
serviceAccountToken:
- path: "token"
+ path: "token"
dashboards:
additionalVolumes:
- name: example-secret
@@ -775,7 +795,7 @@ spec:
env:
- name: MY_ENV_VAR
value: "myvalue"
- # the other options are supported here as well
+ # the other options are supported here as well
```
### Custom cluster domain name
@@ -793,7 +813,7 @@ manager:
During cluster initialization the operator uses init containers as helpers. For these containers a busybox image is used ( specifically `docker.io/busybox:latest`). In case you are working in an offline environment and the cluster cannot access the registry or you want to customize the image, you can override the image used by specifying the `initHelper` image in your cluster spec:
```yaml
- spec:
+ spec:
initHelper:
# You can either only specify the version
version: "1.27.2-buildcustom"
@@ -1393,7 +1413,7 @@ metadata:
spec:
opensearchCluster:
name: my-first-cluster
-
+
name: logs_template # name of the index template - defaults to metadata.name. Can't be updated in-place
indexPatterns: # required index patterns
diff --git a/opensearch-operator/api/v1/opensearch_types.go b/opensearch-operator/api/v1/opensearch_types.go
index 75e822ab..7da1e55a 100644
--- a/opensearch-operator/api/v1/opensearch_types.go
+++ b/opensearch-operator/api/v1/opensearch_types.go
@@ -171,6 +171,8 @@ type BootstrapConfig struct {
Jvm string `json:"jvm,omitempty"`
// Extra items to add to the opensearch.yml, defaults to General.AdditionalConfig
AdditionalConfig map[string]string `json:"additionalConfig,omitempty"`
+ PluginsList []string `json:"pluginsList,omitempty"`
+ Keystore []KeystoreValue `json:"keystore,omitempty"`
}
type DashboardsServiceSpec struct {
diff --git a/opensearch-operator/api/v1/zz_generated.deepcopy.go b/opensearch-operator/api/v1/zz_generated.deepcopy.go
index 055616a0..fb0068d7 100644
--- a/opensearch-operator/api/v1/zz_generated.deepcopy.go
+++ b/opensearch-operator/api/v1/zz_generated.deepcopy.go
@@ -295,6 +295,18 @@ func (in *BootstrapConfig) DeepCopyInto(out *BootstrapConfig) {
(*out)[key] = val
}
}
+ if in.PluginsList != nil {
+ in, out := &in.PluginsList, &out.PluginsList
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+ if in.Keystore != nil {
+ in, out := &in.Keystore, &out.Keystore
+ *out = make([]KeystoreValue, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BootstrapConfig.
diff --git a/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchclusters.yaml b/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchclusters.yaml
index 1ea3615c..b329b1bc 100644
--- a/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchclusters.yaml
+++ b/opensearch-operator/config/crd/bases/opensearch.opster.io_opensearchclusters.yaml
@@ -839,10 +839,35 @@ spec:
type: object
jvm:
type: string
+ keystore:
+ items:
+ properties:
+ keyMappings:
+ additionalProperties:
+ type: string
+ description: Key mappings from secret to keystore keys
+ type: object
+ secret:
+ description: Secret containing key value pairs
+ properties:
+ name:
+ description: |-
+ Name of the referent.
+ More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind, uid?
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
nodeSelector:
additionalProperties:
type: string
type: object
+ pluginsList:
+ items:
+ type: string
+ type: array
resources:
description: ResourceRequirements describes the compute resource
requirements.
diff --git a/opensearch-operator/pkg/builders/cluster.go b/opensearch-operator/pkg/builders/cluster.go
index 60938e9f..0c5c2f76 100644
--- a/opensearch-operator/pkg/builders/cluster.go
+++ b/opensearch-operator/pkg/builders/cluster.go
@@ -870,6 +870,98 @@ func NewBootstrapPod(
})
}
+ // If Keystore Values are set in OpenSearchCluster manifest
+ if cr.Spec.Bootstrap.Keystore != nil && len(cr.Spec.Bootstrap.Keystore) > 0 {
+
+ // Add volume and volume mount for keystore
+ volumes = append(volumes, corev1.Volume{
+ Name: "keystore",
+ VolumeSource: corev1.VolumeSource{
+ EmptyDir: &corev1.EmptyDirVolumeSource{},
+ },
+ })
+
+ volumeMounts = append(volumeMounts, corev1.VolumeMount{
+ Name: "keystore",
+ MountPath: "/usr/share/opensearch/config/opensearch.keystore",
+ SubPath: "opensearch.keystore",
+ })
+
+ initContainerVolumeMounts := []corev1.VolumeMount{
+ {
+ Name: "keystore",
+ MountPath: "/tmp/keystore",
+ },
+ }
+
+ // Add volumes and volume mounts for keystore secrets
+ for _, keystoreValue := range cr.Spec.Bootstrap.Keystore {
+ volumes = append(volumes, corev1.Volume{
+ Name: "keystore-" + keystoreValue.Secret.Name,
+ VolumeSource: corev1.VolumeSource{
+ Secret: &corev1.SecretVolumeSource{
+ SecretName: keystoreValue.Secret.Name,
+ },
+ },
+ })
+
+ if keystoreValue.KeyMappings == nil || len(keystoreValue.KeyMappings) == 0 {
+ // If no renames are necessary, mount secret key-value pairs directly
+ initContainerVolumeMounts = append(initContainerVolumeMounts, corev1.VolumeMount{
+ Name: "keystore-" + keystoreValue.Secret.Name,
+ MountPath: "/tmp/keystoreSecrets/" + keystoreValue.Secret.Name,
+ })
+ } else {
+ keys := helpers.SortedKeys(keystoreValue.KeyMappings)
+ for _, oldKey := range keys {
+ initContainerVolumeMounts = append(initContainerVolumeMounts, corev1.VolumeMount{
+ Name: "keystore-" + keystoreValue.Secret.Name,
+ MountPath: "/tmp/keystoreSecrets/" + keystoreValue.Secret.Name + "/" + keystoreValue.KeyMappings[oldKey],
+ SubPath: oldKey,
+ })
+ }
+ }
+ }
+
+ keystoreInitContainer := corev1.Container{
+ Name: "keystore",
+ Image: image.GetImage(),
+ ImagePullPolicy: image.GetImagePullPolicy(),
+ Resources: resources,
+ Command: []string{
+ "sh",
+ "-c",
+ `
+ #!/usr/bin/env bash
+ set -euo pipefail
+
+ /usr/share/opensearch/bin/opensearch-keystore create
+ for i in /tmp/keystoreSecrets/*/*; do
+ key=$(basename $i)
+ echo "Adding file $i to keystore key $key"
+ /usr/share/opensearch/bin/opensearch-keystore add-file "$key" "$i"
+ done
+
+ # Add the bootstrap password since otherwise the opensearch entrypoint tries to do this on startup
+ if [ ! -z ${PASSWORD+x} ]; then
+ echo 'Adding env $PASSWORD to keystore as key bootstrap.password'
+ echo "$PASSWORD" | /usr/share/opensearch/bin/opensearch-keystore add -x bootstrap.password
+ fi
+
+ cp -a /usr/share/opensearch/config/opensearch.keystore /tmp/keystore/
+ `,
+ },
+ VolumeMounts: initContainerVolumeMounts,
+ SecurityContext: securityContext,
+ }
+
+ initContainers = append(initContainers, keystoreInitContainer)
+ }
+
+ startUpCommand := "./opensearch-docker-entrypoint.sh"
+
+ pluginslist := helpers.RemoveDuplicateStrings(cr.Spec.Bootstrap.PluginsList)
+ mainCommand := helpers.BuildMainCommand("./bin/opensearch-plugin", pluginslist, true, startUpCommand)
pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: BootstrapPodName(cr),
@@ -881,6 +973,7 @@ func NewBootstrapPod(
{
Env: env,
Name: "opensearch",
+ Command: mainCommand,
Image: image.GetImage(),
ImagePullPolicy: image.GetImagePullPolicy(),
Resources: resources,
diff --git a/opensearch-operator/pkg/builders/cluster_test.go b/opensearch-operator/pkg/builders/cluster_test.go
index 7b0e2580..bd6f8824 100644
--- a/opensearch-operator/pkg/builders/cluster_test.go
+++ b/opensearch-operator/pkg/builders/cluster_test.go
@@ -42,6 +42,23 @@ func ClusterDescWithKeystoreSecret(secretName string, keyMappings map[string]str
}
}
+func ClusterDescWithBootstrapKeystoreSecret(secretName string, keyMappings map[string]string) opsterv1.OpenSearchCluster {
+ return opsterv1.OpenSearchCluster{
+ Spec: opsterv1.ClusterSpec{
+ Bootstrap: opsterv1.BootstrapConfig{
+ Keystore: []opsterv1.KeystoreValue{
+ {
+ Secret: corev1.LocalObjectReference{
+ Name: secretName,
+ },
+ KeyMappings: keyMappings,
+ },
+ },
+ },
+ },
+ }
+}
+
func ClusterDescWithAdditionalConfigs(addtitionalConfig map[string]string, bootstrapAdditionalConfig map[string]string) opsterv1.OpenSearchCluster {
return opsterv1.OpenSearchCluster{
Spec: opsterv1.ClusterSpec{
@@ -449,6 +466,77 @@ var _ = Describe("Builders", func() {
Value: mockBootstrapConfig[mockKey2],
}))
})
+ It("should properly setup the main command when installing plugins", func() {
+ clusterObject := ClusterDescWithVersion("2.2.1")
+ pluginA := "some-plugin"
+ pluginB := "another-plugin"
+
+ clusterObject.Spec.Bootstrap.PluginsList = []string{pluginA, pluginB}
+ result := NewBootstrapPod(&clusterObject, nil, nil)
+
+ installCmd := fmt.Sprintf(
+ "./bin/opensearch-plugin install --batch '%s' '%s' && ./opensearch-docker-entrypoint.sh",
+ pluginA,
+ pluginB,
+ )
+
+ expected := []string{
+ "/bin/bash",
+ "-c",
+ installCmd,
+ }
+
+ actual := result.Spec.Containers[0].Command
+
+ Expect(expected).To(Equal(actual))
+ })
+ })
+
+ When("Constructing a bootstrap pod with Keystore Values", func() {
+ It("should create a proper initContainer", func() {
+ mockSecretName := "some-secret"
+ clusterObject := ClusterDescWithBootstrapKeystoreSecret(mockSecretName, nil)
+
+ result := NewBootstrapPod(&clusterObject, nil, nil)
+ Expect(result.Spec.InitContainers[1].VolumeMounts).To(ContainElements([]corev1.VolumeMount{
+ {
+ Name: "keystore",
+ MountPath: "/tmp/keystore",
+ },
+ {
+ Name: "keystore-" + mockSecretName,
+ MountPath: "/tmp/keystoreSecrets/" + mockSecretName,
+ },
+ }))
+ })
+
+ It("should mount the prefilled keystore into the opensearch container", func() {
+ mockSecretName := "some-secret"
+ clusterObject := ClusterDescWithBootstrapKeystoreSecret(mockSecretName, nil)
+ result := NewBootstrapPod(&clusterObject, nil, nil)
+ Expect(result.Spec.Containers[0].VolumeMounts).To(ContainElement(corev1.VolumeMount{
+ Name: "keystore",
+ MountPath: "/usr/share/opensearch/config/opensearch.keystore",
+ SubPath: "opensearch.keystore",
+ }))
+ })
+
+ It("should properly rename secret keys when key mappings are given", func() {
+ mockSecretName := "some-secret"
+ oldKey := "old-key"
+ newKey := "new-key"
+
+ keyMappings := map[string]string{
+ oldKey: newKey,
+ }
+ clusterObject := ClusterDescWithBootstrapKeystoreSecret(mockSecretName, keyMappings)
+ result := NewBootstrapPod(&clusterObject, nil, nil)
+ Expect(result.Spec.InitContainers[1].VolumeMounts).To(ContainElement(corev1.VolumeMount{
+ Name: "keystore-" + mockSecretName,
+ MountPath: "/tmp/keystoreSecrets/" + mockSecretName + "/" + newKey,
+ SubPath: oldKey,
+ }))
+ })
})
When("Constructing a STS for a NodePool with Keystore Values", func() {
From d3bf10cf1f0642137980588e9f4a546847f8ca18 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iv=C3=A1n=20S=C3=A1nchez=20Rojas?=
<100773517+isrojas1@users.noreply.github.com>
Date: Fri, 11 Oct 2024 15:19:44 +0200
Subject: [PATCH 21/27] Fix typo in userguide (#879)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
### Description
Trivial typo in docs/userguide/main.md.
### Issues Resolved
### Check List
- [x] Commits are signed per the DCO using --signoff
- [ ] Unittest added for the new/changed functionality and all unit
tests are successful
- [ ] Customer-visible features documented
- [ ] No linter warnings (`make lint`)
If CRDs are changed:
- [ ] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [ ] Changes to CRDs documented
Please refer to the [PR
guidelines](https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/docs/developing.md#submitting-a-pr)
before submitting this pull request.
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
Signed-off-by: Iván Sánchez Rojas <100773517+isrojas1@users.noreply.github.com>
---
docs/userguide/main.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/userguide/main.md b/docs/userguide/main.md
index 227c3f71..07f83ef6 100644
--- a/docs/userguide/main.md
+++ b/docs/userguide/main.md
@@ -255,7 +255,7 @@ Directly exposing the node HTTP port outside the Kubernetes cluster is not recom
### Adding plugins
-You can extend the functionality of OpenSearch via [plugins](https://opensearch.org/docs/latest/install-and-configure/install-opensearch/plugins/#available-plugins). Commonly used ones are snapshot repository plugins for external backups (e.g. to AWS S3 or Azure Blog Storage). The operator has support to automatically install such plugins during setup.
+You can extend the functionality of OpenSearch via [plugins](https://opensearch.org/docs/latest/install-and-configure/install-opensearch/plugins/#available-plugins). Commonly used ones are snapshot repository plugins for external backups (e.g. to AWS S3 or Azure Blob Storage). The operator has support to automatically install such plugins during setup.
To install a plugin for opensearch add it to the list under `general.pluginsList`:
From c9e7f115d6476fc8cbc4d6829bade87715f99d3e Mon Sep 17 00:00:00 2001
From: Michi Gysel
Date: Fri, 18 Oct 2024 21:47:40 +0200
Subject: [PATCH 22/27] Add labels, annotations, and priorityClassName for
operator pod to chart (#881)
### Description
You can now add labels, annotations, and a priorityClassName to operator
pods via Helm values. This is commonly required for metrics scraping or
sidecar injection.
### Issues Resolved
Closes #274
### Check List
- [x] Commits are signed per the DCO using --signoff
- [x] Unittest added for the new/changed functionality and all unit
tests are successful
- [x] Customer-visible features documented
- [x] No linter warnings (`make lint`)
Please refer to the [PR
guidelines](https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/docs/developing.md#submitting-a-pr)
before submitting this pull request.
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
---------
Signed-off-by: Michael Gysel
---
...nsearch-operator-controller-manager-deployment.yaml | 10 ++++++++++
charts/opensearch-operator/values.yaml | 3 +++
2 files changed, 13 insertions(+)
diff --git a/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml b/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml
index 08f089f4..4b5cb194 100755
--- a/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml
+++ b/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml
@@ -13,6 +13,13 @@ spec:
metadata:
labels:
control-plane: controller-manager
+ {{- with .Values.podLabels }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.podAnnotations }}
+ annotations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
spec:
containers:
{{- if or (.Values.kubeRbacProxy.enable) (eq (.Values.kubeRbacProxy.enable | toString) "") }}
@@ -82,3 +89,6 @@ spec:
{{- end }}
serviceAccountName: {{ include "opensearch-operator.serviceAccountName" . }}
terminationGracePeriodSeconds: 10
+ {{- if .Values.priorityClassName }}
+ priorityClassName: {{ .Values.priorityClassName }}
+ {{- end }}
diff --git a/charts/opensearch-operator/values.yaml b/charts/opensearch-operator/values.yaml
index 0629f261..d2b2ac61 100644
--- a/charts/opensearch-operator/values.yaml
+++ b/charts/opensearch-operator/values.yaml
@@ -1,10 +1,13 @@
nameOverride: ""
fullnameOverride: ""
+podAnnotations: {}
+podLabels: {}
nodeSelector: {}
tolerations: []
securityContext:
runAsNonRoot: true
+priorityClassName: ""
manager:
securityContext:
allowPrivilegeEscalation: false
From a6de94c2c1fc6fee5e3c93c2d0f78a8ce2d3a2f7 Mon Sep 17 00:00:00 2001
From: Nilushan Costa
Date: Tue, 29 Oct 2024 17:58:53 +0530
Subject: [PATCH 23/27] Add the capability to use RoleBindings instead of
ClusterRoleBindings (#841)
### Description
This PR introduces a new key to the Helm values named `useRoleBindings`
which when set to `true` creates Kubernetes RoleBindings instead of
ClusterRoleBindings
### Issues Resolved
Closes
https://github.com/opensearch-project/opensearch-k8s-operator/issues/831
### Check List
- [x] Commits are signed per the DCO using --signoff
- [ ] Unittest added for the new/changed functionality and all unit
tests are successful
- [x] Customer-visible features documented
- [ ] No linter warnings (`make lint`)
If CRDs are changed:
- [ ] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [ ] Changes to CRDs documented
Please refer to the [PR
guidelines](https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/docs/developing.md#submitting-a-pr)
before submitting this pull request.
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
---------
Signed-off-by: Nilushan Costa
Signed-off-by: Nilushan Costa
Co-authored-by: Sebastian Woehrl
---
...arch-operator-manager-rolebinding-crb.yaml | 12 ---------
...ensearch-operator-manager-rolebinding.yaml | 27 +++++++++++++++++++
...search-operator-proxy-rolebinding-crb.yaml | 12 ---------
...opensearch-operator-proxy-rolebinding.yaml | 27 +++++++++++++++++++
charts/opensearch-operator/values.yaml | 6 +++++
5 files changed, 60 insertions(+), 24 deletions(-)
delete mode 100755 charts/opensearch-operator/templates/opensearch-operator-manager-rolebinding-crb.yaml
create mode 100755 charts/opensearch-operator/templates/opensearch-operator-manager-rolebinding.yaml
delete mode 100755 charts/opensearch-operator/templates/opensearch-operator-proxy-rolebinding-crb.yaml
create mode 100755 charts/opensearch-operator/templates/opensearch-operator-proxy-rolebinding.yaml
diff --git a/charts/opensearch-operator/templates/opensearch-operator-manager-rolebinding-crb.yaml b/charts/opensearch-operator/templates/opensearch-operator-manager-rolebinding-crb.yaml
deleted file mode 100755
index e5b1befb..00000000
--- a/charts/opensearch-operator/templates/opensearch-operator-manager-rolebinding-crb.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
- name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-manager-rolebinding
-roleRef:
- apiGroup: rbac.authorization.k8s.io
- kind: ClusterRole
- name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-manager-role
-subjects:
-- kind: ServiceAccount
- name: {{ include "opensearch-operator.serviceAccountName" . }}
- namespace: {{ .Release.Namespace }}
diff --git a/charts/opensearch-operator/templates/opensearch-operator-manager-rolebinding.yaml b/charts/opensearch-operator/templates/opensearch-operator-manager-rolebinding.yaml
new file mode 100755
index 00000000..a528ffdf
--- /dev/null
+++ b/charts/opensearch-operator/templates/opensearch-operator-manager-rolebinding.yaml
@@ -0,0 +1,27 @@
+{{- if .Values.useRoleBindings }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-manager-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-manager-role
+subjects:
+- kind: ServiceAccount
+ name: {{ include "opensearch-operator.serviceAccountName" . }}
+ namespace: {{ .Release.Namespace }}
+{{- else }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-manager-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-manager-role
+subjects:
+- kind: ServiceAccount
+ name: {{ include "opensearch-operator.serviceAccountName" . }}
+ namespace: {{ .Release.Namespace }}
+{{- end }}
diff --git a/charts/opensearch-operator/templates/opensearch-operator-proxy-rolebinding-crb.yaml b/charts/opensearch-operator/templates/opensearch-operator-proxy-rolebinding-crb.yaml
deleted file mode 100755
index 5cba0693..00000000
--- a/charts/opensearch-operator/templates/opensearch-operator-proxy-rolebinding-crb.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRoleBinding
-metadata:
- name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-proxy-rolebinding
-roleRef:
- apiGroup: rbac.authorization.k8s.io
- kind: ClusterRole
- name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-proxy-role
-subjects:
-- kind: ServiceAccount
- name: {{ include "opensearch-operator.serviceAccountName" . }}
- namespace: {{ .Release.Namespace }}
diff --git a/charts/opensearch-operator/templates/opensearch-operator-proxy-rolebinding.yaml b/charts/opensearch-operator/templates/opensearch-operator-proxy-rolebinding.yaml
new file mode 100755
index 00000000..d9a5d339
--- /dev/null
+++ b/charts/opensearch-operator/templates/opensearch-operator-proxy-rolebinding.yaml
@@ -0,0 +1,27 @@
+{{- if .Values.useRoleBindings }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-proxy-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-proxy-role
+subjects:
+- kind: ServiceAccount
+ name: {{ include "opensearch-operator.serviceAccountName" . }}
+ namespace: {{ .Release.Namespace }}
+{{- else }}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-proxy-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-proxy-role
+subjects:
+- kind: ServiceAccount
+ name: {{ include "opensearch-operator.serviceAccountName" . }}
+ namespace: {{ .Release.Namespace }}
+{{- end }}
diff --git a/charts/opensearch-operator/values.yaml b/charts/opensearch-operator/values.yaml
index d2b2ac61..1ee839bb 100644
--- a/charts/opensearch-operator/values.yaml
+++ b/charts/opensearch-operator/values.yaml
@@ -115,3 +115,9 @@ kubeRbacProxy:
image:
repository: "gcr.io/kubebuilder/kube-rbac-proxy"
tag: "v0.15.0"
+
+## If this is set to true, RoleBindings will be used instead of ClusterRoleBindings, inorder to restrict ClusterRoles
+## to the namespace where the operator and OpenSearch cluster are in. In that case, specify the namespace where they
+## are in in manager.watchNamespace field.
+## If false, ClusterRoleBindings will be used
+useRoleBindings: false
From 8fda1480bb4b54d583c258d9d905f6fdf47cb4e1 Mon Sep 17 00:00:00 2001
From: Prudhvi Godithi
Date: Tue, 5 Nov 2024 06:10:29 -0800
Subject: [PATCH 24/27] Decouple Helm Release with the Operator Release (#872)
### Description
Decouple Helm Release with the Operator Release.
- Update the `release.yaml` to exclude the helm release.
- Onboard new helm release workflow `helm-release.yaml`.
- The `helm-release.yaml` use the
[helm/chart-releaser-action](https://github.com/helm/chart-releaser-action)
which identifies the chart version and releases incrementally.
- The `helm-release.yaml` runs once the PR is merged to `main` and
releases only once there is a change with chart `version`, inside the
[charts/
](https://github.com/opensearch-project/opensearch-k8s-operator/tree/main/charts)folder.
The expectation from the user is to create a PR with with updates
changes, along with modifying the `version` of the chart, once merged
the `helm/chart-releaser-action` will identify the new chart version and
does a helm release.
Whenever there is an operator version release, the `appVersion` along
with `version` has to be updated and thereafter the process is same
`helm/chart-releaser-action` will identify the new chart version and
does a helm release.
### Issues Resolved
Coming from
https://github.com/opensearch-project/opensearch-k8s-operator/issues/830
### Check List
- [x] Commits are signed per the DCO using --signoff
- [ ] Unittest added for the new/changed functionality and all unit
tests are successful
- [ ] Customer-visible features documented
- [ ] No linter warnings (`make lint`)
If CRDs are changed:
- [ ] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [ ] Changes to CRDs documented
Please refer to the [PR
guidelines](https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/docs/developing.md#submitting-a-pr)
before submitting this pull request.
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
Signed-off-by: Prudhvi Godithi
---
.github/workflows/helm-release.yaml | 33 ++++++++++++++++++++++++++
.github/workflows/release.yaml | 13 +++++-----
charts/opensearch-cluster/CHANGELOG.md | 28 ++++++++++++++++++++++
charts/opensearch-cluster/Chart.yaml | 8 +++++--
charts/opensearch-operator/Chart.yaml | 4 ++--
5 files changed, 75 insertions(+), 11 deletions(-)
create mode 100644 .github/workflows/helm-release.yaml
create mode 100644 charts/opensearch-cluster/CHANGELOG.md
diff --git a/.github/workflows/helm-release.yaml b/.github/workflows/helm-release.yaml
new file mode 100644
index 00000000..d8a2304f
--- /dev/null
+++ b/.github/workflows/helm-release.yaml
@@ -0,0 +1,33 @@
+name: Release Helm Charts
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - main
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+
+ - name: Configure Git
+ run: |
+ git config user.name "$GITHUB_ACTOR"
+ git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
+
+ - name: Install Helm
+ uses: azure/setup-helm@v1
+ with:
+ version: v3.14.3
+
+ - name: Run chart-releaser for opensearch-cluster
+ uses: helm/chart-releaser-action@v1.6.0
+ with:
+ charts_dir: charts/opensearch-cluster
+ env:
+ CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 23ed9b26..20397015 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -23,24 +23,23 @@ jobs:
id: github-ver
run: |
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV
-
+
- name: Make sure helm chart has correct versions
run: |
sed -i -e 's/^appVersion:.*/appVersion: '$RELEASE_VERSION'/' charts/opensearch-operator/Chart.yaml
sed -i -e 's/^version:.*/version: '$RELEASE_VERSION'/' charts/opensearch-operator/Chart.yaml
- sed -i -e 's/^appVersion:.*/appVersion: '$RELEASE_VERSION'/' charts/opensearch-cluster/Chart.yaml
- sed -i -e 's/^version:.*/version: '$RELEASE_VERSION'/' charts/opensearch-cluster/Chart.yaml
- name: Install Helm
uses: azure/setup-helm@v4
with:
version: v3.14.3
- - name: Publish helm chart
- uses: stefanprodan/helm-gh-pages@master
+ - name: Run chart-releaser for opensearch-operator
+ uses: helm/chart-releaser-action@v1.6.0
with:
- charts_dir: charts/
- token: ${{ secrets.GITHUB_TOKEN }}
+ charts_dir: charts/opensearch-operator
+ env:
+ CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
- name: Create Release
uses: softprops/action-gh-release@v2
diff --git a/charts/opensearch-cluster/CHANGELOG.md b/charts/opensearch-cluster/CHANGELOG.md
new file mode 100644
index 00000000..cda46edd
--- /dev/null
+++ b/charts/opensearch-cluster/CHANGELOG.md
@@ -0,0 +1,28 @@
+# Changelog
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+---
+## [Unreleased]
+### Added
+### Changed
+### Deprecated
+### Removed
+### Fixed
+### Security
+---
+
+## [2.6.1]
+### Added
+### Changed
+- Updated `version` and `appVersion` to `2.6.1` for the initial release after the helm release decouple.
+### Deprecated
+### Removed
+### Fixed
+### Security
+
+[Unreleased]: https://github.com/opensearch-project/opensearch-k8s-operator/compare/opensearch-operator-2.6.1...HEAD
+[2.6.1]: https://github.com/opensearch-project/opensearch-k8s-operator/compare/opensearch-operator-2.6.0...opensearch-operator-2.6.1
+
diff --git a/charts/opensearch-cluster/Chart.yaml b/charts/opensearch-cluster/Chart.yaml
index fe92e924..294ebdc9 100644
--- a/charts/opensearch-cluster/Chart.yaml
+++ b/charts/opensearch-cluster/Chart.yaml
@@ -2,5 +2,9 @@ apiVersion: v2
name: opensearch-cluster
description: A Helm chart for OpenSearch Cluster
type: application
-version: 2.6.0
-appVersion: 2.6.0
+
+## The opensearch-cluster Helm Chart version
+version: 2.6.1
+
+## The operator version
+appVersion: 2.6.1
diff --git a/charts/opensearch-operator/Chart.yaml b/charts/opensearch-operator/Chart.yaml
index 2cf3b638..6623444a 100644
--- a/charts/opensearch-operator/Chart.yaml
+++ b/charts/opensearch-operator/Chart.yaml
@@ -12,12 +12,12 @@ description: The OpenSearch Operator Helm chart for Kubernetes
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
-# This is the chart version. This version number should be incremented each time you make changes
+# This is the opensearch-operator chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 2.6.0
-# This is the version number of the application being deployed. This version number should be
+# This is the version number of the application being deployed (the operator). This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
From beaf4fb82d69584092be561d35c613d5fd2f556e Mon Sep 17 00:00:00 2001
From: Prudhvi Godithi
Date: Tue, 5 Nov 2024 11:23:42 -0800
Subject: [PATCH 25/27] Helm and Operator Release Improvements (#890)
### Description
Coming from initial PR
https://github.com/opensearch-project/opensearch-k8s-operator/pull/872.
Fix the error
https://github.com/opensearch-project/opensearch-k8s-operator/actions/runs/11685891300/job/32540910915
```
Looking up latest tag...
Discovering changed charts since 'v2.6.0'...
WARNING: charts/opensearch-cluster/templates/Chart.yaml is missing, assuming that 'charts/opensearch-cluster/templates' is not a Helm chart. Skipping.
Nothing to do. No chart changes detected.
```
Since moving from `stefanprodan/helm-gh-pages@master` to
`helm/chart-releaser-action@v1.6.0` added `fetch-depth` and
`skip_existing`. The `skip_existing` should not throw an error if the
chart with same `version` is already added to the `gh-pages` branch
`index.yaml` file.
#### Here are some tests done on my fork
##### Release Helm Charts workflow
https://github.com/prudhvigodithi/opensearch-k8s-operator/actions/workflows/helm-release.yaml
##### Publish Release from tag workflow
https://github.com/prudhvigodithi/opensearch-k8s-operator/actions/runs/11687219955
### Issues Resolved
Coming from
https://github.com/opensearch-project/opensearch-k8s-operator/issues/830
### Check List
- [ ] Commits are signed per the DCO using --signoff
- [ ] Unittest added for the new/changed functionality and all unit
tests are successful
- [ ] Customer-visible features documented
- [ ] No linter warnings (`make lint`)
If CRDs are changed:
- [ ] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [ ] Changes to CRDs documented
Please refer to the [PR
guidelines](https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/docs/developing.md#submitting-a-pr)
before submitting this pull request.
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
Signed-off-by: Prudhvi Godithi
---
.github/workflows/helm-release.yaml | 13 ++++++++-----
.github/workflows/release.yaml | 17 ++++++++++++++---
2 files changed, 22 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/helm-release.yaml b/.github/workflows/helm-release.yaml
index d8a2304f..a152f12f 100644
--- a/.github/workflows/helm-release.yaml
+++ b/.github/workflows/helm-release.yaml
@@ -8,10 +8,12 @@ on:
jobs:
release:
+ permissions:
+ contents: write
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
with:
fetch-depth: 0
@@ -21,13 +23,14 @@ jobs:
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Install Helm
- uses: azure/setup-helm@v1
- with:
- version: v3.14.3
+ uses: azure/setup-helm@v4
+ env:
+ GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
- name: Run chart-releaser for opensearch-cluster
uses: helm/chart-releaser-action@v1.6.0
with:
- charts_dir: charts/opensearch-cluster
+ skip_existing: true
+ mark_as_latest: false
env:
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 20397015..16aaac02 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -12,13 +12,18 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
- name: Setup go
uses: actions/setup-go@v5
with:
go-version-file: 'go.work'
cache: false
+
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
+
- name: set Env
id: github-ver
run: |
@@ -29,15 +34,21 @@ jobs:
sed -i -e 's/^appVersion:.*/appVersion: '$RELEASE_VERSION'/' charts/opensearch-operator/Chart.yaml
sed -i -e 's/^version:.*/version: '$RELEASE_VERSION'/' charts/opensearch-operator/Chart.yaml
+ - name: Configure Git
+ run: |
+ git config user.name "$GITHUB_ACTOR"
+ git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
+
- name: Install Helm
uses: azure/setup-helm@v4
- with:
- version: v3.14.3
+ env:
+ GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
- name: Run chart-releaser for opensearch-operator
uses: helm/chart-releaser-action@v1.6.0
with:
- charts_dir: charts/opensearch-operator
+ skip_existing: true
+ mark_as_latest: false
env:
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
From 5e786e794e5a34c9bba11cba36f7d82f3d87c8e6 Mon Sep 17 00:00:00 2001
From: Prudhvi Godithi
Date: Tue, 5 Nov 2024 12:48:59 -0800
Subject: [PATCH 26/27] Prepare for 2.7.0 release (#892)
### Description
Prepare for 2.7.0 release
### Issues Resolved
Part of
https://github.com/opensearch-project/opensearch-k8s-operator/issues/891
### Check List
- [ ] Commits are signed per the DCO using --signoff
- [ ] Unittest added for the new/changed functionality and all unit
tests are successful
- [ ] Customer-visible features documented
- [ ] No linter warnings (`make lint`)
If CRDs are changed:
- [ ] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [ ] Changes to CRDs documented
Please refer to the [PR
guidelines](https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/docs/developing.md#submitting-a-pr)
before submitting this pull request.
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
Signed-off-by: Prudhvi Godithi
---
README.md | 2 +-
charts/opensearch-cluster/Chart.yaml | 4 ++--
charts/opensearch-operator/Chart.yaml | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index fc285e9d..f91943da 100644
--- a/README.md
+++ b/README.md
@@ -52,7 +52,7 @@ The opensearch k8s operator aims to be compatible to all supported opensearch ve
| Operator Version | Min Supported Opensearch Version | Max Supported Opensearch Version | Comment |
| ----------------------------------------------------------- | -------------------------------- | -------------------------------- | ------------------------------------------- |
-| 2.6.0
2.5.1
2.5.0 | 1.3.x | latest 2.x | Supports the latest OpenSearch 2.x version. |
+| 2.7.0
2.6.1
2.6.0
2.5.1
2.5.0 | 1.3.x | latest 2.x | Supports the latest OpenSearch 2.x version. |
This table only lists versions that have been explicitly tested with the operator, the operator will not prevent you from using other versions. Newer minor versions (2.x) not listed here generally also work but you should proceed with caution and test it our in a non-production environment first.
diff --git a/charts/opensearch-cluster/Chart.yaml b/charts/opensearch-cluster/Chart.yaml
index 294ebdc9..c5cb2524 100644
--- a/charts/opensearch-cluster/Chart.yaml
+++ b/charts/opensearch-cluster/Chart.yaml
@@ -4,7 +4,7 @@ description: A Helm chart for OpenSearch Cluster
type: application
## The opensearch-cluster Helm Chart version
-version: 2.6.1
+version: 2.7.0
## The operator version
-appVersion: 2.6.1
+appVersion: 2.7.0
diff --git a/charts/opensearch-operator/Chart.yaml b/charts/opensearch-operator/Chart.yaml
index 6623444a..9ef3bb0a 100644
--- a/charts/opensearch-operator/Chart.yaml
+++ b/charts/opensearch-operator/Chart.yaml
@@ -15,10 +15,10 @@ type: application
# This is the opensearch-operator chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 2.6.0
+version: 2.7.0
# This is the version number of the application being deployed (the operator). This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
-appVersion: 2.6.0
+appVersion: 2.7.0
From 3e04130965dc440a2fa4466f99e85f253b9af734 Mon Sep 17 00:00:00 2001
From: Prudhvi Godithi
Date: Tue, 5 Nov 2024 13:59:14 -0800
Subject: [PATCH 27/27] Exclude helm tags for triggering the jenkins promotion
workflow (#893)
### Description
Exclude helm tags for triggering the jenkins promotion workflow. The
Jenkins job
https://build.ci.opensearch.org/job/opensearch-operator-release/ will be
triggered once the tags are created, exclude this job trigger for helm
chart tags and only trigger for actual release.
### Issues Resolved
https://github.com/opensearch-project/opensearch-k8s-operator/issues/891
### Check List
- [ ] Commits are signed per the DCO using --signoff
- [ ] Unittest added for the new/changed functionality and all unit
tests are successful
- [ ] Customer-visible features documented
- [ ] No linter warnings (`make lint`)
If CRDs are changed:
- [ ] CRD YAMLs updated (`make manifests`) and also copied into the helm
chart
- [ ] Changes to CRDs documented
Please refer to the [PR
guidelines](https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/docs/developing.md#submitting-a-pr)
before submitting this pull request.
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and
signing off your commits, please check
[here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).
Signed-off-by: Prudhvi Godithi
---
jenkins/release.jenkinsfile | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/jenkins/release.jenkinsfile b/jenkins/release.jenkinsfile
index c57141c8..04b91847 100644
--- a/jenkins/release.jenkinsfile
+++ b/jenkins/release.jenkinsfile
@@ -66,7 +66,8 @@ pipeline {
currentBuild.description = """User/Timer: ${ref_url}"""
}
- if (ref_final == null || ref_final == '') {
+
+ if (ref_final == null || ref_final == '' || ref_final.startsWith('opensearch-operator') || ref_final.startsWith('opensearch-cluster')) { {
currentBuild.result = 'ABORTED'
error("Missing git tag reference.")
}