From 5a80c0c847cf9e9d1f97011bcafe5bf32107ddd9 Mon Sep 17 00:00:00 2001 From: Param Dhanoya Date: Wed, 7 Oct 2020 07:31:37 -0700 Subject: [PATCH 01/38] CSPL-444-Added util to get rf sf status from cm --- test/smoke/smoke_test.go | 23 +++++++++++++++ test/testenv/cmutil.go | 60 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 test/testenv/cmutil.go diff --git a/test/smoke/smoke_test.go b/test/smoke/smoke_test.go index 2e2c467db..a55ed79e8 100644 --- a/test/smoke/smoke_test.go +++ b/test/smoke/smoke_test.go @@ -135,6 +135,13 @@ var _ = Describe("Smoke test", func() { // Verify MC Pod is Ready testenv.MCPodReady(testenvInstance.GetName(), deployment) + + // Verify RF SF is met + Eventually(func() bool { + rfSfStatus := testenv.CheckRFSF(testenvInstance.GetName(), deployment.GetName()) + testenvInstance.Log.Info("Verifying RF SF is met", "Status", rfSfStatus) + return rfSfStatus + }, deployment.GetTimeout(), PollInterval).Should(Equal(true)) }) }) @@ -232,6 +239,13 @@ var _ = Describe("Smoke test", func() { // Verify MC Pod is Ready testenv.MCPodReady(testenvInstance.GetName(), deployment) + + // Verify RF SF is met + Eventually(func() bool { + rfSfStatus := testenv.CheckRFSF(testenvInstance.GetName(), deployment.GetName()) + testenvInstance.Log.Info("Verifying RF SF is met", "Status", rfSfStatus) + return rfSfStatus + }, deployment.GetTimeout(), PollInterval).Should(Equal(true)) }) }) @@ -309,6 +323,15 @@ var _ = Describe("Smoke test", func() { return siteIndexerStatus }, deployment.GetTimeout(), PollInterval).Should(Equal(siteIndexerMap)) + // Verify MC Pod is Ready + testenv.MCPodReady(testenvInstance.GetName(), deployment) + + // Verify RF SF is met + Eventually(func() bool { + rfSfStatus := testenv.CheckRFSF(testenvInstance.GetName(), deployment.GetName()) + testenvInstance.Log.Info("Verifying RF SF is met", "Status", rfSfStatus) + return rfSfStatus + }, deployment.GetTimeout(), PollInterval).Should(Equal(true)) }) }) }) diff --git a/test/testenv/cmutil.go b/test/testenv/cmutil.go new file mode 100644 index 000000000..d1b4fa568 --- /dev/null +++ b/test/testenv/cmutil.go @@ -0,0 +1,60 @@ +package testenv + +import ( + "encoding/json" + "fmt" + logf "sigs.k8s.io/controller-runtime/pkg/log" +) + +// ClusterMasterHealthResponse is a representation of the health response by a Splunk cluster-master +// Endpoint: /services/cluster/master/health +type ClusterMasterHealthResponse struct { + Entries []ClusterMasterHealthEntry `json:"entry"` +} + +// ClusterMasterHealthEntry represents a site of an indexer cluster with its metadata +type ClusterMasterHealthEntry struct { + Name string `json:"name"` + Content ClusterMasterHealthContent `json:"content"` +} + +// ClusterMasterHealthContent represents detailed information about a site +type ClusterMasterHealthContent struct { + AllDataIsSearchable string `json:"all_data_is_searchable"` + AllPeersAreUp string `json:"all_peers_are_up"` + Multisite string `json:"multisite"` + NoFixupTasksInProgress string `json:"no_fixup_tasks_in_progress"` + ReplicationFactorMet string `json:"replication_factor_met"` + SearchFactorMet string `json:"search_factor_met"` + SiteReplicationFactorMet string `json:"site_replication_factor_met"` + SiteSearchFactorMet string `json:"site_search_factor_met"` +} + +// CheckRFSF check if cluster has met replication factor and search factor +func CheckRFSF(deployment *Deployment) bool { + //code to execute + podName := fmt.Sprintf("splunk-%s-cluster-master-0", deployment.GetName()) + stdin := "curl -ks -u admin:$(cat /mnt/splunk-secrets/password) https://localhost:8089/services/cluster/master/health?output_mode=json" + command := []string{"/bin/sh"} + stdout, stderr, err := deployment.PodExecCommand(podName, command, stdin, false) + if err != nil { + logf.Log.Error(err, "Failed to execute command on pod", "pod", podName, "command", command) + return false + } + logf.Log.Info("Command executed on pod", "pod", podName, "command", command, "stdin", stdin, "stdout", stdout, "stderr", stderr) + restResponse := ClusterMasterHealthResponse{} + err = json.Unmarshal([]byte(stdout), &restResponse) + if err != nil { + logf.Log.Error(err, "Failed to parse health status") + return false + } + sfMet := restResponse.Entries[0].Content.SearchFactorMet == "1" + if sfMet == false { + logf.Log.Info("Search Factor not met") + } + rfMet := restResponse.Entries[0].Content.ReplicationFactorMet == "1" + if rfMet == false { + logf.Log.Info("Replicaton Factor not met") + } + return rfMet && sfMet +} From 3338a01f14999df0c2c0dba7808f692d985b3f1d Mon Sep 17 00:00:00 2001 From: Param Dhanoya Date: Mon, 19 Oct 2020 10:16:05 -0700 Subject: [PATCH 02/38] CSPL-521: Added more delay and checks for MC pod to be ready --- test/smoke/smoke_test.go | 2 ++ test/testenv/mcutil.go | 8 ++++++++ test/testenv/testenv.go | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/test/smoke/smoke_test.go b/test/smoke/smoke_test.go index d425443ce..ebe76acdd 100644 --- a/test/smoke/smoke_test.go +++ b/test/smoke/smoke_test.go @@ -309,6 +309,8 @@ var _ = Describe("Smoke test", func() { return siteIndexerStatus }, deployment.GetTimeout(), PollInterval).Should(Equal(siteIndexerMap)) + // Verify MC Pod is Ready + testenv.MCPodReady(testenvInstance.GetName(), deployment) }) }) }) diff --git a/test/testenv/mcutil.go b/test/testenv/mcutil.go index abbfd8c51..2542314ca 100644 --- a/test/testenv/mcutil.go +++ b/test/testenv/mcutil.go @@ -116,6 +116,14 @@ func MCPodReady(ns string, deployment *Deployment) { DumpGetPods(ns) return check }, deployment.GetTimeout(), PollInterval).Should(gomega.Equal(true)) + + // Verify MC Pod Stays in ready state + gomega.Consistently(func() bool { + logf.Log.Info("Checking status of Monitoring Console Pod") + check := CheckMCPodReady(ns) + DumpGetPods(ns) + return check + }, ConsistentDuration, ConsistentPollInterval).Should(gomega.Equal(true)) } // GetSearchHeadPeersOnMC GET Search head configured on MC diff --git a/test/testenv/testenv.go b/test/testenv/testenv.go index a13e2376f..f65173b5d 100644 --- a/test/testenv/testenv.go +++ b/test/testenv/testenv.go @@ -31,7 +31,7 @@ const ( defaultSparkImage = "splunk/spark" // defaultTestTimeout is the max timeout in seconds before async test failed. - defaultTestTimeout = 900 + defaultTestTimeout = 1500 // PollInterval specifies the polling interval PollInterval = 5 * time.Second From 39e8302b91e7bc8d12be121d5489b68fde2601fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CGaurav?= Date: Thu, 22 Oct 2020 16:23:15 -0700 Subject: [PATCH 03/38] CSPL-529: Fixing a bug where if we delete CM, it also deletes PVCs for IndexerCluster too --- pkg/splunk/enterprise/finalizers.go | 13 +++++++++---- pkg/splunk/enterprise/finalizers_test.go | 12 ++++++++---- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/pkg/splunk/enterprise/finalizers.go b/pkg/splunk/enterprise/finalizers.go index 63fcf67bb..2c427dccc 100644 --- a/pkg/splunk/enterprise/finalizers.go +++ b/pkg/splunk/enterprise/finalizers.go @@ -31,19 +31,24 @@ func init() { // DeleteSplunkPvc removes all corresponding PersistentVolumeClaims that are associated with a custom resource. func DeleteSplunkPvc(cr splcommon.MetaObject, c splcommon.ControllerClient) error { - scopedLog := log.WithName("DeleteSplunkPvc").WithValues("kind", cr.GetObjectKind().GroupVersionKind().Kind, + var objectKind string + objectKind = cr.GetObjectKind().GroupVersionKind().Kind + + scopedLog := log.WithName("DeleteSplunkPvc").WithValues("kind", objectKind, "name", cr.GetName(), "namespace", cr.GetNamespace()) var component string - switch cr.GetObjectKind().GroupVersionKind().Kind { + switch objectKind { case "Standalone": component = "standalone" case "LicenseMaster": component = "license-master" case "SearchHeadCluster": component = "search-head" - case "IndexerCluster", "ClusterMaster": + case "IndexerCluster": component = "indexer" + case "ClusterMaster": + component = "cluster-master" default: scopedLog.Info("Skipping PVC removal") return nil @@ -51,7 +56,7 @@ func DeleteSplunkPvc(cr splcommon.MetaObject, c splcommon.ControllerClient) erro // get list of PVCs for this cluster labels := map[string]string{ - "app.kubernetes.io/part-of": fmt.Sprintf("splunk-%s-%s", cr.GetName(), component), + "app.kubernetes.io/instance": fmt.Sprintf("splunk-%s-%s", cr.GetName(), component), } listOpts := []client.ListOption{ client.InNamespace(cr.GetNamespace()), diff --git a/pkg/splunk/enterprise/finalizers_test.go b/pkg/splunk/enterprise/finalizers_test.go index 6951859d7..ec06f65a1 100644 --- a/pkg/splunk/enterprise/finalizers_test.go +++ b/pkg/splunk/enterprise/finalizers_test.go @@ -40,8 +40,10 @@ func splunkDeletionTester(t *testing.T, cr splcommon.MetaObject, delete func(spl component = "license-master" case "SearchHeadCluster": component = "search-head" - case "IndexerCluster", "ClusterMaster": + case "IndexerCluster": component = "indexer" + case "ClusterMaster": + component = "cluster-master" } labelsA := map[string]string{ @@ -49,7 +51,7 @@ func splunkDeletionTester(t *testing.T, cr splcommon.MetaObject, delete func(spl "app.kubernetes.io/managed-by": "splunk-operator", } labelsB := map[string]string{ - "app.kubernetes.io/part-of": fmt.Sprintf("splunk-%s-%s", cr.GetName(), component), + "app.kubernetes.io/instance": fmt.Sprintf("splunk-%s-%s", cr.GetName(), component), } listOptsA := []client.ListOption{ client.InNamespace("test"), @@ -169,12 +171,14 @@ func splunkPVCDeletionTester(t *testing.T, cr splcommon.MetaObject, delete func( component = "license-master" case "SearchHeadCluster": component = "search-head" - case "IndexerCluster", "ClusterMaster": + case "IndexerCluster": component = "indexer" + case "ClusterMaster": + component = "cluster-master" } labels := map[string]string{ - "app.kubernetes.io/part-of": fmt.Sprintf("splunk-%s-%s", cr.GetName(), component), + "app.kubernetes.io/instance": fmt.Sprintf("splunk-%s-%s", cr.GetName(), component), } listOpts := []client.ListOption{ client.InNamespace(cr.GetNamespace()), From fb617ad4d27cf196480c746e24a9cd8d9dfbeb29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CGaurav?= Date: Mon, 26 Oct 2020 11:12:14 -0700 Subject: [PATCH 04/38] bugfix/CSPL-466: If in IndexerCluster spec, replicas < RF(which is configured on ClusterMaster), then set replicas=RF. --- pkg/splunk/client/enterprise.go | 4 ++- pkg/splunk/enterprise/indexercluster.go | 47 ++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/pkg/splunk/client/enterprise.go b/pkg/splunk/client/enterprise.go index 29d72200f..5cf7569ae 100644 --- a/pkg/splunk/client/enterprise.go +++ b/pkg/splunk/client/enterprise.go @@ -894,7 +894,9 @@ func (c *SplunkClient) UpdateMonitoringConsoleApp() error { //ClusterInfo is the struct for checking ClusterInfo type ClusterInfo struct { - MultiSite string `json:"multisite"` + MultiSite string `json:"multisite"` + ReplicationFactor int32 `json:"replication_factor"` + SiteReplicationFactor string `json:"site_replication_factor,omitempty"` } // GetClusterInfo queries the cluster about multi-site or single-site. diff --git a/pkg/splunk/enterprise/indexercluster.go b/pkg/splunk/enterprise/indexercluster.go index 98cea1df8..c85ccdd8c 100644 --- a/pkg/splunk/enterprise/indexercluster.go +++ b/pkg/splunk/enterprise/indexercluster.go @@ -18,6 +18,8 @@ import ( "context" "errors" "fmt" + "regexp" + "strconv" "time" appsv1 "k8s.io/api/apps/v1" @@ -73,6 +75,15 @@ func ApplyIndexerCluster(client splcommon.ControllerClient, cr *enterprisev1.Ind return result, err } + mgr := indexerClusterPodManager{log: scopedLog, cr: cr, secrets: namespaceScopedSecret, newSplunkClient: splclient.NewSplunkClient} + // Check if we have configured enough number(<= RF) of replicas + if !mgr.cr.Spec.Mock { + err = mgr.verifyRFPeers(client) + if err != nil { + return result, err + } + } + // check if deletion has been requested if cr.ObjectMeta.DeletionTimestamp != nil { DeleteOwnerReferencesForResources(client, cr, nil) @@ -114,7 +125,7 @@ func ApplyIndexerCluster(client splcommon.ControllerClient, cr *enterprisev1.Ind if err != nil { return result, err } - mgr := indexerClusterPodManager{log: scopedLog, cr: cr, secrets: namespaceScopedSecret, newSplunkClient: splclient.NewSplunkClient} + phase, err := mgr.Update(client, statefulSet, cr.Spec.Replicas) if err != nil { return result, err @@ -414,6 +425,40 @@ func (mgr *indexerClusterPodManager) getClusterMasterClient() *splclient.SplunkC return mgr.newSplunkClient(fmt.Sprintf("https://%s:8089", fqdnName), "admin", adminPwd) } +func getSiteRepFactor(siteRepFactor string) int32 { + //re := regexp.MustCompile(".*total:(?P.*) }") + re := regexp.MustCompile(".*origin:(?P.*),.*") + match := re.FindStringSubmatch(siteRepFactor) + siteRF, _ := strconv.Atoi(match[1]) + return int32(siteRF) +} + +// verifyRFPeers verifies the number of peers specified in the replicas section +// of IndexerClsuster CR. If it is less than RF, than we se it to RF. +func (mgr *indexerClusterPodManager) verifyRFPeers(c splcommon.ControllerClient) error { + if mgr.c == nil { + mgr.c = c + } + cm := mgr.getClusterMasterClient() + clusterInfo, err := cm.GetClusterInfo(false) + if err != nil { + return fmt.Errorf("Could not get cluster info from cluster master") + } + var replicationFactor int32 + // if it is a multisite indexer cluster, check site_replication_factor + if clusterInfo.MultiSite == "true" { + replicationFactor = getSiteRepFactor(clusterInfo.SiteReplicationFactor) + } else { // for single site, check replication factor + replicationFactor = clusterInfo.ReplicationFactor + } + + if mgr.cr.Spec.Replicas < replicationFactor { + mgr.log.Info("Changing number of replicas as it is less than RF number of peers", "replicas", mgr.cr.Spec.Replicas) + mgr.cr.Spec.Replicas = replicationFactor + } + return nil +} + // updateStatus for indexerClusterPodManager uses the REST API to update the status for an IndexerCluster custom resource func (mgr *indexerClusterPodManager) updateStatus(statefulSet *appsv1.StatefulSet) error { mgr.cr.Status.ReadyReplicas = statefulSet.Status.ReadyReplicas From 5e4fd3f31bebb74f4e5768afe69bcad02fecda68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CGaurav?= Date: Mon, 26 Oct 2020 14:57:57 -0700 Subject: [PATCH 05/38] Removed a commented line --- pkg/splunk/enterprise/indexercluster.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/splunk/enterprise/indexercluster.go b/pkg/splunk/enterprise/indexercluster.go index c85ccdd8c..37ae42ca6 100644 --- a/pkg/splunk/enterprise/indexercluster.go +++ b/pkg/splunk/enterprise/indexercluster.go @@ -426,7 +426,6 @@ func (mgr *indexerClusterPodManager) getClusterMasterClient() *splclient.SplunkC } func getSiteRepFactor(siteRepFactor string) int32 { - //re := regexp.MustCompile(".*total:(?P.*) }") re := regexp.MustCompile(".*origin:(?P.*),.*") match := re.FindStringSubmatch(siteRepFactor) siteRF, _ := strconv.Atoi(match[1]) From f819c517c9e0477bd8c7ab884c57f43f205b580b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CGaurav?= Date: Mon, 26 Oct 2020 16:35:09 -0700 Subject: [PATCH 06/38] Addressed minor review comment --- pkg/splunk/enterprise/finalizers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/splunk/enterprise/finalizers.go b/pkg/splunk/enterprise/finalizers.go index 2c427dccc..1034bb03f 100644 --- a/pkg/splunk/enterprise/finalizers.go +++ b/pkg/splunk/enterprise/finalizers.go @@ -54,7 +54,7 @@ func DeleteSplunkPvc(cr splcommon.MetaObject, c splcommon.ControllerClient) erro return nil } - // get list of PVCs for this cluster + // get list of PVCs associated with this CR labels := map[string]string{ "app.kubernetes.io/instance": fmt.Sprintf("splunk-%s-%s", cr.GetName(), component), } From 7e3cf910409c6e0993881b6dc4584a20ca0a2e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CGaurav?= Date: Tue, 27 Oct 2020 13:12:13 -0700 Subject: [PATCH 07/38] Addressed review comments --- docs/Examples.md | 9 +++++++-- pkg/splunk/enterprise/indexercluster.go | 5 +++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/Examples.md b/docs/Examples.md index 9463b82b3..290cf775f 100644 --- a/docs/Examples.md +++ b/docs/Examples.md @@ -79,8 +79,11 @@ spec: EOF ``` -This will automatically configure a cluster master with a single indexer -peer. +This will automatically configure a cluster with RF(replication_factor) number of indexer peers. + +NOTE: Whenever we try to specify `replicas` on IndexerCluster CR less than RF(as set on ClusterMaster), +the operator will always scale the number of peers to either `replication_factor`(in case of single site indexer cluster) +or to `origin` count in `site_replication_factor`(in case of multi-site indexer cluster). ``` $ kubectl get pods @@ -88,6 +91,8 @@ NAME READY STATUS RESTARTS AGE splunk-cm-cluster-master-0 1/1 Running 0 29s splunk-default-monitoring-console-0 1/1 Running 0 15s splunk-example-indexer-0 1/1 Running 0 29s +splunk-example-indexer-1 1/1 Running 0 29s +splunk-example-indexer-2 1/1 Running 0 29s splunk-operator-7c5599546c-wt4xl 1/1 Running 0 14h ``` Notes: diff --git a/pkg/splunk/enterprise/indexercluster.go b/pkg/splunk/enterprise/indexercluster.go index 37ae42ca6..814d965ac 100644 --- a/pkg/splunk/enterprise/indexercluster.go +++ b/pkg/splunk/enterprise/indexercluster.go @@ -425,7 +425,8 @@ func (mgr *indexerClusterPodManager) getClusterMasterClient() *splclient.SplunkC return mgr.newSplunkClient(fmt.Sprintf("https://%s:8089", fqdnName), "admin", adminPwd) } -func getSiteRepFactor(siteRepFactor string) int32 { +// getSiteRepFactorOriginCount gets the origin count of the site_replication_factor +func getSiteRepFactorOriginCount(siteRepFactor string) int32 { re := regexp.MustCompile(".*origin:(?P.*),.*") match := re.FindStringSubmatch(siteRepFactor) siteRF, _ := strconv.Atoi(match[1]) @@ -446,7 +447,7 @@ func (mgr *indexerClusterPodManager) verifyRFPeers(c splcommon.ControllerClient) var replicationFactor int32 // if it is a multisite indexer cluster, check site_replication_factor if clusterInfo.MultiSite == "true" { - replicationFactor = getSiteRepFactor(clusterInfo.SiteReplicationFactor) + replicationFactor = getSiteRepFactorOriginCount(clusterInfo.SiteReplicationFactor) } else { // for single site, check replication factor replicationFactor = clusterInfo.ReplicationFactor } From 251d01f67bac7362597fb58810ef3f51f2568686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CGaurav?= Date: Thu, 29 Oct 2020 08:02:55 -0700 Subject: [PATCH 08/38] Added Unit test and removed check for speck.Mock in code --- pkg/splunk/enterprise/finalizers_test.go | 1 + pkg/splunk/enterprise/indexercluster.go | 25 +++-- pkg/splunk/enterprise/indexercluster_test.go | 102 ++++++++++++++++--- 3 files changed, 99 insertions(+), 29 deletions(-) diff --git a/pkg/splunk/enterprise/finalizers_test.go b/pkg/splunk/enterprise/finalizers_test.go index 6951859d7..bfd1eec5a 100644 --- a/pkg/splunk/enterprise/finalizers_test.go +++ b/pkg/splunk/enterprise/finalizers_test.go @@ -135,6 +135,7 @@ func splunkDeletionTester(t *testing.T, cr splcommon.MetaObject, delete func(spl {MetaName: "*v1.StatefulSet-test-splunk-test-monitoring-console"}, {MetaName: "*v1.Secret-test-splunk-test-secret"}, {MetaName: "*v1.Secret-test-splunk-test-secret"}, + {MetaName: "*v1beta1.ClusterMaster-test-master1"}, {MetaName: "*v1.Secret-test-splunk-test-secret"}, } } diff --git a/pkg/splunk/enterprise/indexercluster.go b/pkg/splunk/enterprise/indexercluster.go index 814d965ac..d01eb5b23 100644 --- a/pkg/splunk/enterprise/indexercluster.go +++ b/pkg/splunk/enterprise/indexercluster.go @@ -75,9 +75,20 @@ func ApplyIndexerCluster(client splcommon.ControllerClient, cr *enterprisev1.Ind return result, err } + namespacedName := types.NamespacedName{ + Namespace: cr.GetNamespace(), + Name: cr.Spec.ClusterMasterRef.Name, + } + masterIdxCluster := &enterprisev1.ClusterMaster{} + err = client.Get(context.TODO(), namespacedName, masterIdxCluster) + if err == nil { + cr.Status.ClusterMasterPhase = masterIdxCluster.Status.Phase + } else { + cr.Status.ClusterMasterPhase = splcommon.PhaseError + } mgr := indexerClusterPodManager{log: scopedLog, cr: cr, secrets: namespaceScopedSecret, newSplunkClient: splclient.NewSplunkClient} // Check if we have configured enough number(<= RF) of replicas - if !mgr.cr.Spec.Mock { + if mgr.cr.Status.ClusterMasterPhase == splcommon.PhaseReady { err = mgr.verifyRFPeers(client) if err != nil { return result, err @@ -108,18 +119,6 @@ func ApplyIndexerCluster(client splcommon.ControllerClient, cr *enterprisev1.Ind return result, err } - namespacedName := types.NamespacedName{ - Namespace: cr.GetNamespace(), - Name: cr.Spec.ClusterMasterRef.Name, - } - masterIdxCluster := &enterprisev1.ClusterMaster{} - err = client.Get(context.TODO(), namespacedName, masterIdxCluster) - if err == nil { - cr.Status.ClusterMasterPhase = masterIdxCluster.Status.Phase - } else { - cr.Status.ClusterMasterPhase = splcommon.PhaseError - } - // create or update statefulset for the indexers statefulSet, err := getIndexerStatefulSet(client, cr) if err != nil { diff --git a/pkg/splunk/enterprise/indexercluster_test.go b/pkg/splunk/enterprise/indexercluster_test.go index b8547b9fc..497b808a3 100644 --- a/pkg/splunk/enterprise/indexercluster_test.go +++ b/pkg/splunk/enterprise/indexercluster_test.go @@ -37,9 +37,9 @@ func TestApplyIndexerCluster(t *testing.T) { funcCalls := []spltest.MockFuncCall{ {MetaName: "*v1.Secret-test-splunk-test-secret"}, {MetaName: "*v1.Secret-test-splunk-test-secret"}, + {MetaName: "*v1beta1.ClusterMaster-test-master1"}, {MetaName: "*v1.Service-test-splunk-stack1-indexer-headless"}, {MetaName: "*v1.Service-test-splunk-stack1-indexer-service"}, - {MetaName: "*v1beta1.ClusterMaster-test-master1"}, {MetaName: "*v1.Secret-test-splunk-test-secret"}, {MetaName: "*v1.Secret-test-splunk-stack1-indexer-secret-v1"}, {MetaName: "*v1.StatefulSet-test-splunk-stack1-indexer"}, @@ -55,7 +55,7 @@ func TestApplyIndexerCluster(t *testing.T) { } listmockCall := []spltest.MockFuncCall{ {ListOpts: listOpts}} - createCalls := map[string][]spltest.MockFuncCall{"Get": funcCalls, "Create": {funcCalls[0], funcCalls[2], funcCalls[3], funcCalls[6], funcCalls[7]}, "Update": {funcCalls[0]}, "List": {listmockCall[0]}} + createCalls := map[string][]spltest.MockFuncCall{"Get": funcCalls, "Create": {funcCalls[0], funcCalls[3], funcCalls[4], funcCalls[6], funcCalls[7]}, "Update": {funcCalls[0]}, "List": {listmockCall[0]}} updateCalls := map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[2], funcCalls[3], funcCalls[4], funcCalls[5], funcCalls[6], funcCalls[7], funcCalls[8]}, "Update": {funcCalls[7]}, "List": {listmockCall[0]}} current := enterprisev1.IndexerCluster{ @@ -96,11 +96,7 @@ func TestApplyIndexerCluster(t *testing.T) { splunkDeletionTester(t, revised, deleteFunc) } -func indexerClusterPodManagerTester(t *testing.T, method string, mockHandlers []spltest.MockHTTPHandler, - desiredReplicas int32, wantPhase splcommon.Phase, statefulSet *appsv1.StatefulSet, - wantCalls map[string][]spltest.MockFuncCall, wantError error, initObjects ...runtime.Object) { - - // test for updating +func getIndexerClusterPodManagerAndClient(method string, mockHandlers []spltest.MockHTTPHandler, replicas int32) (*indexerClusterPodManager, *spltest.MockHTTPClient) { scopedLog := log.WithName(method) cr := enterprisev1.IndexerCluster{ TypeMeta: metav1.TypeMeta{ @@ -111,7 +107,7 @@ func indexerClusterPodManagerTester(t *testing.T, method string, mockHandlers [] Namespace: "test", }, Spec: enterprisev1.IndexerClusterSpec{ - Replicas: 1, + Replicas: replicas, CommonSplunkSpec: enterprisev1.CommonSplunkSpec{ ClusterMasterRef: corev1.ObjectReference{ Name: "master1", @@ -145,6 +141,81 @@ func indexerClusterPodManagerTester(t *testing.T, method string, mockHandlers [] return c }, } + return mgr, mockSplunkClient +} + +// indexerClusterpodManagerVerifyRFPeersTester is used to verify replicas against RF using a indexerClusterPodManager +func indexerClusterPodManagerVerifyRFPeersTester(t *testing.T, method string, mgr *indexerClusterPodManager, + desiredReplicas int32, wantPhase splcommon.Phase, wantCalls map[string][]spltest.MockFuncCall, wantError error) { + + // initialize client + c := spltest.NewMockClient() + + // test update + err := mgr.verifyRFPeers(c) + if (err == nil && wantError != nil) || + (err != nil && wantError == nil) || + (err != nil && wantError != nil && err.Error() != wantError.Error()) { + t.Errorf("%s returned error %v; want %v", method, err, wantError) + } + + if mgr.cr.Spec.Replicas != desiredReplicas { + t.Errorf("spec has replicas as %d ; want %d", mgr.cr.Spec.Replicas, desiredReplicas) + } + // check calls + c.CheckCalls(t, method, wantCalls) +} + +func indexerClusterPodManagerReplicasTester(t *testing.T, method string, mockHandlers []spltest.MockHTTPHandler, + replicas int32, desiredReplicas int32, wantPhase splcommon.Phase, + wantCalls map[string][]spltest.MockFuncCall, wantError error) { + + mgr, mockSplunkClient := getIndexerClusterPodManagerAndClient(method, mockHandlers, replicas) + indexerClusterPodManagerVerifyRFPeersTester(t, method, mgr, desiredReplicas, wantPhase, wantCalls, wantError) + mockSplunkClient.CheckRequests(t, method) +} + +func TestVerifyRFPeers(t *testing.T) { + + funcCalls := []spltest.MockFuncCall{ + {MetaName: "*v1.Pod-test-splunk-master1-cluster-master-0"}, + } + + wantCalls := map[string][]spltest.MockFuncCall{"Get": {funcCalls[0]}} + + // test 1 ready pod + mockHandlers := []spltest.MockHTTPHandler{ + { + Method: "GET", + URL: "https://splunk-master1-cluster-master-service.test.svc.cluster.local:8089/services/cluster/config?count=0&output_mode=json", + Status: 200, + Err: nil, + Body: `{"links":{"_reload":"/services/cluster/config/_reload","_acl":"/services/cluster/config/_acl"},"origin":"https://localhost:8089/services/cluster/config","updated":"2020-10-28T21:37:07+00:00","generator":{"build":"152fb4b2bb96","version":"8.0.6"},"entry":[{"name":"config","id":"https://localhost:8089/services/cluster/config/config","updated":"1970-01-01T00:00:00+00:00","links":{"alternate":"/services/cluster/config/config","list":"/services/cluster/config/config","_reload":"/services/cluster/config/config/_reload","edit":"/services/cluster/config/config","disable":"/services/cluster/config/config/disable"},"author":"system","acl":{"app":"","can_list":true,"can_write":true,"modifiable":false,"owner":"system","perms":{"read":["admin","splunk-system-role"],"write":["admin","splunk-system-role"]},"removable":false,"sharing":"system"},"content":{"access_logging_for_heartbeats":false,"auto_rebalance_primaries":true,"buckets_to_summarize":"primaries","cluster_label":"idxc_label","cxn_timeout":60,"decommission_force_finish_idle_time":0,"decommission_force_timeout":180,"disabled":false,"eai:acl":null,"forwarderdata_rcv_port":0,"forwarderdata_use_ssl":false,"frozen_notifications_per_batch":10,"guid":"F643BA71-0D3C-4D63-A0BC-A1604AC928E3","heartbeat_period":18446744073709552000,"heartbeat_timeout":60,"master_uri":"https://127.0.0.1:8089","max_auto_service_interval":30,"max_fixup_time_ms":5000,"max_peer_build_load":2,"max_peer_rep_load":5,"max_peer_sum_rep_load":5,"max_peers_to_download_bundle":5,"max_primary_backups_per_service":10,"mode":"master","multisite":"false","notify_buckets_period":10,"notify_scan_min_period":10,"notify_scan_period":10,"percent_peers_to_restart":10,"ping_flag":true,"quiet_period":60,"rcv_timeout":60,"rebalance_primaries_execution_limit_ms":0,"rebalance_threshold":0.9,"register_forwarder_address":"","register_replication_address":"","register_search_address":"","remote_storage_upload_timeout":60,"rep_cxn_timeout":60,"rep_max_rcv_timeout":180,"rep_max_send_timeout":180,"rep_rcv_timeout":60,"rep_send_timeout":60,"replication_factor":3,"replication_port":null,"replication_use_ssl":false,"report_remote_storage_bucket_upload_to_targets":false,"reporting_delay_period":30,"restart_inactivity_timeout":600,"restart_timeout":60,"rolling_restart":"restart","search_factor":3,"search_files_retry_timeout":600,"secret":"********","send_timeout":60,"service_interval":0,"site":"default","site_by_site":true,"summary_replication":"false","use_batch_discard":"true","use_batch_mask_changes":"true","use_batch_remote_rep_changes":"false"}}],"paging":{"total":1,"perPage":10000000,"offset":0},"messages":[]}`, + }, + } + + method := "indexerClusterPodManager.verifyRFPeers(All pods ready)" + // test for singlesite i.e. with replication_factor=3(on ClusterMaster) and replicas=3(on IndexerCluster) + indexerClusterPodManagerReplicasTester(t, method, mockHandlers, 3 /*replicas*/, 3 /*desired replicas*/, splcommon.PhaseReady, wantCalls, nil) + + // test for singlesite i.e. with replication_factor=3(on ClusterMaster) and replicas=1(on IndexerCluster) + indexerClusterPodManagerReplicasTester(t, method, mockHandlers, 1 /*replicas*/, 3 /*desired replicas*/, splcommon.PhaseReady, wantCalls, nil) + + // Now test for multi-site too + mockHandlers[0].Body = `{"links":{"_reload":"/services/cluster/config/_reload","_acl":"/services/cluster/config/_acl"},"origin":"https://localhost:8089/services/cluster/config","updated":"2020-10-28T21:37:07+00:00","generator":{"build":"152fb4b2bb96","version":"8.0.6"},"entry":[{"name":"config","id":"https://localhost:8089/services/cluster/config/config","updated":"1970-01-01T00:00:00+00:00","links":{"alternate":"/services/cluster/config/config","list":"/services/cluster/config/config","_reload":"/services/cluster/config/config/_reload","edit":"/services/cluster/config/config","disable":"/services/cluster/config/config/disable"},"author":"system","acl":{"app":"","can_list":true,"can_write":true,"modifiable":false,"owner":"system","perms":{"read":["admin","splunk-system-role"],"write":["admin","splunk-system-role"]},"removable":false,"sharing":"system"},"content":{"access_logging_for_heartbeats":false,"auto_rebalance_primaries":true,"buckets_to_summarize":"primaries","cluster_label":"idxc_label","cxn_timeout":60,"decommission_force_finish_idle_time":0,"decommission_force_timeout":180,"disabled":false,"eai:acl":null,"forwarderdata_rcv_port":0,"forwarderdata_use_ssl":false,"frozen_notifications_per_batch":10,"guid":"F643BA71-0D3C-4D63-A0BC-A1604AC928E3","heartbeat_period":18446744073709552000,"heartbeat_timeout":60,"master_uri":"https://127.0.0.1:8089","max_auto_service_interval":30,"max_fixup_time_ms":5000,"max_peer_build_load":2,"max_peer_rep_load":5,"max_peer_sum_rep_load":5,"max_peers_to_download_bundle":5,"max_primary_backups_per_service":10,"mode":"master","multisite":"true","notify_buckets_period":10,"notify_scan_min_period":10,"notify_scan_period":10,"percent_peers_to_restart":10,"ping_flag":true,"quiet_period":60,"rcv_timeout":60,"rebalance_primaries_execution_limit_ms":0,"rebalance_threshold":0.9,"register_forwarder_address":"","register_replication_address":"","register_search_address":"","remote_storage_upload_timeout":60,"rep_cxn_timeout":60,"rep_max_rcv_timeout":180,"rep_max_send_timeout":180,"rep_rcv_timeout":60,"rep_send_timeout":60,"replication_factor":3,"replication_port":null,"replication_use_ssl":false,"report_remote_storage_bucket_upload_to_targets":false,"reporting_delay_period":30,"restart_inactivity_timeout":600,"restart_timeout":60,"rolling_restart":"restart","search_factor":3,"search_files_retry_timeout":600,"secret":"********","send_timeout":60,"service_interval":0,"site":"site1","site_by_site":true,"site_replication_factor":"{ origin:2, total:2 }","site_search_factor":"{ origin:2, total:2 }","summary_replication":"false","use_batch_discard":"true","use_batch_mask_changes":"true","use_batch_remote_rep_changes":"false"}}],"paging":{"total":1,"perPage":10000000,"offset":0},"messages":[]}` + + //test for multisite i.e. with site_replication_factor=origin:2,total:2(on ClusterMaster) and replicas=2(on IndexerCluster) + indexerClusterPodManagerReplicasTester(t, method, mockHandlers, 2 /*replicas*/, 2 /*desired replicas*/, splcommon.PhaseReady, wantCalls, nil) + + //test for multisite i.e. with site_replication_factor=origin:2,total:2(on ClusterMaster) and replicas=1(on IndexerCluster) + indexerClusterPodManagerReplicasTester(t, method, mockHandlers, 1 /*replicas*/, 2 /*desired replicas*/, splcommon.PhaseReady, wantCalls, nil) +} + +func indexerClusterPodManagerUpdateTester(t *testing.T, method string, mockHandlers []spltest.MockHTTPHandler, + desiredReplicas int32, wantPhase splcommon.Phase, statefulSet *appsv1.StatefulSet, + wantCalls map[string][]spltest.MockFuncCall, wantError error, initObjects ...runtime.Object) { + // get indexerClusterPodManager and mockSplunkClient instances + mgr, mockSplunkClient := getIndexerClusterPodManagerAndClient(method, mockHandlers, 1) spltest.PodManagerUpdateTester(t, method, mgr, desiredReplicas, wantPhase, statefulSet, wantCalls, wantError, initObjects...) mockSplunkClient.CheckRequests(t, method) } @@ -223,7 +294,7 @@ func TestIndexerClusterPodManager(t *testing.T) { }, } method := "indexerClusterPodManager.Update(All pods ready)" - indexerClusterPodManagerTester(t, method, mockHandlers, 1, splcommon.PhaseReady, statefulSet, wantCalls, nil, statefulSet, pod) + indexerClusterPodManagerUpdateTester(t, method, mockHandlers, 1, splcommon.PhaseReady, statefulSet, wantCalls, nil, statefulSet, pod) // test pod needs update => decommission mockHandlers = append(mockHandlers, spltest.MockHTTPHandler{ @@ -236,26 +307,26 @@ func TestIndexerClusterPodManager(t *testing.T) { pod.ObjectMeta.Labels["controller-revision-hash"] = "v0" method = "indexerClusterPodManager.Update(Decommission Pod)" wantDecomPodCalls := map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[3], funcCalls[4], funcCalls[2]}, "Create": {funcCalls[1]}} - indexerClusterPodManagerTester(t, method, mockHandlers, 1, splcommon.PhaseUpdating, statefulSet, wantDecomPodCalls, nil, statefulSet, pod) + indexerClusterPodManagerUpdateTester(t, method, mockHandlers, 1, splcommon.PhaseUpdating, statefulSet, wantDecomPodCalls, nil, statefulSet, pod) // test pod needs update => wait for decommission to complete mockHandlers = []spltest.MockHTTPHandler{mockHandlers[0], mockHandlers[1]} mockHandlers[1].Body = strings.Replace(mockHandlers[1].Body, `"status":"Up"`, `"status":"ReassigningPrimaries"`, 1) method = "indexerClusterPodManager.Update(ReassigningPrimaries)" wantReasCalls := map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[3], funcCalls[4]}, "Create": {funcCalls[1]}} - indexerClusterPodManagerTester(t, method, mockHandlers, 1, splcommon.PhaseUpdating, statefulSet, wantReasCalls, nil, statefulSet, pod) + indexerClusterPodManagerUpdateTester(t, method, mockHandlers, 1, splcommon.PhaseUpdating, statefulSet, wantReasCalls, nil, statefulSet, pod) // test pod needs update => wait for decommission to complete mockHandlers[1].Body = strings.Replace(mockHandlers[1].Body, `"status":"ReassigningPrimaries"`, `"status":"Decommissioning"`, 1) method = "indexerClusterPodManager.Update(Decommissioning)" wantDecomCalls := map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[3], funcCalls[4]}, "Create": {funcCalls[1]}} - indexerClusterPodManagerTester(t, method, mockHandlers, 1, splcommon.PhaseUpdating, statefulSet, wantDecomCalls, nil, statefulSet, pod) + indexerClusterPodManagerUpdateTester(t, method, mockHandlers, 1, splcommon.PhaseUpdating, statefulSet, wantDecomCalls, nil, statefulSet, pod) // test pod needs update => delete pod wantCalls = map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[3], funcCalls[4]}, "Create": {funcCalls[1]}, "Delete": {funcCalls[4]}} mockHandlers[1].Body = strings.Replace(mockHandlers[1].Body, `"status":"Decommissioning"`, `"status":"Down"`, 1) method = "indexerClusterPodManager.Update(Delete Pod)" - indexerClusterPodManagerTester(t, method, mockHandlers, 1, splcommon.PhaseUpdating, statefulSet, wantCalls, nil, statefulSet, pod) + indexerClusterPodManagerUpdateTester(t, method, mockHandlers, 1, splcommon.PhaseUpdating, statefulSet, wantCalls, nil, statefulSet, pod) // test scale down => pod not found pod.ObjectMeta.Name = "splunk-stack1-2" @@ -265,7 +336,7 @@ func TestIndexerClusterPodManager(t *testing.T) { statefulSet.Status.UpdatedReplicas = 2 wantCalls = map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[3]}, "Create": {funcCalls[1]}} method = "indexerClusterPodManager.Update(Pod Not Found)" - indexerClusterPodManagerTester(t, method, mockHandlers, 1, splcommon.PhaseScalingDown, statefulSet, wantCalls, nil, statefulSet, pod) + indexerClusterPodManagerUpdateTester(t, method, mockHandlers, 1, splcommon.PhaseScalingDown, statefulSet, wantCalls, nil, statefulSet, pod) // test scale down => decommission pod mockHandlers[1].Body = `{"entry":[{"name":"aa45bf46-7f46-47af-a760-590d5c606d10","content":{"status":"Up","label":"splunk-stack1-indexer-0"}},{"name":"D39B1729-E2C5-4273-B9B2-534DA7C2F866","content":{"status":"GracefulShutdown","label":"splunk-stack1-indexer-1"}}]}` @@ -280,7 +351,6 @@ func TestIndexerClusterPodManager(t *testing.T) { {MetaName: "*v1.PersistentVolumeClaim-test-pvc-etc-splunk-stack1-1"}, {MetaName: "*v1.PersistentVolumeClaim-test-pvc-var-splunk-stack1-1"}, } - //funcCalls[1] = spltest.MockFuncCall{MetaName: "*v1.Pod-test-splunk-stack1-0"} wantCalls = map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[3], funcCalls[3]}, "Create": {funcCalls[1]}, "Delete": pvcCalls, "Update": {funcCalls[0]}} wantCalls["Get"] = append(wantCalls["Get"], pvcCalls...) pvcList := []*corev1.PersistentVolumeClaim{ @@ -288,7 +358,7 @@ func TestIndexerClusterPodManager(t *testing.T) { {ObjectMeta: metav1.ObjectMeta{Name: "pvc-var-splunk-stack1-1", Namespace: "test"}}, } method = "indexerClusterPodManager.Update(Decommission)" - indexerClusterPodManagerTester(t, method, mockHandlers, 1, splcommon.PhaseScalingDown, statefulSet, wantCalls, nil, statefulSet, pod, pvcList[0], pvcList[1]) + indexerClusterPodManagerUpdateTester(t, method, mockHandlers, 1, splcommon.PhaseScalingDown, statefulSet, wantCalls, nil, statefulSet, pod, pvcList[0], pvcList[1]) } func TestSetClusterMaintenanceMode(t *testing.T) { From c6bf6289462746aa479e5c3df96557db6644da99 Mon Sep 17 00:00:00 2001 From: akondur Date: Fri, 30 Oct 2020 13:30:18 -0700 Subject: [PATCH 09/38] Update readme.md for platforms tested by operator --- docs/README.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/docs/README.md b/docs/README.md index 547d63f0c..4a3b331a0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -45,20 +45,11 @@ previous releases. ## Prerequisites for the Splunk Operator -We have tested basic functionality of the Splunk Operator with the following: - -* [Amazon Elastic Kubernetes Service](https://aws.amazon.com/eks/) (EKS) -* [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/) (GKE) -* [Azure Kubernetes Service](https://azure.microsoft.com/en-us/services/kubernetes-service/) (AKS) -* [Red Hat OpenShift](https://www.openshift.com/) (4.1) -* [Docker Enterprise Edition](https://docs.docker.com/ee/) (3.1) -* [Open Source Kubernetes](https://kubernetes.io/) (1.15.1) - While we are only able to test and support a small subset of configurations, the Splunk Operator should work with any CNCF certified distribution of Kubernetes, version 1.12 or later. Setting up, configuring and managing Kubernetes clusters is outside the scope of this guide and Splunk’s coverage -of support. For evaluation, we recommend using EKS or GKE. +of support. Please submit bugs to https://github.com/splunk/splunk-operator/issues. *Kubernetes releases 1.16.0 and 1.16.1 contain a [critical bug(https://github.com/kubernetes/kubernetes/pull/83789) that can From f43189b8f01697607cdd7e28ed35addc66716db5 Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Fri, 30 Oct 2020 14:18:30 -0700 Subject: [PATCH 10/38] minor typo correction --- pkg/splunk/enterprise/indexercluster.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/splunk/enterprise/indexercluster.go b/pkg/splunk/enterprise/indexercluster.go index d01eb5b23..083e011f2 100644 --- a/pkg/splunk/enterprise/indexercluster.go +++ b/pkg/splunk/enterprise/indexercluster.go @@ -433,7 +433,7 @@ func getSiteRepFactorOriginCount(siteRepFactor string) int32 { } // verifyRFPeers verifies the number of peers specified in the replicas section -// of IndexerClsuster CR. If it is less than RF, than we se it to RF. +// of IndexerClsuster CR. If it is less than RF, than we set it to RF. func (mgr *indexerClusterPodManager) verifyRFPeers(c splcommon.ControllerClient) error { if mgr.c == nil { mgr.c = c From 4a14f062346eeef01f771c992d31ba918874e585 Mon Sep 17 00:00:00 2001 From: Jeff Rybczynski Date: Thu, 1 Oct 2020 13:43:35 -0700 Subject: [PATCH 11/38] CSPL-441 Create utilities to run search on a pod deployed using splunk operator Add test utils for running searchs Add test utils for creating and ingesting log files Add test suite that ingests a log and searches it --- .../ingest_search/ingest_search_suite_test.go | 47 ++++ test/ingest_search/ingest_search_test.go | 236 ++++++++++++++++++ test/testenv/ingest_utils.go | 207 +++++++++++++++ test/testenv/search_utils.go | 146 +++++++++++ 4 files changed, 636 insertions(+) create mode 100644 test/ingest_search/ingest_search_suite_test.go create mode 100644 test/ingest_search/ingest_search_test.go create mode 100644 test/testenv/ingest_utils.go create mode 100644 test/testenv/search_utils.go diff --git a/test/ingest_search/ingest_search_suite_test.go b/test/ingest_search/ingest_search_suite_test.go new file mode 100644 index 000000000..ff71d88e6 --- /dev/null +++ b/test/ingest_search/ingest_search_suite_test.go @@ -0,0 +1,47 @@ +package ingestsearchtest + +import ( + "testing" + "time" + + . "github.com/onsi/ginkgo" + "github.com/onsi/ginkgo/reporters" + . "github.com/onsi/gomega" + + "github.com/splunk/splunk-operator/test/testenv" +) + +const ( + // PollInterval specifies the polling interval + PollInterval = 5 * time.Second + + // ConsistentPollInterval is the interval to use to consistently check a state is stable + ConsistentPollInterval = 200 * time.Millisecond + ConsistentDuration = 2000 * time.Millisecond +) + +var ( + testenvInstance *testenv.TestEnv + testSuiteName = "mc-" + testenv.RandomDNSName(2) +) + +// TestBasic is the main entry point +func TestBasic(t *testing.T) { + + RegisterFailHandler(Fail) + + junitReporter := reporters.NewJUnitReporter(testSuiteName + "_junit.xml") + RunSpecsWithDefaultAndCustomReporters(t, "Running "+testSuiteName, []Reporter{junitReporter}) +} + +var _ = BeforeSuite(func() { + var err error + testenvInstance, err = testenv.NewDefaultTestEnv(testSuiteName) + Expect(err).ToNot(HaveOccurred()) +}) + +var _ = AfterSuite(func() { + if testenvInstance != nil { + Expect(testenvInstance.Teardown()).ToNot(HaveOccurred()) + } +}) diff --git a/test/ingest_search/ingest_search_test.go b/test/ingest_search/ingest_search_test.go new file mode 100644 index 000000000..ae1492372 --- /dev/null +++ b/test/ingest_search/ingest_search_test.go @@ -0,0 +1,236 @@ +package ingestsearchtest + +import ( + "bufio" + "encoding/json" + "fmt" + "io" + "os" + "strings" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + splcommon "github.com/splunk/splunk-operator/pkg/splunk/common" + "github.com/splunk/splunk-operator/test/testenv" +) + +var _ = Describe("Ingest and Search Test", func() { + + var deployment *testenv.Deployment + var firstLine string + + BeforeEach(func() { + var err error + deployment, err = testenvInstance.NewDeployment(testenv.RandomDNSName(3)) + Expect(err).To(Succeed(), "Unable to create deployment") + }) + + AfterEach(func() { + // When a test spec failed, skip the teardown so we can troubleshoot. + if CurrentGinkgoTestDescription().Failed { + testenvInstance.SkipTeardown = true + } + if deployment != nil { + deployment.Teardown() + } + }) + + Context("Standalone deployment (S1)", func() { + It("can search internal logs for standalone instance", func() { + + standalone, err := deployment.DeployStandalone(deployment.GetName()) + Expect(err).To(Succeed(), "Unable to deploy standalone instance ") + + // Wait for standalone to be in READY Status + testenv.StandaloneReady(deployment, deployment.GetName(), standalone, testenvInstance) + + Eventually(func() splcommon.Phase { + podName := fmt.Sprintf("splunk-%s-standalone-0", deployment.GetName()) + + searchString := "index=_internal | stats count by host" + searchResultsResp, err := testenv.PerformSearchSync(podName, searchString, deployment) + if err != nil { + testenvInstance.Log.Error(err, "Failed to execute search on pod", "pod", podName, "searchString", searchString) + return splcommon.PhaseError + } + testenvInstance.Log.Info("Performed a search", "searchString", searchString) + + var searchResults map[string]interface{} + unmarshalErr := json.Unmarshal([]byte(searchResultsResp), &searchResults) + if unmarshalErr != nil { + testenvInstance.Log.Error(unmarshalErr, "Failed to unmarshal JSON response") + } + + prettyResults, jsonErr := json.MarshalIndent(searchResults, "", " ") + if jsonErr != nil { + testenvInstance.Log.Error(jsonErr, "Failed to generate pretty json") + } else { + testenvInstance.Log.Info("Sync Search results:", "prettyResults", string(prettyResults)) + } + + return standalone.Status.Phase + }, deployment.GetTimeout(), PollInterval).Should(Equal(splcommon.PhaseReady)) + + Eventually(func() splcommon.Phase { + podName := fmt.Sprintf("splunk-%s-standalone-0", deployment.GetName()) + searchString := "index=_internal GUID component=ServerConfig" + + // Perform a simple search + sid, reqErr := testenv.PerformSearchReq(podName, searchString, deployment) + if reqErr != nil { + testenvInstance.Log.Error(reqErr, "Failed to execute search on pod", "pod", podName, "searchString", searchString) + return splcommon.PhaseError + } + testenvInstance.Log.Info("Got a search with sid", "sid", sid) + + // Check SID status until done + searchStatusResult, statusErr := testenv.GetSearchStatus(podName, sid, deployment) + if statusErr != nil { + testenvInstance.Log.Error(statusErr, "Failed to get search status on pod", "pod", podName, "sid", sid) + return splcommon.PhaseError + } + testenvInstance.Log.Info("Search status:", "searchStatusResult", searchStatusResult) + + // Get SID results + searchResultsResp, resErr := testenv.GetSearchResults(podName, sid, deployment) + if resErr != nil { + testenvInstance.Log.Error(resErr, "Failed to get search results on pod", "pod", podName, "sid", sid) + return splcommon.PhaseError + } + + // Display results for debug purposes + prettyResults, jsonErr := json.MarshalIndent(searchResultsResp, "", " ") + if jsonErr != nil { + testenvInstance.Log.Error(jsonErr, "Failed to generate pretty json") + } else { + testenvInstance.Log.Info("Search results:", "prettyResults", string(prettyResults)) + } + + return standalone.Status.Phase + }, deployment.GetTimeout(), PollInterval).Should(Equal(splcommon.PhaseReady)) + }) + }) + + Context("Standalone deployment (S1)", func() { + It("can ingest custom data to new index and search", func() { + + standalone, err := deployment.DeployStandalone(deployment.GetName()) + Expect(err).To(Succeed(), "Unable to deploy standalone instance ") + + // Wait for standalone to be in READY Status + testenv.StandaloneReady(deployment, deployment.GetName(), standalone, testenvInstance) + + // Verify splunk status is up + Eventually(func() splcommon.Phase { + podName := fmt.Sprintf("splunk-%s-standalone-0", deployment.GetName()) + + splunkBin := "/opt/splunk/bin/splunk" + username := "admin" + password := "$(cat /mnt/splunk-secrets/password)" + splunkCmd := "status" + + statusCmd := fmt.Sprintf("%s %s -auth %s:%s", splunkBin, splunkCmd, username, password) + command := []string{"/bin/bash"} + statusCmdResp, stderr, err := deployment.PodExecCommand(podName, command, statusCmd, false) + if err != nil { + testenvInstance.Log.Error(err, "Failed to execute command on pod", "pod", podName, "statusCmd", statusCmd, "statusCmdResp", statusCmdResp, "stderr", stderr) + return splcommon.PhaseError + } + + if !strings.Contains(strings.ToLower(statusCmdResp), strings.ToLower("splunkd is running")) { + testenvInstance.Log.Error(err, "Failed to find splunkd running", "pod", podName, "statusCmdResp", statusCmdResp) + return splcommon.PhaseError + } + + testenvInstance.Log.Info("Waiting for standalone splunkd status to be ready", "instance", standalone.ObjectMeta.Name, "Phase", standalone.Status.Phase) + return standalone.Status.Phase + }, deployment.GetTimeout(), PollInterval).Should(Equal(splcommon.PhaseReady)) + + // Create an index + podName := fmt.Sprintf("splunk-%s-standalone-0", deployment.GetName()) + indexName := "myTestIndex" + + // Create an index on a standalone instance + err = testenv.CreateAnIndexStandalone(indexName, podName, deployment) + Expect(err).To(Succeed(), "Failed response to add index to splunk") + + // Create a mock logfile to ingest + logFile := "/tmp/test.log" + err = testenv.CreateMockLogfile(logFile, 1) + Expect(err).To(Succeed(), "Failed response to add index to splunk logfile %s", logFile) + + // Copy log file and ingest it + err = testenv.IngestFileViaOneshot(logFile, indexName, podName, deployment) + Expect(err).To(Succeed(), "Failed to ingest logfile %s on pod %s", logFile, podName) + + // Read first line to find a search token + var file, openErr = os.Open(logFile) + Expect(openErr).To(Succeed(), "Failed to open newly created logfile %s on pod %s", logFile, podName) + + reader := bufio.NewReader(file) + var readErr error + firstLine, readErr = reader.ReadString('\n') + Expect(readErr).Should(Or(BeNil(), Equal(io.EOF)), "Failed to read first line of logfile %s on pod ", logFile, podName) + + tokens := strings.Fields(firstLine) + Expect(len(tokens)).To(BeNumerically(">=", 2), "Incorrect tokens (%s) in first logline %s for logfile %s", tokens, firstLine, logFile) + + searchToken := tokens[len(tokens)-1] + testenvInstance.Log.Info("Got search token successfully", "logFile", logFile, "searchToken", searchToken) + + searchString := fmt.Sprintf("index=%s | stats count by host", indexName) + + // Wait for ingestion lag prior to searching + time.Sleep(2 * time.Second) + searchResultsResp, err := testenv.PerformSearchSync(podName, searchString, deployment) + Expect(err).To(Succeed(), "Failed to execute search '%s' on pod %s", podName, searchString) + + // Verify result. Should get count 1. result:{count:1} + var searchResults map[string]interface{} + jsonErr := json.Unmarshal([]byte(searchResultsResp), &searchResults) + Expect(jsonErr).To(Succeed(), "Failed to unmarshal JSON Search Results from response '%s'", searchResultsResp) + + testenvInstance.Log.Info("Search results :", "searchResults", searchResults["result"]) + Expect(searchResults["result"]).ShouldNot(BeNil(), "No results in search response '%s' on pod %s", searchResults, podName) + + hostCount := searchResults["result"].(map[string]interface{}) + testenvInstance.Log.Info("Sync Search results host count:", "count", hostCount["count"].(string), "host", hostCount["host"].(string)) + testHostCnt := strings.Compare(hostCount["count"].(string), "1") + testHostname := strings.Compare(hostCount["host"].(string), podName) + Expect(testHostCnt).To(Equal(0), "Incorrect search results for count. Expect: 1 Got: %d", hostCount["count"].(string)) + Expect(testHostname).To(Equal(0), "Incorrect search result hostname. Expect: %s Got: %s", podName, hostCount["host"].(string)) + + searchString2 := fmt.Sprintf("index=%s %s", indexName, searchToken) + sid, reqErr := testenv.PerformSearchReq(podName, searchString2, deployment) + Expect(reqErr).To(Succeed(), "Failed to execute search '%s' on pod %s", searchString, podName) + testenvInstance.Log.Info("Got a search with sid", "sid", sid) + + // Check SID status until done + searchStatusResult, statusErr := testenv.GetSearchStatus(podName, sid, deployment) + Expect(statusErr).To(Succeed(), "Failed to get search status on pod %s for sid %s", podName, sid) + testenvInstance.Log.Info("Search status:", "searchStatusResult", searchStatusResult) + + // Get SID results + searchResultsResp, resErr := testenv.GetSearchResults(podName, sid, deployment) + Expect(resErr).To(Succeed(), "Failed to get search results on pod %s for sid %s", podName, sid) + + testenvInstance.Log.Info("Raw Search results:", "searchResultsResp", searchResultsResp) + var searchResults2 testenv.SearchJobResultsResponse + jsonErr = json.Unmarshal([]byte(searchResultsResp), &searchResults2) + Expect(jsonErr).To(Succeed(), "Failed to unmarshal JSON Search Results from response '%s'", searchResultsResp) + + found := false + for key, elem := range searchResults2.Results { + testenvInstance.Log.Info("Search results _raw and host:", "_raw", elem.Raw, "host", elem.SplunkServer, "firstLine", firstLine) + trimFirstLine := strings.TrimSuffix(firstLine, "\n") + if strings.Compare(elem.Raw, trimFirstLine) == 0 { + testenvInstance.Log.Info("Found search results in _raw and splunk_server", "key", key, "podName", podName, "elem", elem) + found = true + } + } + Expect(found).To(Equal(true), "Incorrect search results %s", searchResults) + }) + }) +}) diff --git a/test/testenv/ingest_utils.go b/test/testenv/ingest_utils.go new file mode 100644 index 000000000..5d7227b08 --- /dev/null +++ b/test/testenv/ingest_utils.go @@ -0,0 +1,207 @@ +package testenv + +import ( + "bytes" + "errors" + "fmt" + "io" + "math/rand" + "net/http" + "os" + "path" + "strings" + "time" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/remotecommand" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + "sigs.k8s.io/controller-runtime/pkg/client/config" + logf "sigs.k8s.io/controller-runtime/pkg/log" + // Used to move files between pods + _ "k8s.io/kubernetes/pkg/kubectl/cmd/cp" + _ "unsafe" +) + +// CreateMockLogfile creates a mock logfile with n entries to be ingested. +// - Each logline has a random number that can be searched after ingest. +// - Timestamps for the logs are set to a line a second ending with the current time +func CreateMockLogfile(logFile string, totalLines int) error { + // Create data log + var file, err = os.Create(logFile) + if err != nil { + logf.Log.Error(err, "Failed File Created", "logFile", logFile) + return err + } + logf.Log.Info("File Created Successfully", "logFile", logFile) + + // Write some text line-by-line to file. + var logLine strings.Builder + level := "DEBUG" + component := "GenericComponent" + msg := "This log line is special!" + timestamp := time.Now() + + // Simulate a log every second. This could be adjusted however for this simple case its probably sufficient + timestamp = timestamp.Add(time.Second * time.Duration(-totalLines)) + rand.Seed(time.Now().UnixNano()) + + // Write each line to the file + for i := 0; i < totalLines; i++ { + fmt.Fprintf(&logLine, "%s %s %s %s randomNumber=%d\n", timestamp.Format("01-02-2006 15:04:05.000"), level, component, msg, rand.Int63()) + _, err = file.WriteString(logLine.String()) + if err != nil { + logf.Log.Error(err, "Failed File Write", "logFile", logFile, "logLine", logLine.String()) + return err + } + timestamp = timestamp.Add(time.Second) + } + + // Save logFile + err = file.Sync() + if err != nil { + logf.Log.Error(err, "Failed File Save", "logFile", logFile, "logLine", logLine) + return err + } + logf.Log.Info("File Updated Successfully", "logFile", logFile) + + return nil +} + +// CreateAnIndexStandalone creates an index on a standalone instance using the CLI +func CreateAnIndexStandalone(indexName string, podName string, deployment *Deployment) error { + + var addIndexCmd strings.Builder + splunkBin := "/opt/splunk/bin/splunk" + username := "admin" + password := "$(cat /mnt/splunk-secrets/password) " + splunkCmd := "add index" + + fmt.Fprintf(&addIndexCmd, "%s %s %s -auth %s:%s", splunkBin, splunkCmd, indexName, username, password) + command := []string{"/bin/bash"} + stdin := addIndexCmd.String() + addIndexResp, stderr, err := deployment.PodExecCommand(podName, command, stdin, false) + if err != nil { + logf.Log.Error(err, "Failed to execute command on pod", "pod", podName, "stdin", stdin, "addIndexResp", addIndexResp, "stderr", stderr) + return err + } + + // Validate the response of the CLI command + var expectedResp strings.Builder + fmt.Fprintf(&expectedResp, "Index \"%s\" added.", indexName) + if strings.Compare(addIndexResp, expectedResp.String()) == 0 { + logf.Log.Error(err, "Failed response to add index to splunk", "pod", podName, "addIndexResp", addIndexResp) + return errors.New("Failed response to add index to splunk") + } + + logf.Log.Info("Added index to Splunk", "podName", podName, "addIndexResp", addIndexResp) + return nil +} + +// IngestFileViaOneshot ingests a file into an instance using the oneshot CLI +func IngestFileViaOneshot(logFile string, indexName string, podName string, deployment *Deployment) error { + + // Send it to the instance + resp, stderr, cpErr := CopyFileToPod(podName, logFile, logFile, deployment) + if cpErr != nil { + logf.Log.Error(cpErr, "Failed File Copy to pod", "logFile", logFile, "podName", podName, "stderr", stderr) + return cpErr + } + logf.Log.Info("File Copied Successfully", "logFile", logFile, "resp", resp) + + // oneshot log into specified index + var addOneshotCmd strings.Builder + splunkBin := "/opt/splunk/bin/splunk" + username := "admin" + password := "$(cat /mnt/splunk-secrets/password) " + splunkCmd := "add oneshot" + + fmt.Fprintf(&addOneshotCmd, "%s %s %s -index %s -auth %s:%s", splunkBin, splunkCmd, logFile, indexName, username, password) + command := []string{"/bin/bash"} + stdin := addOneshotCmd.String() + addOneshotResp, stderr, err := deployment.PodExecCommand(podName, command, stdin, false) + if err != nil { + logf.Log.Error(err, "Failed to execute command on pod", "pod", podName, "stdin", stdin, "addOneshotResp", addOneshotResp, "stderr", stderr) + return err + } + + // Validate the expected CLI response + var expectedResp strings.Builder + fmt.Fprintf(&expectedResp, "Oneshot '%s' added", indexName) + if strings.Compare(addOneshotResp, expectedResp.String()) == 0 { + logf.Log.Error(err, "Failed response to add oneshot to splunk", "pod", podName, "addOneshotResp", addOneshotResp) + return err + } + logf.Log.Info("File Ingested via add oneshot Successfully", "logFile", logFile, "addOneshotResp", addOneshotResp) + return nil +} + +// CopyFileToPod copies a file locally from srcPath to the destPath on the pod specified in podName +func CopyFileToPod(podName string, srcPath string, destPath string, deployment *Deployment) (string, string, error) { + // Create tar file stream + reader, writer := io.Pipe() + if destPath != "/" && strings.HasSuffix(string(destPath[len(destPath)-1]), "/") { + destPath = destPath[:len(destPath)-1] + } + go func() { + defer writer.Close() + err := cpMakeTar(srcPath, destPath, writer) + if err != nil { + return + } + }() + var cmdArr []string + + cmdArr = []string{"tar", "-xf", "-"} + destDir := path.Dir(destPath) + if len(destDir) > 0 { + cmdArr = append(cmdArr, "-C", destDir) + } + + // Setup exec command for pod + pod := &corev1.Pod{} + deployment.GetInstance(podName, pod) + gvk, _ := apiutil.GVKForObject(pod, scheme.Scheme) + restConfig, err := config.GetConfig() + if err != nil { + return "", "", err + } + restClient, err := apiutil.RESTClientForGVK(gvk, restConfig, serializer.NewCodecFactory(scheme.Scheme)) + if err != nil { + return "", "", err + } + + execReq := restClient.Post().Resource("pods").Name(podName).Namespace(deployment.testenv.namespace).SubResource("exec") + option := &corev1.PodExecOptions{ + Command: cmdArr, + Stdin: true, + Stdout: true, + Stderr: true, + TTY: false, + } + + execReq.VersionedParams( + option, + scheme.ParameterCodec, + ) + exec, err := remotecommand.NewSPDYExecutor(restConfig, http.MethodPost, execReq.URL()) + if err != nil { + return "", "", err + } + + stdout := new(bytes.Buffer) + stderr := new(bytes.Buffer) + err = exec.Stream(remotecommand.StreamOptions{ + Stdin: reader, + Stdout: stdout, + Stderr: stderr, + }) + if err != nil { + return "", "", err + } + return stdout.String(), stderr.String(), nil +} + +//go:linkname cpMakeTar k8s.io/kubernetes/pkg/kubectl/cmd/cp.makeTar +func cpMakeTar(srcPath, destPath string, writer io.Writer) error diff --git a/test/testenv/search_utils.go b/test/testenv/search_utils.go new file mode 100644 index 000000000..9fa3b887a --- /dev/null +++ b/test/testenv/search_utils.go @@ -0,0 +1,146 @@ +package testenv + +import ( + "encoding/json" + "fmt" + + logf "sigs.k8s.io/controller-runtime/pkg/log" +) + +// SearchJobStatusResponse represents the search status returned by splunk for +// endpoint: https://localhost:8089/services/search/jobs/ +type SearchJobStatusResponse struct { + Entries []SearchJobStatusEntry `json:"entry"` +} + +// SearchJobStatusEntry represents the metadata for a given sid returned as part of the search status +type SearchJobStatusEntry struct { + Name string + ID string + Content SearchJobStatusContent +} + +// SearchJobStatusContent represents the search metadata returned as part of the search status +type SearchJobStatusContent struct { + IsDone bool +} + +// SearchJobResultsResponse represents the search results on non-transforming searches +type SearchJobResultsResponse struct { + Fields []SearchJobResponseFields `json:"fields"` + Results []SearchJobResponseResults `json:"results"` +} + +// SearchJobResponseFields represents the fields in results from non-transforming searches +type SearchJobResponseFields struct { + Name string +} + +// SearchJobResponseResults represents the results from non-transforming searches +type SearchJobResponseResults struct { + Raw string `json:"_raw"` + Source string `json:"source"` + Sourcetype string `json:"sourcetype"` + SplunkServer string `json:"splunk_server"` +} + +// PerformSearchSync performs a syncronous search within splunk and returns the search results +func PerformSearchSync(podName string, search string, deployment *Deployment) (string, error) { + // Build the search curl command and send it to an instance + curlCmd := "curl -ks -u" + username := "admin" + password := "$(cat /mnt/splunk-secrets/password)" + url := "https://localhost:8089/services/search/jobs/export" + + searchReq := fmt.Sprintf("%s %s:%s %s -d output_mode=json -d search=\"search %s\"", curlCmd, username, password, url, search) + command := []string{"/bin/sh"} + searchReqResp, stderr, err := deployment.PodExecCommand(podName, command, searchReq, false) + _ = stderr + if err != nil { + logf.Log.Error(err, "Failed to execute cmd on pod", "pod", podName, "command", command) + return "", err + } + + // Since results can have multiple formats depending on the search SPL, leave this response as a string + return searchReqResp, err +} + +// PerformSearchReq makes a search request for a search to be performed. Returns a sid to be used to check for status and results +func PerformSearchReq(podName string, search string, deployment *Deployment) (string, error) { + // Build the search curl command + curlCmd := "curl -ks -u" + url := "https://localhost:8089/services/search/jobs" + username := "admin" + password := "$(cat /mnt/splunk-secrets/password)" + + searchReq := fmt.Sprintf("%s %s:%s %s -d output_mode=json -d search=\"search %s\"", curlCmd, username, password, url, search) + + // Send search request to instance + command := []string{"/bin/sh"} + stdout, stderr, err := deployment.PodExecCommand(podName, command, searchReq, false) + _ = stderr + if err != nil { + logf.Log.Error(err, "Failed to execute cmd on pod", "pod", podName, "command", command) + return "", err + } + + // Get SID + var searchReqResult map[string]interface{} + jsonErr := json.Unmarshal([]byte(stdout), &searchReqResult) + if jsonErr != nil { + logf.Log.Error(jsonErr, "Failed to unmarshal JSON Search Request Response to get SID") + return "", jsonErr + } + sid := searchReqResult["sid"].(string) + return sid, err +} + +// GetSearchStatus checks the search status for a given +func GetSearchStatus(podName string, sid string, deployment *Deployment) (*SearchJobStatusResponse, error) { + // Build search status request curl command + curlCmd := "curl -ks -u" + url := "https://localhost:8089/services/search/jobs" + username := "admin" + password := "$(cat /mnt/splunk-secrets/password)" + + searchStatusReq := fmt.Sprintf("%s %s:%s %s/%s -d output_mode=json", curlCmd, username, password, url, sid) + + // Send search status request to instance + command := []string{"/bin/sh"} + searchStatusResp, stderr, err := deployment.PodExecCommand(podName, command, searchStatusReq, false) + if err != nil { + logf.Log.Error(err, "Failed to execute cmd on pod", "pod", podName, "command", command, "stderr", stderr) + return nil, err + } + + // Parse resulting JSON + searchStatusResult := SearchJobStatusResponse{} + jsonErr := json.Unmarshal([]byte(searchStatusResp), &searchStatusResult) + if jsonErr != nil { + logf.Log.Error(jsonErr, "Failed to unmarshal JSON Search Status Response to get SID") + return nil, jsonErr + } + return &searchStatusResult, err +} + +// GetSearchResults retrieve the results for a given once the search status isDone == true +func GetSearchResults(podName string, sid string, deployment *Deployment) (string, error) { + // Build search results request curl command + curlCmd := "curl -ks -u" + url := "https://localhost:8089/services/search/jobs" + username := "admin" + password := "$(cat /mnt/splunk-secrets/password)" + + searchResultsReq := fmt.Sprintf("%s %s:%s %s/%s/results/ --get -d output_mode=json", curlCmd, username, password, url, sid) + + // Send search results request to instance + command := []string{"/bin/sh"} + searchResultsResp, stderr, err := deployment.PodExecCommand(podName, command, searchResultsReq, false) + if err != nil { + logf.Log.Error(err, "Failed to execute cmd on pod", "pod", podName, "command", command, "stderr", stderr) + return "", err + } + + // Since results can have multiple formats depending on the search SPL (transforming vs. non-transforming, etc.), leave this response as a string + return searchResultsResp, err +} From 5fdb933e9a54bbc63486dd0a0111ff1f02791c52 Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Thu, 5 Nov 2020 16:43:38 -0800 Subject: [PATCH 12/38] Only create idxc statefulset and secrets if CM is ready --- pkg/splunk/enterprise/indexercluster.go | 10 +++++++++- pkg/splunk/enterprise/indexercluster_test.go | 7 +++---- pkg/splunk/util/secrets_test.go | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/pkg/splunk/enterprise/indexercluster.go b/pkg/splunk/enterprise/indexercluster.go index 083e011f2..053fc56e0 100644 --- a/pkg/splunk/enterprise/indexercluster.go +++ b/pkg/splunk/enterprise/indexercluster.go @@ -288,12 +288,20 @@ func ApplyIdxcSecret(mgr *indexerClusterPodManager, replicas int32, mock bool) e // Update for indexerClusterPodManager handles all updates for a statefulset of indexers func (mgr *indexerClusterPodManager) Update(c splcommon.ControllerClient, statefulSet *appsv1.StatefulSet, desiredReplicas int32) (splcommon.Phase, error) { + + var err error + //Don't even try to create a statefulset and secret if CM is not ready yet. + if mgr.cr.Status.ClusterMasterPhase != splcommon.PhaseReady { + mgr.log.Error(err, "Cluster Master is not ready yet") + return splcommon.PhaseError, err + } + // Assign client if mgr.c == nil { mgr.c = c } // update statefulset, if necessary - _, err := splctrl.ApplyStatefulSet(mgr.c, statefulSet) + _, err = splctrl.ApplyStatefulSet(mgr.c, statefulSet) if err != nil { return splcommon.PhaseError, err } diff --git a/pkg/splunk/enterprise/indexercluster_test.go b/pkg/splunk/enterprise/indexercluster_test.go index 497b808a3..8175f71e4 100644 --- a/pkg/splunk/enterprise/indexercluster_test.go +++ b/pkg/splunk/enterprise/indexercluster_test.go @@ -42,8 +42,6 @@ func TestApplyIndexerCluster(t *testing.T) { {MetaName: "*v1.Service-test-splunk-stack1-indexer-service"}, {MetaName: "*v1.Secret-test-splunk-test-secret"}, {MetaName: "*v1.Secret-test-splunk-stack1-indexer-secret-v1"}, - {MetaName: "*v1.StatefulSet-test-splunk-stack1-indexer"}, - {MetaName: "*v1.Secret-test-splunk-test-secret"}, } labels := map[string]string{ "app.kubernetes.io/component": "versionedSecrets", @@ -55,8 +53,8 @@ func TestApplyIndexerCluster(t *testing.T) { } listmockCall := []spltest.MockFuncCall{ {ListOpts: listOpts}} - createCalls := map[string][]spltest.MockFuncCall{"Get": funcCalls, "Create": {funcCalls[0], funcCalls[3], funcCalls[4], funcCalls[6], funcCalls[7]}, "Update": {funcCalls[0]}, "List": {listmockCall[0]}} - updateCalls := map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[2], funcCalls[3], funcCalls[4], funcCalls[5], funcCalls[6], funcCalls[7], funcCalls[8]}, "Update": {funcCalls[7]}, "List": {listmockCall[0]}} + createCalls := map[string][]spltest.MockFuncCall{"Get": funcCalls, "Create": {funcCalls[0], funcCalls[3], funcCalls[4], funcCalls[6]}, "Update": {funcCalls[0]}, "List": {listmockCall[0]}} + updateCalls := map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[2], funcCalls[3], funcCalls[4], funcCalls[5], funcCalls[6]}, "List": {listmockCall[0]}} current := enterprisev1.IndexerCluster{ TypeMeta: metav1.TypeMeta{ @@ -76,6 +74,7 @@ func TestApplyIndexerCluster(t *testing.T) { }, }, } + current.Status.ClusterMasterPhase = splcommon.PhaseReady current.Status.IndexerSecretChanged = append(current.Status.IndexerSecretChanged, true) revised := current.DeepCopy() revised.Spec.Image = "splunk/test" diff --git a/pkg/splunk/util/secrets_test.go b/pkg/splunk/util/secrets_test.go index 9604b6a51..f8ef59c50 100644 --- a/pkg/splunk/util/secrets_test.go +++ b/pkg/splunk/util/secrets_test.go @@ -428,7 +428,7 @@ func TestGetVersionedSecretVersion(t *testing.T) { // Negative testing with non-integer version for testVersion := 0; testVersion < 10; testVersion++ { - testSecretName = splcommon.GetVersionedSecretName(versionedSecretIdentifier, string('A'-1+testVersion)) + testSecretName = splcommon.GetVersionedSecretName(versionedSecretIdentifier, string(rune('A'-1+testVersion))) _, err := GetVersionedSecretVersion(testSecretName, versionedSecretIdentifier) if err.Error() != nonIntegerVersionError { t.Errorf("Failed to detect incorrect versioning") From 25751051ce48935a49f1e546799b267b8c7792dd Mon Sep 17 00:00:00 2001 From: akondur Date: Fri, 6 Nov 2020 16:25:46 -0800 Subject: [PATCH 13/38] Set idxc.secret on the secrets mounted on the indexer pods when idxc.secret is changed --- ...rprise.splunk.com_indexerclusters_crd.yaml | 5 ++ ...rprise.splunk.com_indexerclusters_crd.yaml | 5 ++ .../v1beta1/indexercluster_types.go | 3 + .../v1beta1/zz_generated.deepcopy.go | 7 +++ pkg/splunk/enterprise/indexercluster.go | 58 ++++++++++++++++++- pkg/splunk/enterprise/indexercluster_test.go | 2 +- 6 files changed, 76 insertions(+), 4 deletions(-) diff --git a/deploy/crds/enterprise.splunk.com_indexerclusters_crd.yaml b/deploy/crds/enterprise.splunk.com_indexerclusters_crd.yaml index 2abb01ba8..4652896eb 100644 --- a/deploy/crds/enterprise.splunk.com_indexerclusters_crd.yaml +++ b/deploy/crds/enterprise.splunk.com_indexerclusters_crd.yaml @@ -2236,6 +2236,11 @@ spec: description: IndexerClusterStatus defines the observed state of a Splunk Enterprise indexer cluster properties: + IdxcPasswordChangedSecrets: + additionalProperties: + type: boolean + description: Holds secrets whose IDXC password has changed + type: object clusterMasterPhase: description: current phase of the cluster master enum: diff --git a/deploy/olm-catalog/splunk/0.2.0/enterprise.splunk.com_indexerclusters_crd.yaml b/deploy/olm-catalog/splunk/0.2.0/enterprise.splunk.com_indexerclusters_crd.yaml index 2abb01ba8..4652896eb 100644 --- a/deploy/olm-catalog/splunk/0.2.0/enterprise.splunk.com_indexerclusters_crd.yaml +++ b/deploy/olm-catalog/splunk/0.2.0/enterprise.splunk.com_indexerclusters_crd.yaml @@ -2236,6 +2236,11 @@ spec: description: IndexerClusterStatus defines the observed state of a Splunk Enterprise indexer cluster properties: + IdxcPasswordChangedSecrets: + additionalProperties: + type: boolean + description: Holds secrets whose IDXC password has changed + type: object clusterMasterPhase: description: current phase of the cluster master enum: diff --git a/pkg/apis/enterprise/v1beta1/indexercluster_types.go b/pkg/apis/enterprise/v1beta1/indexercluster_types.go index 5969a8706..964df0850 100644 --- a/pkg/apis/enterprise/v1beta1/indexercluster_types.go +++ b/pkg/apis/enterprise/v1beta1/indexercluster_types.go @@ -89,6 +89,9 @@ type IndexerClusterStatus struct { // Indicates resource version of namespace scoped secret NamespaceSecretResourceVersion string `json:"namespace_scoped_secret_resource_version"` + // Holds secrets whose IDXC password has changed + IdxcPasswordChangedSecrets map[string]bool `json:"IdxcPasswordChangedSecrets"` + // Indicates if the cluster is in maintenance mode. MaintenanceMode bool `json:"maintenance_mode"` diff --git a/pkg/apis/enterprise/v1beta1/zz_generated.deepcopy.go b/pkg/apis/enterprise/v1beta1/zz_generated.deepcopy.go index d17e1f7ca..643175b41 100644 --- a/pkg/apis/enterprise/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/enterprise/v1beta1/zz_generated.deepcopy.go @@ -341,6 +341,13 @@ func (in *IndexerClusterStatus) DeepCopyInto(out *IndexerClusterStatus) { *out = make([]bool, len(*in)) copy(*out, *in) } + if in.IdxcPasswordChangedSecrets != nil { + in, out := &in.IdxcPasswordChangedSecrets, &out.IdxcPasswordChangedSecrets + *out = make(map[string]bool, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.Peers != nil { in, out := &in.Peers, &out.Peers *out = make([]IndexerClusterMemberStatus, len(*in)) diff --git a/pkg/splunk/enterprise/indexercluster.go b/pkg/splunk/enterprise/indexercluster.go index 98cea1df8..eae99b74e 100644 --- a/pkg/splunk/enterprise/indexercluster.go +++ b/pkg/splunk/enterprise/indexercluster.go @@ -60,6 +60,9 @@ func ApplyIndexerCluster(client splcommon.ControllerClient, cr *enterprisev1.Ind if cr.Status.IndexerSecretChanged == nil { cr.Status.IndexerSecretChanged = []bool{} } + if cr.Status.IdxcPasswordChangedSecrets == nil { + cr.Status.IdxcPasswordChangedSecrets = make(map[string]bool) + } defer func() { err = client.Status().Update(context.TODO(), cr) if err != nil { @@ -138,6 +141,7 @@ func ApplyIndexerCluster(client splcommon.ControllerClient, cr *enterprisev1.Ind // Reset idxc secret changed and namespace secret revision cr.Status.IndexerSecretChanged = []bool{} cr.Status.NamespaceSecretResourceVersion = namespaceScopedSecret.ObjectMeta.ResourceVersion + cr.Status.IdxcPasswordChangedSecrets = make(map[string]bool) result.Requeue = false } @@ -193,6 +197,7 @@ func SetClusterMaintenanceMode(c splcommon.ControllerClient, cr *enterprisev1.In // ApplyIdxcSecret checks if any of the indexer's have a different idxc_secret from namespace scoped secret and changes it func ApplyIdxcSecret(mgr *indexerClusterPodManager, replicas int32, mock bool) error { + var indIdxcSecret string // Get namespace scoped secret namespaceSecret, err := splutil.ApplyNamespaceScopedSecretObject(mgr.c, mgr.cr.GetNamespace()) if err != nil { @@ -221,10 +226,17 @@ func ApplyIdxcSecret(mgr *indexerClusterPodManager, replicas int32, mock bool) e // Get Indexer's name indexerPodName := GetSplunkStatefulsetPodName(SplunkIndexer, mgr.cr.GetName(), i) - // Retrieve idxc_secret password from Pod - indIdxcSecret, err := splutil.GetSpecificSecretTokenFromPod(mgr.c, indexerPodName, mgr.cr.GetNamespace(), "idxc_secret") + // Retrieve secret from pod + podSecret, err := splutil.GetSecretFromPod(mgr.c, indexerPodName, mgr.cr.GetNamespace()) if err != nil { - return fmt.Errorf("Couldn't Retrieve idxc_secret from secret data %s", err.Error()) + return fmt.Errorf("Couldn't retrieve secret from pod %s", err.Error()) + } + + // Retrieve idxc_secret token + if indIdxcSecretByte, ok := podSecret.Data["idxc_secret"]; ok { + indIdxcSecret = string(indIdxcSecretByte) + } else { + return fmt.Errorf("Couldn't retrieve idxc_secret from secret %s mounted on pod %s", podSecret.GetName(), indexerPodName) } // If idxc secret is different from namespace scoped secret change it @@ -264,6 +276,9 @@ func ApplyIdxcSecret(mgr *indexerClusterPodManager, replicas int32, mock bool) e } scopedLog.Info("Restarted splunk") + // Keep a track of all the secrets on pods to change their idxc secret below + mgr.cr.Status.IdxcPasswordChangedSecrets[podSecret.GetName()] = true + // Set the idxc_secret changed flag to true if i < int32(len(mgr.cr.Status.IndexerSecretChanged)) { mgr.cr.Status.IndexerSecretChanged[i] = true @@ -273,6 +288,43 @@ func ApplyIdxcSecret(mgr *indexerClusterPodManager, replicas int32, mock bool) e } } + /* + During the recycle of indexer pods due to an idxc secret change, if there is a container + restart(for example if the splunkd process dies) before the operator + deletes the pod, the container restart fails due to mismatch of idxc password between Cluster + master and that particular indexer. + + Changing the idxc passwords on the secrets mounted on the indexer pods to avoid the above. + */ + if len(mgr.cr.Status.IdxcPasswordChangedSecrets) > 0 { + for podSecretName := range mgr.cr.Status.IdxcPasswordChangedSecrets { + if mgr.cr.Status.IdxcPasswordChangedSecrets[podSecretName] { + podSecret, err := splutil.GetSecretByName(mgr.c, mgr.cr, podSecretName) + if err != nil { + return fmt.Errorf("Could not read secret %s, reason - %v", podSecretName, err) + } + + // Retrieve namespaced scoped secret data in splunk readable format + splunkReadableData, err := splutil.GetSplunkReadableNamespaceScopedSecretData(mgr.c, mgr.cr.GetNamespace()) + if err != nil { + return err + } + + podSecret.Data["idxc_secret"] = splunkReadableData["idxc_secret"] + podSecret.Data["default.yml"] = splunkReadableData["default.yml"] + + _, err = splctrl.ApplySecret(mgr.c, podSecret) + if err != nil { + return err + } + scopedLog.Info("idxc password changed on the secret mounted on pod", "Secret on Pod:", podSecretName) + + // Set to false marking the idxc password change in the secret + mgr.cr.Status.IdxcPasswordChangedSecrets[podSecretName] = false + } + } + } + return nil } diff --git a/pkg/splunk/enterprise/indexercluster_test.go b/pkg/splunk/enterprise/indexercluster_test.go index b8547b9fc..c2eb9e244 100644 --- a/pkg/splunk/enterprise/indexercluster_test.go +++ b/pkg/splunk/enterprise/indexercluster_test.go @@ -506,7 +506,7 @@ func TestApplyIdxcSecret(t *testing.T) { Namespace: "test", }, } - + cr.Status.IdxcPasswordChangedSecrets = make(map[string]bool) cr.Spec.ClusterMasterRef.Name = cr.GetName() mockSplunkClient := &spltest.MockHTTPClient{} mockSplunkClient.AddHandlers(mockHandlers...) From 0e258d67839301292a6d77f882ff68fae458c105 Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Thu, 12 Nov 2020 14:32:12 -0800 Subject: [PATCH 14/38] CSPL-556(Part-1): This change is first part of increasing the code coverage for clustering code. This PR focuses on indexercluster.go --- pkg/splunk/enterprise/indexercluster.go | 15 +- pkg/splunk/enterprise/indexercluster_test.go | 321 ++++++++++++++++++- pkg/splunk/enterprise/util_test.go | 21 -- pkg/splunk/spark/spark_test.go | 8 +- pkg/splunk/test/controller.go | 73 +++-- 5 files changed, 377 insertions(+), 61 deletions(-) diff --git a/pkg/splunk/enterprise/indexercluster.go b/pkg/splunk/enterprise/indexercluster.go index 053fc56e0..b7ba82d1f 100644 --- a/pkg/splunk/enterprise/indexercluster.go +++ b/pkg/splunk/enterprise/indexercluster.go @@ -290,20 +290,19 @@ func ApplyIdxcSecret(mgr *indexerClusterPodManager, replicas int32, mock bool) e func (mgr *indexerClusterPodManager) Update(c splcommon.ControllerClient, statefulSet *appsv1.StatefulSet, desiredReplicas int32) (splcommon.Phase, error) { var err error - //Don't even try to create a statefulset and secret if CM is not ready yet. - if mgr.cr.Status.ClusterMasterPhase != splcommon.PhaseReady { - mgr.log.Error(err, "Cluster Master is not ready yet") - return splcommon.PhaseError, err - } // Assign client if mgr.c == nil { mgr.c = c } // update statefulset, if necessary - _, err = splctrl.ApplyStatefulSet(mgr.c, statefulSet) - if err != nil { - return splcommon.PhaseError, err + if mgr.cr.Status.ClusterMasterPhase == splcommon.PhaseReady { + _, err = splctrl.ApplyStatefulSet(mgr.c, statefulSet) + if err != nil { + return splcommon.PhaseError, err + } + } else { + mgr.log.Error(err, "Cluster Master is not ready yet") } // Check if a recycle of idxc pods is necessary(due to idxc_secret mismatch with CM) diff --git a/pkg/splunk/enterprise/indexercluster_test.go b/pkg/splunk/enterprise/indexercluster_test.go index 8175f71e4..359af1e0d 100644 --- a/pkg/splunk/enterprise/indexercluster_test.go +++ b/pkg/splunk/enterprise/indexercluster_test.go @@ -42,6 +42,7 @@ func TestApplyIndexerCluster(t *testing.T) { {MetaName: "*v1.Service-test-splunk-stack1-indexer-service"}, {MetaName: "*v1.Secret-test-splunk-test-secret"}, {MetaName: "*v1.Secret-test-splunk-stack1-indexer-secret-v1"}, + {MetaName: "*v1.Secret-test-splunk-test-secret"}, } labels := map[string]string{ "app.kubernetes.io/component": "versionedSecrets", @@ -54,7 +55,7 @@ func TestApplyIndexerCluster(t *testing.T) { listmockCall := []spltest.MockFuncCall{ {ListOpts: listOpts}} createCalls := map[string][]spltest.MockFuncCall{"Get": funcCalls, "Create": {funcCalls[0], funcCalls[3], funcCalls[4], funcCalls[6]}, "Update": {funcCalls[0]}, "List": {listmockCall[0]}} - updateCalls := map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[2], funcCalls[3], funcCalls[4], funcCalls[5], funcCalls[6]}, "List": {listmockCall[0]}} + updateCalls := map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[2], funcCalls[3], funcCalls[4], funcCalls[5], funcCalls[6], funcCalls[7]}, "List": {listmockCall[0]}} current := enterprisev1.IndexerCluster{ TypeMeta: metav1.TypeMeta{ @@ -95,7 +96,58 @@ func TestApplyIndexerCluster(t *testing.T) { splunkDeletionTester(t, revised, deleteFunc) } -func getIndexerClusterPodManagerAndClient(method string, mockHandlers []spltest.MockHTTPHandler, replicas int32) (*indexerClusterPodManager, *spltest.MockHTTPClient) { +func TestGetClusterMasterClient(t *testing.T) { + scopedLog := log.WithName("TestGetClusterMasterClient") + cr := enterprisev1.IndexerCluster{ + TypeMeta: metav1.TypeMeta{ + Kind: "IndexerCluster", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "stack1", + Namespace: "test", + }, + Spec: enterprisev1.IndexerClusterSpec{ + Replicas: 1, + CommonSplunkSpec: enterprisev1.CommonSplunkSpec{ + ClusterMasterRef: corev1.ObjectReference{ + Name: "", /* Empty ClusterMasterRef */ + }, + }, + }, + Status: enterprisev1.IndexerClusterStatus{ + ClusterMasterPhase: splcommon.PhaseReady, + }, + } + cr.Status.IndexerSecretChanged = append(cr.Status.IndexerSecretChanged, true) + secrets := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-master1-indexer-secrets", + Namespace: "test", + }, + Data: map[string][]byte{ + "password": {'1', '2', '3'}, + }, + } + mockSplunkClient := &spltest.MockHTTPClient{} + mgr := &indexerClusterPodManager{ + log: scopedLog, + cr: &cr, + secrets: secrets, + newSplunkClient: func(managementURI, username, password string) *splclient.SplunkClient { + c := splclient.NewSplunkClient(managementURI, username, password) + c.Client = mockSplunkClient + return c + }, + } + c := spltest.NewMockClient() + mgr.c = c + cm := mgr.getClusterMasterClient() + if cm.ManagementURI != "https://splunk--cluster-master-service.test.svc.cluster.local:8089" { + t.Errorf("getClusterMasterClient() should have returned incorrect mgmt URI") + } +} + +func getIndexerClusterPodManager(method string, mockHandlers []spltest.MockHTTPHandler, mockSplunkClient *spltest.MockHTTPClient, replicas int32) *indexerClusterPodManager { scopedLog := log.WithName(method) cr := enterprisev1.IndexerCluster{ TypeMeta: metav1.TypeMeta{ @@ -128,8 +180,7 @@ func getIndexerClusterPodManagerAndClient(method string, mockHandlers []spltest. "password": {'1', '2', '3'}, }, } - mockSplunkClient := &spltest.MockHTTPClient{} - mockSplunkClient.AddHandlers(mockHandlers...) + mgr := &indexerClusterPodManager{ log: scopedLog, cr: &cr, @@ -140,7 +191,7 @@ func getIndexerClusterPodManagerAndClient(method string, mockHandlers []spltest. return c }, } - return mgr, mockSplunkClient + return mgr } // indexerClusterpodManagerVerifyRFPeersTester is used to verify replicas against RF using a indexerClusterPodManager @@ -169,7 +220,10 @@ func indexerClusterPodManagerReplicasTester(t *testing.T, method string, mockHan replicas int32, desiredReplicas int32, wantPhase splcommon.Phase, wantCalls map[string][]spltest.MockFuncCall, wantError error) { - mgr, mockSplunkClient := getIndexerClusterPodManagerAndClient(method, mockHandlers, replicas) + mockSplunkClient := &spltest.MockHTTPClient{} + mockSplunkClient.AddHandlers(mockHandlers...) + + mgr := getIndexerClusterPodManager(method, mockHandlers, mockSplunkClient, replicas) indexerClusterPodManagerVerifyRFPeersTester(t, method, mgr, desiredReplicas, wantPhase, wantCalls, wantError) mockSplunkClient.CheckRequests(t, method) } @@ -210,11 +264,212 @@ func TestVerifyRFPeers(t *testing.T) { indexerClusterPodManagerReplicasTester(t, method, mockHandlers, 1 /*replicas*/, 2 /*desired replicas*/, splcommon.PhaseReady, wantCalls, nil) } +func checkResponseFromUpdateStatus(t *testing.T, method string, mockHandlers []spltest.MockHTTPHandler, replicas int32, statefulSet *appsv1.StatefulSet, retry bool) error { + mockSplunkClient := &spltest.MockHTTPClient{} + mockSplunkClient.AddHandlers(mockHandlers...) + + mgr := getIndexerClusterPodManager(method, mockHandlers, mockSplunkClient, replicas) + + c := spltest.NewMockClient() + mgr.c = c + + err := mgr.updateStatus(statefulSet) + if retry == true { + err = mgr.updateStatus(statefulSet) + } + return err +} + +func TestUpdateStatusInvalidResponse(t *testing.T) { + mockHandlers := []spltest.MockHTTPHandler{ + { + Method: "GET", + URL: "https://splunk-master1-cluster-master-service.test.svc.cluster.local:8089/services/cluster/master/info?count=0&output_mode=json", + Status: 200, + Err: nil, + Body: ``, + }, + } + var replicas int32 = 1 + statefulSet := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1", + Namespace: "test", + }, + Spec: appsv1.StatefulSetSpec{ + Replicas: &replicas, + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-etc", Namespace: "test"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-var", Namespace: "test"}}, + }, + }, + Status: appsv1.StatefulSetStatus{ + Replicas: replicas, + ReadyReplicas: replicas, + UpdatedReplicas: replicas, + UpdateRevision: "v1", + }, + } + + method := "indexerClusterPodManager.UpdateStatus(Invalid response)" + err := checkResponseFromUpdateStatus(t, method, mockHandlers, 1, statefulSet, false) + if err == nil { + t.Errorf("mgr.updateStatus() should have returned an error here") + } + + mockHandlers[0].Body = `{"links":{},"origin":"https://localhost:8089/services/cluster/master/info","updated":"2020-03-18T01:04:53+00:00","generator":{"build":"a7f645ddaf91","version":"8.0.2"},"entry":[{"name":"master","id":"https://localhost:8089/services/cluster/master/info/master","updated":"1970-01-01T00:00:00+00:00","links":{"alternate":"/services/cluster/master/info/master","list":"/services/cluster/master/info/master"},"author":"system","acl":{"app":"","can_list":true,"can_write":true,"modifiable":false,"owner":"system","perms":{"read":["admin","splunk-system-role"],"write":["admin","splunk-system-role"]},"removable":false,"sharing":"system"},"content":{"active_bundle":{"bundle_path":"/opt/splunk/var/run/splunk/cluster/remote-bundle/506c58d5aeda1dd6017889e3186e7337-1583870198.bundle","checksum":"14310A4AABD23E85BBD4559C4A3B59F8","timestamp":1583870198},"apply_bundle_status":{"invalid_bundle":{"bundle_path":"","bundle_validation_errors_on_master":[],"checksum":"","timestamp":0},"reload_bundle_issued":false,"status":"None"},"backup_and_restore_primaries":false,"controlled_rolling_restart_flag":false,"eai:acl":null,"indexing_ready_flag":true,"initialized_flag":true,"label":"splunk-stack1-cluster-master-0","last_check_restart_bundle_result":false,"last_dry_run_bundle":{"bundle_path":"","checksum":"","timestamp":0},"last_validated_bundle":{"bundle_path":"/opt/splunk/var/run/splunk/cluster/remote-bundle/0af7c0e95f313f7be3b0cb1d878df9a1-1583948640.bundle","checksum":"14310A4AABD23E85BBD4559C4A3B59F8","is_valid_bundle":true,"timestamp":1583948640},"latest_bundle":{"bundle_path":"/opt/splunk/var/run/splunk/cluster/remote-bundle/506c58d5aeda1dd6017889e3186e7337-1583870198.bundle","checksum":"14310A4AABD23E85BBD4559C4A3B59F8","timestamp":1583870198},"maintenance_mode":false,"multisite":false,"previous_active_bundle":{"bundle_path":"","checksum":"","timestamp":0},"primaries_backup_status":"No on-going (or) completed primaries backup yet. Check back again in few minutes if you expect a backup.","quiet_period_flag":false,"rolling_restart_flag":false,"rolling_restart_or_upgrade":false,"service_ready_flag":true,"start_time":1583948636,"summary_replication":"false"}}],"paging":{"total":1,"perPage":30,"offset":0},"messages":[]}` + + mockHandler := spltest.MockHTTPHandler{ + Method: "GET", + URL: "https://splunk-master1-cluster-master-service.test.svc.cluster.local:8089/services/cluster/master/peers?count=0&output_mode=json", + Status: 200, + Err: nil, + Body: ``, + } + + mockHandlers = append(mockHandlers, mockHandler) + err = checkResponseFromUpdateStatus(t, method, mockHandlers, 1, statefulSet, false) + if err == nil { + t.Errorf("mgr.updateStatus() should have returned an error here") + } + + mockHandlers[1].Body = `{"links":{"create":"/services/cluster/master/peers/_new"},"origin":"https://localhost:8089/services/cluster/master/peers","updated":"2020-03-18T01:08:53+00:00","generator":{"build":"a7f645ddaf91","version":"8.0.2"},"entry":[{"name":"D39B1729-E2C5-4273-B9B2-534DA7C2F866","id":"https://localhost:8089/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866","updated":"1970-01-01T00:00:00+00:00","links":{"alternate":"/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866","list":"/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866","edit":"/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866"},"author":"system","acl":{"app":"","can_list":true,"can_write":true,"modifiable":false,"owner":"system","perms":{"read":["admin","splunk-system-role"],"write":["admin","splunk-system-role"]},"removable":false,"sharing":"system"},"content":{"active_bundle_id":"14310A4AABD23E85BBD4559C4A3B59F8","apply_bundle_status":{"invalid_bundle":{"bundle_validation_errors":[],"invalid_bundle_id":""},"reasons_for_restart":[],"restart_required_for_apply_bundle":false,"status":"None"},"base_generation_id":26,"bucket_count":73,"bucket_count_by_index":{"_audit":24,"_internal":45,"_telemetry":4},"buckets_rf_by_origin_site":{"default":73},"buckets_sf_by_origin_site":{"default":73},"delayed_buckets_to_discard":[],"eai:acl":null,"fixup_set":[],"heartbeat_started":true,"host_port_pair":"10.36.0.6:8089","indexing_disk_space":210707374080,"is_searchable":true,"is_valid_bundle":true,"label":"splunk-stack1-indexer-0","last_dry_run_bundle":"","last_heartbeat":1584493732,"last_validated_bundle":"14310A4AABD23E85BBD4559C4A3B59F8","latest_bundle_id":"14310A4AABD23E85BBD4559C4A3B59F8","peer_registered_summaries":true,"pending_builds":[],"pending_job_count":0,"primary_count":73,"primary_count_remote":0,"register_search_address":"10.36.0.6:8089","replication_count":0,"replication_port":9887,"replication_use_ssl":false,"restart_required_for_applying_dry_run_bundle":false,"search_state_counter":{"PendingSearchable":0,"Searchable":73,"SearchablePendingMask":0,"Unsearchable":0},"site":"default","splunk_version":"8.0.2","status":"Up","status_counter":{"Complete":69,"NonStreamingTarget":0,"StreamingSource":4,"StreamingTarget":0},"summary_replication_count":0}}],"paging":{"total":1,"perPage":30,"offset":0},"messages":[]}` + + // We would like to call mgr.updateStatus() here twice just to mimic calling reconcile twice, + // so that the first call fill the field `mgr.cr.Status.Peers` and the next call can use that. + err = checkResponseFromUpdateStatus(t, method, mockHandlers, 1, statefulSet, true) + if err != nil { + t.Errorf("mgr.updateStatus() should not have returned an error here") + } +} + +func TestInvalidPeerStatusInScaleDown(t *testing.T) { + var replicas int32 = 1 + statefulSet := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1", + Namespace: "test", + }, + Spec: appsv1.StatefulSetSpec{ + Replicas: &replicas, + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-etc", Namespace: "test"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-var", Namespace: "test"}}, + }, + }, + Status: appsv1.StatefulSetStatus{ + Replicas: replicas, + ReadyReplicas: replicas, + UpdatedReplicas: replicas, + UpdateRevision: "v1", + }, + } + + // Create a mock handler that returns an invalid peer status as response + mockHandlers := []spltest.MockHTTPHandler{ + { + Method: "GET", + URL: "https://splunk-master1-cluster-master-service.test.svc.cluster.local:8089/services/cluster/master/info?count=0&output_mode=json", + Status: 200, + Err: nil, + Body: `{"links":{},"origin":"https://localhost:8089/services/cluster/master/info","updated":"2020-03-18T01:04:53+00:00","generator":{"build":"a7f645ddaf91","version":"8.0.2"},"entry":[{"name":"master","id":"https://localhost:8089/services/cluster/master/info/master","updated":"1970-01-01T00:00:00+00:00","links":{"alternate":"/services/cluster/master/info/master","list":"/services/cluster/master/info/master"},"author":"system","acl":{"app":"","can_list":true,"can_write":true,"modifiable":false,"owner":"system","perms":{"read":["admin","splunk-system-role"],"write":["admin","splunk-system-role"]},"removable":false,"sharing":"system"},"content":{"active_bundle":{"bundle_path":"/opt/splunk/var/run/splunk/cluster/remote-bundle/506c58d5aeda1dd6017889e3186e7337-1583870198.bundle","checksum":"14310A4AABD23E85BBD4559C4A3B59F8","timestamp":1583870198},"apply_bundle_status":{"invalid_bundle":{"bundle_path":"","bundle_validation_errors_on_master":[],"checksum":"","timestamp":0},"reload_bundle_issued":false,"status":"None"},"backup_and_restore_primaries":false,"controlled_rolling_restart_flag":false,"eai:acl":null,"indexing_ready_flag":true,"initialized_flag":true,"label":"splunk-stack1-cluster-master-0","last_check_restart_bundle_result":false,"last_dry_run_bundle":{"bundle_path":"","checksum":"","timestamp":0},"last_validated_bundle":{"bundle_path":"/opt/splunk/var/run/splunk/cluster/remote-bundle/0af7c0e95f313f7be3b0cb1d878df9a1-1583948640.bundle","checksum":"14310A4AABD23E85BBD4559C4A3B59F8","is_valid_bundle":true,"timestamp":1583948640},"latest_bundle":{"bundle_path":"/opt/splunk/var/run/splunk/cluster/remote-bundle/506c58d5aeda1dd6017889e3186e7337-1583870198.bundle","checksum":"14310A4AABD23E85BBD4559C4A3B59F8","timestamp":1583870198},"maintenance_mode":false,"multisite":false,"previous_active_bundle":{"bundle_path":"","checksum":"","timestamp":0},"primaries_backup_status":"No on-going (or) completed primaries backup yet. Check back again in few minutes if you expect a backup.","quiet_period_flag":false,"rolling_restart_flag":false,"rolling_restart_or_upgrade":false,"service_ready_flag":true,"start_time":1583948636,"summary_replication":"false"}}],"paging":{"total":1,"perPage":30,"offset":0},"messages":[]}`, + }, + { + Method: "GET", + URL: "https://splunk-master1-cluster-master-service.test.svc.cluster.local:8089/services/cluster/master/peers?count=0&output_mode=json", + Status: 200, + Err: nil, + Body: `{"links":{"create":"/services/cluster/master/peers/_new"},"origin":"https://localhost:8089/services/cluster/master/peers","updated":"2020-03-18T01:08:53+00:00","generator":{"build":"a7f645ddaf91","version":"8.0.2"},"entry":[{"name":"D39B1729-E2C5-4273-B9B2-534DA7C2F866","id":"https://localhost:8089/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866","updated":"1970-01-01T00:00:00+00:00","links":{"alternate":"/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866","list":"/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866","edit":"/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866"},"author":"system","acl":{"app":"","can_list":true,"can_write":true,"modifiable":false,"owner":"system","perms":{"read":["admin","splunk-system-role"],"write":["admin","splunk-system-role"]},"removable":false,"sharing":"system"},"content":{"active_bundle_id":"14310A4AABD23E85BBD4559C4A3B59F8","apply_bundle_status":{"invalid_bundle":{"bundle_validation_errors":[],"invalid_bundle_id":""},"reasons_for_restart":[],"restart_required_for_apply_bundle":false,"status":"None"},"base_generation_id":26,"bucket_count":73,"bucket_count_by_index":{"_audit":24,"_internal":45,"_telemetry":4},"buckets_rf_by_origin_site":{"default":73},"buckets_sf_by_origin_site":{"default":73},"delayed_buckets_to_discard":[],"eai:acl":null,"fixup_set":[],"heartbeat_started":true,"host_port_pair":"10.36.0.6:8089","indexing_disk_space":210707374080,"is_searchable":true,"is_valid_bundle":true,"label":"splunk-stack1-indexer-0","last_dry_run_bundle":"","last_heartbeat":1584493732,"last_validated_bundle":"14310A4AABD23E85BBD4559C4A3B59F8","latest_bundle_id":"14310A4AABD23E85BBD4559C4A3B59F8","peer_registered_summaries":true,"pending_builds":[],"pending_job_count":0,"primary_count":73,"primary_count_remote":0,"register_search_address":"10.36.0.6:8089","replication_count":0,"replication_port":9887,"replication_use_ssl":false,"restart_required_for_applying_dry_run_bundle":false,"search_state_counter":{"PendingSearchable":0,"Searchable":73,"SearchablePendingMask":0,"Unsearchable":0},"site":"default","splunk_version":"8.0.2","status":"Invalid_Status","status_counter":{"Complete":69,"NonStreamingTarget":0,"StreamingSource":4,"StreamingTarget":0},"summary_replication_count":0}}],"paging":{"total":1,"perPage":30,"offset":0},"messages":[]}`, + }, + } + + method := "indexerClusterPodManager.decommission" + mockSplunkClient := &spltest.MockHTTPClient{} + mockSplunkClient.AddHandlers(mockHandlers...) + + mgr := getIndexerClusterPodManager(method, mockHandlers, mockSplunkClient, replicas) + + c := spltest.NewMockClient() + mgr.c = c + + err := mgr.updateStatus(statefulSet) + if err != nil { + t.Errorf("mgr.updateStatus() should not have returned an error here") + } + + _, err = mgr.PrepareScaleDown(0) + if err == nil { + t.Errorf("mgr.PrepareScaleDown() should have returned an error here") + } +} + +func TestInvalidPeerInFinishRecycle(t *testing.T) { + var replicas int32 = 1 + statefulSet := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1", + Namespace: "test", + }, + Spec: appsv1.StatefulSetSpec{ + Replicas: &replicas, + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-etc", Namespace: "test"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-var", Namespace: "test"}}, + }, + }, + Status: appsv1.StatefulSetStatus{ + Replicas: replicas, + ReadyReplicas: replicas, + UpdatedReplicas: replicas, + UpdateRevision: "v1", + }, + } + + mockHandlers := []spltest.MockHTTPHandler{ + { + Method: "GET", + URL: "https://splunk-master1-cluster-master-service.test.svc.cluster.local:8089/services/cluster/master/info?count=0&output_mode=json", + Status: 200, + Err: nil, + Body: `{"links":{},"origin":"https://localhost:8089/services/cluster/master/info","updated":"2020-03-18T01:04:53+00:00","generator":{"build":"a7f645ddaf91","version":"8.0.2"},"entry":[{"name":"master","id":"https://localhost:8089/services/cluster/master/info/master","updated":"1970-01-01T00:00:00+00:00","links":{"alternate":"/services/cluster/master/info/master","list":"/services/cluster/master/info/master"},"author":"system","acl":{"app":"","can_list":true,"can_write":true,"modifiable":false,"owner":"system","perms":{"read":["admin","splunk-system-role"],"write":["admin","splunk-system-role"]},"removable":false,"sharing":"system"},"content":{"active_bundle":{"bundle_path":"/opt/splunk/var/run/splunk/cluster/remote-bundle/506c58d5aeda1dd6017889e3186e7337-1583870198.bundle","checksum":"14310A4AABD23E85BBD4559C4A3B59F8","timestamp":1583870198},"apply_bundle_status":{"invalid_bundle":{"bundle_path":"","bundle_validation_errors_on_master":[],"checksum":"","timestamp":0},"reload_bundle_issued":false,"status":"None"},"backup_and_restore_primaries":false,"controlled_rolling_restart_flag":false,"eai:acl":null,"indexing_ready_flag":true,"initialized_flag":true,"label":"splunk-stack1-cluster-master-0","last_check_restart_bundle_result":false,"last_dry_run_bundle":{"bundle_path":"","checksum":"","timestamp":0},"last_validated_bundle":{"bundle_path":"/opt/splunk/var/run/splunk/cluster/remote-bundle/0af7c0e95f313f7be3b0cb1d878df9a1-1583948640.bundle","checksum":"14310A4AABD23E85BBD4559C4A3B59F8","is_valid_bundle":true,"timestamp":1583948640},"latest_bundle":{"bundle_path":"/opt/splunk/var/run/splunk/cluster/remote-bundle/506c58d5aeda1dd6017889e3186e7337-1583870198.bundle","checksum":"14310A4AABD23E85BBD4559C4A3B59F8","timestamp":1583870198},"maintenance_mode":false,"multisite":false,"previous_active_bundle":{"bundle_path":"","checksum":"","timestamp":0},"primaries_backup_status":"No on-going (or) completed primaries backup yet. Check back again in few minutes if you expect a backup.","quiet_period_flag":false,"rolling_restart_flag":false,"rolling_restart_or_upgrade":false,"service_ready_flag":true,"start_time":1583948636,"summary_replication":"false"}}],"paging":{"total":1,"perPage":30,"offset":0},"messages":[]}`, + }, + { + Method: "GET", + URL: "https://splunk-master1-cluster-master-service.test.svc.cluster.local:8089/services/cluster/master/peers?count=0&output_mode=json", + Status: 200, + Err: nil, + Body: `{"links":{"create":"/services/cluster/master/peers/_new"},"origin":"https://localhost:8089/services/cluster/master/peers","updated":"2020-03-18T01:08:53+00:00","generator":{"build":"a7f645ddaf91","version":"8.0.2"},"entry":[{"name":"D39B1729-E2C5-4273-B9B2-534DA7C2F866","id":"https://localhost:8089/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866","updated":"1970-01-01T00:00:00+00:00","links":{"alternate":"/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866","list":"/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866","edit":"/services/cluster/master/peers/D39B1729-E2C5-4273-B9B2-534DA7C2F866"},"author":"system","acl":{"app":"","can_list":true,"can_write":true,"modifiable":false,"owner":"system","perms":{"read":["admin","splunk-system-role"],"write":["admin","splunk-system-role"]},"removable":false,"sharing":"system"},"content":{"active_bundle_id":"14310A4AABD23E85BBD4559C4A3B59F8","apply_bundle_status":{"invalid_bundle":{"bundle_validation_errors":[],"invalid_bundle_id":""},"reasons_for_restart":[],"restart_required_for_apply_bundle":false,"status":"None"},"base_generation_id":26,"bucket_count":73,"bucket_count_by_index":{"_audit":24,"_internal":45,"_telemetry":4},"buckets_rf_by_origin_site":{"default":73},"buckets_sf_by_origin_site":{"default":73},"delayed_buckets_to_discard":[],"eai:acl":null,"fixup_set":[],"heartbeat_started":true,"host_port_pair":"10.36.0.6:8089","indexing_disk_space":210707374080,"is_searchable":true,"is_valid_bundle":true,"label":"splunk-stack1-indexer-0","last_dry_run_bundle":"","last_heartbeat":1584493732,"last_validated_bundle":"14310A4AABD23E85BBD4559C4A3B59F8","latest_bundle_id":"14310A4AABD23E85BBD4559C4A3B59F8","peer_registered_summaries":true,"pending_builds":[],"pending_job_count":0,"primary_count":73,"primary_count_remote":0,"register_search_address":"10.36.0.6:8089","replication_count":0,"replication_port":9887,"replication_use_ssl":false,"restart_required_for_applying_dry_run_bundle":false,"search_state_counter":{"PendingSearchable":0,"Searchable":73,"SearchablePendingMask":0,"Unsearchable":0},"site":"default","splunk_version":"8.0.2","status":"Up","status_counter":{"Complete":69,"NonStreamingTarget":0,"StreamingSource":4,"StreamingTarget":0},"summary_replication_count":0}}],"paging":{"total":1,"perPage":30,"offset":0},"messages":[]}`, + }, + } + + method := "indexerClusterPodManager.FinishRecycle" + mockSplunkClient := &spltest.MockHTTPClient{} + mockSplunkClient.AddHandlers(mockHandlers...) + + mgr := getIndexerClusterPodManager(method, mockHandlers, mockSplunkClient, replicas) + + c := spltest.NewMockClient() + mgr.c = c + + err := mgr.updateStatus(statefulSet) + if err != nil { + t.Errorf("mgr.updateStatus() should not have returned an error here") + } + + // Here we are trying to call FinishRecycle for a peer which is not in the list. + _, err = mgr.FinishRecycle(1) + if err == nil { + t.Errorf("mgr.FinishRecycle() should have returned an error here") + } +} + func indexerClusterPodManagerUpdateTester(t *testing.T, method string, mockHandlers []spltest.MockHTTPHandler, desiredReplicas int32, wantPhase splcommon.Phase, statefulSet *appsv1.StatefulSet, wantCalls map[string][]spltest.MockFuncCall, wantError error, initObjects ...runtime.Object) { - // get indexerClusterPodManager and mockSplunkClient instances - mgr, mockSplunkClient := getIndexerClusterPodManagerAndClient(method, mockHandlers, 1) + mockSplunkClient := &spltest.MockHTTPClient{} + mockSplunkClient.AddHandlers(mockHandlers...) + // get indexerClusterPodManager instance + mgr := getIndexerClusterPodManager(method, mockHandlers, mockSplunkClient, 1) spltest.PodManagerUpdateTester(t, method, mgr, desiredReplicas, wantPhase, statefulSet, wantCalls, wantError, initObjects...) mockSplunkClient.CheckRequests(t, method) } @@ -619,6 +874,53 @@ func TestApplyIdxcSecret(t *testing.T) { } } +func TestInvalidIndexerClusterSpec(t *testing.T) { + + cr := enterprisev1.IndexerCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "stack1", + Namespace: "test", + }, + } + + cm := enterprisev1.ClusterMaster{ + TypeMeta: metav1.TypeMeta{ + Kind: "ClusterMaster", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "master1", + Namespace: "test", + }, + Spec: enterprisev1.ClusterMasterSpec{ + CommonSplunkSpec: enterprisev1.CommonSplunkSpec{ + Mock: true, + }, + }, + } + + c := spltest.NewMockClient() + c.AddObject(&cm) + + cm.Status.Phase = splcommon.PhaseReady + // Empty ClusterMasterRef should return an error + cr.Spec.ClusterMasterRef.Name = "" + if _, err := ApplyIndexerCluster(c, &cr); err == nil { + t.Errorf("ApplyIndxerCluster() should have returned error") + } + + cr.Spec.ClusterMasterRef.Name = "master1" + // verifyRFPeers should return err here + if _, err := ApplyIndexerCluster(c, &cr); err == nil { + t.Errorf("ApplyIndxerCluster() should have returned error") + } + + cm.Status.Phase = splcommon.PhaseError + cr.Spec.CommonSplunkSpec.EtcStorage = "-abcd" + if _, err := ApplyIndexerCluster(c, &cr); err == nil { + t.Errorf("ApplyIndxerCluster() should have returned error") + } +} + func TestGetIndexerStatefulSet(t *testing.T) { cr := enterprisev1.IndexerCluster{ ObjectMeta: metav1.ObjectMeta{ @@ -644,6 +946,9 @@ func TestGetIndexerStatefulSet(t *testing.T) { configTester(t, "getIndexerStatefulSet()", f, want) } + cr.Spec.Replicas = 0 + test(`{"kind":"StatefulSet","apiVersion":"apps/v1","metadata":{"name":"splunk-stack1-indexer","namespace":"test","creationTimestamp":null,"ownerReferences":[{"apiVersion":"","kind":"","name":"stack1","uid":"","controller":true}]},"spec":{"replicas":1,"selector":{"matchLabels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-indexer","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"indexer","app.kubernetes.io/part-of":"splunk-master1-indexer"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-indexer","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"indexer","app.kubernetes.io/part-of":"splunk-master1-indexer"},"annotations":{"traffic.sidecar.istio.io/excludeOutboundPorts":"8089,8191,9997,7777,9000,17000,17500,19000","traffic.sidecar.istio.io/includeInboundPorts":"8000,8088"}},"spec":{"volumes":[{"name":"mnt-splunk-secrets","secret":{"secretName":"splunk-stack1-indexer-secret-v1","defaultMode":420}}],"containers":[{"name":"splunk","image":"splunk/splunk","ports":[{"name":"splunkweb","containerPort":8000,"protocol":"TCP"},{"name":"hec","containerPort":8088,"protocol":"TCP"},{"name":"splunkd","containerPort":8089,"protocol":"TCP"},{"name":"s2s","containerPort":9997,"protocol":"TCP"}],"env":[{"name":"SPLUNK_HOME","value":"/opt/splunk"},{"name":"SPLUNK_START_ARGS","value":"--accept-license"},{"name":"SPLUNK_DEFAULTS_URL","value":"/mnt/splunk-secrets/default.yml"},{"name":"SPLUNK_HOME_OWNERSHIP_ENFORCEMENT","value":"false"},{"name":"SPLUNK_ROLE","value":"splunk_indexer"},{"name":"SPLUNK_DECLARATIVE_ADMIN_PASSWORD","value":"true"},{"name":"SPLUNK_INDEXER_URL","value":"splunk-stack1-indexer-0.splunk-stack1-indexer-headless.test.svc.cluster.local"},{"name":"SPLUNK_CLUSTER_MASTER_URL","value":"splunk-master1-cluster-master-service"}],"resources":{"limits":{"cpu":"4","memory":"8Gi"},"requests":{"cpu":"100m","memory":"512Mi"}},"volumeMounts":[{"name":"pvc-etc","mountPath":"/opt/splunk/etc"},{"name":"pvc-var","mountPath":"/opt/splunk/var"},{"name":"mnt-splunk-secrets","mountPath":"/mnt/splunk-secrets"}],"livenessProbe":{"exec":{"command":["/sbin/checkstate.sh"]},"initialDelaySeconds":300,"timeoutSeconds":30,"periodSeconds":30},"readinessProbe":{"exec":{"command":["/bin/grep","started","/opt/container_artifact/splunk-container.state"]},"initialDelaySeconds":10,"timeoutSeconds":5,"periodSeconds":5},"imagePullPolicy":"IfNotPresent"}],"securityContext":{"runAsUser":41812,"fsGroup":41812},"affinity":{"podAntiAffinity":{"preferredDuringSchedulingIgnoredDuringExecution":[{"weight":100,"podAffinityTerm":{"labelSelector":{"matchExpressions":[{"key":"app.kubernetes.io/instance","operator":"In","values":["splunk-stack1-indexer"]}]},"topologyKey":"kubernetes.io/hostname"}}]}},"schedulerName":"default-scheduler"}},"volumeClaimTemplates":[{"metadata":{"name":"pvc-etc","namespace":"test","creationTimestamp":null,"labels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-indexer","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"indexer","app.kubernetes.io/part-of":"splunk-master1-indexer"}},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"10Gi"}}},"status":{}},{"metadata":{"name":"pvc-var","namespace":"test","creationTimestamp":null,"labels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-indexer","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"indexer","app.kubernetes.io/part-of":"splunk-master1-indexer"}},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"100Gi"}}},"status":{}}],"serviceName":"splunk-stack1-indexer-headless","podManagementPolicy":"Parallel","updateStrategy":{"type":"OnDelete"}},"status":{"replicas":0}}`) + cr.Spec.Replicas = 1 test(`{"kind":"StatefulSet","apiVersion":"apps/v1","metadata":{"name":"splunk-stack1-indexer","namespace":"test","creationTimestamp":null,"ownerReferences":[{"apiVersion":"","kind":"","name":"stack1","uid":"","controller":true}]},"spec":{"replicas":1,"selector":{"matchLabels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-indexer","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"indexer","app.kubernetes.io/part-of":"splunk-master1-indexer"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-indexer","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"indexer","app.kubernetes.io/part-of":"splunk-master1-indexer"},"annotations":{"traffic.sidecar.istio.io/excludeOutboundPorts":"8089,8191,9997,7777,9000,17000,17500,19000","traffic.sidecar.istio.io/includeInboundPorts":"8000,8088"}},"spec":{"volumes":[{"name":"mnt-splunk-secrets","secret":{"secretName":"splunk-stack1-indexer-secret-v1","defaultMode":420}}],"containers":[{"name":"splunk","image":"splunk/splunk","ports":[{"name":"splunkweb","containerPort":8000,"protocol":"TCP"},{"name":"hec","containerPort":8088,"protocol":"TCP"},{"name":"splunkd","containerPort":8089,"protocol":"TCP"},{"name":"s2s","containerPort":9997,"protocol":"TCP"}],"env":[{"name":"SPLUNK_HOME","value":"/opt/splunk"},{"name":"SPLUNK_START_ARGS","value":"--accept-license"},{"name":"SPLUNK_DEFAULTS_URL","value":"/mnt/splunk-secrets/default.yml"},{"name":"SPLUNK_HOME_OWNERSHIP_ENFORCEMENT","value":"false"},{"name":"SPLUNK_ROLE","value":"splunk_indexer"},{"name":"SPLUNK_DECLARATIVE_ADMIN_PASSWORD","value":"true"},{"name":"SPLUNK_INDEXER_URL","value":"splunk-stack1-indexer-0.splunk-stack1-indexer-headless.test.svc.cluster.local"},{"name":"SPLUNK_CLUSTER_MASTER_URL","value":"splunk-master1-cluster-master-service"}],"resources":{"limits":{"cpu":"4","memory":"8Gi"},"requests":{"cpu":"100m","memory":"512Mi"}},"volumeMounts":[{"name":"pvc-etc","mountPath":"/opt/splunk/etc"},{"name":"pvc-var","mountPath":"/opt/splunk/var"},{"name":"mnt-splunk-secrets","mountPath":"/mnt/splunk-secrets"}],"livenessProbe":{"exec":{"command":["/sbin/checkstate.sh"]},"initialDelaySeconds":300,"timeoutSeconds":30,"periodSeconds":30},"readinessProbe":{"exec":{"command":["/bin/grep","started","/opt/container_artifact/splunk-container.state"]},"initialDelaySeconds":10,"timeoutSeconds":5,"periodSeconds":5},"imagePullPolicy":"IfNotPresent"}],"securityContext":{"runAsUser":41812,"fsGroup":41812},"affinity":{"podAntiAffinity":{"preferredDuringSchedulingIgnoredDuringExecution":[{"weight":100,"podAffinityTerm":{"labelSelector":{"matchExpressions":[{"key":"app.kubernetes.io/instance","operator":"In","values":["splunk-stack1-indexer"]}]},"topologyKey":"kubernetes.io/hostname"}}]}},"schedulerName":"default-scheduler"}},"volumeClaimTemplates":[{"metadata":{"name":"pvc-etc","namespace":"test","creationTimestamp":null,"labels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-indexer","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"indexer","app.kubernetes.io/part-of":"splunk-master1-indexer"}},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"10Gi"}}},"status":{}},{"metadata":{"name":"pvc-var","namespace":"test","creationTimestamp":null,"labels":{"app.kubernetes.io/component":"indexer","app.kubernetes.io/instance":"splunk-stack1-indexer","app.kubernetes.io/managed-by":"splunk-operator","app.kubernetes.io/name":"indexer","app.kubernetes.io/part-of":"splunk-master1-indexer"}},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"100Gi"}}},"status":{}}],"serviceName":"splunk-stack1-indexer-headless","podManagementPolicy":"Parallel","updateStrategy":{"type":"OnDelete"}},"status":{"replicas":0}}`) diff --git a/pkg/splunk/enterprise/util_test.go b/pkg/splunk/enterprise/util_test.go index d837dfc9e..cbe3f1daa 100644 --- a/pkg/splunk/enterprise/util_test.go +++ b/pkg/splunk/enterprise/util_test.go @@ -19,7 +19,6 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" enterprisev1 "github.com/splunk/splunk-operator/pkg/apis/enterprise/v1beta1" splcommon "github.com/splunk/splunk-operator/pkg/splunk/common" @@ -29,26 +28,6 @@ import ( ) func init() { - spltest.MockObjectCopiers = append(spltest.MockObjectCopiers, enterpriseObjectCopier) -} - -// enterpriseObjectCopier is used to copy enterprise runtime.Objects -func enterpriseObjectCopier(dst, src runtime.Object) bool { - switch src.(type) { - case *enterprisev1.IndexerCluster: - *dst.(*enterprisev1.IndexerCluster) = *src.(*enterprisev1.IndexerCluster) - case *enterprisev1.LicenseMaster: - *dst.(*enterprisev1.LicenseMaster) = *src.(*enterprisev1.LicenseMaster) - case *enterprisev1.SearchHeadCluster: - *dst.(*enterprisev1.SearchHeadCluster) = *src.(*enterprisev1.SearchHeadCluster) - case *enterprisev1.Spark: - *dst.(*enterprisev1.Spark) = *src.(*enterprisev1.Spark) - case *enterprisev1.Standalone: - *dst.(*enterprisev1.Standalone) = *src.(*enterprisev1.Standalone) - default: - return false - } - return true } func TestApplySplunkConfig(t *testing.T) { diff --git a/pkg/splunk/spark/spark_test.go b/pkg/splunk/spark/spark_test.go index 0aef6172e..7a9fb8711 100644 --- a/pkg/splunk/spark/spark_test.go +++ b/pkg/splunk/spark/spark_test.go @@ -34,10 +34,12 @@ func init() { } // sparkObjectCopier is used to copy enterprisev1.Spark runtime.Objects -func sparkObjectCopier(dst, src runtime.Object) bool { - switch src.(type) { +func sparkObjectCopier(dst, src *runtime.Object) bool { + dstP := *dst + srcP := *src + switch srcP.(type) { case *enterprisev1.Spark: - *dst.(*enterprisev1.Spark) = *src.(*enterprisev1.Spark) + *dstP.(*enterprisev1.Spark) = *srcP.(*enterprisev1.Spark) default: return false } diff --git a/pkg/splunk/test/controller.go b/pkg/splunk/test/controller.go index da420800e..92e21af5e 100644 --- a/pkg/splunk/test/controller.go +++ b/pkg/splunk/test/controller.go @@ -21,6 +21,7 @@ import ( "reflect" "testing" + enterprisev1 "github.com/splunk/splunk-operator/pkg/apis/enterprise/v1beta1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -31,7 +32,7 @@ import ( ) func init() { - MockObjectCopiers = append(MockObjectCopiers, coreObjectCopier, appsObjectCopier) + MockObjectCopiers = append(MockObjectCopiers, coreObjectCopier, appsObjectCopier, enterpriseObjCopier) } // MockObjectCopiers is a slice of MockObjectCopier methods that MockClient uses to copy runtime.Objects @@ -39,26 +40,51 @@ var MockObjectCopiers []MockObjectCopier // MockObjectCopier is a method used to perform the typed copy of a runtime.Object from src to dst. // It returns true if the runtime.Object was copied, or false if the type is unknown. -type MockObjectCopier func(dst, src runtime.Object) bool +type MockObjectCopier func(dst, src *runtime.Object) bool + +// enterpriseObjCopier is used to copy enterprise runtime.Objects +func enterpriseObjCopier(dst, src *runtime.Object) bool { + dstP := *dst + srcP := *src + switch srcP.(type) { + case *enterprisev1.ClusterMaster: + *dstP.(*enterprisev1.ClusterMaster) = *srcP.(*enterprisev1.ClusterMaster) + case *enterprisev1.IndexerCluster: + *dstP.(*enterprisev1.IndexerCluster) = *srcP.(*enterprisev1.IndexerCluster) + case *enterprisev1.LicenseMaster: + *dstP.(*enterprisev1.LicenseMaster) = *srcP.(*enterprisev1.LicenseMaster) + case *enterprisev1.SearchHeadCluster: + *dstP.(*enterprisev1.SearchHeadCluster) = *srcP.(*enterprisev1.SearchHeadCluster) + case *enterprisev1.Spark: + *dstP.(*enterprisev1.Spark) = *srcP.(*enterprisev1.Spark) + case *enterprisev1.Standalone: + *dstP.(*enterprisev1.Standalone) = *srcP.(*enterprisev1.Standalone) + default: + return false + } + return true +} // coreObjectCopier is used to copy corev1 runtime.Objects -func coreObjectCopier(dst, src runtime.Object) bool { - if reflect.TypeOf(dst).String() == reflect.TypeOf(src.(runtime.Object)).String() { - switch src.(type) { +func coreObjectCopier(dst, src *runtime.Object) bool { + dstP := *dst + srcP := *src + if reflect.TypeOf(dstP).String() == reflect.TypeOf((*src).(runtime.Object)).String() { + switch (srcP).(type) { case *corev1.ConfigMap: - *dst.(*corev1.ConfigMap) = *src.(*corev1.ConfigMap) + *dstP.(*corev1.ConfigMap) = *srcP.(*corev1.ConfigMap) case *corev1.Secret: - *dst.(*corev1.Secret) = *src.(*corev1.Secret) + *dstP.(*corev1.Secret) = *srcP.(*corev1.Secret) case *corev1.SecretList: - *dst.(*corev1.SecretList) = *src.(*corev1.SecretList) + *dstP.(*corev1.SecretList) = *srcP.(*corev1.SecretList) case *corev1.PersistentVolumeClaim: - *dst.(*corev1.PersistentVolumeClaim) = *src.(*corev1.PersistentVolumeClaim) + *dstP.(*corev1.PersistentVolumeClaim) = *srcP.(*corev1.PersistentVolumeClaim) case *corev1.PersistentVolumeClaimList: - *dst.(*corev1.PersistentVolumeClaimList) = *src.(*corev1.PersistentVolumeClaimList) + *dstP.(*corev1.PersistentVolumeClaimList) = *srcP.(*corev1.PersistentVolumeClaimList) case *corev1.Service: - *dst.(*corev1.Service) = *src.(*corev1.Service) + *dstP.(*corev1.Service) = *srcP.(*corev1.Service) case *corev1.Pod: - *dst.(*corev1.Pod) = *src.(*corev1.Pod) + *dstP.(*corev1.Pod) = *srcP.(*corev1.Pod) default: return false } @@ -67,12 +93,14 @@ func coreObjectCopier(dst, src runtime.Object) bool { } // appsObjectCopier is used to copy appsv1 runtime.Objects -func appsObjectCopier(dst, src runtime.Object) bool { - switch src.(type) { +func appsObjectCopier(dst, src *runtime.Object) bool { + srcP := *src + dstP := *dst + switch srcP.(type) { case *appsv1.Deployment: - *dst.(*appsv1.Deployment) = *src.(*appsv1.Deployment) + *dstP.(*appsv1.Deployment) = *srcP.(*appsv1.Deployment) case *appsv1.StatefulSet: - *dst.(*appsv1.StatefulSet) = *src.(*appsv1.StatefulSet) + *dstP.(*appsv1.StatefulSet) = *srcP.(*appsv1.StatefulSet) default: return false } @@ -80,15 +108,15 @@ func appsObjectCopier(dst, src runtime.Object) bool { } // copyMockObject uses the global MockObjectCopiers to perform the typed copy of a runtime.Object from src to dst -func copyMockObject(dst, src runtime.Object) { +func copyMockObject(dst, src *runtime.Object) { for n := range MockObjectCopiers { if MockObjectCopiers[n](dst, src) { return } } - + srcP := *src // default if no types match - dst = src.DeepCopyObject() + *dst = srcP.DeepCopyObject() } // MockFuncCall is used to record a function call to MockClient methods @@ -156,8 +184,10 @@ func (c MockClient) Get(ctx context.Context, key client.ObjectKey, obj runtime.O Obj: obj, }) getObj := c.State[getStateKeyWithKey(key, obj)] + if getObj != nil { - copyMockObject(obj, getObj.(runtime.Object)) + srcObj := getObj.(runtime.Object) + copyMockObject(&obj, &srcObj) return nil } return c.NotFoundError @@ -172,7 +202,8 @@ func (c MockClient) List(ctx context.Context, obj runtime.Object, opts ...client }) listObj := c.ListObj if listObj != nil { - copyMockObject(obj, listObj.(runtime.Object)) + srcObj := listObj.(runtime.Object) + copyMockObject(&obj, &srcObj) return nil } return c.NotFoundError From 0d3a2bd8316be424d79554c48708fd10e713df75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Fri, 13 Nov 2020 10:36:18 -0500 Subject: [PATCH 15/38] Correct typo --- test/monitoring_console/monitoring_console_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/monitoring_console/monitoring_console_test.go b/test/monitoring_console/monitoring_console_test.go index fc08eb78c..3da166084 100644 --- a/test/monitoring_console/monitoring_console_test.go +++ b/test/monitoring_console/monitoring_console_test.go @@ -13,7 +13,7 @@ import ( "github.com/splunk/splunk-operator/test/testenv" ) -var _ = Describe("Montioring Console test", func() { +var _ = Describe("Monitoring Console test", func() { var deployment *testenv.Deployment From 180d2bccaf856447bc44725056f229fe339c218b Mon Sep 17 00:00:00 2001 From: akondur Date: Fri, 13 Nov 2020 11:26:10 -0800 Subject: [PATCH 16/38] Code coverage for password management --- pkg/splunk/common/messages.go | 6 + pkg/splunk/enterprise/indexercluster.go | 2 +- pkg/splunk/enterprise/indexercluster_test.go | 51 ++++++++ pkg/splunk/enterprise/names.go | 4 +- pkg/splunk/enterprise/searchheadcluster.go | 4 +- .../enterprise/searchheadcluster_test.go | 80 +++++++++++- pkg/splunk/util/messages.go | 6 + pkg/splunk/util/secrets.go | 20 ++- pkg/splunk/util/secrets_test.go | 116 +++++++++++++++++- 9 files changed, 263 insertions(+), 26 deletions(-) diff --git a/pkg/splunk/common/messages.go b/pkg/splunk/common/messages.go index 86ab1d3cb..4f8da7aa8 100644 --- a/pkg/splunk/common/messages.go +++ b/pkg/splunk/common/messages.go @@ -23,4 +23,10 @@ const ( // SecretNotFoundError indicates Pod is not found SecretNotFoundError = "Couldn't find secret" + + // SecretTokenNotRetrievable indicates missing secret token in pod secret + SecretTokenNotRetrievable = "Couldn't retrieve %s from secret data" + + // EmptyClusterMasterRef indicates an empty cluster master reference + EmptyClusterMasterRef = "Empty cluster master reference" ) diff --git a/pkg/splunk/enterprise/indexercluster.go b/pkg/splunk/enterprise/indexercluster.go index 053fc56e0..685a0ad92 100644 --- a/pkg/splunk/enterprise/indexercluster.go +++ b/pkg/splunk/enterprise/indexercluster.go @@ -234,7 +234,7 @@ func ApplyIdxcSecret(mgr *indexerClusterPodManager, replicas int32, mock bool) e // Retrieve idxc_secret password from Pod indIdxcSecret, err := splutil.GetSpecificSecretTokenFromPod(mgr.c, indexerPodName, mgr.cr.GetNamespace(), "idxc_secret") if err != nil { - return fmt.Errorf("Couldn't Retrieve idxc_secret from secret data %s", err.Error()) + return fmt.Errorf("Couldn't retrieve idxc_secret from secret data") } // If idxc secret is different from namespace scoped secret change it diff --git a/pkg/splunk/enterprise/indexercluster_test.go b/pkg/splunk/enterprise/indexercluster_test.go index 8175f71e4..9fe368087 100644 --- a/pkg/splunk/enterprise/indexercluster_test.go +++ b/pkg/splunk/enterprise/indexercluster_test.go @@ -455,6 +455,13 @@ func TestSetClusterMaintenanceMode(t *testing.T) { if err.Error() != splcommon.PodNotFoundError { t.Errorf("Couldn't enable cm maintenance mode %s", err.Error()) } + + // Empty clusterMaster reference + cr.Spec.ClusterMasterRef.Name = "" + err = SetClusterMaintenanceMode(c, &cr, false, true) + if err.Error() != splcommon.EmptyClusterMasterRef { + t.Errorf("Couldn't detect empty Cluster Master reference %s", err.Error()) + } } func TestApplyIdxcSecret(t *testing.T) { @@ -617,6 +624,50 @@ func TestApplyIdxcSecret(t *testing.T) { if err != nil { t.Errorf("Couldn't apply idxc secret %s", err.Error()) } + + // Test the setCmMode failure + mgr.cr.Status.NamespaceSecretResourceVersion = "2" + mgr.cr.Spec.ClusterMasterRef.Name = "" + mgr.cr.Status.MaintenanceMode = false + mgr.cr.Status.IndexerSecretChanged = []bool{} + err = ApplyIdxcSecret(mgr, 1, true) + if err.Error() != splcommon.EmptyClusterMasterRef { + t.Errorf("Couldn't apply idxc secret %s", err.Error()) + } + + // Remove idxc secret + secrets = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "stack1-secrets", + Namespace: "test", + }, + Data: map[string][]byte{ + "password": {'1', '2', '3'}, + }, + } + + err = splutil.UpdateResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } + + err = ApplyIdxcSecret(mgr, 1, true) + if err.Error() != fmt.Sprintf(splcommon.SecretTokenNotRetrievable, "idxc_secret") { + t.Errorf("Couldn't recognize missing idxc secret %s", err.Error()) + } + + // Test scenario with same namespace secret and cr status resource version + nsSecret.ResourceVersion = "1" + mgr.cr.Status.NamespaceSecretResourceVersion = nsSecret.ResourceVersion + err = splutil.UpdateResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } + + err = ApplyIdxcSecret(mgr, 1, true) + if err != nil { + t.Errorf("Couldn't apply idxc secret %s", err.Error()) + } } func TestGetIndexerStatefulSet(t *testing.T) { diff --git a/pkg/splunk/enterprise/names.go b/pkg/splunk/enterprise/names.go index b38b51ac9..4c298e83d 100644 --- a/pkg/splunk/enterprise/names.go +++ b/pkg/splunk/enterprise/names.go @@ -52,6 +52,7 @@ const ( //identifier for monitoring console configMap revision monitoringConsoleConfigRev = "monitoringConsoleConfigRev" + // identifier to track the smartstore config rev. on Pod smartStoreConfigRev = "SmartStoreConfigRev" @@ -70,9 +71,6 @@ const ( // command for init container on a CM commandForCMSmartstore = "mkdir -p /opt/splk/etc/master-apps/splunk-operator/local && ln -sfn /mnt/splunk-operator/local/indexes.conf /opt/splk/etc/master-apps/splunk-operator/local/indexes.conf && ln -sfn /mnt/splunk-operator/local/server.conf /opt/splk/etc/master-apps/splunk-operator/local/server.conf" - // Used to identify if the configMap Changed - //configMapUpdated = "configMapChanged" - //smartstoreconfigToken used to track if the config is reflecting on Pod or not configToken = "conftoken" ) diff --git a/pkg/splunk/enterprise/searchheadcluster.go b/pkg/splunk/enterprise/searchheadcluster.go index c7312a405..86c4b4249 100644 --- a/pkg/splunk/enterprise/searchheadcluster.go +++ b/pkg/splunk/enterprise/searchheadcluster.go @@ -203,13 +203,13 @@ func ApplyShcSecret(mgr *searchHeadClusterPodManager, replicas int32, mock bool) // Retrieve shc_secret password from Pod shcSecret, err := splutil.GetSpecificSecretTokenFromPod(mgr.c, shPodName, mgr.cr.GetNamespace(), "shc_secret") if err != nil { - return fmt.Errorf("Couldn't retrieve shc_secret from secret data %s", err.Error()) + return fmt.Errorf("Couldn't retrieve shc_secret from secret data") } // Retrieve admin password from Pod adminPwd, err := splutil.GetSpecificSecretTokenFromPod(mgr.c, shPodName, mgr.cr.GetNamespace(), "password") if err != nil { - return fmt.Errorf("Couldn't retrieve admin password from secret data %s", err.Error()) + return fmt.Errorf("Couldn't retrieve admin password from secret data") } // If shc secret is different from namespace scoped secret change it diff --git a/pkg/splunk/enterprise/searchheadcluster_test.go b/pkg/splunk/enterprise/searchheadcluster_test.go index 9eadb43e2..e8ea2ad46 100644 --- a/pkg/splunk/enterprise/searchheadcluster_test.go +++ b/pkg/splunk/enterprise/searchheadcluster_test.go @@ -320,7 +320,7 @@ func TestApplyShcSecret(t *testing.T) { c := spltest.NewMockClient() // Get namespace scoped secret - _, err := splutil.ApplyNamespaceScopedSecretObject(c, "test") + nsSecret, err := splutil.ApplyNamespaceScopedSecretObject(c, "test") if err != nil { t.Errorf("Apply namespace scoped secret failed") } @@ -432,8 +432,84 @@ func TestApplyShcSecret(t *testing.T) { t.Errorf("Couldn't apply shc secret %s", err.Error()) } + // Update admin password in secret again to hit already set scenario + secrets.Data["password"] = []byte{'1'} + err = splutil.UpdateResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } + + mgr.cr.Status.ShcSecretChanged[0] = false + // Test set again for shc_secret + err = ApplyShcSecret(mgr, 1, true) + if err != nil { + t.Errorf("Couldn't apply shc secret %s", err.Error()) + } + + // Update admin password in secret again to hit already set scenario + secrets.Data["password"] = []byte{'1'} + err = splutil.UpdateResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } + mgr.cr.Status.ShcSecretChanged[0] = false - // Test set again + mgr.cr.Status.AdminSecretChanged[0] = false + // Test set again for admin password + err = ApplyShcSecret(mgr, 1, true) + if err != nil { + t.Errorf("Couldn't apply shc secret %s", err.Error()) + } + + // Missing shc_secret scenario + secrets = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "stack1-secrets", + Namespace: "test", + }, + Data: map[string][]byte{ + "password": {'1', '2', '3'}, + }, + } + err = splutil.UpdateResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } + + err = ApplyShcSecret(mgr, 1, true) + if err.Error() != fmt.Sprintf(splcommon.SecretTokenNotRetrievable, "shc_secret") { + t.Errorf("Couldn't recognize missing shc_secret %s", err.Error()) + } + + // Missing admin password scenario + secrets = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "stack1-secrets", + Namespace: "test", + }, + Data: map[string][]byte{ + "shc_secret": {'a'}, + }, + } + + err = splutil.UpdateResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } + + err = ApplyShcSecret(mgr, 1, true) + if err.Error() != fmt.Sprintf(splcommon.SecretTokenNotRetrievable, "admin password") { + t.Errorf("Couldn't recognize missing admin password %s", err.Error()) + } + + // Make resource version of ns secret and cr the same + mgr.cr.Status.NamespaceSecretResourceVersion = "1" + nsSecret.ResourceVersion = mgr.cr.Status.NamespaceSecretResourceVersion + err = splutil.UpdateResource(c, nsSecret) + if err != nil { + t.Errorf("Couldn't update resource") + } + err = ApplyShcSecret(mgr, 1, true) if err != nil { t.Errorf("Couldn't apply shc secret %s", err.Error()) diff --git a/pkg/splunk/util/messages.go b/pkg/splunk/util/messages.go index 36c5b67f5..74b9dd75d 100644 --- a/pkg/splunk/util/messages.go +++ b/pkg/splunk/util/messages.go @@ -32,4 +32,10 @@ const ( // emptySecretTokenError indicates empty secret token emptySecretTokenError = "Empty secret token" + + // nonExistingSecret indicates a non-existing secret + emptyPodSpecVolumes = "Empty pod spec volumes" + + // emptySecretVolumeSource indicates an empty + emptySecretVolumeSource = "Didn't find secret volume source in any pod volume" ) diff --git a/pkg/splunk/util/secrets.go b/pkg/splunk/util/secrets.go index 538130da0..de9901f35 100644 --- a/pkg/splunk/util/secrets.go +++ b/pkg/splunk/util/secrets.go @@ -74,7 +74,7 @@ func GetSecretFromPod(c splcommon.ControllerClient, PodName string, namespace st var found bool = false for i := range podSpecVolumes { - if podSpecVolumes[i].Name == "mnt-splunk-secrets" { + if podSpecVolumes[i].Name == "mnt-splunk-secrets" && podSpecVolumes[i].VolumeSource.Size() > 0 { secretName = podSpecVolumes[i].VolumeSource.Secret.SecretName if len(secretName) > 0 { found = true @@ -85,7 +85,7 @@ func GetSecretFromPod(c splcommon.ControllerClient, PodName string, namespace st // Check if we find the secret if !found { - return nil, errors.New("Didn't find secret in any pod volume") + return nil, errors.New("Didn't find secret volume source in any pod volume") } // Retrieve the secret @@ -175,13 +175,7 @@ func RemoveUnwantedSecrets(c splcommon.ControllerClient, versionedSecretIdentifi } // Atleast one exists - for _, secret := range list { - // Get version from name - version, err := GetVersionedSecretVersion(secret.GetName(), versionedSecretIdentifier) - if err != nil { - return err - } - + for version, secret := range list { if (latestVersion - version) >= splcommon.MinimumVersionedSecrets { // Delete secret err := DeleteResource(c, &secret) @@ -236,7 +230,7 @@ func GetVersionedSecretVersion(secretName string, versionedSecretIdentifier stri } // GetExistingLatestVersionedSecret retrieves latest EXISTING versionedSecretIdentifier based secret existing currently in the namespace -func GetExistingLatestVersionedSecret(c splcommon.ControllerClient, namespace string, versionedSecretIdentifier string, list bool) (*corev1.Secret, int, []corev1.Secret) { +func GetExistingLatestVersionedSecret(c splcommon.ControllerClient, namespace string, versionedSecretIdentifier string, list bool) (*corev1.Secret, int, map[int]corev1.Secret) { scopedLog := log.WithName("GetExistingLatestVersionedSecret").WithValues( "versionedSecretIdentifier", versionedSecretIdentifier, "namespace", namespace) @@ -265,8 +259,8 @@ func GetExistingLatestVersionedSecret(c splcommon.ControllerClient, namespace st // existingLatestVersionedSecret holds the latest versionedSecretIdentifier based secret, if atleast one exists var existingLatestVersionedSecret corev1.Secret - // List of versionedSecretIdentifier based secrets - var secretListRetr []corev1.Secret + // map of versionedSecretIdentifier based secrets + secretListRetr := make(map[int]corev1.Secret) // Loop through all secrets in K8S cluster for _, secret := range secretList.Items { @@ -279,7 +273,7 @@ func GetExistingLatestVersionedSecret(c splcommon.ControllerClient, namespace st // Append to list of secrets if required if list { - secretListRetr = append(secretListRetr, secret) + secretListRetr[version] = secret } // Version extracted successfully, checking for latest version diff --git a/pkg/splunk/util/secrets_test.go b/pkg/splunk/util/secrets_test.go index f8ef59c50..2a4f57ea3 100644 --- a/pkg/splunk/util/secrets_test.go +++ b/pkg/splunk/util/secrets_test.go @@ -114,11 +114,35 @@ func TestGetSpecificSecretTokenFromPod(t *testing.T) { if err != nil { t.Errorf("Couldn't update secret %s", current.GetName()) } + // Retrieve secret data from non-existing pod gotData, err = GetSpecificSecretTokenFromPod(c, pod.GetName(), "test", "key1") if err.Error() != invalidSecretDataError { t.Errorf("Didn't recognize nil secret data") } + + // Update pod to remove pod spec volumes + pod = &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1-0", + Namespace: "test", + Labels: map[string]string{ + "controller-revision-hash": "v0", + }, + }, + } + + // Update pod spec to remove the volume mount + err = UpdateResource(c, pod) + if err != nil { + t.Errorf("Failed to update pod %s", pod.GetName()) + } + + // Retrieve secret data from non-existing pod + gotData, err = GetSpecificSecretTokenFromPod(c, pod.GetName(), "test", "key1") + if err.Error() != emptyPodSpecVolumes { + t.Errorf("Didn't recognize empty pod spec volumes") + } } func TestGetSecretFromPod(t *testing.T) { @@ -204,6 +228,70 @@ func TestGetSecretFromPod(t *testing.T) { if err.Error() != splcommon.SecretNotFoundError { t.Errorf("Didn't recognize non-existing secret %s", "test") } + + // Update pod to remove pod spec volumes + pod = &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1-0", + Namespace: "test", + Labels: map[string]string{ + "controller-revision-hash": "v0", + }, + }, + } + + // Update pod spec to remove the volume mount + err = UpdateResource(c, pod) + if err != nil { + t.Errorf("Failed to update pod %s", pod.GetName()) + } + + // Retrieve secret data from Pod + gotSecret, err = GetSecretFromPod(c, pod.GetName(), "test") + if err.Error() != emptyPodSpecVolumes { + t.Errorf("Couldn't recognize empty pod spec volumes") + } + + // Update pod spec to add volume mount but not a secret + pod = &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1-0", + Namespace: "test", + Labels: map[string]string{ + "controller-revision-hash": "v0", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + MountPath: "/mnt/splunk-secrets", + Name: "mnt-splunk-secrets", + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: "mnt-splunk-secrets", + }, + }, + }, + } + + // Update pod spec to remove the volume mount + err = UpdateResource(c, pod) + if err != nil { + t.Errorf("Failed to update pod %s", pod.GetName()) + } + + // Retrieve secret data from Pod + gotSecret, err = GetSecretFromPod(c, pod.GetName(), "test") + if err.Error() != emptySecretVolumeSource { + t.Errorf("Couldn't recognize empty pod spec volumes") + } + } func TestGetSecretLabels(t *testing.T) { @@ -249,15 +337,18 @@ func TestSetSecretOwnerRef(t *testing.T) { t.Errorf("Failed to create secret %s", current.GetName()) } - // Reset secret owner reference - // current.OwnerReferences = nil - - // Test existing secret + // Test adding secret owner reference err = SetSecretOwnerRef(c, current.GetName(), &cr) if err != nil { t.Errorf("Couldn't set owner ref for secret %s", current.GetName()) } + // Test existing secret owner reference + err = SetSecretOwnerRef(c, current.GetName(), &cr) + if err != nil { + t.Errorf("Couldn't bail out once owner ref for secret %s is already set", current.GetName()) + } + removedReferralCount, err := RemoveSecretOwnerRef(c, current.GetName(), &cr) if removedReferralCount == 0 || err != nil { @@ -265,6 +356,21 @@ func TestSetSecretOwnerRef(t *testing.T) { } } +func TestRemoveSecretOwnerRef(t *testing.T) { + c := spltest.NewMockClient() + cr := TestResource{ + ObjectMeta: metav1.ObjectMeta{ + Name: "stack1", + Namespace: "test", + }, + } + // Test secret not found error condition + _, err := RemoveSecretOwnerRef(c, "testSecretName", &cr) + if err.Error() != "NotFound" { + t.Errorf("Couldn't find secret not found error") + } + +} func TestRemoveUnwantedSecrets(t *testing.T) { var current corev1.Secret var err error @@ -327,7 +433,7 @@ func TestRemoveUnwantedSecrets(t *testing.T) { // Add an invalid secret to test error leg current = corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: "random", + Name: splcommon.GetVersionedSecretName(versionedSecretIdentifier, "-1"), Namespace: "test", }, Data: make(map[string][]byte), From 4efcf23677d96b0c228027cbb79ad9245170e951 Mon Sep 17 00:00:00 2001 From: Subba Gontla Date: Thu, 12 Nov 2020 12:37:47 -0800 Subject: [PATCH 17/38] cspl-558: Improve code coverage for Index management --- pkg/splunk/client/enterprise.go | 5 +- pkg/splunk/client/enterprise_test.go | 2 +- pkg/splunk/controller/configmap_test.go | 60 ++++ pkg/splunk/enterprise/clustermaster.go | 6 +- pkg/splunk/enterprise/clustermaster_test.go | 45 ++- pkg/splunk/enterprise/configuration.go | 17 - pkg/splunk/enterprise/configuration_test.go | 329 +++++++++++++++++++- pkg/splunk/enterprise/util_test.go | 91 ++++++ 8 files changed, 519 insertions(+), 36 deletions(-) diff --git a/pkg/splunk/client/enterprise.go b/pkg/splunk/client/enterprise.go index 5cf7569ae..a61e5e277 100644 --- a/pkg/splunk/client/enterprise.go +++ b/pkg/splunk/client/enterprise.go @@ -629,10 +629,7 @@ func (c *SplunkClient) DecommissionIndexerClusterPeer(enforceCounts bool) error } // BundlePush pushes the CM master apps bundle to all the indexer peers -func (c *SplunkClient) BundlePush(ignoreIdenticalBundle bool, mock bool) error { - if mock { - return nil - } +func (c *SplunkClient) BundlePush(ignoreIdenticalBundle bool) error { endpoint := fmt.Sprintf("%s/services/cluster/master/control/default/apply", c.ManagementURI) reqBody := fmt.Sprintf("&ignore_identical_bundle=%t", ignoreIdenticalBundle) diff --git a/pkg/splunk/client/enterprise_test.go b/pkg/splunk/client/enterprise_test.go index 9f6fc8ef6..42903e887 100644 --- a/pkg/splunk/client/enterprise_test.go +++ b/pkg/splunk/client/enterprise_test.go @@ -179,7 +179,7 @@ func TestBundlePush(t *testing.T) { wantRequest, _ := http.NewRequest("POST", "https://localhost:8089/services/cluster/master/control/default/apply", body) test := func(c SplunkClient) error { - return c.BundlePush(true, false) + return c.BundlePush(true) } splunkClientTester(t, "TestBundlePush", 200, "", wantRequest, test) } diff --git a/pkg/splunk/controller/configmap_test.go b/pkg/splunk/controller/configmap_test.go index 4cdaf981a..3385ca5e0 100644 --- a/pkg/splunk/controller/configmap_test.go +++ b/pkg/splunk/controller/configmap_test.go @@ -19,6 +19,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" spltest "github.com/splunk/splunk-operator/pkg/splunk/test" ) @@ -41,3 +42,62 @@ func TestApplyConfigMap(t *testing.T) { } spltest.ReconcileTester(t, "TestApplyConfigMap", ¤t, revised, createCalls, updateCalls, reconcile, false) } + +func TestGetConfigMap(t *testing.T) { + current := corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "defaults", + Namespace: "test", + }, + } + + client := spltest.NewMockClient() + namespacedName := types.NamespacedName{Namespace: current.GetNamespace(), Name: current.GetName()} + + _, err := GetConfigMap(client, namespacedName) + if err == nil { + t.Errorf("Should return an error, when the configMap doesn't exist") + } + + _, err = ApplyConfigMap(client, ¤t) + if err != nil { + t.Errorf("Failed to create the configMap. Error: %s", err.Error()) + } + + _, err = GetConfigMap(client, namespacedName) + if err != nil { + t.Errorf("Should not return an error, when the configMap exists") + } +} + +func TestGetConfigMapResourceVersion(t *testing.T) { + current := corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "defaults", + Namespace: "test", + }, + } + + client := spltest.NewMockClient() + namespacedName := types.NamespacedName{Namespace: current.GetNamespace(), Name: current.GetName()} + + _, err := GetConfigMap(client, namespacedName) + if err == nil { + t.Errorf("Should return an error, when the configMap doesn't exist") + } + + _, err = GetConfigMapResourceVersion(client, namespacedName) + if err == nil { + t.Errorf("Should return an error, when the configMap doesn't exist") + } + + _, err = ApplyConfigMap(client, ¤t) + if err != nil { + t.Errorf("Failed to create the configMap. Error: %s", err.Error()) + } + + _, err = GetConfigMapResourceVersion(client, namespacedName) + if err != nil { + t.Errorf("Should not return an error, when the configMap exists") + } +} diff --git a/pkg/splunk/enterprise/clustermaster.go b/pkg/splunk/enterprise/clustermaster.go index 9fea8bad7..1676db702 100644 --- a/pkg/splunk/enterprise/clustermaster.go +++ b/pkg/splunk/enterprise/clustermaster.go @@ -186,9 +186,7 @@ func CheckIfsmartstoreConfigMapUpdatedToPod(c splcommon.ControllerClient, cr *en command := fmt.Sprintf("cat /mnt/splunk-operator/local/%s", configToken) stdOut, stdErr, err := splutil.PodExecCommand(c, cmPodName, cr.GetNamespace(), []string{"/bin/sh"}, command, false, false) if err != nil || stdErr != "" { - if cr.Spec.Mock == false { - return fmt.Errorf("Failed to check config token value on pod. stdout=%s, stderror=%s, error=%v", stdOut, stdErr, err) - } + return fmt.Errorf("Failed to check config token value on pod. stdout=%s, stderror=%s, error=%v", stdOut, stdErr, err) } configMap, exists := getSmartstoreConfigMap(c, cr, SplunkClusterMaster) @@ -267,5 +265,5 @@ func PushMasterAppsBundle(c splcommon.ControllerClient, cr *enterprisev1.Cluster // Get a Splunk client to execute the REST call splunkClient := splclient.NewSplunkClient(fmt.Sprintf("https://%s:8089", fqdnName), "admin", string(adminPwd)) - return splunkClient.BundlePush(true, cr.Spec.Mock) + return splunkClient.BundlePush(true) } diff --git a/pkg/splunk/enterprise/clustermaster_test.go b/pkg/splunk/enterprise/clustermaster_test.go index 94a3259a1..adba53ad3 100644 --- a/pkg/splunk/enterprise/clustermaster_test.go +++ b/pkg/splunk/enterprise/clustermaster_test.go @@ -16,6 +16,7 @@ package enterprise import ( "fmt" + "strings" "testing" "time" @@ -243,6 +244,13 @@ func TestPerformCmBundlePush(t *testing.T) { client := spltest.NewMockClient() + // When the secret object is not present, should return an error + current.Status.BundlePushTracker.NeedToPushMasterApps = true + err := PerformCmBundlePush(client, ¤t) + if err == nil { + t.Errorf("Should return error, when the secret object is not present") + } + secret, err := splutil.ApplyNamespaceScopedSecretObject(client, "test") if err != nil { t.Errorf(err.Error()) @@ -261,23 +269,36 @@ func TestPerformCmBundlePush(t *testing.T) { Data: map[string]string{configToken: ""}, } + _, err = splctrl.ApplyConfigMap(client, &smartstoreConfigMap) + if err != nil { + t.Errorf(err.Error()) + } + current.Status.BundlePushTracker.NeedToPushMasterApps = true - current.Status.BundlePushTracker.LastCheckInterval = time.Now().Unix() - 100 - _, err = splctrl.ApplyConfigMap(client, &smartstoreConfigMap) + //Re-attempting to push the CM bundle in less than 5 seconds should return an error + current.Status.BundlePushTracker.LastCheckInterval = time.Now().Unix() - 1 + err = PerformCmBundlePush(client, ¤t) + if err == nil { + t.Errorf("Bundle Push Should fail, if attempted to push within 5 seconds interval") + } + //Re-attempting to push the CM bundle after 5 seconds passed, should not return an error + current.Status.BundlePushTracker.LastCheckInterval = time.Now().Unix() - 10 err = PerformCmBundlePush(client, ¤t) - if err != nil { - t.Errorf("Bundle Push failed") + if err != nil && strings.HasPrefix(err.Error(), "Will re-attempt to push the bundle after the 5 seconds") { + t.Errorf("Bundle Push Should not fail if reattempted after 5 seconds interval passed. Error: %s", err.Error()) } + // When the CM Bundle push is not pending, should not return an error + current.Status.BundlePushTracker.NeedToPushMasterApps = false err = PerformCmBundlePush(client, ¤t) if err != nil { - t.Errorf("Bundle Push failed") + t.Errorf("Should not return an error when the Bundle push is not required. Error: %s", err.Error()) } } -func TestPushMasterAppsBundlePush(t *testing.T) { +func TestPushMasterAppsBundle(t *testing.T) { current := enterprisev1.ClusterMaster{ TypeMeta: metav1.TypeMeta{ @@ -296,6 +317,12 @@ func TestPushMasterAppsBundlePush(t *testing.T) { client := spltest.NewMockClient() + //Without global secret object, should return an error + err := PushMasterAppsBundle(client, ¤t) + if err == nil { + t.Errorf("Bundle push should fail, when the secret object is not found") + } + secret, err := splutil.ApplyNamespaceScopedSecretObject(client, "test") if err != nil { t.Errorf(err.Error()) @@ -306,8 +333,10 @@ func TestPushMasterAppsBundlePush(t *testing.T) { t.Errorf(err.Error()) } + //Without password, should return an error + delete(secret.Data, "password") err = PushMasterAppsBundle(client, ¤t) - if err != nil { - t.Errorf("Bundle Push failed") + if err == nil { + t.Errorf("Bundle push should fail, when the password is not found") } } diff --git a/pkg/splunk/enterprise/configuration.go b/pkg/splunk/enterprise/configuration.go index 368b822ed..48f2a4fce 100644 --- a/pkg/splunk/enterprise/configuration.go +++ b/pkg/splunk/enterprise/configuration.go @@ -659,23 +659,6 @@ func updateSplunkPodTemplateWithConfig(client splcommon.ControllerClient, podTem } } -// LogSmartStoreVolumes logs smartstore volumes -func LogSmartStoreVolumes(volumeList []enterprisev1.VolumeSpec) { - scopedLog := logC.WithName("LogSmartStoreVolumes") - //var temp string - for _, volume := range volumeList { - scopedLog.Info("Volume: ", "name: ", volume.Name, "endpoint: ", volume.Endpoint, "path: ", volume.Path) - } -} - -// LogSmartStoreIndexes logs smartstore indexes -func LogSmartStoreIndexes(indexList []enterprisev1.IndexSpec) { - scopedLog := logC.WithName("LogSmartStoreIndexes") - for _, index := range indexList { - scopedLog.Info("Index: ", "name: ", index.Name, "remotePath: ", index.RemotePath, "volumeName", index.VolName) - } -} - // isSmartstoreEnabled checks and returns true if smartstore is configured func isSmartstoreConfigured(smartstore *enterprisev1.SmartStoreSpec) bool { if smartstore == nil { diff --git a/pkg/splunk/enterprise/configuration_test.go b/pkg/splunk/enterprise/configuration_test.go index 9fc58b208..ec37dfb13 100644 --- a/pkg/splunk/enterprise/configuration_test.go +++ b/pkg/splunk/enterprise/configuration_test.go @@ -21,6 +21,9 @@ import ( enterprisev1 "github.com/splunk/splunk-operator/pkg/apis/enterprise/v1beta1" splcommon "github.com/splunk/splunk-operator/pkg/splunk/common" + splctrl "github.com/splunk/splunk-operator/pkg/splunk/controller" + spltest "github.com/splunk/splunk-operator/pkg/splunk/test" + splutil "github.com/splunk/splunk-operator/pkg/splunk/util" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" @@ -457,9 +460,199 @@ func TestValidateSplunkSmartstoreSpec(t *testing.T) { if err != nil { t.Errorf("Smartstore config is optional, should not cause an error. But, got the error: %v", err) } + + // Configuring indexes without volume config should return error + SmartStoreWithoutVolumes := enterprisev1.SmartStoreSpec{ + IndexList: []enterprisev1.IndexSpec{ + {Name: "salesdata1", RemotePath: "remotepath1", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata2", RemotePath: "remotepath2", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata3", RemotePath: "remotepath3", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + }, + } + + err = ValidateSplunkSmartstoreSpec(&SmartStoreWithoutVolumes) + if err == nil { + t.Errorf("Smartstore config without volume details should return error") + } + + // Duplicate volume names should be rejected + SmartStoreWithDuplicateVolumes := enterprisev1.SmartStoreSpec{ + VolList: []enterprisev1.VolumeSpec{ + {Name: "msos_s2s3_vol-1", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "s3-secret"}, + {Name: "msos_s2s3_vol-2", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "s3-secret"}, + {Name: "msos_s2s3_vol-1", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "s3-secret"}, + }, + IndexList: []enterprisev1.IndexSpec{ + {Name: "salesdata1", RemotePath: "remotepath1", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata2", RemotePath: "remotepath2", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata3", RemotePath: "remotepath3", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + }, + } + + err = ValidateSplunkSmartstoreSpec(&SmartStoreWithDuplicateVolumes) + if err == nil { + t.Errorf("Duplicate volume configuration should return an error") + } + + // Defaults with invalid volume reference should return error + SmartStoreDefaultsWithNonExistingVolume := enterprisev1.SmartStoreSpec{ + Defaults: enterprisev1.IndexConfDefaultsSpec{ + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol-2"}, + }, + VolList: []enterprisev1.VolumeSpec{ + {Name: "msos_s2s3_vol-1", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "s3-secret"}, + }, + } + + err = ValidateSplunkSmartstoreSpec(&SmartStoreDefaultsWithNonExistingVolume) + if err == nil { + t.Errorf("Volume referred in the indexes defaults should be a valid volume") + } + + //Duplicate index names should return an error + SmartStoreWithDuplicateIndexes := enterprisev1.SmartStoreSpec{ + VolList: []enterprisev1.VolumeSpec{ + {Name: "msos_s2s3_vol", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "s3-secret"}, + }, + IndexList: []enterprisev1.IndexSpec{ + {Name: "salesdata1", RemotePath: "remotepath1", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata1", RemotePath: "remotepath2", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + }, + } + + err = ValidateSplunkSmartstoreSpec(&SmartStoreWithDuplicateIndexes) + if err == nil { + t.Errorf("Duplicate index names should return an error") + } + + // If the default volume is not configured, then each index should be configured + // with an explicit volume info. If not, should return an error + SmartStoreVolumeMissingBothFromDefaultsAndIndex := enterprisev1.SmartStoreSpec{ + VolList: []enterprisev1.VolumeSpec{ + {Name: "msos_s2s3_vol-1", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "s3-secret"}, + }, + IndexList: []enterprisev1.IndexSpec{ + {Name: "salesdata1", RemotePath: "remotepath1"}, + {Name: "salesdata2", RemotePath: "remotepath2", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + }, + } + + err = ValidateSplunkSmartstoreSpec(&SmartStoreVolumeMissingBothFromDefaultsAndIndex) + if err == nil { + t.Errorf("If no default volume, index with missing volume info should return an error") + } + + // Volume referenced from an index must be a valid volume + SmartStoreIndexesWithInvalidVolumeName := enterprisev1.SmartStoreSpec{ + VolList: []enterprisev1.VolumeSpec{ + {Name: "msos_s2s3_vol-1", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "s3-secret"}, + }, + IndexList: []enterprisev1.IndexSpec{ + {Name: "salesdata1", RemotePath: "remotepath1", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol-2"}, + }, + }, + } + + err = ValidateSplunkSmartstoreSpec(&SmartStoreIndexesWithInvalidVolumeName) + if err == nil { + t.Errorf("Index with an invalid volume name should return error") + } } -func TestValidateSplunkSmartstoreCacheManagerSpec(t *testing.T) { +func TestGetSmartstoreIndexesConfig(t *testing.T) { + SmartStoreIndexes := enterprisev1.SmartStoreSpec{ + IndexList: []enterprisev1.IndexSpec{ + {Name: "salesdata1", RemotePath: "remotepath1", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + MaxGlobalDataSizeMB: 6000, + MaxGlobalRawDataSizeMB: 7000, + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata2", RemotePath: "remotepath2", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata3", // Missing RemotePath should be filled with the default "$_index_name" + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + MaxGlobalDataSizeMB: 2000, + MaxGlobalRawDataSizeMB: 3000, + VolName: "msos_s2s3_vol"}, + IndexAndCacheManagerCommonSpec: enterprisev1.IndexAndCacheManagerCommonSpec{ + HotlistBloomFilterRecencyHours: 48, + HotlistRecencySecs: 48 * 60 * 60}, + }, + {Name: "salesdata4", // Missing RemotePath should be filled with the default "$_index_name" + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + MaxGlobalDataSizeMB: 4000, + MaxGlobalRawDataSizeMB: 5000, + VolName: "msos_s2s3_vol"}, + IndexAndCacheManagerCommonSpec: enterprisev1.IndexAndCacheManagerCommonSpec{ + HotlistBloomFilterRecencyHours: 24, + HotlistRecencySecs: 24 * 60 * 60}, + }, + }, + } + + expectedINIFormatString := fmt.Sprintf(` +[salesdata1] +remotePath = volume:msos_s2s3_vol/remotepath1 +maxGlobalDataSizeMB = 6000 +maxGlobalRawDataSizeMB = 7000 + +[salesdata2] +remotePath = volume:msos_s2s3_vol/remotepath2 + +[salesdata3] +remotePath = volume:msos_s2s3_vol/$_index_name +hotlist_bloom_filter_recency_hours = 48 +hotlist_recency_secs = 172800 +maxGlobalDataSizeMB = 2000 +maxGlobalRawDataSizeMB = 3000 + +[salesdata4] +remotePath = volume:msos_s2s3_vol/$_index_name +hotlist_bloom_filter_recency_hours = 24 +hotlist_recency_secs = 86400 +maxGlobalDataSizeMB = 4000 +maxGlobalRawDataSizeMB = 5000 +`) + + indexesConfIni := GetSmartstoreIndexesConfig(SmartStoreIndexes.IndexList) + if indexesConfIni != expectedINIFormatString { + t.Errorf("expected: %s, returned: %s", expectedINIFormatString, indexesConfIni) + } +} +func TestGetServerConfigEntries(t *testing.T) { SmartStoreCacheManager := enterprisev1.CacheManagerSpec{ IndexAndCacheManagerCommonSpec: enterprisev1.IndexAndCacheManagerCommonSpec{ @@ -489,9 +682,16 @@ max_concurrent_uploads = 6 if expectedIniContents != serverConfForCacheManager { t.Errorf("Expected: %s \n Received: %s", expectedIniContents, serverConfForCacheManager) } + + // Empty config should return empty string + serverConfForCacheManager = GetServerConfigEntries(nil) + if serverConfForCacheManager != "" { + t.Errorf("Expected empty string, but received: %s", serverConfForCacheManager) + } + } -func TestValidateSplunkSmartstoreDefaultsSpec(t *testing.T) { +func TestGetSmartstoreIndexesDefaults(t *testing.T) { SmartStoreDefaultsConf := enterprisev1.IndexConfDefaultsSpec{ IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ @@ -520,3 +720,128 @@ maxGlobalRawDataSizeMB = 61440 } } + +func TestCheckIfVolumeExists(t *testing.T) { + SmartStoreConfig := enterprisev1.SmartStoreSpec{ + VolList: []enterprisev1.VolumeSpec{ + {Name: "msos_s2s3_vol", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "s3-secret"}, + }, + IndexList: []enterprisev1.IndexSpec{ + {Name: "salesdata1", RemotePath: "remotepath1", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata2", RemotePath: "remotepath2", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata3", RemotePath: "remotepath3", + IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + }, + } + + // Volume that doesn't should error out + _, err := checkIfVolumeExists(SmartStoreConfig.VolList, "random_volume_name") + + if err == nil { + t.Errorf("if the volume doesn't exists, error should be reported") + } + + // Volume that exists should not error out + index := len(SmartStoreConfig.VolList) - 1 + returnedIndex, err := checkIfVolumeExists(SmartStoreConfig.VolList, SmartStoreConfig.VolList[index].Name) + + if err != nil { + t.Errorf("existing volume should not error out. index id: %d, error: %s", index, err.Error()) + } else if index != returnedIndex { + t.Errorf("Expected index: %d, but returned index id: %d", index, returnedIndex) + } +} + +func TestAreRemoteVolumeKeysChanged(t *testing.T) { + cr := enterprisev1.ClusterMaster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "CM", + Namespace: "test", + }, + Spec: enterprisev1.ClusterMasterSpec{ + SmartStore: enterprisev1.SmartStoreSpec{ + VolList: []enterprisev1.VolumeSpec{ + {Name: "msos_s2s3_vol", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-secret"}, + }, + + IndexList: []enterprisev1.IndexSpec{ + {Name: "salesdata1", RemotePath: "remotepath1", IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata2", RemotePath: "remotepath2", IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + {Name: "salesdata3", RemotePath: "remotepath3", IndexAndGlobalCommonSpec: enterprisev1.IndexAndGlobalCommonSpec{ + VolName: "msos_s2s3_vol"}, + }, + }, + }, + }, + } + + client := spltest.NewMockClient() + var err error + ResourceRev := map[string]string{ + "secret1": "12345", + "secret2": "67890", + } + + // Missing secret object should return an error + keysChanged := AreRemoteVolumeKeysChanged(client, &cr, SplunkClusterMaster, &cr.Spec.SmartStore, ResourceRev, &err) + if err == nil { + t.Errorf("Missing secret object should return an error. keyChangedFlag: %t", keysChanged) + } else if keysChanged { + t.Errorf("When the S3 secret object is not present, Key change should not be reported") + } + + // First time secret version reference should be updated + // Just to simplify the test, assume that the keys are stored as part of the splunk-test-scret + secret, err := splutil.ApplyNamespaceScopedSecretObject(client, "test") + if err != nil { + t.Errorf(err.Error()) + } + + _, err = splctrl.ApplySecret(client, secret) + if err != nil { + t.Errorf(err.Error()) + } + + keysChanged = AreRemoteVolumeKeysChanged(client, &cr, SplunkClusterMaster, &cr.Spec.SmartStore, ResourceRev, &err) + + _, ok := ResourceRev["splunk-test-secret"] + if !ok { + t.Errorf("Failed to update the Resource Version for first time") + } + + // Change the Resource Version, and see if that is being detected + resourceVersion := "3434" + secret.SetResourceVersion(resourceVersion) + + keysChanged = AreRemoteVolumeKeysChanged(client, &cr, SplunkClusterMaster, &cr.Spec.SmartStore, ResourceRev, &err) + resourceVersionUpdated, ok := ResourceRev["splunk-test-secret"] + if !keysChanged || resourceVersion != resourceVersionUpdated { + t.Errorf("Failed detect the secret object change. Key changed: %t, Expected resource version: %s, Updated resource version %s", keysChanged, resourceVersion, resourceVersionUpdated) + } + + // No change on the secret object should return false + keysChanged = AreRemoteVolumeKeysChanged(client, &cr, SplunkClusterMaster, &cr.Spec.SmartStore, ResourceRev, &err) + resourceVersionUpdated, ok = ResourceRev["splunk-test-secret"] + if keysChanged { + t.Errorf("If there is no change on secret object, should return false") + } + + // Empty volume list should return false + cr.Spec.SmartStore.VolList = nil + keysChanged = AreRemoteVolumeKeysChanged(client, &cr, SplunkClusterMaster, &cr.Spec.SmartStore, ResourceRev, &err) + if keysChanged { + t.Errorf("Empty volume should not report a key change") + } +} diff --git a/pkg/splunk/enterprise/util_test.go b/pkg/splunk/enterprise/util_test.go index d837dfc9e..731215537 100644 --- a/pkg/splunk/enterprise/util_test.go +++ b/pkg/splunk/enterprise/util_test.go @@ -202,6 +202,13 @@ func TestApplySmartstoreConfigMap(t *testing.T) { } test(client, &cr, &cr.Spec.SmartStore, `{"metadata":{"name":"splunk-idxCluster--smartstore","namespace":"test","creationTimestamp":null},"data":{"conftoken":"1601945361","indexes.conf":"[default]\nrepFactor = auto\nmaxDataSize = auto\nhomePath = $SPLUNK_DB/$_index_name/db\ncoldPath = $SPLUNK_DB/$_index_name/colddb\nthawedPath = $SPLUNK_DB/$_index_name/thaweddb\n \n[volume:msos_s2s3_vol]\nstorageType = remote\npath = s3://testbucket-rs-london\nremote.s3.access_key = abcdJDckRkxhMEdmSk5FekFRRzBFOXV6bGNldzJSWE9IenhVUy80aa\nremote.s3.secret_key = g4NVp0a29PTzlPdGczWk1vekVUcVBSa0o4NkhBWWMvR1NadDV4YVEy\nremote.s3.endpoint = https://s3-eu-west-2.amazonaws.com\n \n[salesdata1]\nremotePath = volume:msos_s2s3_vol/remotepath1\n\n[salesdata2]\nremotePath = volume:msos_s2s3_vol/remotepath2\n\n[salesdata3]\nremotePath = volume:msos_s2s3_vol/remotepath3\n","server.conf":""}}`) + + // Missing Volume config should return an error + cr.Spec.SmartStore.VolList = nil + _, _, err = ApplySmartstoreConfigMap(client, &cr, &cr.Spec.SmartStore) + if err == nil { + t.Errorf("Configuring Indexes without volumes should return an error") + } } func TestRemoveOwenerReferencesForSecretObjectsReferredBySmartstoreVolumes(t *testing.T) { @@ -214,6 +221,9 @@ func TestRemoveOwenerReferencesForSecretObjectsReferredBySmartstoreVolumes(t *te SmartStore: enterprisev1.SmartStoreSpec{ VolList: []enterprisev1.VolumeSpec{ {Name: "msos_s2s3_vol", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-secret"}, + {Name: "msos_s2s3_vol_2", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-secret"}, + {Name: "msos_s2s3_vol_3", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-secret"}, + {Name: "msos_s2s3_vol_4", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-secret"}, }, IndexList: []enterprisev1.IndexSpec{ @@ -260,4 +270,85 @@ func TestRemoveOwenerReferencesForSecretObjectsReferredBySmartstoreVolumes(t *te if err != nil { t.Errorf("Couldn't Remove S3 Secret object references %v", err) } + + // If the secret object doesn't exist, should return an error + // Here in the volume references, secrets splunk-test-sec_1, to splunk-test-sec_4 doesn't exist + cr = enterprisev1.ClusterMaster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "idxCluster", + Namespace: "testWithNoSecret", + }, + Spec: enterprisev1.ClusterMasterSpec{ + SmartStore: enterprisev1.SmartStoreSpec{ + VolList: []enterprisev1.VolumeSpec{ + {Name: "msos_s2s3_vol", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-sec_1"}, + {Name: "msos_s2s3_vol_2", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-sec_2"}, + {Name: "msos_s2s3_vol_3", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-sec_3"}, + {Name: "msos_s2s3_vol_4", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-sec_4"}, + }, + }, + }, + } + + // S3 secret owner reference removal, with non-existing secret objects + err = DeleteOwnerReferencesForS3SecretObjects(client, secret, &cr.Spec.SmartStore) + if err == nil { + t.Errorf("Should report an error, when the secret object referenced in the volume config doesn't exist") + } + + // Smartstore volume config with non-existing secret objects + err = DeleteOwnerReferencesForResources(client, &cr, &cr.Spec.SmartStore) + if err == nil { + t.Errorf("Should report an error, when the secret objects doesn't exist") + } +} + +func TestGetSmartstoreRemoteVolumeSecrets(t *testing.T) { + cr := enterprisev1.ClusterMaster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "CM", + Namespace: "test", + }, + Spec: enterprisev1.ClusterMasterSpec{ + SmartStore: enterprisev1.SmartStoreSpec{ + VolList: []enterprisev1.VolumeSpec{ + {Name: "msos_s2s3_vol", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "splunk-test-secret"}, + }, + }, + }, + } + + client := spltest.NewMockClient() + + // Just to simplify the test, assume that the keys are stored as part of the splunk-test-secret object, hence create that secret object + secret, err := splutil.ApplyNamespaceScopedSecretObject(client, "test") + if err != nil { + t.Errorf(err.Error()) + } + + _, err = splctrl.ApplySecret(client, secret) + if err != nil { + t.Errorf(err.Error()) + } + + // Missing S3 access key should return error + _, _, _, err = GetSmartstoreRemoteVolumeSecrets(cr.Spec.SmartStore.VolList[0], client, &cr, &cr.Spec.SmartStore) + if err == nil { + t.Errorf("Missing S3 access key should return an error") + } + + secret.Data[s3AccessKey] = []byte("abcdJDckRkxhMEdmSk5FekFRRzBFOXV6bGNldzJSWE9IenhVUy80aa") + + // Missing S3 secret key should return error + _, _, _, err = GetSmartstoreRemoteVolumeSecrets(cr.Spec.SmartStore.VolList[0], client, &cr, &cr.Spec.SmartStore) + if err == nil { + t.Errorf("Missing S3 secret key should return an error") + } + + // When access key and secret keys are present, returned keys should not be empty. Also, should not return an error + secret.Data[s3SecretKey] = []byte("g4NVp0a29PTzlPdGczWk1vekVUcVBSa0o4NkhBWWMvR1NadDV4YVEy") + accessKey, secretKey, _, err := GetSmartstoreRemoteVolumeSecrets(cr.Spec.SmartStore.VolList[0], client, &cr, &cr.Spec.SmartStore) + if accessKey == "" || secretKey == "" || err != nil { + t.Errorf("Missing S3 Keys / Error not expected, when the Secret object with the S3 specific keys are present") + } } From e0c253072963575502f822c4b81b50b01933e921 Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Tue, 17 Nov 2020 19:13:04 -0800 Subject: [PATCH 18/38] Addressed review comments --- pkg/splunk/enterprise/indexercluster_test.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pkg/splunk/enterprise/indexercluster_test.go b/pkg/splunk/enterprise/indexercluster_test.go index 359af1e0d..be2084e21 100644 --- a/pkg/splunk/enterprise/indexercluster_test.go +++ b/pkg/splunk/enterprise/indexercluster_test.go @@ -118,7 +118,6 @@ func TestGetClusterMasterClient(t *testing.T) { ClusterMasterPhase: splcommon.PhaseReady, }, } - cr.Status.IndexerSecretChanged = append(cr.Status.IndexerSecretChanged, true) secrets := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "splunk-master1-indexer-secrets", @@ -891,11 +890,6 @@ func TestInvalidIndexerClusterSpec(t *testing.T) { Name: "master1", Namespace: "test", }, - Spec: enterprisev1.ClusterMasterSpec{ - CommonSplunkSpec: enterprisev1.CommonSplunkSpec{ - Mock: true, - }, - }, } c := spltest.NewMockClient() From 3171f19fb3331d6f3db5be4c9216b7e37ad9d37e Mon Sep 17 00:00:00 2001 From: akondur Date: Wed, 18 Nov 2020 12:36:47 -0800 Subject: [PATCH 19/38] Fixing merge conflicts --- pkg/splunk/common/messages.go | 3 ++ pkg/splunk/enterprise/indexercluster.go | 4 +-- pkg/splunk/enterprise/indexercluster_test.go | 31 +++++++++++++++++++- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/pkg/splunk/common/messages.go b/pkg/splunk/common/messages.go index 4f8da7aa8..39df3be7e 100644 --- a/pkg/splunk/common/messages.go +++ b/pkg/splunk/common/messages.go @@ -21,6 +21,9 @@ const ( // PodNotFoundError indicates Pod is not found PodNotFoundError = "Couldn't find pod" + // PodSecretNotFoundError indicates that a mounted secret wasn't found on the Pod + PodSecretNotFoundError = "Couldn't find secret in Pod %s" + // SecretNotFoundError indicates Pod is not found SecretNotFoundError = "Couldn't find secret" diff --git a/pkg/splunk/enterprise/indexercluster.go b/pkg/splunk/enterprise/indexercluster.go index d118ce76c..78be62724 100644 --- a/pkg/splunk/enterprise/indexercluster.go +++ b/pkg/splunk/enterprise/indexercluster.go @@ -239,14 +239,14 @@ func ApplyIdxcSecret(mgr *indexerClusterPodManager, replicas int32, mock bool) e // Retrieve secret from pod podSecret, err := splutil.GetSecretFromPod(mgr.c, indexerPodName, mgr.cr.GetNamespace()) if err != nil { - return fmt.Errorf("Couldn't retrieve idxc_secret from secret data") + return fmt.Errorf("Couldn't find secret in Pod %s", indexerPodName) } // Retrieve idxc_secret token if indIdxcSecretByte, ok := podSecret.Data["idxc_secret"]; ok { indIdxcSecret = string(indIdxcSecretByte) } else { - return fmt.Errorf("Couldn't retrieve idxc_secret from secret %s mounted on pod %s", podSecret.GetName(), indexerPodName) + return fmt.Errorf("Couldn't retrieve idxc_secret from secret data") } // If idxc secret is different from namespace scoped secret change it diff --git a/pkg/splunk/enterprise/indexercluster_test.go b/pkg/splunk/enterprise/indexercluster_test.go index 229b4ff56..4e94fc018 100644 --- a/pkg/splunk/enterprise/indexercluster_test.go +++ b/pkg/splunk/enterprise/indexercluster_test.go @@ -731,10 +731,11 @@ func TestApplyIdxcSecret(t *testing.T) { t.Errorf("Apply namespace scoped secret failed") } + podName := "splunk-stack1-indexer-0" // Create pod pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ - Name: "splunk-stack1-indexer-0", + Name: podName, Namespace: "test", Labels: map[string]string{ "controller-revision-hash": "v0", @@ -867,12 +868,22 @@ func TestApplyIdxcSecret(t *testing.T) { mockSplunkClient.CheckRequests(t, method) // Don't set as it is set already + secrets.Data["idxc_secret"] = []byte{'a'} + err = splutil.UpdateResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } err = ApplyIdxcSecret(mgr, 1, true) if err != nil { t.Errorf("Couldn't apply idxc secret %s", err.Error()) } mgr.cr.Status.IndexerSecretChanged[0] = false + secrets.Data["idxc_secret"] = []byte{'a'} + err = splutil.UpdateResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } // Test set again err = ApplyIdxcSecret(mgr, 1, true) if err != nil { @@ -880,6 +891,12 @@ func TestApplyIdxcSecret(t *testing.T) { } // Test the setCmMode failure + secrets.Data["idxc_secret"] = []byte{'a'} + err = splutil.UpdateResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } + mgr.cr.Status.NamespaceSecretResourceVersion = "2" mgr.cr.Spec.ClusterMasterRef.Name = "" mgr.cr.Status.MaintenanceMode = false @@ -922,6 +939,18 @@ func TestApplyIdxcSecret(t *testing.T) { if err != nil { t.Errorf("Couldn't apply idxc secret %s", err.Error()) } + + // Test missing secret from pod + mgr.cr.Status.NamespaceSecretResourceVersion = "10" + err = splutil.DeleteResource(c, secrets) + if err != nil { + t.Errorf("Couldn't update resource") + } + + err = ApplyIdxcSecret(mgr, 1, true) + if err.Error() != fmt.Sprintf(splcommon.PodSecretNotFoundError, podName) { + t.Errorf("Couldn't recognize missing secret from Pod, error: %s", err.Error()) + } } func TestInvalidIndexerClusterSpec(t *testing.T) { From a7d8745a0282587b5c32ccd19c887f6812dbaa46 Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Mon, 23 Nov 2020 20:07:37 -0800 Subject: [PATCH 20/38] CSPL-588: This PR improves code coverage for clustermaster specific code --- pkg/splunk/enterprise/clustermaster.go | 6 +- pkg/splunk/enterprise/clustermaster_test.go | 76 +++++++++++++++++++-- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/pkg/splunk/enterprise/clustermaster.go b/pkg/splunk/enterprise/clustermaster.go index 1676db702..90b2e47fe 100644 --- a/pkg/splunk/enterprise/clustermaster.go +++ b/pkg/splunk/enterprise/clustermaster.go @@ -58,9 +58,6 @@ func ApplyClusterMaster(client splcommon.ControllerClient, cr *enterprisev1.Clus if !reflect.DeepEqual(cr.Status.SmartStore, cr.Spec.SmartStore) || AreRemoteVolumeKeysChanged(client, cr, SplunkClusterMaster, &cr.Spec.SmartStore, cr.Status.ResourceRevMap, &err) { - if err != nil { - return result, err - } _, configMapDataChanged, err := ApplySmartstoreConfigMap(client, cr, &cr.Spec.SmartStore) if err != nil { return result, err @@ -167,6 +164,9 @@ func getClusterMasterStatefulSet(client splcommon.ControllerClient, cr *enterpri var extraEnvVar []corev1.EnvVar ss, err := getSplunkStatefulSet(client, cr, &cr.Spec.CommonSplunkSpec, SplunkClusterMaster, 1, extraEnvVar) + if err != nil { + return ss, err + } _, exists := getSmartstoreConfigMap(client, cr, SplunkClusterMaster) if exists { diff --git a/pkg/splunk/enterprise/clustermaster_test.go b/pkg/splunk/enterprise/clustermaster_test.go index adba53ad3..22e7c854e 100644 --- a/pkg/splunk/enterprise/clustermaster_test.go +++ b/pkg/splunk/enterprise/clustermaster_test.go @@ -22,6 +22,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" enterprisev1 "github.com/splunk/splunk-operator/pkg/apis/enterprise/v1beta1" @@ -140,6 +141,15 @@ func TestApplyClusterMasterWithSmartstore(t *testing.T) { {MetaName: "*v1.ConfigMap-test-splunk-stack1-clustermaster-smartstore"}, {MetaName: "*v1.ConfigMap-test-splunk-stack1-clustermaster-smartstore"}, {MetaName: "*v1.StatefulSet-test-splunk-stack1-cluster-master"}, + {MetaName: "*v1.Pod-test-splunk-stack1-cluster-master-0"}, + {MetaName: "*v1.Secret-test-splunk-test-secret"}, + {MetaName: "*v1.Secret-test-splunk-test-monitoring-console-secret-v1"}, + {MetaName: "*v1.Service-test-splunk-test-monitoring-console-service"}, + {MetaName: "*v1.Service-test-splunk-test-monitoring-console-headless"}, + {MetaName: "*v1.ConfigMap-test-splunk-test-monitoring-console"}, + {MetaName: "*v1.ConfigMap-test-splunk-test-monitoring-console"}, + {MetaName: "*v1.StatefulSet-test-splunk-test-monitoring-console"}, + {MetaName: "*v1.StatefulSet-test-splunk-test-monitoring-console"}, } labels := map[string]string{ "app.kubernetes.io/component": "versionedSecrets", @@ -151,7 +161,7 @@ func TestApplyClusterMasterWithSmartstore(t *testing.T) { } listmockCall := []spltest.MockFuncCall{ {ListOpts: listOpts}} - createCalls := map[string][]spltest.MockFuncCall{"Get": funcCalls, "Create": {funcCalls[6], funcCalls[7], funcCalls[9], funcCalls[12]}, "List": {listmockCall[0]}, "Update": {funcCalls[0], funcCalls[3]}} + createCalls := map[string][]spltest.MockFuncCall{"Get": funcCalls, "Create": {funcCalls[6], funcCalls[7], funcCalls[9], funcCalls[15], funcCalls[16], funcCalls[17], funcCalls[18], funcCalls[20]}, "List": {listmockCall[0], listmockCall[0], listmockCall[0]}, "Update": {funcCalls[0], funcCalls[3], funcCalls[20]}} updateCalls := map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[2], funcCalls[3], funcCalls[5], funcCalls[5], funcCalls[6], funcCalls[7], funcCalls[8], funcCalls[9], funcCalls[11], funcCalls[11], funcCalls[12]}, "Update": {funcCalls[10], funcCalls[12]}, "List": {listmockCall[0]}} current := enterprisev1.ClusterMaster{ @@ -189,10 +199,11 @@ func TestApplyClusterMasterWithSmartstore(t *testing.T) { }, } client := spltest.NewMockClient() - // Without S3 keys, ApplyStandalone should fail + + // Without S3 keys, ApplyClusterMaster should fail _, err := ApplyClusterMaster(client, ¤t) if err == nil { - t.Errorf("ApplyIndexerCluster should fail without S3 secrets configured") + t.Errorf("ApplyClusterMaster should fail without S3 secrets configured") } // Create namespace scoped secret @@ -222,7 +233,59 @@ func TestApplyClusterMasterWithSmartstore(t *testing.T) { _, err := ApplyClusterMaster(c, cr.(*enterprisev1.ClusterMaster)) return err } - spltest.ReconcileTesterWithoutRedundantCheck(t, "TestApplyClusterMasterWithSmartstore", ¤t, revised, createCalls, updateCalls, reconcile, true, secret, &smartstoreConfigMap) + + client.AddObject(&smartstoreConfigMap) + ss, _ := getClusterMasterStatefulSet(client, ¤t) + ss.Status.ReadyReplicas = 1 + + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1-cluster-master-0", + Namespace: "test", + Labels: map[string]string{ + "controller-revision-hash": "v1", + }, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + ContainerStatuses: []corev1.ContainerStatus{ + {Ready: true}, + }, + }, + } + + spltest.ReconcileTesterWithoutRedundantCheck(t, "TestApplyClusterMasterWithSmartstore-0", ¤t, revised, createCalls, updateCalls, reconcile, true, secret, &smartstoreConfigMap, ss, pod) + + current.Status.BundlePushTracker.NeedToPushMasterApps = true + if _, err = ApplyClusterMaster(client, ¤t); err != nil { + t.Errorf("ApplyClusterMaster() should not have returned error") + } + + current.Spec.CommonSplunkSpec.EtcStorage = "-abcd" + if _, err := ApplyClusterMaster(client, ¤t); err == nil { + t.Errorf("ApplyClusterMaster() should have returned error") + } + + var replicas int32 = 3 + current.Spec.CommonSplunkSpec.EtcStorage = "" + ss.Status.ReadyReplicas = 3 + ss.Spec.Replicas = &replicas + ss.Spec.Template.Spec.Containers[0].Image = "splunk/splunk" + client.AddObject(ss) + if _, err := ApplyClusterMaster(client, ¤t); err == nil { + t.Errorf("ApplyClusterMaster() should have returned error") + } + + ss.Status.ReadyReplicas = 1 + *ss.Spec.Replicas = ss.Status.ReadyReplicas + objects := []runtime.Object{ss, pod} + client.AddObjects(objects) + current.Spec.CommonSplunkSpec.Mock = false + + // This should fail at ApplyMonitoringConsole + if _, err := ApplyClusterMaster(client, ¤t); err == nil { + t.Errorf("ApplyClusterMaster() should have returned error") + } } func TestPerformCmBundlePush(t *testing.T) { @@ -333,6 +396,11 @@ func TestPushMasterAppsBundle(t *testing.T) { t.Errorf(err.Error()) } + err = PushMasterAppsBundle(client, ¤t) + if err == nil { + t.Errorf("Bundle push should fail, when the password is not found") + } + //Without password, should return an error delete(secret.Data, "password") err = PushMasterAppsBundle(client, ¤t) From c68dc94921d4624b1cd9123ddac40cd5a7c0f9aa Mon Sep 17 00:00:00 2001 From: akondur Date: Tue, 24 Nov 2020 14:47:02 -0800 Subject: [PATCH 21/38] Fixing review comments --- pkg/splunk/common/names.go | 3 +++ pkg/splunk/enterprise/indexercluster.go | 10 +++++----- pkg/splunk/enterprise/indexercluster_test.go | 14 +++++++------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/pkg/splunk/common/names.go b/pkg/splunk/common/names.go index e71621c5b..3865760bb 100644 --- a/pkg/splunk/common/names.go +++ b/pkg/splunk/common/names.go @@ -34,6 +34,9 @@ const ( // MinimumVersionedSecrets holds the minimum number of secrets to be held per version MinimumVersionedSecrets = 3 + + // IdxcSecret represents indexer cluster pass4Symmkey secret token + IdxcSecret = "idxc_secret" ) // GetVersionedSecretName returns a versioned secret name diff --git a/pkg/splunk/enterprise/indexercluster.go b/pkg/splunk/enterprise/indexercluster.go index 78be62724..30c130763 100644 --- a/pkg/splunk/enterprise/indexercluster.go +++ b/pkg/splunk/enterprise/indexercluster.go @@ -229,7 +229,7 @@ func ApplyIdxcSecret(mgr *indexerClusterPodManager, replicas int32, mock bool) e scopedLog.Info("Namespaced scoped secret revision has changed") // Retrieve idxc_secret password from secret data - nsIdxcSecret := string(namespaceSecret.Data["idxc_secret"]) + nsIdxcSecret := string(namespaceSecret.Data[splcommon.IdxcSecret]) // Loop over all indexer pods and get individual pod's idxc password for i := int32(0); i <= replicas-1; i++ { @@ -239,14 +239,14 @@ func ApplyIdxcSecret(mgr *indexerClusterPodManager, replicas int32, mock bool) e // Retrieve secret from pod podSecret, err := splutil.GetSecretFromPod(mgr.c, indexerPodName, mgr.cr.GetNamespace()) if err != nil { - return fmt.Errorf("Couldn't find secret in Pod %s", indexerPodName) + return fmt.Errorf(fmt.Sprintf(splcommon.PodSecretNotFoundError, indexerPodName)) } // Retrieve idxc_secret token - if indIdxcSecretByte, ok := podSecret.Data["idxc_secret"]; ok { + if indIdxcSecretByte, ok := podSecret.Data[splcommon.IdxcSecret]; ok { indIdxcSecret = string(indIdxcSecretByte) } else { - return fmt.Errorf("Couldn't retrieve idxc_secret from secret data") + return fmt.Errorf(fmt.Sprintf(splcommon.SecretTokenNotRetrievable, splcommon.IdxcSecret)) } // If idxc secret is different from namespace scoped secret change it @@ -320,7 +320,7 @@ func ApplyIdxcSecret(mgr *indexerClusterPodManager, replicas int32, mock bool) e return err } - podSecret.Data["idxc_secret"] = splunkReadableData["idxc_secret"] + podSecret.Data[splcommon.IdxcSecret] = splunkReadableData[splcommon.IdxcSecret] podSecret.Data["default.yml"] = splunkReadableData["default.yml"] _, err = splctrl.ApplySecret(mgr.c, podSecret) diff --git a/pkg/splunk/enterprise/indexercluster_test.go b/pkg/splunk/enterprise/indexercluster_test.go index 4e94fc018..37ba56841 100644 --- a/pkg/splunk/enterprise/indexercluster_test.go +++ b/pkg/splunk/enterprise/indexercluster_test.go @@ -805,8 +805,8 @@ func TestApplyIdxcSecret(t *testing.T) { Namespace: "test", }, Data: map[string][]byte{ - "password": {'1', '2', '3'}, - "idxc_secret": {'a'}, + "password": {'1', '2', '3'}, + splcommon.IdxcSecret: {'a'}, }, } initObjectList = append(initObjectList, secrets) @@ -816,7 +816,7 @@ func TestApplyIdxcSecret(t *testing.T) { mockHandlers := []spltest.MockHTTPHandler{ { Method: "POST", - URL: fmt.Sprintf("https://splunk-stack1-indexer-0.splunk-stack1-indexer-headless.test.svc.cluster.local:8089/services/cluster/config/config?secret=%s", string(nsSecret.Data["idxc_secret"])), + URL: fmt.Sprintf("https://splunk-stack1-indexer-0.splunk-stack1-indexer-headless.test.svc.cluster.local:8089/services/cluster/config/config?secret=%s", string(nsSecret.Data[splcommon.IdxcSecret])), Status: 200, Err: nil, }, @@ -868,7 +868,7 @@ func TestApplyIdxcSecret(t *testing.T) { mockSplunkClient.CheckRequests(t, method) // Don't set as it is set already - secrets.Data["idxc_secret"] = []byte{'a'} + secrets.Data[splcommon.IdxcSecret] = []byte{'a'} err = splutil.UpdateResource(c, secrets) if err != nil { t.Errorf("Couldn't update resource") @@ -879,7 +879,7 @@ func TestApplyIdxcSecret(t *testing.T) { } mgr.cr.Status.IndexerSecretChanged[0] = false - secrets.Data["idxc_secret"] = []byte{'a'} + secrets.Data[splcommon.IdxcSecret] = []byte{'a'} err = splutil.UpdateResource(c, secrets) if err != nil { t.Errorf("Couldn't update resource") @@ -891,7 +891,7 @@ func TestApplyIdxcSecret(t *testing.T) { } // Test the setCmMode failure - secrets.Data["idxc_secret"] = []byte{'a'} + secrets.Data[splcommon.IdxcSecret] = []byte{'a'} err = splutil.UpdateResource(c, secrets) if err != nil { t.Errorf("Couldn't update resource") @@ -923,7 +923,7 @@ func TestApplyIdxcSecret(t *testing.T) { } err = ApplyIdxcSecret(mgr, 1, true) - if err.Error() != fmt.Sprintf(splcommon.SecretTokenNotRetrievable, "idxc_secret") { + if err.Error() != fmt.Sprintf(splcommon.SecretTokenNotRetrievable, splcommon.IdxcSecret) { t.Errorf("Couldn't recognize missing idxc secret %s", err.Error()) } From ac332293cc815ea15f555928fa40a83f2b63795f Mon Sep 17 00:00:00 2001 From: Bill Chung Date: Tue, 1 Dec 2020 16:05:40 +0900 Subject: [PATCH 22/38] fix IndexerCluster clusterMasterRef yaml in doc --- docs/CustomResources.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/CustomResources.md b/docs/CustomResources.md index d43b9332a..3506e9741 100644 --- a/docs/CustomResources.md +++ b/docs/CustomResources.md @@ -244,7 +244,8 @@ metadata: name: example spec: replicas: 3 - clusterMasterRef: example-cm + clusterMasterRef: + name: example-cm ``` Note: `clusterMasterRef` is required field in case of IndexerCluster resource since it will be used to connect the IndexerCluster to ClusterMaster resource. From 1cb5f32bdf0612db9062ffa9df28356d167082e0 Mon Sep 17 00:00:00 2001 From: Param Dhanoya Date: Tue, 1 Dec 2020 09:18:31 -0800 Subject: [PATCH 23/38] Updated create eks cluster to use existing VPC --- test/deploy-eks-cluster.sh | 2 +- test/env.sh | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/deploy-eks-cluster.sh b/test/deploy-eks-cluster.sh index c379a5653..6d8a3abb3 100755 --- a/test/deploy-eks-cluster.sh +++ b/test/deploy-eks-cluster.sh @@ -20,7 +20,7 @@ function createCluster() { found=$(eksctl get cluster --name "${CLUSTER_NAME}") if [ -z "${found}" ]; then - eksctl create cluster --name=${CLUSTER_NAME} --nodes=${NUM_WORKERS} + eksctl create cluster --name=${CLUSTER_NAME} --nodes=${NUM_WORKERS} --vpc-public-subnets=${VPC_PUBLIC_SUBNET_STRING} --vpc-private-subnets=${VPC_PRIVATE_SUBNET_STRING} if [ $? -ne 0 ]; then echo "Unable to create cluster - ${CLUSTER_NAME}" return 1 diff --git a/test/env.sh b/test/env.sh index 12967ca8a..a9c092238 100644 --- a/test/env.sh +++ b/test/env.sh @@ -8,6 +8,8 @@ : "${NUM_NODES:=2}" : "${COMMIT_HASH:=}" : "${ECR_REGISTRY:=}" +: "${VPC_PUBLIC_SUBNET_STRING:=}" +: "${VPC_PRIVATE_SUBNET_STRING:=}" # Docker registry to use to push the test images to and pull from in the cluster if [ -z "${PRIVATE_REGISTRY}" ]; then From 3c1f39f66ee7c732973452f0096f746cfff0a84b Mon Sep 17 00:00:00 2001 From: akondur Date: Tue, 1 Dec 2020 12:24:08 -0800 Subject: [PATCH 24/38] Fix read global object link --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 4a3b331a0..e86d60c9e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -157,7 +157,7 @@ splunk-default-monitoring-console-0 1/1 Running 0 30s splunk-s1-standalone-0 1/1 Running 0 45s ``` -The passwords for the instance are generated automatically. To review the passwords, please refer to the [Reading global kubernetes secret object](#reading-global-kubernetes-secret-object) instructions. +The passwords for the instance are generated automatically. To review the passwords, please refer to the [Reading global kubernetes secret object](Examples.md#reading-global-kubernetes-secret-object) instructions. *Note: if your shell prints a `%` at the end, leave that out when you copy the output.* From d5050b6adcc0adacb7b87933a4870cda91bbaca9 Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Tue, 1 Dec 2020 14:59:34 -0800 Subject: [PATCH 25/38] address review comment --- pkg/splunk/enterprise/clustermaster.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/splunk/enterprise/clustermaster.go b/pkg/splunk/enterprise/clustermaster.go index 90b2e47fe..88313f027 100644 --- a/pkg/splunk/enterprise/clustermaster.go +++ b/pkg/splunk/enterprise/clustermaster.go @@ -72,6 +72,11 @@ func ApplyClusterMaster(client splcommon.ControllerClient, cr *enterprisev1.Clus cr.Status.SmartStore = cr.Spec.SmartStore } + // This is to take care of case where AreRemoteVolumeKeysChanged returns an error if it returns false. + if err != nil { + return result, err + } + defer func() { err = client.Status().Update(context.TODO(), cr) if err != nil { From 665d2b59920c037ed43189f3ab0927a59b56daef Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Tue, 1 Dec 2020 14:38:08 -0800 Subject: [PATCH 26/38] CSPL-534: Fix unnecessary pod recycle during scale up/down --- .../v1beta1/indexercluster_types.go | 3 + .../v1beta1/searchheadcluster_types.go | 3 + pkg/splunk/controller/statefulset.go | 142 +++++++++++++- pkg/splunk/controller/statefulset_test.go | 178 ++++++++++++++++++ pkg/splunk/enterprise/indexercluster.go | 2 +- pkg/splunk/enterprise/indexercluster_test.go | 3 +- pkg/splunk/enterprise/searchheadcluster.go | 2 +- .../enterprise/searchheadcluster_test.go | 7 +- pkg/splunk/test/controller.go | 73 +++---- 9 files changed, 372 insertions(+), 41 deletions(-) diff --git a/pkg/apis/enterprise/v1beta1/indexercluster_types.go b/pkg/apis/enterprise/v1beta1/indexercluster_types.go index 964df0850..702af40f8 100644 --- a/pkg/apis/enterprise/v1beta1/indexercluster_types.go +++ b/pkg/apis/enterprise/v1beta1/indexercluster_types.go @@ -95,6 +95,9 @@ type IndexerClusterStatus struct { // Indicates if the cluster is in maintenance mode. MaintenanceMode bool `json:"maintenance_mode"` + // Indicates if we need to recheck the revision update on pods + SkipRecheckUpdate bool `json:"skip_recheck_update"` + // status of each indexer cluster peer Peers []IndexerClusterMemberStatus `json:"peers"` } diff --git a/pkg/apis/enterprise/v1beta1/searchheadcluster_types.go b/pkg/apis/enterprise/v1beta1/searchheadcluster_types.go index ea3dd3f21..04dd455f9 100644 --- a/pkg/apis/enterprise/v1beta1/searchheadcluster_types.go +++ b/pkg/apis/enterprise/v1beta1/searchheadcluster_types.go @@ -109,6 +109,9 @@ type SearchHeadClusterStatus struct { // Indicates resource version of namespace scoped secret NamespaceSecretResourceVersion string `json:"namespace_scoped_secret_resource_version"` + // Indicates if we need to recheck the revision update on pods + SkipRecheckUpdate bool `json:"skip_recheck_update"` + // status of each search head cluster member Members []SearchHeadClusterMemberStatus `json:"members"` } diff --git a/pkg/splunk/controller/statefulset.go b/pkg/splunk/controller/statefulset.go index 721367ba8..ee5c7ff28 100644 --- a/pkg/splunk/controller/statefulset.go +++ b/pkg/splunk/controller/statefulset.go @@ -34,8 +34,9 @@ type DefaultStatefulSetPodManager struct{} // Update for DefaultStatefulSetPodManager handles all updates for a statefulset of standard pods func (mgr *DefaultStatefulSetPodManager) Update(client splcommon.ControllerClient, statefulSet *appsv1.StatefulSet, desiredReplicas int32) (splcommon.Phase, error) { phase, err := ApplyStatefulSet(client, statefulSet) + skipRecheckUpdate := false if err == nil && phase == splcommon.PhaseReady { - phase, err = UpdateStatefulSetPods(client, statefulSet, mgr, desiredReplicas) + phase, err = UpdateStatefulSetPods(client, statefulSet, mgr, desiredReplicas, &skipRecheckUpdate) } return phase, err } @@ -85,8 +86,102 @@ func ApplyStatefulSet(c splcommon.ControllerClient, revised *appsv1.StatefulSet) return splcommon.PhaseReady, nil } +// UpdatePodRevisionHash updates the controller-revision-hash label on pods. +func UpdatePodRevisionHash(c splcommon.ControllerClient, statefulSet *appsv1.StatefulSet, readyReplicas int32) error { + scopedLog := log.WithName("updatePodRevisionHash").WithValues( + "name", statefulSet.GetObjectMeta().GetName(), + "namespace", statefulSet.GetObjectMeta().GetNamespace()) + + namespacedName := types.NamespacedName{Namespace: statefulSet.GetNamespace(), Name: statefulSet.GetName()} + var current appsv1.StatefulSet + + err := c.Get(context.TODO(), namespacedName, ¤t) + if err != nil { + scopedLog.Error(err, "Unable to Get statefulset", "statefulset", statefulSet.GetName()) + } + + for n := readyReplicas - 1; n >= 0; n-- { + // get Pod + podName := fmt.Sprintf("%s-%d", current.GetName(), n) + namespacedName := types.NamespacedName{Namespace: current.GetNamespace(), Name: podName} + var pod corev1.Pod + err := c.Get(context.TODO(), namespacedName, &pod) + if err != nil { + scopedLog.Error(err, "Unable to find Pod", "podName", podName) + return err + } + if pod.Status.Phase != corev1.PodRunning || len(pod.Status.ContainerStatuses) == 0 || pod.Status.ContainerStatuses[0].Ready != true { + scopedLog.Error(err, "Waiting for Pod to become ready", "podName", podName) + return err + } + + labels := pod.GetLabels() + revisionHash := labels["controller-revision-hash"] + // update pod controller revision hash + if current.Status.UpdateRevision != "" && current.Status.UpdateRevision != revisionHash { + // update controller-revision-hash label for pod with statefulset update revision + labels["controller-revision-hash"] = current.Status.UpdateRevision + pod.SetLabels(labels) + err = splutil.UpdateResource(c, &pod) + if err != nil { + scopedLog.Error(err, "Unable to update controller-revision-hash label for pod", "podName", podName) + return err + } + scopedLog.Info("Updated controller-revision-hash label for pod", "podName", podName, "revision", current.Status.UpdateRevision) + } + } + + scopedLog.Info("Updated controller-revision-hash label for all pods") + return nil +} + +func isRevisionUpdateSuccessful(c splcommon.ControllerClient, statefulSet *appsv1.StatefulSet) bool { + scopedLog := log.WithName("isUpdateSuccessful").WithValues( + "name", statefulSet.GetObjectMeta().GetName(), + "namespace", statefulSet.GetObjectMeta().GetNamespace()) + + namespacedName := types.NamespacedName{Namespace: statefulSet.GetNamespace(), Name: statefulSet.GetName()} + var current appsv1.StatefulSet + + err := c.Get(context.TODO(), namespacedName, ¤t) + if err != nil { + scopedLog.Error(err, "Unable to Get statefulset", "statefulset", statefulSet.GetName()) + return false + } + + // Check if current revision is different from update revision + if current.Status.CurrentRevision == current.Status.UpdateRevision { + scopedLog.Error(err, "Statefulset UpdateRevision not updated yet") + return false + } + + return true +} + +func checkAndUpdatePodRevision(c splcommon.ControllerClient, statefulSet *appsv1.StatefulSet, readyReplicas int32, skipRecheckUpdate *bool) (bool, error) { + scopedLog := log.WithName("UpdateStatefulSetPods").WithValues( + "name", statefulSet.GetObjectMeta().GetName(), + "namespace", statefulSet.GetObjectMeta().GetNamespace()) + var err error + if !isRevisionUpdateSuccessful(c, statefulSet) { + scopedLog.Error(err, "Statefulset not updated yet") + *skipRecheckUpdate = false + return false, err + } + // update the controller-revision-hash label on pods to + // to avoid unnecessary recycle of pods + err = UpdatePodRevisionHash(c, statefulSet, readyReplicas) + if err != nil { + scopedLog.Error(err, "Unable to update pod-revision-hash for the pods") + *skipRecheckUpdate = false + return false, err + } + *skipRecheckUpdate = true + return true, nil +} + // UpdateStatefulSetPods manages scaling and config updates for StatefulSets -func UpdateStatefulSetPods(c splcommon.ControllerClient, statefulSet *appsv1.StatefulSet, mgr splcommon.StatefulSetPodManager, desiredReplicas int32) (splcommon.Phase, error) { +func UpdateStatefulSetPods(c splcommon.ControllerClient, statefulSet *appsv1.StatefulSet, mgr splcommon.StatefulSetPodManager, desiredReplicas int32, skipRecheckUpdate *bool) (splcommon.Phase, error) { scopedLog := log.WithName("UpdateStatefulSetPods").WithValues( "name", statefulSet.GetObjectMeta().GetName(), "namespace", statefulSet.GetObjectMeta().GetNamespace()) @@ -97,11 +192,25 @@ func UpdateStatefulSetPods(c splcommon.ControllerClient, statefulSet *appsv1.Sta if readyReplicas < replicas { scopedLog.Info("Waiting for pods to become ready") if readyReplicas > 0 { + if !*skipRecheckUpdate { + ret, err := checkAndUpdatePodRevision(c, statefulSet, readyReplicas, skipRecheckUpdate) + if !ret || err != nil { + scopedLog.Error(err, "Unable to update pod-revision-hash for the pods") + return splcommon.PhaseError, err + } + } return splcommon.PhaseScalingUp, nil } return splcommon.PhasePending, nil } else if readyReplicas > replicas { scopedLog.Info("Waiting for scale down to complete") + if !*skipRecheckUpdate { + ret, err := checkAndUpdatePodRevision(c, statefulSet, readyReplicas-1, skipRecheckUpdate) + if !ret || err != nil { + scopedLog.Error(err, "Unable to update pod-revision-hash for the pods") + return splcommon.PhaseError, err + } + } return splcommon.PhaseScalingDown, nil } @@ -112,7 +221,23 @@ func UpdateStatefulSetPods(c splcommon.ControllerClient, statefulSet *appsv1.Sta // scale up StatefulSet to match desiredReplicas scopedLog.Info("Scaling replicas up", "replicas", desiredReplicas) *statefulSet.Spec.Replicas = desiredReplicas - return splcommon.PhaseScalingUp, splutil.UpdateResource(c, statefulSet) + err := splutil.UpdateResource(c, statefulSet) + if err != nil { + scopedLog.Error(err, "Unable to update statefulset") + return splcommon.PhaseError, err + } + + // Check if the revision was updated successfully for Statefulset. + // It so can happen that it may take few seconds for the update to be + // reflected in the resource. In that case, just return from here and + // check the status back in the next reconcile loop. + var ret bool + ret, err = checkAndUpdatePodRevision(c, statefulSet, readyReplicas, skipRecheckUpdate) + if !ret || err != nil { + scopedLog.Error(err, "Unable to update pod-revision-hash for the pods") + return splcommon.PhaseError, err + } + return splcommon.PhaseScalingUp, err } // check for scaling down @@ -139,6 +264,17 @@ func UpdateStatefulSetPods(c splcommon.ControllerClient, statefulSet *appsv1.Sta return splcommon.PhaseError, err } + // Check if the revision was updated successfully for Statefulset. + // It so can happen that it may take few seconds for the update to be + // reflected in the resource. In that case, just return from here and + // check the status back in the next reconcile loop. + var ret bool + ret, err = checkAndUpdatePodRevision(c, statefulSet, readyReplicas-1, skipRecheckUpdate) + if !ret || err != nil { + scopedLog.Error(err, "Unable to update pod-revision-hash for the pods") + return splcommon.PhaseError, err + } + // delete PVCs used by the pod so that a future scale up will have clean state for _, vol := range statefulSet.Spec.VolumeClaimTemplates { namespacedName := types.NamespacedName{ diff --git a/pkg/splunk/controller/statefulset_test.go b/pkg/splunk/controller/statefulset_test.go index e1c0c29e6..214ffaa7a 100644 --- a/pkg/splunk/controller/statefulset_test.go +++ b/pkg/splunk/controller/statefulset_test.go @@ -18,13 +18,16 @@ import ( "testing" appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" spltest "github.com/splunk/splunk-operator/pkg/splunk/test" splutil "github.com/splunk/splunk-operator/pkg/splunk/util" enterprisev1 "github.com/splunk/splunk-operator/pkg/apis/enterprise/v1beta1" + splcommon "github.com/splunk/splunk-operator/pkg/splunk/common" ) func TestApplyStatefulSet(t *testing.T) { @@ -57,6 +60,181 @@ func TestDefaultStatefulSetPodManager(t *testing.T) { spltest.PodManagerTester(t, method, &mgr) } +func updateStatefulSetPodsTester(t *testing.T, mgr splcommon.StatefulSetPodManager, statefulSet *appsv1.StatefulSet, desiredReplicas int32, initObjects ...runtime.Object) (splcommon.Phase, error) { + // initialize client + c := spltest.NewMockClient() + c.AddObjects(initObjects) + skipRecheckUpdate := false + phase, err := UpdateStatefulSetPods(c, statefulSet, mgr, desiredReplicas, &skipRecheckUpdate) + return phase, err +} + +func TestUpdateStatefulSetPods(t *testing.T) { + mgr := DefaultStatefulSetPodManager{} + var replicas int32 = 1 + statefulSet := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1", + Namespace: "test", + }, + Spec: appsv1.StatefulSetSpec{ + Replicas: &replicas, + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-etc", Namespace: "test"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-var", Namespace: "test"}}, + }, + }, + Status: appsv1.StatefulSetStatus{ + Replicas: replicas, + ReadyReplicas: replicas, + UpdatedReplicas: replicas, + UpdateRevision: "v1", + }, + } + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1-0", + Namespace: "test", + Labels: map[string]string{ + "controller-revision-hash": "v0", + }, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + ContainerStatuses: []corev1.ContainerStatus{ + {Ready: true}, + }, + }, + } + + var phase splcommon.Phase + phase, err := updateStatefulSetPodsTester(t, &mgr, statefulSet, 1 /*desiredReplicas*/, statefulSet, pod) + if err != nil && phase != splcommon.PhaseUpdating { + t.Errorf("UpdateStatefulSetPods should not have returned error") + } + + // Check the scenario where UpdatePodRevisionHash should return error when Pod is not added to client. + phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 2 /*desiredReplicas*/, statefulSet) + if err == nil && phase != splcommon.PhaseError { + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError") + } + + replicas = 3 + statefulSet.Status.ReadyReplicas = 3 + statefulSet.Spec.Replicas = &replicas + // Check the scenario where UpdatePodRevisionHash should return error when Pod is not added to client. + phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 1 /*desiredReplicas*/, statefulSet) + if err == nil && phase != splcommon.PhaseError { + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError") + } + + // readyReplicas < replicas + replicas = 3 + statefulSet.Status.ReadyReplicas = 2 + statefulSet.Spec.Replicas = &replicas + // Check the scenario where UpdatePodRevisionHash should return error when readyReplicas < replicas and Pod is not found + phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 1 /*desiredReplicas*/, statefulSet, pod) + if err == nil && phase != splcommon.PhaseError { + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError") + } + + // CurrentRevision = UpdateRevision + statefulSet.Status.CurrentRevision = "v1" + phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 1 /*desiredReplicas*/, statefulSet, pod) + if err == nil && phase != splcommon.PhaseError { + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError") + } + + // readyReplicas > replicas + replicas = 2 + statefulSet.Status.ReadyReplicas = 3 + statefulSet.Spec.Replicas = &replicas + statefulSet.Status.CurrentRevision = "" + // Check the scenario where UpdatePodRevisionHash should return error when readyReplicas > replicas and Pod is not found + phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 1 /*desiredReplicas*/, statefulSet, pod) + if err == nil && phase != splcommon.PhaseError { + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError") + } + + // CurrentRevision = UpdateRevision + statefulSet.Status.CurrentRevision = "v1" + phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 1 /*desiredReplicas*/, statefulSet, pod) + if err == nil && phase != splcommon.PhaseError { + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError") + } + + // Check the scenario where UpdatePodRevisionHash should return error when statefulset is not added to client. + phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 2 /*desiredReplicas*/, pod) + if err == nil && phase != splcommon.PhaseError { + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError") + } + +} + +func updatePodRevisionHashTester(t *testing.T, statefulSet *appsv1.StatefulSet, replicas int32, initObjects ...runtime.Object) error { + c := spltest.NewMockClient() + c.AddObjects(initObjects) + return UpdatePodRevisionHash(c, statefulSet, replicas) +} + +func TestUpdatePodRevisionHash(t *testing.T) { + var replicas int32 = 1 + statefulSet := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1", + Namespace: "test", + }, + Spec: appsv1.StatefulSetSpec{ + Replicas: &replicas, + VolumeClaimTemplates: []corev1.PersistentVolumeClaim{ + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-etc", Namespace: "test"}}, + {ObjectMeta: metav1.ObjectMeta{Name: "pvc-var", Namespace: "test"}}, + }, + }, + Status: appsv1.StatefulSetStatus{ + Replicas: replicas, + ReadyReplicas: replicas, + UpdatedReplicas: replicas, + UpdateRevision: "v1", + }, + } + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1-0", + Namespace: "test", + Labels: map[string]string{ + "controller-revision-hash": "v0", + }, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + ContainerStatuses: []corev1.ContainerStatus{ + {Ready: true}, + }, + }, + } + + err := updatePodRevisionHashTester(t, statefulSet, replicas, statefulSet, pod) + if err != nil { + t.Errorf("UpdatePodRevisionHash should not have returned error") + } + + // Test the negative case where phase != corev1.PodRunning + pod.Status.Phase = corev1.PodPending + err = updatePodRevisionHashTester(t, statefulSet, replicas, statefulSet, pod) + if err != nil { + t.Errorf("UpdatePodRevisionHash should not have returned error") + } + + // Test invalid pod name splunk-stack1-1 + pod.Status.Phase = corev1.PodRunning + replicas = 2 + err = updatePodRevisionHashTester(t, statefulSet, replicas, statefulSet, pod) + if err == nil { + t.Errorf("UpdatePodRevisionHash should have returned error") + } +} + func TestSetStatefulSetOwnerRef(t *testing.T) { cr := enterprisev1.Standalone{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/splunk/enterprise/indexercluster.go b/pkg/splunk/enterprise/indexercluster.go index a328cea61..153535ca4 100644 --- a/pkg/splunk/enterprise/indexercluster.go +++ b/pkg/splunk/enterprise/indexercluster.go @@ -371,7 +371,7 @@ func (mgr *indexerClusterPodManager) Update(c splcommon.ControllerClient, statef } // manage scaling and updates - return splctrl.UpdateStatefulSetPods(c, statefulSet, mgr, desiredReplicas) + return splctrl.UpdateStatefulSetPods(c, statefulSet, mgr, desiredReplicas, &mgr.cr.Status.SkipRecheckUpdate) } // PrepareScaleDown for indexerClusterPodManager prepares indexer pod to be removed via scale down event; it returns true when ready diff --git a/pkg/splunk/enterprise/indexercluster_test.go b/pkg/splunk/enterprise/indexercluster_test.go index d2c940410..0a8988ebe 100644 --- a/pkg/splunk/enterprise/indexercluster_test.go +++ b/pkg/splunk/enterprise/indexercluster_test.go @@ -604,13 +604,14 @@ func TestIndexerClusterPodManager(t *testing.T) { {MetaName: "*v1.PersistentVolumeClaim-test-pvc-etc-splunk-stack1-1"}, {MetaName: "*v1.PersistentVolumeClaim-test-pvc-var-splunk-stack1-1"}, } - wantCalls = map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[3], funcCalls[3]}, "Create": {funcCalls[1]}, "Delete": pvcCalls, "Update": {funcCalls[0]}} + wantCalls = map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[3], funcCalls[3], funcCalls[0], funcCalls[0], funcCalls[4]}, "Create": {funcCalls[1]}, "Delete": pvcCalls, "Update": {funcCalls[0], funcCalls[4]}} wantCalls["Get"] = append(wantCalls["Get"], pvcCalls...) pvcList := []*corev1.PersistentVolumeClaim{ {ObjectMeta: metav1.ObjectMeta{Name: "pvc-etc-splunk-stack1-1", Namespace: "test"}}, {ObjectMeta: metav1.ObjectMeta{Name: "pvc-var-splunk-stack1-1", Namespace: "test"}}, } method = "indexerClusterPodManager.Update(Decommission)" + pod.ObjectMeta.Name = "splunk-stack1-0" indexerClusterPodManagerUpdateTester(t, method, mockHandlers, 1, splcommon.PhaseScalingDown, statefulSet, wantCalls, nil, statefulSet, pod, pvcList[0], pvcList[1]) } diff --git a/pkg/splunk/enterprise/searchheadcluster.go b/pkg/splunk/enterprise/searchheadcluster.go index c7312a405..67b17d604 100644 --- a/pkg/splunk/enterprise/searchheadcluster.go +++ b/pkg/splunk/enterprise/searchheadcluster.go @@ -347,7 +347,7 @@ func (mgr *searchHeadClusterPodManager) Update(c splcommon.ControllerClient, sta } // manage scaling and updates - return splctrl.UpdateStatefulSetPods(mgr.c, statefulSet, mgr, desiredReplicas) + return splctrl.UpdateStatefulSetPods(mgr.c, statefulSet, mgr, desiredReplicas, &mgr.cr.Status.SkipRecheckUpdate) } // PrepareScaleDown for searchHeadClusterPodManager prepares search head pod to be removed via scale down event; it returns true when ready diff --git a/pkg/splunk/enterprise/searchheadcluster_test.go b/pkg/splunk/enterprise/searchheadcluster_test.go index 9eadb43e2..c83799ae8 100644 --- a/pkg/splunk/enterprise/searchheadcluster_test.go +++ b/pkg/splunk/enterprise/searchheadcluster_test.go @@ -294,8 +294,11 @@ func TestSearchHeadClusterPodManager(t *testing.T) { extraCalls := []spltest.MockFuncCall{ {MetaName: "*v1.Pod-test-splunk-stack1-search-head-1"}, {MetaName: "*v1.Pod-test-splunk-stack1-search-head-1"}, + {MetaName: "*v1.StatefulSet-test-splunk-stack1"}, + {MetaName: "*v1.StatefulSet-test-splunk-stack1"}, + {MetaName: "*v1.Pod-test-splunk-stack1-0"}, } - //funcCalls[1] = spltest.MockFuncCall{MetaName: "*v1.Pod-test-splunk-stack1-0"} + wantCalls = map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[1], funcCalls[2]}, "Delete": pvcCalls, "Update": {funcCalls[0]}, "Create": {funcCalls[1]}} wantCalls["Get"] = append(wantCalls["Get"], extraCalls...) wantCalls["Get"] = append(wantCalls["Get"], pvcCalls...) @@ -303,7 +306,7 @@ func TestSearchHeadClusterPodManager(t *testing.T) { {ObjectMeta: metav1.ObjectMeta{Name: "pvc-etc-splunk-stack1-1", Namespace: "test"}}, {ObjectMeta: metav1.ObjectMeta{Name: "pvc-var-splunk-stack1-1", Namespace: "test"}}, } - pod.ObjectMeta.Name = "splunk-stack1-2" + pod.ObjectMeta.Name = "splunk-stack1-0" replicas = 2 statefulSet.Status.Replicas = 2 statefulSet.Status.ReadyReplicas = 2 diff --git a/pkg/splunk/test/controller.go b/pkg/splunk/test/controller.go index 92e21af5e..099177361 100644 --- a/pkg/splunk/test/controller.go +++ b/pkg/splunk/test/controller.go @@ -451,8 +451,11 @@ func PodManagerUpdateTester(t *testing.T, method string, mgr splcommon.StatefulS // PodManagerTester is used to test StatefulSetPodManager reconcile operations func PodManagerTester(t *testing.T, method string, mgr splcommon.StatefulSetPodManager) { // test create - funcCalls := []MockFuncCall{{MetaName: "*v1.StatefulSet-test-splunk-stack1"}} - createCalls := map[string][]MockFuncCall{"Get": funcCalls, "Create": funcCalls} + funcCalls := []MockFuncCall{ + {MetaName: "*v1.StatefulSet-test-splunk-stack1"}, + {MetaName: "*v1.Pod-test-splunk-stack1-0"}, + } + createCalls := map[string][]MockFuncCall{"Get": {funcCalls[0]}, "Create": {funcCalls[0]}} var replicas int32 = 1 current := &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ @@ -473,19 +476,35 @@ func PodManagerTester(t *testing.T, method string, mgr splcommon.StatefulSetPodM UpdateRevision: "v1", }, } + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "splunk-stack1-0", + Namespace: "test", + Labels: map[string]string{ + "controller-revision-hash": "v0", + }, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodRunning, + ContainerStatuses: []corev1.ContainerStatus{ + {Ready: true}, + }, + }, + } + PodManagerUpdateTester(t, method, mgr, 1, splcommon.PhasePending, current, createCalls, nil) // test update revised := current.DeepCopy() revised.Spec.Template.ObjectMeta.Labels = map[string]string{"one": "two"} - updateCalls := map[string][]MockFuncCall{"Get": funcCalls, "Update": funcCalls} + updateCalls := map[string][]MockFuncCall{"Get": {funcCalls[0]}, "Update": {funcCalls[0]}} methodPlus := fmt.Sprintf("%s(%s)", method, "Update StatefulSet") PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhaseUpdating, revised, updateCalls, nil, current) // test scale up (zero ready so far; wait for ready) revised = current.DeepCopy() current.Status.ReadyReplicas = 0 - scaleUpCalls := map[string][]MockFuncCall{"Get": funcCalls} + scaleUpCalls := map[string][]MockFuncCall{"Get": {funcCalls[0]}} methodPlus = fmt.Sprintf("%s(%s)", method, "ScalingUp, 0 ready") PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhasePending, revised, scaleUpCalls, nil, current) @@ -493,22 +512,26 @@ func PodManagerTester(t *testing.T, method string, mgr splcommon.StatefulSetPodM replicas = 2 current.Status.Replicas = 2 current.Status.ReadyReplicas = 1 + scaleUpCalls["Get"] = append(scaleUpCalls["Get"], funcCalls[0], funcCalls[0], funcCalls[1]) + scaleUpCalls["Update"] = append(scaleUpCalls["Update"], funcCalls[1]) methodPlus = fmt.Sprintf("%s(%s)", method, "ScalingUp, 1/2 ready") - PodManagerUpdateTester(t, methodPlus, mgr, 2, splcommon.PhaseScalingUp, revised, scaleUpCalls, nil, current) + PodManagerUpdateTester(t, methodPlus, mgr, 2, splcommon.PhaseScalingUp, revised, scaleUpCalls, nil, current, pod) // test scale up (1 ready scaling to 2) replicas = 1 current.Status.Replicas = 1 current.Status.ReadyReplicas = 1 + updateCalls["Get"] = append(updateCalls["Get"], funcCalls[0], funcCalls[0], funcCalls[1]) methodPlus = fmt.Sprintf("%s(%s)", method, "ScalingUp, Update Replicas 1=>2") - PodManagerUpdateTester(t, methodPlus, mgr, 2, splcommon.PhaseScalingUp, revised, updateCalls, nil, current) + PodManagerUpdateTester(t, methodPlus, mgr, 2, splcommon.PhaseScalingUp, revised, updateCalls, nil, current, pod) // test scale down (2 ready, 1 desired) replicas = 1 current.Status.Replicas = 1 current.Status.ReadyReplicas = 2 + delete(scaleUpCalls, "Update") methodPlus = fmt.Sprintf("%s(%s)", method, "ScalingDown, Ready > Replicas") - PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhaseScalingDown, revised, scaleUpCalls, nil, current) + PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhaseScalingDown, revised, scaleUpCalls, nil, current, pod) // test scale down (2 ready scaling down to 1) pvcCalls := []MockFuncCall{ @@ -516,7 +539,7 @@ func PodManagerTester(t *testing.T, method string, mgr splcommon.StatefulSetPodM {MetaName: "*v1.PersistentVolumeClaim-test-pvc-var-splunk-stack1-1"}, } scaleDownCalls := map[string][]MockFuncCall{ - "Get": {funcCalls[0], pvcCalls[0], pvcCalls[1]}, + "Get": {funcCalls[0], funcCalls[0], funcCalls[0], funcCalls[1], pvcCalls[0], pvcCalls[1]}, "Update": {funcCalls[0]}, "Delete": pvcCalls, } @@ -528,7 +551,7 @@ func PodManagerTester(t *testing.T, method string, mgr splcommon.StatefulSetPodM current.Status.Replicas = 2 current.Status.ReadyReplicas = 2 methodPlus = fmt.Sprintf("%s(%s)", method, "ScalingDown, Update Replicas 2=>1") - PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhaseScalingDown, revised, scaleDownCalls, nil, current, pvcList[0], pvcList[1]) + PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhaseScalingDown, revised, scaleDownCalls, nil, current, pod, pvcList[0], pvcList[1]) // test pod not found replicas = 1 @@ -539,30 +562,6 @@ func PodManagerTester(t *testing.T, method string, mgr splcommon.StatefulSetPodM methodPlus = fmt.Sprintf("%s(%s)", method, "Pod not found") PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhaseError, revised, getPodCalls, errors.New("NotFound"), current) - // test pod updated - pod := &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "splunk-stack1-0", - Namespace: "test", - Labels: map[string]string{ - "controller-revision-hash": "v0", - }, - }, - Status: corev1.PodStatus{ - Phase: corev1.PodRunning, - ContainerStatuses: []corev1.ContainerStatus{ - {Ready: true}, - }, - }, - } - updatePodCalls := map[string][]MockFuncCall{"Get": podCalls, "Delete": {podCalls[1]}} - methodPlus = fmt.Sprintf("%s(%s)", method, "Recycle pod") - PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhaseUpdating, revised, updatePodCalls, nil, current, pod) - - // test all pods ready - pod.ObjectMeta.Labels["controller-revision-hash"] = "v1" - methodPlus = fmt.Sprintf("%s(%s)", method, "All pods ready") - labels := map[string]string{ "app.kubernetes.io/component": "versionedSecrets", "app.kubernetes.io/managed-by": "splunk-operator", @@ -574,6 +573,14 @@ func PodManagerTester(t *testing.T, method string, mgr splcommon.StatefulSetPodM listmockCall := []MockFuncCall{ {ListOpts: listOpts}} + updatePodCalls := map[string][]MockFuncCall{"Get": podCalls, "Delete": {}, "List": {listmockCall[0]}} + methodPlus = fmt.Sprintf("%s(%s)", method, "Recycle pod") + PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhaseReady, revised, updatePodCalls, nil, current, pod) + + // test all pods ready + pod.ObjectMeta.Labels["controller-revision-hash"] = "v1" + methodPlus = fmt.Sprintf("%s(%s)", method, "All pods ready") + getPodCalls["List"] = []MockFuncCall{listmockCall[0]} PodManagerUpdateTester(t, methodPlus, mgr, 1, splcommon.PhaseReady, revised, getPodCalls, nil, current, pod) From 52228406e785c2ecbc99e7d33b2eeac4e5f35cc7 Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Wed, 2 Dec 2020 17:51:18 -0800 Subject: [PATCH 27/38] fix a UT failure due to merge --- pkg/splunk/enterprise/clustermaster_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/splunk/enterprise/clustermaster_test.go b/pkg/splunk/enterprise/clustermaster_test.go index 22e7c854e..2a8d377e7 100644 --- a/pkg/splunk/enterprise/clustermaster_test.go +++ b/pkg/splunk/enterprise/clustermaster_test.go @@ -272,8 +272,8 @@ func TestApplyClusterMasterWithSmartstore(t *testing.T) { ss.Spec.Replicas = &replicas ss.Spec.Template.Spec.Containers[0].Image = "splunk/splunk" client.AddObject(ss) - if _, err := ApplyClusterMaster(client, ¤t); err == nil { - t.Errorf("ApplyClusterMaster() should have returned error") + if result, err := ApplyClusterMaster(client, ¤t); err == nil && !result.Requeue { + t.Errorf("ApplyClusterMaster() should have returned error or result.requeue should have been false") } ss.Status.ReadyReplicas = 1 From c4022d47db54f235556e6727d2587a25a0414160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Fri, 4 Dec 2020 22:37:36 -0500 Subject: [PATCH 28/38] Bring orbs current in CircleCI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gábor Lipták --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 25da76898..66c86b3b9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,9 +2,9 @@ version: 2.1 orbs: - kubernetes: circleci/kubernetes@0.4.0 - aws-cli: circleci/aws-cli@0.1.22 - aws-eks: sandbox/aws-eks@0.1.2 + kubernetes: circleci/kubernetes@0.11.2 + aws-cli: circleci/aws-cli@1.3.0 + aws-eks: circleci/aws-eks@1.0.3 workflows: version: 2 From 15b96ddb53ac7c0e3d454929b3a1c9bcd4b6cf9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sat, 5 Dec 2020 09:24:19 -0500 Subject: [PATCH 29/38] Set pkg.go.dev badge in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8a87782c2..aabf90b9a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Splunk Operator for Kubernetes [![License](https://img.shields.io/:license-apache-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) -[![GoDoc](https://godoc.org/github.com/splunk/splunk-operator?status.svg)](https://godoc.org/github.com/splunk/splunk-operator) +[![PkgGoDev](https://pkg.go.dev/badge/github.com/splunk/splunk-operator)](https://pkg.go.dev/github.com/splunk/splunk-operator) [![Go Report Card](https://goreportcard.com/badge/github.com/splunk/splunk-operator)](https://goreportcard.com/report/github.com/splunk/splunk-operator) [![CircleCI](https://circleci.com/gh/splunk/splunk-operator/tree/master.svg?style=shield)](https://circleci.com/gh/splunk/splunk-operator/tree/master) [![Coverage Status](https://coveralls.io/repos/github/splunk/splunk-operator/badge.svg?branch=master)](https://coveralls.io/github/splunk/splunk-operator?branch=master) From 275fafc769c0e1faf13d73c32f87ffbfaefe8d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Sat, 5 Dec 2020 09:26:19 -0500 Subject: [PATCH 30/38] Correct typo --- pkg/splunk/enterprise/clustermaster.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/splunk/enterprise/clustermaster.go b/pkg/splunk/enterprise/clustermaster.go index 9fea8bad7..0a61482e9 100644 --- a/pkg/splunk/enterprise/clustermaster.go +++ b/pkg/splunk/enterprise/clustermaster.go @@ -259,7 +259,7 @@ func PushMasterAppsBundle(c splcommon.ControllerClient, cr *enterprisev1.Cluster return fmt.Errorf("Could not find admin password while trying to push the master apps bundle") } - scopedLog.Info("Issueing REST call to push master aps bundle") + scopedLog.Info("Issuing REST call to push master aps bundle") masterIdxcName := cr.GetName() fqdnName := splcommon.GetServiceFQDN(cr.GetNamespace(), GetSplunkServiceName(SplunkClusterMaster, masterIdxcName, false)) From 96a94c717d87fbd073f1786a1820f0fbe014480c Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Mon, 7 Dec 2020 14:41:24 -0800 Subject: [PATCH 31/38] addressed review comments --- pkg/splunk/controller/statefulset.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/splunk/controller/statefulset.go b/pkg/splunk/controller/statefulset.go index ee5c7ff28..13891a5e9 100644 --- a/pkg/splunk/controller/statefulset.go +++ b/pkg/splunk/controller/statefulset.go @@ -135,6 +135,7 @@ func UpdatePodRevisionHash(c splcommon.ControllerClient, statefulSet *appsv1.Sta return nil } +// isRevisionUpdateSuccessful checks if current revision is different from updated revision func isRevisionUpdateSuccessful(c splcommon.ControllerClient, statefulSet *appsv1.StatefulSet) bool { scopedLog := log.WithName("isUpdateSuccessful").WithValues( "name", statefulSet.GetObjectMeta().GetName(), @@ -158,6 +159,7 @@ func isRevisionUpdateSuccessful(c splcommon.ControllerClient, statefulSet *appsv return true } +// checkAndUpdatePodRevision updates the pod revision hash labels on pods if statefulset update was successful func checkAndUpdatePodRevision(c splcommon.ControllerClient, statefulSet *appsv1.StatefulSet, readyReplicas int32, skipRecheckUpdate *bool) (bool, error) { scopedLog := log.WithName("UpdateStatefulSetPods").WithValues( "name", statefulSet.GetObjectMeta().GetName(), From 908607b7373995d0af6f76804c1c5aa643cc45f1 Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Tue, 8 Dec 2020 13:05:29 -0800 Subject: [PATCH 32/38] Addressed review comments-2 --- pkg/splunk/controller/statefulset.go | 11 ++++++----- pkg/splunk/controller/statefulset_test.go | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/pkg/splunk/controller/statefulset.go b/pkg/splunk/controller/statefulset.go index 13891a5e9..728edab0c 100644 --- a/pkg/splunk/controller/statefulset.go +++ b/pkg/splunk/controller/statefulset.go @@ -98,6 +98,7 @@ func UpdatePodRevisionHash(c splcommon.ControllerClient, statefulSet *appsv1.Sta err := c.Get(context.TODO(), namespacedName, ¤t) if err != nil { scopedLog.Error(err, "Unable to Get statefulset", "statefulset", statefulSet.GetName()) + return err } for n := readyReplicas - 1; n >= 0; n-- { @@ -137,7 +138,7 @@ func UpdatePodRevisionHash(c splcommon.ControllerClient, statefulSet *appsv1.Sta // isRevisionUpdateSuccessful checks if current revision is different from updated revision func isRevisionUpdateSuccessful(c splcommon.ControllerClient, statefulSet *appsv1.StatefulSet) bool { - scopedLog := log.WithName("isUpdateSuccessful").WithValues( + scopedLog := log.WithName("isRevisionUpdateSuccessful").WithValues( "name", statefulSet.GetObjectMeta().GetName(), "namespace", statefulSet.GetObjectMeta().GetNamespace()) @@ -161,14 +162,14 @@ func isRevisionUpdateSuccessful(c splcommon.ControllerClient, statefulSet *appsv // checkAndUpdatePodRevision updates the pod revision hash labels on pods if statefulset update was successful func checkAndUpdatePodRevision(c splcommon.ControllerClient, statefulSet *appsv1.StatefulSet, readyReplicas int32, skipRecheckUpdate *bool) (bool, error) { - scopedLog := log.WithName("UpdateStatefulSetPods").WithValues( + scopedLog := log.WithName("checkAndUpdatePodRevision").WithValues( "name", statefulSet.GetObjectMeta().GetName(), "namespace", statefulSet.GetObjectMeta().GetNamespace()) var err error if !isRevisionUpdateSuccessful(c, statefulSet) { scopedLog.Error(err, "Statefulset not updated yet") *skipRecheckUpdate = false - return false, err + return *skipRecheckUpdate, err } // update the controller-revision-hash label on pods to // to avoid unnecessary recycle of pods @@ -176,10 +177,10 @@ func checkAndUpdatePodRevision(c splcommon.ControllerClient, statefulSet *appsv1 if err != nil { scopedLog.Error(err, "Unable to update pod-revision-hash for the pods") *skipRecheckUpdate = false - return false, err + return *skipRecheckUpdate, err } *skipRecheckUpdate = true - return true, nil + return *skipRecheckUpdate, nil } // UpdateStatefulSetPods manages scaling and config updates for StatefulSets diff --git a/pkg/splunk/controller/statefulset_test.go b/pkg/splunk/controller/statefulset_test.go index 214ffaa7a..f97a80820 100644 --- a/pkg/splunk/controller/statefulset_test.go +++ b/pkg/splunk/controller/statefulset_test.go @@ -110,13 +110,13 @@ func TestUpdateStatefulSetPods(t *testing.T) { var phase splcommon.Phase phase, err := updateStatefulSetPodsTester(t, &mgr, statefulSet, 1 /*desiredReplicas*/, statefulSet, pod) if err != nil && phase != splcommon.PhaseUpdating { - t.Errorf("UpdateStatefulSetPods should not have returned error") + t.Errorf("UpdateStatefulSetPods should not have returned error=%s with phase=%s", err, phase) } // Check the scenario where UpdatePodRevisionHash should return error when Pod is not added to client. phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 2 /*desiredReplicas*/, statefulSet) if err == nil && phase != splcommon.PhaseError { - t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError") + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError, but we got phase=%s", phase) } replicas = 3 @@ -125,7 +125,7 @@ func TestUpdateStatefulSetPods(t *testing.T) { // Check the scenario where UpdatePodRevisionHash should return error when Pod is not added to client. phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 1 /*desiredReplicas*/, statefulSet) if err == nil && phase != splcommon.PhaseError { - t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError") + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError, but we got phase=%s", phase) } // readyReplicas < replicas @@ -135,14 +135,14 @@ func TestUpdateStatefulSetPods(t *testing.T) { // Check the scenario where UpdatePodRevisionHash should return error when readyReplicas < replicas and Pod is not found phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 1 /*desiredReplicas*/, statefulSet, pod) if err == nil && phase != splcommon.PhaseError { - t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError") + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError, but we got phase=%s", phase) } // CurrentRevision = UpdateRevision statefulSet.Status.CurrentRevision = "v1" phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 1 /*desiredReplicas*/, statefulSet, pod) if err == nil && phase != splcommon.PhaseError { - t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError") + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError, but we got phase=%s", phase) } // readyReplicas > replicas @@ -153,20 +153,20 @@ func TestUpdateStatefulSetPods(t *testing.T) { // Check the scenario where UpdatePodRevisionHash should return error when readyReplicas > replicas and Pod is not found phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 1 /*desiredReplicas*/, statefulSet, pod) if err == nil && phase != splcommon.PhaseError { - t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError") + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError, but we got phase=%s", phase) } // CurrentRevision = UpdateRevision statefulSet.Status.CurrentRevision = "v1" phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 1 /*desiredReplicas*/, statefulSet, pod) if err == nil && phase != splcommon.PhaseError { - t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError") + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError, but we got phase=%s", phase) } // Check the scenario where UpdatePodRevisionHash should return error when statefulset is not added to client. phase, err = updateStatefulSetPodsTester(t, &mgr, statefulSet, 2 /*desiredReplicas*/, pod) if err == nil && phase != splcommon.PhaseError { - t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError") + t.Errorf("UpdateStatefulSetPods should have returned error or phase should have been PhaseError, but we got phase=%s", phase) } } @@ -216,14 +216,14 @@ func TestUpdatePodRevisionHash(t *testing.T) { err := updatePodRevisionHashTester(t, statefulSet, replicas, statefulSet, pod) if err != nil { - t.Errorf("UpdatePodRevisionHash should not have returned error") + t.Errorf("UpdatePodRevisionHash should not have returned error=%s", err) } // Test the negative case where phase != corev1.PodRunning pod.Status.Phase = corev1.PodPending err = updatePodRevisionHashTester(t, statefulSet, replicas, statefulSet, pod) if err != nil { - t.Errorf("UpdatePodRevisionHash should not have returned error") + t.Errorf("UpdatePodRevisionHash should not have returned error=%s", err) } // Test invalid pod name splunk-stack1-1 From be70733b32048b00095dad4735b1057cba5c8bd0 Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Tue, 8 Dec 2020 13:39:09 -0800 Subject: [PATCH 33/38] removed redundant boolean return --- pkg/splunk/controller/statefulset.go | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/pkg/splunk/controller/statefulset.go b/pkg/splunk/controller/statefulset.go index 728edab0c..9bdd5df17 100644 --- a/pkg/splunk/controller/statefulset.go +++ b/pkg/splunk/controller/statefulset.go @@ -161,7 +161,7 @@ func isRevisionUpdateSuccessful(c splcommon.ControllerClient, statefulSet *appsv } // checkAndUpdatePodRevision updates the pod revision hash labels on pods if statefulset update was successful -func checkAndUpdatePodRevision(c splcommon.ControllerClient, statefulSet *appsv1.StatefulSet, readyReplicas int32, skipRecheckUpdate *bool) (bool, error) { +func checkAndUpdatePodRevision(c splcommon.ControllerClient, statefulSet *appsv1.StatefulSet, readyReplicas int32, skipRecheckUpdate *bool) error { scopedLog := log.WithName("checkAndUpdatePodRevision").WithValues( "name", statefulSet.GetObjectMeta().GetName(), "namespace", statefulSet.GetObjectMeta().GetNamespace()) @@ -169,7 +169,7 @@ func checkAndUpdatePodRevision(c splcommon.ControllerClient, statefulSet *appsv1 if !isRevisionUpdateSuccessful(c, statefulSet) { scopedLog.Error(err, "Statefulset not updated yet") *skipRecheckUpdate = false - return *skipRecheckUpdate, err + return err } // update the controller-revision-hash label on pods to // to avoid unnecessary recycle of pods @@ -177,10 +177,10 @@ func checkAndUpdatePodRevision(c splcommon.ControllerClient, statefulSet *appsv1 if err != nil { scopedLog.Error(err, "Unable to update pod-revision-hash for the pods") *skipRecheckUpdate = false - return *skipRecheckUpdate, err + return err } *skipRecheckUpdate = true - return *skipRecheckUpdate, nil + return nil } // UpdateStatefulSetPods manages scaling and config updates for StatefulSets @@ -196,8 +196,8 @@ func UpdateStatefulSetPods(c splcommon.ControllerClient, statefulSet *appsv1.Sta scopedLog.Info("Waiting for pods to become ready") if readyReplicas > 0 { if !*skipRecheckUpdate { - ret, err := checkAndUpdatePodRevision(c, statefulSet, readyReplicas, skipRecheckUpdate) - if !ret || err != nil { + err := checkAndUpdatePodRevision(c, statefulSet, readyReplicas, skipRecheckUpdate) + if !*skipRecheckUpdate || err != nil { scopedLog.Error(err, "Unable to update pod-revision-hash for the pods") return splcommon.PhaseError, err } @@ -208,8 +208,8 @@ func UpdateStatefulSetPods(c splcommon.ControllerClient, statefulSet *appsv1.Sta } else if readyReplicas > replicas { scopedLog.Info("Waiting for scale down to complete") if !*skipRecheckUpdate { - ret, err := checkAndUpdatePodRevision(c, statefulSet, readyReplicas-1, skipRecheckUpdate) - if !ret || err != nil { + err := checkAndUpdatePodRevision(c, statefulSet, readyReplicas-1, skipRecheckUpdate) + if !*skipRecheckUpdate || err != nil { scopedLog.Error(err, "Unable to update pod-revision-hash for the pods") return splcommon.PhaseError, err } @@ -234,9 +234,8 @@ func UpdateStatefulSetPods(c splcommon.ControllerClient, statefulSet *appsv1.Sta // It so can happen that it may take few seconds for the update to be // reflected in the resource. In that case, just return from here and // check the status back in the next reconcile loop. - var ret bool - ret, err = checkAndUpdatePodRevision(c, statefulSet, readyReplicas, skipRecheckUpdate) - if !ret || err != nil { + err = checkAndUpdatePodRevision(c, statefulSet, readyReplicas, skipRecheckUpdate) + if !*skipRecheckUpdate || err != nil { scopedLog.Error(err, "Unable to update pod-revision-hash for the pods") return splcommon.PhaseError, err } @@ -271,9 +270,8 @@ func UpdateStatefulSetPods(c splcommon.ControllerClient, statefulSet *appsv1.Sta // It so can happen that it may take few seconds for the update to be // reflected in the resource. In that case, just return from here and // check the status back in the next reconcile loop. - var ret bool - ret, err = checkAndUpdatePodRevision(c, statefulSet, readyReplicas-1, skipRecheckUpdate) - if !ret || err != nil { + err = checkAndUpdatePodRevision(c, statefulSet, readyReplicas-1, skipRecheckUpdate) + if !*skipRecheckUpdate || err != nil { scopedLog.Error(err, "Unable to update pod-revision-hash for the pods") return splcommon.PhaseError, err } From 4a8379f48502c893203d9120a6129d1a65383867 Mon Sep 17 00:00:00 2001 From: akondur Date: Wed, 9 Dec 2020 22:25:33 -0800 Subject: [PATCH 34/38] Docs fix for pass4Symmkey --- docs/Examples.md | 12 ++++++------ docs/PasswordManagement.md | 16 ++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/Examples.md b/docs/Examples.md index 290cf775f..4c865ea20 100644 --- a/docs/Examples.md +++ b/docs/Examples.md @@ -643,7 +643,7 @@ pass4SymmKey for authentication. There are two ways to configure `pass4Symmkey` with an External LM: #### Approach 1 -- Setup the desired plain-text [`pass4Symmkey`](PasswordManagement.md#pass4symmkey) in the global secret object(Note: The `pass4Symmkey` would be stored in a base64 encoded format). For details see [updating global kubernetes secret object](#updating-global-kubernetes-secret-object). +- Setup the desired plain-text [`pass4Symmkey`](PasswordManagement.md#pass4Symmkey) in the global secret object(Note: The `pass4Symmkey` would be stored in a base64 encoded format). For details see [updating global kubernetes secret object](#updating-global-kubernetes-secret-object). - Setup the same plain-text `pass4SymmKey` in the `[general]` section of your LM's `server.conf` file. #### Approach 2 @@ -662,7 +662,7 @@ There are two ways to configure `pass4Symmkey` with an External LM: ``` $SPLUNK_HOME/bin/splunk show-decrypted --value '$7$Sw0A+wvJdTztMcA2Ge7u435XmpTzPqyaq49kUZqn0yfAgwFpwrArM2JjWJ3mUyf/FyHAnCZkE/U=' ``` -- Setup the above decrypted plain-text [`pass4Symmkey`](PasswordManagement.md#pass4symmkey) in the global secret object(Note: The `pass4Symmkey` would be stored in a base64 encoded format). For details see [updating global kubernetes secret object](#updating-global-kubernetes-secret-object) +- Setup the above decrypted plain-text [`pass4Symmkey`](PasswordManagement.md#pass4Symmkey) in the global secret object(Note: The `pass4Symmkey` would be stored in a base64 encoded format). For details see [updating global kubernetes secret object](#updating-global-kubernetes-secret-object) ### Configuring license_master_url: @@ -712,7 +712,7 @@ operator & the external indexer cluster, and configure the `splunk.cluster_maste There are two ways to configure `IDXC pass4Symmkey` with an External Indexer Cluster: #### Approach 1 -- Setup the desired plain-text [`IDXC pass4Symmkey`](PasswordManagement.md#idxc-pass4symmkey) in the global secret object(Note: The `IDXC pass4Symmkey` would be stored in a base64 encoded format). For details see [updating global kubernetes secret object](#updating-global-kubernetes-secret-object). +- Setup the desired plain-text [`IDXC pass4Symmkey`](PasswordManagement.md#idxc-pass4Symmkey) in the global secret object(Note: The `IDXC pass4Symmkey` would be stored in a base64 encoded format). For details see [updating global kubernetes secret object](#updating-global-kubernetes-secret-object). - Setup the same plain-text `IDXC pass4SymmKey` in the `[clustering]` section of your cluster master's and indexers' `server.conf` file. #### Approach 2 @@ -731,7 +731,7 @@ There are two ways to configure `IDXC pass4Symmkey` with an External Indexer Clu ``` $SPLUNK_HOME/bin/splunk show-decrypted --value '$7$Sw0A+wvJdTztMcA2Ge7u435XmpTzPqyaq49kUZqn0yfAgwFpwrArM2JjWJ3mUyf/FyHAnCZkE/U=' ``` -- Setup the above decrypted plain-text [`IDXC pass4Symmkey`](PasswordManagement.md#idxc-pass4symmkey) in the global secret object(Note: The `IDXC pass4Symmkey` would be stored in a base64 encoded format). For details see [updating global kubernetes secret object](#updating-global-kubernetes-secret-object) +- Setup the above decrypted plain-text [`IDXC pass4Symmkey`](PasswordManagement.md#idxc-pass4Symmkey) in the global secret object(Note: The `IDXC pass4Symmkey` would be stored in a base64 encoded format). For details see [updating global kubernetes secret object](#updating-global-kubernetes-secret-object) ### Configuring cluster_master_url: @@ -777,8 +777,8 @@ Use the kubectl command to create the global kubernetes secret object: 2. Gather the password values for the secret tokens you want to configure. To see all available secret tokens defined for the global kubernetes secret object, review [password management](PasswordManagement.md#splunk-secret-tokens-in-the-global-secret-object) 3. Create a kubernetes secret object referencing the namespace. Example: splunk-`-secret. -In the example below, we are creating the global kubernetes secret object, defining the default administrator and pass4symmkey tokens, and passing in the values. -`kubectl create secret generic splunk--secret --from-literal='password=' --from-literal='pass4symmkey='` +In the example below, we are creating the global kubernetes secret object, defining the default administrator and pass4Symmkey tokens, and passing in the values. +`kubectl create secret generic splunk--secret --from-literal='password=' --from-literal='pass4Symmkey='` ### Reading global kubernetes secret object diff --git a/docs/PasswordManagement.md b/docs/PasswordManagement.md index adde67004..7a64b638d 100644 --- a/docs/PasswordManagement.md +++ b/docs/PasswordManagement.md @@ -5,9 +5,9 @@ - [Splunk Secret Tokens in the global secret object](#splunk-secret-tokens-in-the-global-secret-object) - [HEC Token](#hec-token) - [Default administrator password](#default-administrator-password) - - [Pass4symmkey](#pass4symmkey) - - [IDXC Pass4symmkey](#idxc-pass4symmkey) - - [SHC Pass4symmkey](#shc-pass4symmkey) + - [pass4Symmkey](#pass4Symmkey) + - [IDXC pass4Symmkey](#idxc-pass4Symmkey) + - [SHC pass4Symmkey](#shc-pass4Symmkey) - [Information for Splunk Enterprise administrator](#information-for-splunk-enterprise-administrator) - [Secrets on Docker Splunk](#secrets-on-docker-splunk) @@ -42,15 +42,15 @@ The configurable Splunk Secret Tokens include: **Key name in global kubernetes secret object**: `password` **Description**: password refers to the default administrator password for Splunk. -#### Pass4symmkey -**Key name in global kubernetes secret object**: `pass4symmkey` -**Description**: pass4symmkey is an authentication token for inter-communication within Splunk Enterprise. +#### pass4Symmkey +**Key name in global kubernetes secret object**: `pass4Symmkey` +**Description**: pass4Symmkey is an authentication token for inter-communication within Splunk Enterprise. -#### IDXC Pass4symmkey +#### IDXC pass4Symmkey **Key name in global kubernetes secret object**: `idxc.secret` **Description**: idxc.secret is an authentication token for inter-communication specifically for indexer clustering in Splunk Enterprise. -#### SHC Pass4symmkey +#### SHC pass4Symmkey **Key name in global kubernetes secret object**: `shc.secret` **Description**: shc.secret is an authentication token for inter-communication specifically for search head clustering in Splunk Enterprise. From cfaaf888a0d87c41d1478f3fb80e579bb892b76f Mon Sep 17 00:00:00 2001 From: akondur Date: Thu, 10 Dec 2020 11:25:54 -0800 Subject: [PATCH 35/38] Docs fix for using defaults --- docs/Examples.md | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/docs/Examples.md b/docs/Examples.md index 4c865ea20..604ebfff8 100644 --- a/docs/Examples.md +++ b/docs/Examples.md @@ -434,32 +434,12 @@ installation. defaultsUrl: "http://myco.com/splunk/generic.yml,/mnt/defaults/apps.yml" ``` -Suppose you want to just override the admin password for your deployment -(instead of using the automatically generated one), you can also specify -inline overrides using the `defaults` parameter: - -```yaml -apiVersion: enterprise.splunk.com/v1beta1 -kind: Standalone -metadata: - name: example - finalizers: - - enterprise.splunk.com/delete-pvc -spec: - volumes: - - name: defaults - configMap: - name: splunk-defaults - defaultsUrl: /mnt/defaults/default.yml - defaults: |- - splunk: - password: helloworld456 -``` - -*Setting passwords in your CRDs may be OK for testing, but it is discouraged.* - Inline `defaults` are always processed last, after any `defaultsUrl` files. +Any password management related configuration via `defaults` and `defaultsUrl` +has been disabled. Please review [`PasswordManagement.md`](PasswordManagement.md) +and [`Managing global kubernetes secret object`](#managing-global-kubernetes-secret-object) +for more details. ## Installing Splunk Apps From 7642f9fe3986dfdb4e21e865553fcf6d12bbe9b2 Mon Sep 17 00:00:00 2001 From: Jeff Rybczynski Date: Tue, 8 Dec 2020 13:12:57 -0800 Subject: [PATCH 36/38] CSPL-592 Trigger app install on defaultsURL change When applying an updated CRD, check the environment variables and push those changes to the running pods. --- pkg/splunk/controller/util.go | 17 +++++++++++++++++ pkg/splunk/controller/util_test.go | 12 ++++++++++++ 2 files changed, 29 insertions(+) diff --git a/pkg/splunk/controller/util.go b/pkg/splunk/controller/util.go index 85db31534..03595e94f 100644 --- a/pkg/splunk/controller/util.go +++ b/pkg/splunk/controller/util.go @@ -16,6 +16,7 @@ package controller import ( "reflect" + "strings" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -166,6 +167,22 @@ func MergePodSpecUpdates(current *corev1.PodSpec, revised *corev1.PodSpec, name current.Containers[idx].Resources = revised.Containers[idx].Resources result = true } + + // check Env + // Skip this check for the Monitoring Console + // This is temporary until the MC has it's own CR to control the MC pod env. + skipForMC := false + if strings.Contains(name, "monitoring-console") { + scopedLog.Info("Ignoring Pod Container Envs differences for MC pods", "name", name) + skipForMC = true + } + if !skipForMC && splcommon.CompareEnvs(current.Containers[idx].Env, revised.Containers[idx].Env) { + scopedLog.Info("Pod Container Envs differ", + "current", current.Containers[idx].Env, + "revised", revised.Containers[idx].Env) + current.Containers[idx].Env = revised.Containers[idx].Env + result = true + } } } diff --git a/pkg/splunk/controller/util_test.go b/pkg/splunk/controller/util_test.go index 66b47fe06..a736818a3 100644 --- a/pkg/splunk/controller/util_test.go +++ b/pkg/splunk/controller/util_test.go @@ -121,6 +121,18 @@ func TestMergePodUpdates(t *testing.T) { matcher = func() bool { return reflect.DeepEqual(current.Spec.Containers, revised.Spec.Containers) } podUpdateTester("Container Resources") + // check pod env update + current.Spec.Containers[0].Env = append(current.Spec.Containers[0].Env, corev1.EnvVar{ + Name: "SPLUNK_DEFAULTS_URL", + Value: "defaults1.yaml", + }) + revised.Spec.Containers[0].Env = append(revised.Spec.Containers[0].Env, corev1.EnvVar{ + Name: "SPLUNK_DEFAULTS_URL", + Value: "defaults2.yaml", + }) + matcher = func() bool { return reflect.DeepEqual(current.Spec.Containers[0].Env, revised.Spec.Containers[0].Env) } + podUpdateTester("Pod Env changed") + // check container removed revised.Spec.Containers = []corev1.Container{} matcher = func() bool { return reflect.DeepEqual(current.Spec.Containers, revised.Spec.Containers) } From 269a715c5f826cc46a2c57bc57192ed2816de41d Mon Sep 17 00:00:00 2001 From: akondur Date: Mon, 14 Dec 2020 17:08:09 -0800 Subject: [PATCH 37/38] Helper commit for 0.2.1Beta release --- ...rprise.splunk.com_indexerclusters_crd.yaml | 4 + ...ise.splunk.com_searchheadclusters_crd.yaml | 4 + ...erprise.splunk.com_clustermasters_crd.yaml | 2463 ++++++++++++++++ ...rprise.splunk.com_indexerclusters_crd.yaml | 2344 +++++++++++++++ ...erprise.splunk.com_licensemasters_crd.yaml | 2240 +++++++++++++++ ...ise.splunk.com_searchheadclusters_crd.yaml | 2394 ++++++++++++++++ .../enterprise.splunk.com_sparks_crd.yaml | 996 +++++++ ...enterprise.splunk.com_standalones_crd.yaml | 2518 +++++++++++++++++ .../splunk.v0.2.1.clusterserviceversion.yaml | 261 ++ deploy/operator.yaml | 2 +- docs/ChangeLog.md | 17 + docs/Install.md | 6 +- docs/README.md | 2 +- version/version.go | 2 +- 14 files changed, 13247 insertions(+), 6 deletions(-) create mode 100644 deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_clustermasters_crd.yaml create mode 100644 deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_indexerclusters_crd.yaml create mode 100644 deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_licensemasters_crd.yaml create mode 100644 deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_searchheadclusters_crd.yaml create mode 100644 deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_sparks_crd.yaml create mode 100644 deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_standalones_crd.yaml create mode 100644 deploy/olm-catalog/splunk/0.2.1/splunk.v0.2.1.clusterserviceversion.yaml diff --git a/deploy/crds/enterprise.splunk.com_indexerclusters_crd.yaml b/deploy/crds/enterprise.splunk.com_indexerclusters_crd.yaml index 4652896eb..5b0f94156 100644 --- a/deploy/crds/enterprise.splunk.com_indexerclusters_crd.yaml +++ b/deploy/crds/enterprise.splunk.com_indexerclusters_crd.yaml @@ -2325,6 +2325,10 @@ spec: description: Indicates whether the master is ready to begin servicing, based on whether it is initialized. type: boolean + skip_recheck_update: + description: Indicates if we need to recheck the revision update on + pods + type: boolean type: object type: object version: v1beta1 diff --git a/deploy/crds/enterprise.splunk.com_searchheadclusters_crd.yaml b/deploy/crds/enterprise.splunk.com_searchheadclusters_crd.yaml index 1db73dcc0..77d75f9aa 100644 --- a/deploy/crds/enterprise.splunk.com_searchheadclusters_crd.yaml +++ b/deploy/crds/enterprise.splunk.com_searchheadclusters_crd.yaml @@ -2375,6 +2375,10 @@ spec: items: type: boolean type: array + skip_recheck_update: + description: Indicates if we need to recheck the revision update on + pods + type: boolean type: object type: object version: v1beta1 diff --git a/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_clustermasters_crd.yaml b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_clustermasters_crd.yaml new file mode 100644 index 000000000..5c9b2adc7 --- /dev/null +++ b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_clustermasters_crd.yaml @@ -0,0 +1,2463 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: clustermasters.enterprise.splunk.com +spec: + additionalPrinterColumns: + - JSONPath: .status.phase + description: Status of cluster master + name: Phase + type: string + group: enterprise.splunk.com + names: + kind: ClusterMaster + listKind: ClusterMasterList + plural: clustermasters + shortNames: + - cm-idxc + singular: clustermaster + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + description: ClusterMaster is the Schema for the clustermasters API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ClusterMasterSpec defines the desired state of ClusterMaster + properties: + Mock: + description: Mock to differentiate between UTs and actual reconcile + type: boolean + affinity: + description: Kubernetes Affinity rules that control how pods are assigned + to particular nodes. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches all + objects with implicit weight 0 (i.e. it's a no-op). A null + preferred scheduling term matches no objects (i.e. is also + a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The + terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node has pods which matches the corresponding podAffinityTerm; + the node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may not + try to eventually evict the pod from its node. When there + are multiple elements, the lists of nodes corresponding to + each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some other + pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the anti-affinity expressions specified by this + field, but it may choose a node that violates one or more + of the expressions. The node that is most preferred is the + one with the greatest sum of weights, i.e. for each node that + meets all of the scheduling requirements (resource request, + requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field + and adding "weight" to the sum if the node has pods which + matches the corresponding podAffinityTerm; the node(s) with + the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will not + be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + clusterMasterRef: + description: ClusterMasterRef refers to a Splunk Enterprise indexer + cluster managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + defaults: + description: Inline map of default.yml overrides used to initialize + the environment + type: string + defaultsUrl: + description: Full path or URL for one or more default.yml files, separated + by commas + type: string + ephemeralStorage: + description: If true, ephemeral (emptyDir) storage will be used for + /opt/splunk/etc and /opt/splunk/var volumes + type: boolean + etcStorage: + description: Storage capacity to request for /opt/splunk/etc persistent + volume claims (default=”1Gi”) + type: string + image: + description: Image to use for Splunk pod containers (overrides RELATED_IMAGE_SPLUNK_ENTERPRISE + environment variables) + type: string + imagePullPolicy: + description: 'Sets pull policy for all images (either “Always” or the + default: “IfNotPresent”)' + enum: + - Always + - IfNotPresent + type: string + licenseMasterRef: + description: LicenseMasterRef refers to a Splunk Enterprise license + master managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + licenseUrl: + description: Full path or URL for a Splunk Enterprise license file + type: string + resources: + description: resource requirements for the pod containers + properties: + limits: + additionalProperties: + type: string + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + type: string + 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. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + schedulerName: + description: Name of Scheduler to use for pod placement (defaults to + “default-scheduler”) + type: string + serviceTemplate: + description: ServiceTemplate is a template used to create Kubernetes + services + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the + latest internal value, and may reject unrecognized values. More + info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + spec: + description: Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + clusterIP: + description: 'clusterIP is the IP address of the service and + is usually assigned randomly by the master. If an address + is specified manually and is not in use by others, it will + be allocated to the service; otherwise, creation of the service + will fail. This field can not be changed through updates. + Valid values are "None", empty string (""), or a valid IP + address. "None" can be specified for headless services when + proxying is not required. Only applies to types ClusterIP, + NodePort, and LoadBalancer. Ignored if type is ExternalName. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + externalIPs: + description: externalIPs is a list of IP addresses for which + nodes in the cluster will also accept traffic for this service. These + IPs are not managed by Kubernetes. The user is responsible + for ensuring that traffic arrives at a node with this IP. A + common example is external load-balancers that are not part + of the Kubernetes system. + items: + type: string + type: array + externalName: + description: externalName is the external reference that kubedns + or equivalent will return as a CNAME record for this service. + No proxying will be involved. Must be a valid RFC-1123 hostname + (https://tools.ietf.org/html/rfc1123) and requires Type to + be ExternalName. + type: string + externalTrafficPolicy: + description: externalTrafficPolicy denotes if this Service desires + to route external traffic to node-local or cluster-wide endpoints. + "Local" preserves the client source IP and avoids a second + hop for LoadBalancer and Nodeport type services, but risks + potentially imbalanced traffic spreading. "Cluster" obscures + the client source IP and may cause a second hop to another + node, but should have good overall load-spreading. + type: string + healthCheckNodePort: + description: healthCheckNodePort specifies the healthcheck nodePort + for the service. If not specified, HealthCheckNodePort is + created by the service api backend with the allocated nodePort. + Will use user-specified nodePort value if specified by the + client. Only effects when Type is set to LoadBalancer and + ExternalTrafficPolicy is set to Local. + format: int32 + type: integer + ipFamily: + description: ipFamily specifies whether this Service has a preference + for a particular IP family (e.g. IPv4 vs. IPv6). If a specific + IP family is requested, the clusterIP field will be allocated + from that family, if it is available in the cluster. If no + IP family is requested, the cluster's primary IP family will + be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, + externalIPs) and controllers which allocate external load-balancers + should use the same IP family. Endpoints for this Service + will be of this family. This field is immutable after creation. + Assigning a ServiceIPFamily not available in the cluster (e.g. + IPv6 in IPv4 only cluster) is an error condition and will + fail during clusterIP assignment. + type: string + loadBalancerIP: + description: 'Only applies to Service Type: LoadBalancer LoadBalancer + will get created with the IP specified in this field. This + feature depends on whether the underlying cloud-provider supports + specifying the loadBalancerIP when a load balancer is created. + This field will be ignored if the cloud-provider does not + support the feature.' + type: string + loadBalancerSourceRanges: + description: 'If specified and supported by the platform, this + will restrict traffic through the cloud-provider load-balancer + will be restricted to the specified client IPs. This field + will be ignored if the cloud-provider does not support the + feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/' + items: + type: string + type: array + ports: + description: 'The list of ports that are exposed by this service. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + items: + description: ServicePort contains information on service's + port. + properties: + name: + description: The name of this port within the service. + This must be a DNS_LABEL. All ports within a ServiceSpec + must have unique names. When considering the endpoints + for a Service, this must match the 'name' field in the + EndpointPort. Optional if only one ServicePort is defined + on this service. + type: string + nodePort: + description: 'The port on each node on which this service + is exposed when type=NodePort or LoadBalancer. Usually + assigned by the system. If specified, it will be allocated + to the service if unused or else creation of the service + will fail. Default is to auto-allocate a port if the + ServiceType of this Service requires one. More info: + https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: 'Number or name of the port to access on + the pods targeted by the service. Number must be in + the range 1 to 65535. Name must be an IANA_SVC_NAME. + If this is a string, it will be looked up as a named + port in the target Pod''s container ports. If this is + not specified, the value of the ''port'' field is used + (an identity map). This field is ignored for services + with clusterIP=None, and should be omitted or set equal + to the ''port'' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + publishNotReadyAddresses: + description: publishNotReadyAddresses, when set to true, indicates + that DNS implementations must publish the notReadyAddresses + of subsets for the Endpoints associated with the Service. + The default value is false. The primary use case for setting + this field is to use a StatefulSet's Headless Service to propagate + SRV records for its Pods without respect to their readiness + for purpose of peer discovery. + type: boolean + selector: + additionalProperties: + type: string + description: 'Route service traffic to pods with label keys + and values matching this selector. If empty or not present, + the service is assumed to have an external process managing + its endpoints, which Kubernetes will not modify. Only applies + to types ClusterIP, NodePort, and LoadBalancer. Ignored if + type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/' + type: object + sessionAffinity: + description: 'Supports "ClientIP" and "None". Used to maintain + session affinity. Enable client IP based session affinity. + Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations of Client + IP based session affinity. + properties: + timeoutSeconds: + description: timeoutSeconds specifies the seconds of + ClientIP type session sticky time. The value must + be >0 && <=86400(for 1 day) if ServiceAffinity == + "ClientIP". Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: 'type determines how the Service is exposed. Defaults + to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, + and LoadBalancer. "ExternalName" maps to the specified externalName. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or + if that is not specified, by manual construction of an Endpoints + object. If clusterIP is "None", no virtual IP is allocated + and the endpoints are published as a set of endpoints rather + than a stable IP. "NodePort" builds on ClusterIP and allocates + a port on every node which routes to the clusterIP. "LoadBalancer" + builds on NodePort and creates an external load-balancer (if + supported in the current cloud) which routes to the clusterIP. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types' + type: string + type: object + status: + description: 'Most recently observed status of the service. Populated + by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + properties: + loadBalancer: + description: LoadBalancer contains the current status of the + load-balancer, if one is present. + properties: + ingress: + description: Ingress is a list containing ingress points + for the load-balancer. Traffic intended for the service + should be sent to these ingress points. + items: + description: 'LoadBalancerIngress represents the status + of a load-balancer ingress point: traffic intended for + the service should be sent to an ingress point.' + properties: + hostname: + description: Hostname is set for load-balancer ingress + points that are DNS based (typically AWS load-balancers) + type: string + ip: + description: IP is set for load-balancer ingress points + that are IP based (typically GCE or OpenStack load-balancers) + type: string + type: object + type: array + type: object + type: object + type: object + smartstore: + description: Splunk Smartstore configuration. Refer to indexes.conf.spec + and server.conf.spec on docs.splunk.com + properties: + cacheManager: + description: Defines Cache manager settings + properties: + evictionPadding: + description: Additional size beyond 'minFreeSize' before eviction + kicks in + type: integer + evictionPolicy: + description: Eviction policy to use + type: string + hotlistBloomFilterRecencyHours: + description: Time period relative to the bucket's age, during + which the bloom filter file is protected from cache eviction + type: integer + hotlistRecencySecs: + description: Time period relative to the bucket's age, during + which the bucket is protected from cache eviction + type: integer + maxCacheSize: + description: Max cache size per partition + type: integer + maxConcurrentDownloads: + description: Maximum number of buckets that can be downloaded + from remote storage in parallel + type: integer + maxConcurrentUploads: + description: Maximum number of buckets that can be uploaded + to remote storage in parallel + type: integer + type: object + defaults: + description: Default configuration for indexes + properties: + maxGlobalDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of space for warm and cold buckets of an index + type: integer + maxGlobalRawDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of cumulative space for warm and cold buckets of an index + type: integer + volumeName: + description: Remote Volume name + type: string + type: object + indexes: + description: List of Splunk indexes + items: + description: IndexSpec defines Splunk index name and storage path + properties: + hotlistBloomFilterRecencyHours: + description: Time period relative to the bucket's age, during + which the bloom filter file is protected from cache eviction + type: integer + hotlistRecencySecs: + description: Time period relative to the bucket's age, during + which the bucket is protected from cache eviction + type: integer + maxGlobalDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of space for warm and cold buckets of an index + type: integer + maxGlobalRawDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of cumulative space for warm and cold buckets of an index + type: integer + name: + description: Splunk index name + type: string + remotePath: + description: Index location relative to the remote volume + path + type: string + volumeName: + description: Remote Volume name + type: string + type: object + type: array + volumes: + description: List of remote storage volumes + items: + description: VolumeSpec defines remote volume name and remote + volume URI + properties: + endpoint: + description: Remote volume URI + type: string + name: + description: Remote volume name + type: string + path: + description: Remote volume path + type: string + secretRef: + description: Secret object name + type: string + type: object + type: array + type: object + sparkImage: + description: Image to use for Spark pod containers (overrides RELATED_IMAGE_SPLUNK_SPARK + environment variables). Also used on cluster master for init container + to setup the soft links from ../master-apps/splunk-operator/local/ + to /mnt/splunk-operator/local/ + type: string + storageClassName: + description: Name of StorageClass to use for persistent volume claims + type: string + tolerations: + description: Pod's tolerations for Kubernetes node's taint + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, operator + must be Exists; this combination means to match all values and + all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. Exists + is equivalent to wildcard for value, so that a pod can tolerate + all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the + toleration (which must be of effect NoExecute, otherwise this + field is ignored) tolerates the taint. By default, it is not + set, which means tolerate the taint forever (do not evict). + Zero and negative values will be treated as 0 (evict immediately) + by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise + just a regular string. + type: string + type: object + type: array + varStorage: + description: Storage capacity to request for /opt/splunk/var persistent + volume claims (default=”50Gi”) + type: string + volumes: + description: List of one or more Kubernetes volumes. These will be mounted + in all pod containers as as /mnt/ + items: + description: Volume represents a named volume in a pod that may be + accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents an AWS Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'Specify "true" to force and set the ReadOnly + property in VolumeMounts to "true". If omitted, the default + is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent disk resource in + AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: AzureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'Host Caching mode: None, Read Only, Read Write.' + type: string + diskName: + description: The Name of the data disk in the blob storage + type: string + diskURI: + description: The URI the data disk in the blob storage + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'Expected values Shared: multiple blob disks + per storage account Dedicated: single blob disk per storage + account Managed: azure managed data disk (only in managed + availability set). defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: AzureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret that contains Azure Storage + Account Name and Key + type: string + shareName: + description: Share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: CephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: 'Required: Monitors is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'Optional: Used as the mounted root, rather than + the full Ceph tree, default is /' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile is the path to key ring + for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef is reference to the authentication + secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + 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 + user: + description: 'Optional: User is the rados user name, default + is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'Cinder represents a cinder volume attached and mounted + on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to a secret object containing + parameters used to connect to OpenStack.' + 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 + volumeID: + description: 'volume id used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: ConfigMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. 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 + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. 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: 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: Specify whether the ConfigMap or its keys must + be defined + type: boolean + type: object + csi: + description: CSI (Container Storage Interface) represents storage + that is handled by an external CSI driver (Alpha feature). + properties: + driver: + description: Driver is the name of the CSI driver that handles + this volume. Consult with your admin for the correct name + as registered in the cluster. + type: string + fsType: + description: Filesystem type to mount. Ex. "ext4", "xfs", + "ntfs". If not provided, the empty value is passed to the + associated CSI driver which will determine the default filesystem + to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef is a reference to the secret + object containing sensitive information to pass to the CSI + driver to complete the CSI NodePublishVolume and NodeUnpublishVolume + calls. This field is optional, and may be empty if no secret + is required. If the secret object contains more than one + secret, all secret references are passed. + 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 + readOnly: + description: Specifies a read-only configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: VolumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: DownwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. 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 + items: + description: Items is a list of downward API volume 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 + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. 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: + description: Specifies the output format of the + exposed resources, defaults to "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'EmptyDir represents a temporary directory that shares + a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'What type of storage medium should back this + directory. The default is "" which means to use the node''s + default medium. Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + description: 'Total amount of local storage required for this + EmptyDir volume. The size limit is also applicable for memory + medium. The maximum usage on memory medium EmptyDir would + be the minimum value between the SizeLimit specified here + and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + type: string + type: object + fc: + description: FC represents a Fibre Channel resource that is attached + to a kubelet's host machine and then exposed to the pod. + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + lun: + description: 'Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target worldwide names (WWNs)' + items: + type: string + type: array + wwids: + description: 'Optional: FC volume world wide identifiers (wwids) + Either wwids or combination of targetWWNs and lun must be + set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: FlexVolume represents a generic volume resource that + is provisioned/attached using an exec based plugin. + properties: + driver: + description: Driver is the name of the driver to use for this + volume. + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". The default filesystem depends on FlexVolume + script. + type: string + options: + additionalProperties: + type: string + description: 'Optional: Extra command options if any.' + type: object + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef is reference to the secret + object containing sensitive information to pass to the plugin + scripts. This may be empty if no secret object is specified. + If the secret object contains more than one secret, all + secrets are passed to the plugin scripts.' + 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 + required: + - driver + type: object + flocker: + description: Flocker represents a Flocker volume attached to a + kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: Name of the dataset stored as metadata -> name + on the dataset for Flocker should be considered as deprecated + type: string + datasetUUID: + description: UUID of the dataset. This is unique identifier + of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'GCEPersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'Unique name of the PD resource in GCE. Used + to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'GitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated. To provision a + container with a git repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then mount the EmptyDir into + the Pod''s container.' + properties: + directory: + description: Target directory name. Must not contain or start + with '..'. If '.' is supplied, the volume directory will + be the git repository. Otherwise, if specified, the volume + will contain the git repository in the subdirectory with + the given name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'Glusterfs represents a Glusterfs mount on the host + that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'EndpointsName is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs volume path. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'HostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + This is generally used for system agents or other privileged + things that are allowed to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host directory + mounts and who can/can not mount host directories as read/write.' + properties: + path: + description: 'Path of the directory on the host. If the path + is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume Defaults to "" More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'ISCSI represents an ISCSI Disk resource that is + attached to a kubelet''s host machine and then exposed to the + pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: whether support iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: whether support iSCSI Session CHAP authentication + type: boolean + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator Name. If initiatorName + is specified with iscsiInterface simultaneously, new iSCSI + interface : will be created + for the connection. + type: string + iqn: + description: Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iSCSI Interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: iSCSI Target Portal List. The portal is either + an IP or ip_addr:port if the port is other than default + (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI target and initiator authentication + 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 + targetPortal: + description: iSCSI Target Portal. The Portal is either an + IP or ip_addr:port if the port is other than default (typically + TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'Volume''s name. Must be a DNS_LABEL and unique within + the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS mount on the host that shares + a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'Path that is exported by the NFS server. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will force the NFS export to be + mounted with read-only permissions. Defaults to false. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname or IP address of the + NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource represents a reference + to a PersistentVolumeClaim in the same namespace. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'ClaimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: PhotonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: ID that identifies Photon Controller persistent + disk + type: string + required: + - pdID + type: object + portworxVolume: + description: PortworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: FSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: Items for all in one resources secrets, configmaps, + and downward API + properties: + defaultMode: + description: Mode bits to use on created files by default. + Must be a value between 0 and 0777. 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: list of volume projections + items: + description: Projection that may be projected along with + other supported volume types + properties: + configMap: + description: information about the configMap data to + project + properties: + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. 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: 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: Specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + downwardAPI: + description: 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 + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. 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: + description: Specifies the output format + of the exposed resources, defaults to + "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: information about the secret data to project + properties: + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. 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: 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: Specify whether the Secret or its key + must be defined + type: boolean + type: object + serviceAccountToken: + description: 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 + required: + - sources + type: object + quobyte: + description: Quobyte represents a Quobyte mount on the host that + shares a pod's lifetime + properties: + group: + description: Group to map volume access to Default is no group + type: string + readOnly: + description: ReadOnly here will force the Quobyte volume to + be mounted with read-only permissions. Defaults to false. + type: boolean + registry: + description: Registry represents a single or multiple Quobyte + Registry services specified as a string as host:port pair + (multiple entries are separated with commas) which acts + as the central registry for volumes + type: string + tenant: + description: Tenant owning the given Quobyte volume in the + Backend Used with dynamically provisioned Quobyte volumes, + value is set by the plugin + type: string + user: + description: User to map volume access to Defaults to serivceaccount + user + type: string + volume: + description: Volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'RBD represents a Rados Block Device mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + image: + description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'The rados pool name. Default is rbd. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name of the authentication secret + for RBDUser. If provided overrides keyring. Default is nil. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + 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 + user: + description: 'The rados user name. Default is admin. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: ScaleIO represents a ScaleIO persistent volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: The host address of the ScaleIO API Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO Protection Domain for + the configured storage. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references to the secret for ScaleIO + user and other sensitive information. If this is not provided, + Login operation will fail. + 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 + sslEnabled: + description: Flag to enable/disable SSL communication with + Gateway, default false + type: boolean + storageMode: + description: Indicates whether the storage for a volume should + be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage Pool associated with the + protection domain. + type: string + system: + description: The name of the storage system as configured + in ScaleIO. + type: string + volumeName: + description: The name of a volume already created in the ScaleIO + system that is associated with this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'Secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. 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 + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. 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: 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 + optional: + description: Specify whether the Secret or its keys must be + defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace to + use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: StorageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies the secret to use for obtaining + the StorageOS API credentials. If not specified, default + values will be attempted. + 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 + volumeName: + description: VolumeName is the human-readable name of the + StorageOS volume. Volume names are only unique within a + namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies the scope of the volume + within StorageOS. If no namespace is specified then the + Pod's namespace will be used. This allows the Kubernetes + name scoping to be mirrored within StorageOS for tighter + integration. Set VolumeName to any name to override the + default behaviour. Set to "default" if you are not using + namespaces within StorageOS. Namespaces that do not pre-exist + within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: VsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based Management (SPBM) profile + ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based Management (SPBM) profile + name. + type: string + volumePath: + description: Path that identifies vSphere volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + status: + description: ClusterMasterStatus defines the observed state of ClusterMaster + properties: + bundlePushInfo: + description: Bundle push status tracker + properties: + lastCheckInterval: + format: int64 + type: integer + needToPushMasterApps: + type: boolean + type: object + phase: + description: current phase of the cluster master + enum: + - Pending + - Ready + - Updating + - ScalingUp + - ScalingDown + - Terminating + - Error + type: string + resourceRevMap: + additionalProperties: + type: string + description: Resource Revision tracker + type: object + selector: + description: selector for pods, used by HorizontalPodAutoscaler + type: string + smartstore: + description: Splunk Smartstore configuration. Refer to indexes.conf.spec + and server.conf.spec on docs.splunk.com + properties: + cacheManager: + description: Defines Cache manager settings + properties: + evictionPadding: + description: Additional size beyond 'minFreeSize' before eviction + kicks in + type: integer + evictionPolicy: + description: Eviction policy to use + type: string + hotlistBloomFilterRecencyHours: + description: Time period relative to the bucket's age, during + which the bloom filter file is protected from cache eviction + type: integer + hotlistRecencySecs: + description: Time period relative to the bucket's age, during + which the bucket is protected from cache eviction + type: integer + maxCacheSize: + description: Max cache size per partition + type: integer + maxConcurrentDownloads: + description: Maximum number of buckets that can be downloaded + from remote storage in parallel + type: integer + maxConcurrentUploads: + description: Maximum number of buckets that can be uploaded + to remote storage in parallel + type: integer + type: object + defaults: + description: Default configuration for indexes + properties: + maxGlobalDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of space for warm and cold buckets of an index + type: integer + maxGlobalRawDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of cumulative space for warm and cold buckets of an index + type: integer + volumeName: + description: Remote Volume name + type: string + type: object + indexes: + description: List of Splunk indexes + items: + description: IndexSpec defines Splunk index name and storage path + properties: + hotlistBloomFilterRecencyHours: + description: Time period relative to the bucket's age, during + which the bloom filter file is protected from cache eviction + type: integer + hotlistRecencySecs: + description: Time period relative to the bucket's age, during + which the bucket is protected from cache eviction + type: integer + maxGlobalDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of space for warm and cold buckets of an index + type: integer + maxGlobalRawDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of cumulative space for warm and cold buckets of an index + type: integer + name: + description: Splunk index name + type: string + remotePath: + description: Index location relative to the remote volume + path + type: string + volumeName: + description: Remote Volume name + type: string + type: object + type: array + volumes: + description: List of remote storage volumes + items: + description: VolumeSpec defines remote volume name and remote + volume URI + properties: + endpoint: + description: Remote volume URI + type: string + name: + description: Remote volume name + type: string + path: + description: Remote volume path + type: string + secretRef: + description: Secret object name + type: string + type: object + type: array + type: object + type: object + type: object + version: v1beta1 + versions: + - name: v1beta1 + served: true + storage: true + - name: v1alpha3 + served: true + storage: false + - name: v1alpha2 + served: true + storage: false diff --git a/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_indexerclusters_crd.yaml b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_indexerclusters_crd.yaml new file mode 100644 index 000000000..5b0f94156 --- /dev/null +++ b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_indexerclusters_crd.yaml @@ -0,0 +1,2344 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: indexerclusters.enterprise.splunk.com +spec: + additionalPrinterColumns: + - JSONPath: .status.phase + description: Status of indexer cluster + name: Phase + type: string + - JSONPath: .status.clusterMasterPhase + description: Status of cluster master + name: Master + type: string + - JSONPath: .status.replicas + description: Desired number of indexer peers + name: Desired + type: integer + - JSONPath: .status.readyReplicas + description: Current number of ready indexer peers + name: Ready + type: integer + - JSONPath: .metadata.creationTimestamp + description: Age of indexer cluster + name: Age + type: date + group: enterprise.splunk.com + names: + kind: IndexerCluster + listKind: IndexerClusterList + plural: indexerclusters + shortNames: + - idc + - idxc + singular: indexercluster + scope: Namespaced + subresources: + scale: + labelSelectorPath: .status.selector + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} + validation: + openAPIV3Schema: + description: IndexerCluster is the Schema for a Splunk Enterprise indexer cluster + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: IndexerClusterSpec defines the desired state of a Splunk Enterprise + indexer cluster + properties: + Mock: + description: Mock to differentiate between UTs and actual reconcile + type: boolean + affinity: + description: Kubernetes Affinity rules that control how pods are assigned + to particular nodes. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches all + objects with implicit weight 0 (i.e. it's a no-op). A null + preferred scheduling term matches no objects (i.e. is also + a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The + terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node has pods which matches the corresponding podAffinityTerm; + the node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may not + try to eventually evict the pod from its node. When there + are multiple elements, the lists of nodes corresponding to + each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some other + pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the anti-affinity expressions specified by this + field, but it may choose a node that violates one or more + of the expressions. The node that is most preferred is the + one with the greatest sum of weights, i.e. for each node that + meets all of the scheduling requirements (resource request, + requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field + and adding "weight" to the sum if the node has pods which + matches the corresponding podAffinityTerm; the node(s) with + the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will not + be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + clusterMasterRef: + description: ClusterMasterRef refers to a Splunk Enterprise indexer + cluster managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + defaults: + description: Inline map of default.yml overrides used to initialize + the environment + type: string + defaultsUrl: + description: Full path or URL for one or more default.yml files, separated + by commas + type: string + ephemeralStorage: + description: If true, ephemeral (emptyDir) storage will be used for + /opt/splunk/etc and /opt/splunk/var volumes + type: boolean + etcStorage: + description: Storage capacity to request for /opt/splunk/etc persistent + volume claims (default=”1Gi”) + type: string + image: + description: Image to use for Splunk pod containers (overrides RELATED_IMAGE_SPLUNK_ENTERPRISE + environment variables) + type: string + imagePullPolicy: + description: 'Sets pull policy for all images (either “Always” or the + default: “IfNotPresent”)' + enum: + - Always + - IfNotPresent + type: string + licenseMasterRef: + description: LicenseMasterRef refers to a Splunk Enterprise license + master managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + licenseUrl: + description: Full path or URL for a Splunk Enterprise license file + type: string + replicas: + description: Number of search head pods; a search head cluster will + be created if > 1 + format: int32 + type: integer + resources: + description: resource requirements for the pod containers + properties: + limits: + additionalProperties: + type: string + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + type: string + 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. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + schedulerName: + description: Name of Scheduler to use for pod placement (defaults to + “default-scheduler”) + type: string + serviceTemplate: + description: ServiceTemplate is a template used to create Kubernetes + services + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the + latest internal value, and may reject unrecognized values. More + info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + spec: + description: Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + clusterIP: + description: 'clusterIP is the IP address of the service and + is usually assigned randomly by the master. If an address + is specified manually and is not in use by others, it will + be allocated to the service; otherwise, creation of the service + will fail. This field can not be changed through updates. + Valid values are "None", empty string (""), or a valid IP + address. "None" can be specified for headless services when + proxying is not required. Only applies to types ClusterIP, + NodePort, and LoadBalancer. Ignored if type is ExternalName. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + externalIPs: + description: externalIPs is a list of IP addresses for which + nodes in the cluster will also accept traffic for this service. These + IPs are not managed by Kubernetes. The user is responsible + for ensuring that traffic arrives at a node with this IP. A + common example is external load-balancers that are not part + of the Kubernetes system. + items: + type: string + type: array + externalName: + description: externalName is the external reference that kubedns + or equivalent will return as a CNAME record for this service. + No proxying will be involved. Must be a valid RFC-1123 hostname + (https://tools.ietf.org/html/rfc1123) and requires Type to + be ExternalName. + type: string + externalTrafficPolicy: + description: externalTrafficPolicy denotes if this Service desires + to route external traffic to node-local or cluster-wide endpoints. + "Local" preserves the client source IP and avoids a second + hop for LoadBalancer and Nodeport type services, but risks + potentially imbalanced traffic spreading. "Cluster" obscures + the client source IP and may cause a second hop to another + node, but should have good overall load-spreading. + type: string + healthCheckNodePort: + description: healthCheckNodePort specifies the healthcheck nodePort + for the service. If not specified, HealthCheckNodePort is + created by the service api backend with the allocated nodePort. + Will use user-specified nodePort value if specified by the + client. Only effects when Type is set to LoadBalancer and + ExternalTrafficPolicy is set to Local. + format: int32 + type: integer + ipFamily: + description: ipFamily specifies whether this Service has a preference + for a particular IP family (e.g. IPv4 vs. IPv6). If a specific + IP family is requested, the clusterIP field will be allocated + from that family, if it is available in the cluster. If no + IP family is requested, the cluster's primary IP family will + be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, + externalIPs) and controllers which allocate external load-balancers + should use the same IP family. Endpoints for this Service + will be of this family. This field is immutable after creation. + Assigning a ServiceIPFamily not available in the cluster (e.g. + IPv6 in IPv4 only cluster) is an error condition and will + fail during clusterIP assignment. + type: string + loadBalancerIP: + description: 'Only applies to Service Type: LoadBalancer LoadBalancer + will get created with the IP specified in this field. This + feature depends on whether the underlying cloud-provider supports + specifying the loadBalancerIP when a load balancer is created. + This field will be ignored if the cloud-provider does not + support the feature.' + type: string + loadBalancerSourceRanges: + description: 'If specified and supported by the platform, this + will restrict traffic through the cloud-provider load-balancer + will be restricted to the specified client IPs. This field + will be ignored if the cloud-provider does not support the + feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/' + items: + type: string + type: array + ports: + description: 'The list of ports that are exposed by this service. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + items: + description: ServicePort contains information on service's + port. + properties: + name: + description: The name of this port within the service. + This must be a DNS_LABEL. All ports within a ServiceSpec + must have unique names. When considering the endpoints + for a Service, this must match the 'name' field in the + EndpointPort. Optional if only one ServicePort is defined + on this service. + type: string + nodePort: + description: 'The port on each node on which this service + is exposed when type=NodePort or LoadBalancer. Usually + assigned by the system. If specified, it will be allocated + to the service if unused or else creation of the service + will fail. Default is to auto-allocate a port if the + ServiceType of this Service requires one. More info: + https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: 'Number or name of the port to access on + the pods targeted by the service. Number must be in + the range 1 to 65535. Name must be an IANA_SVC_NAME. + If this is a string, it will be looked up as a named + port in the target Pod''s container ports. If this is + not specified, the value of the ''port'' field is used + (an identity map). This field is ignored for services + with clusterIP=None, and should be omitted or set equal + to the ''port'' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + publishNotReadyAddresses: + description: publishNotReadyAddresses, when set to true, indicates + that DNS implementations must publish the notReadyAddresses + of subsets for the Endpoints associated with the Service. + The default value is false. The primary use case for setting + this field is to use a StatefulSet's Headless Service to propagate + SRV records for its Pods without respect to their readiness + for purpose of peer discovery. + type: boolean + selector: + additionalProperties: + type: string + description: 'Route service traffic to pods with label keys + and values matching this selector. If empty or not present, + the service is assumed to have an external process managing + its endpoints, which Kubernetes will not modify. Only applies + to types ClusterIP, NodePort, and LoadBalancer. Ignored if + type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/' + type: object + sessionAffinity: + description: 'Supports "ClientIP" and "None". Used to maintain + session affinity. Enable client IP based session affinity. + Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations of Client + IP based session affinity. + properties: + timeoutSeconds: + description: timeoutSeconds specifies the seconds of + ClientIP type session sticky time. The value must + be >0 && <=86400(for 1 day) if ServiceAffinity == + "ClientIP". Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: 'type determines how the Service is exposed. Defaults + to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, + and LoadBalancer. "ExternalName" maps to the specified externalName. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or + if that is not specified, by manual construction of an Endpoints + object. If clusterIP is "None", no virtual IP is allocated + and the endpoints are published as a set of endpoints rather + than a stable IP. "NodePort" builds on ClusterIP and allocates + a port on every node which routes to the clusterIP. "LoadBalancer" + builds on NodePort and creates an external load-balancer (if + supported in the current cloud) which routes to the clusterIP. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types' + type: string + type: object + status: + description: 'Most recently observed status of the service. Populated + by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + properties: + loadBalancer: + description: LoadBalancer contains the current status of the + load-balancer, if one is present. + properties: + ingress: + description: Ingress is a list containing ingress points + for the load-balancer. Traffic intended for the service + should be sent to these ingress points. + items: + description: 'LoadBalancerIngress represents the status + of a load-balancer ingress point: traffic intended for + the service should be sent to an ingress point.' + properties: + hostname: + description: Hostname is set for load-balancer ingress + points that are DNS based (typically AWS load-balancers) + type: string + ip: + description: IP is set for load-balancer ingress points + that are IP based (typically GCE or OpenStack load-balancers) + type: string + type: object + type: array + type: object + type: object + type: object + storageClassName: + description: Name of StorageClass to use for persistent volume claims + type: string + tolerations: + description: Pod's tolerations for Kubernetes node's taint + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, operator + must be Exists; this combination means to match all values and + all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. Exists + is equivalent to wildcard for value, so that a pod can tolerate + all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the + toleration (which must be of effect NoExecute, otherwise this + field is ignored) tolerates the taint. By default, it is not + set, which means tolerate the taint forever (do not evict). + Zero and negative values will be treated as 0 (evict immediately) + by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise + just a regular string. + type: string + type: object + type: array + varStorage: + description: Storage capacity to request for /opt/splunk/var persistent + volume claims (default=”50Gi”) + type: string + volumes: + description: List of one or more Kubernetes volumes. These will be mounted + in all pod containers as as /mnt/ + items: + description: Volume represents a named volume in a pod that may be + accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents an AWS Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'Specify "true" to force and set the ReadOnly + property in VolumeMounts to "true". If omitted, the default + is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent disk resource in + AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: AzureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'Host Caching mode: None, Read Only, Read Write.' + type: string + diskName: + description: The Name of the data disk in the blob storage + type: string + diskURI: + description: The URI the data disk in the blob storage + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'Expected values Shared: multiple blob disks + per storage account Dedicated: single blob disk per storage + account Managed: azure managed data disk (only in managed + availability set). defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: AzureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret that contains Azure Storage + Account Name and Key + type: string + shareName: + description: Share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: CephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: 'Required: Monitors is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'Optional: Used as the mounted root, rather than + the full Ceph tree, default is /' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile is the path to key ring + for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef is reference to the authentication + secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + 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 + user: + description: 'Optional: User is the rados user name, default + is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'Cinder represents a cinder volume attached and mounted + on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to a secret object containing + parameters used to connect to OpenStack.' + 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 + volumeID: + description: 'volume id used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: ConfigMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. 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 + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. 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: 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: Specify whether the ConfigMap or its keys must + be defined + type: boolean + type: object + csi: + description: CSI (Container Storage Interface) represents storage + that is handled by an external CSI driver (Alpha feature). + properties: + driver: + description: Driver is the name of the CSI driver that handles + this volume. Consult with your admin for the correct name + as registered in the cluster. + type: string + fsType: + description: Filesystem type to mount. Ex. "ext4", "xfs", + "ntfs". If not provided, the empty value is passed to the + associated CSI driver which will determine the default filesystem + to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef is a reference to the secret + object containing sensitive information to pass to the CSI + driver to complete the CSI NodePublishVolume and NodeUnpublishVolume + calls. This field is optional, and may be empty if no secret + is required. If the secret object contains more than one + secret, all secret references are passed. + 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 + readOnly: + description: Specifies a read-only configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: VolumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: DownwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. 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 + items: + description: Items is a list of downward API volume 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 + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. 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: + description: Specifies the output format of the + exposed resources, defaults to "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'EmptyDir represents a temporary directory that shares + a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'What type of storage medium should back this + directory. The default is "" which means to use the node''s + default medium. Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + description: 'Total amount of local storage required for this + EmptyDir volume. The size limit is also applicable for memory + medium. The maximum usage on memory medium EmptyDir would + be the minimum value between the SizeLimit specified here + and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + type: string + type: object + fc: + description: FC represents a Fibre Channel resource that is attached + to a kubelet's host machine and then exposed to the pod. + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + lun: + description: 'Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target worldwide names (WWNs)' + items: + type: string + type: array + wwids: + description: 'Optional: FC volume world wide identifiers (wwids) + Either wwids or combination of targetWWNs and lun must be + set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: FlexVolume represents a generic volume resource that + is provisioned/attached using an exec based plugin. + properties: + driver: + description: Driver is the name of the driver to use for this + volume. + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". The default filesystem depends on FlexVolume + script. + type: string + options: + additionalProperties: + type: string + description: 'Optional: Extra command options if any.' + type: object + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef is reference to the secret + object containing sensitive information to pass to the plugin + scripts. This may be empty if no secret object is specified. + If the secret object contains more than one secret, all + secrets are passed to the plugin scripts.' + 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 + required: + - driver + type: object + flocker: + description: Flocker represents a Flocker volume attached to a + kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: Name of the dataset stored as metadata -> name + on the dataset for Flocker should be considered as deprecated + type: string + datasetUUID: + description: UUID of the dataset. This is unique identifier + of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'GCEPersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'Unique name of the PD resource in GCE. Used + to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'GitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated. To provision a + container with a git repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then mount the EmptyDir into + the Pod''s container.' + properties: + directory: + description: Target directory name. Must not contain or start + with '..'. If '.' is supplied, the volume directory will + be the git repository. Otherwise, if specified, the volume + will contain the git repository in the subdirectory with + the given name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'Glusterfs represents a Glusterfs mount on the host + that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'EndpointsName is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs volume path. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'HostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + This is generally used for system agents or other privileged + things that are allowed to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host directory + mounts and who can/can not mount host directories as read/write.' + properties: + path: + description: 'Path of the directory on the host. If the path + is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume Defaults to "" More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'ISCSI represents an ISCSI Disk resource that is + attached to a kubelet''s host machine and then exposed to the + pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: whether support iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: whether support iSCSI Session CHAP authentication + type: boolean + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator Name. If initiatorName + is specified with iscsiInterface simultaneously, new iSCSI + interface : will be created + for the connection. + type: string + iqn: + description: Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iSCSI Interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: iSCSI Target Portal List. The portal is either + an IP or ip_addr:port if the port is other than default + (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI target and initiator authentication + 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 + targetPortal: + description: iSCSI Target Portal. The Portal is either an + IP or ip_addr:port if the port is other than default (typically + TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'Volume''s name. Must be a DNS_LABEL and unique within + the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS mount on the host that shares + a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'Path that is exported by the NFS server. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will force the NFS export to be + mounted with read-only permissions. Defaults to false. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname or IP address of the + NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource represents a reference + to a PersistentVolumeClaim in the same namespace. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'ClaimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: PhotonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: ID that identifies Photon Controller persistent + disk + type: string + required: + - pdID + type: object + portworxVolume: + description: PortworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: FSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: Items for all in one resources secrets, configmaps, + and downward API + properties: + defaultMode: + description: Mode bits to use on created files by default. + Must be a value between 0 and 0777. 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: list of volume projections + items: + description: Projection that may be projected along with + other supported volume types + properties: + configMap: + description: information about the configMap data to + project + properties: + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. 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: 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: Specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + downwardAPI: + description: 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 + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. 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: + description: Specifies the output format + of the exposed resources, defaults to + "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: information about the secret data to project + properties: + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. 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: 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: Specify whether the Secret or its key + must be defined + type: boolean + type: object + serviceAccountToken: + description: 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 + required: + - sources + type: object + quobyte: + description: Quobyte represents a Quobyte mount on the host that + shares a pod's lifetime + properties: + group: + description: Group to map volume access to Default is no group + type: string + readOnly: + description: ReadOnly here will force the Quobyte volume to + be mounted with read-only permissions. Defaults to false. + type: boolean + registry: + description: Registry represents a single or multiple Quobyte + Registry services specified as a string as host:port pair + (multiple entries are separated with commas) which acts + as the central registry for volumes + type: string + tenant: + description: Tenant owning the given Quobyte volume in the + Backend Used with dynamically provisioned Quobyte volumes, + value is set by the plugin + type: string + user: + description: User to map volume access to Defaults to serivceaccount + user + type: string + volume: + description: Volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'RBD represents a Rados Block Device mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + image: + description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'The rados pool name. Default is rbd. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name of the authentication secret + for RBDUser. If provided overrides keyring. Default is nil. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + 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 + user: + description: 'The rados user name. Default is admin. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: ScaleIO represents a ScaleIO persistent volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: The host address of the ScaleIO API Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO Protection Domain for + the configured storage. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references to the secret for ScaleIO + user and other sensitive information. If this is not provided, + Login operation will fail. + 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 + sslEnabled: + description: Flag to enable/disable SSL communication with + Gateway, default false + type: boolean + storageMode: + description: Indicates whether the storage for a volume should + be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage Pool associated with the + protection domain. + type: string + system: + description: The name of the storage system as configured + in ScaleIO. + type: string + volumeName: + description: The name of a volume already created in the ScaleIO + system that is associated with this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'Secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. 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 + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. 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: 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 + optional: + description: Specify whether the Secret or its keys must be + defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace to + use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: StorageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies the secret to use for obtaining + the StorageOS API credentials. If not specified, default + values will be attempted. + 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 + volumeName: + description: VolumeName is the human-readable name of the + StorageOS volume. Volume names are only unique within a + namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies the scope of the volume + within StorageOS. If no namespace is specified then the + Pod's namespace will be used. This allows the Kubernetes + name scoping to be mirrored within StorageOS for tighter + integration. Set VolumeName to any name to override the + default behaviour. Set to "default" if you are not using + namespaces within StorageOS. Namespaces that do not pre-exist + within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: VsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based Management (SPBM) profile + ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based Management (SPBM) profile + name. + type: string + volumePath: + description: Path that identifies vSphere volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + status: + description: IndexerClusterStatus defines the observed state of a Splunk + Enterprise indexer cluster + properties: + IdxcPasswordChangedSecrets: + additionalProperties: + type: boolean + description: Holds secrets whose IDXC password has changed + type: object + clusterMasterPhase: + description: current phase of the cluster master + enum: + - Pending + - Ready + - Updating + - ScalingUp + - ScalingDown + - Terminating + - Error + type: string + indexer_secret_changed_flag: + description: Indicates when the idxc_secret has been changed for a peer + items: + type: boolean + type: array + indexing_ready_flag: + description: Indicates if the cluster is ready for indexing. + type: boolean + initialized_flag: + description: Indicates if the cluster is initialized. + type: boolean + maintenance_mode: + description: Indicates if the cluster is in maintenance mode. + type: boolean + namespace_scoped_secret_resource_version: + description: Indicates resource version of namespace scoped secret + type: string + peers: + description: status of each indexer cluster peer + items: + description: IndexerClusterMemberStatus is used to track the status + of each indexer cluster peer. + properties: + active_bundle_id: + description: The ID of the configuration bundle currently being + used by the master. + type: string + bucket_count: + description: Count of the number of buckets on this peer, across + all indexes. + format: int64 + type: integer + guid: + description: Unique identifier or GUID for the peer + type: string + is_searchable: + description: Flag indicating if this peer belongs to the current + committed generation and is searchable. + type: boolean + name: + description: Name of the indexer cluster peer + type: string + status: + description: Status of the indexer cluster peer + type: string + type: object + type: array + phase: + description: current phase of the indexer cluster + enum: + - Pending + - Ready + - Updating + - ScalingUp + - ScalingDown + - Terminating + - Error + type: string + readyReplicas: + description: current number of ready indexer peers + format: int32 + type: integer + replicas: + description: desired number of indexer peers + format: int32 + type: integer + selector: + description: selector for pods, used by HorizontalPodAutoscaler + type: string + service_ready_flag: + description: Indicates whether the master is ready to begin servicing, + based on whether it is initialized. + type: boolean + skip_recheck_update: + description: Indicates if we need to recheck the revision update on + pods + type: boolean + type: object + type: object + version: v1beta1 + versions: + - name: v1beta1 + served: true + storage: true + - name: v1alpha3 + served: true + storage: false + - name: v1alpha2 + served: true + storage: false diff --git a/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_licensemasters_crd.yaml b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_licensemasters_crd.yaml new file mode 100644 index 000000000..5b2239343 --- /dev/null +++ b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_licensemasters_crd.yaml @@ -0,0 +1,2240 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: licensemasters.enterprise.splunk.com +spec: + additionalPrinterColumns: + - JSONPath: .status.phase + description: Status of license master + name: Phase + type: string + - JSONPath: .metadata.creationTimestamp + description: Age of license master + name: Age + type: date + group: enterprise.splunk.com + names: + kind: LicenseMaster + listKind: LicenseMasterList + plural: licensemasters + shortNames: + - lm + singular: licensemaster + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + description: LicenseMaster is the Schema for a Splunk Enterprise license master. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: LicenseMasterSpec defines the desired state of a Splunk Enterprise + license master. + properties: + Mock: + description: Mock to differentiate between UTs and actual reconcile + type: boolean + affinity: + description: Kubernetes Affinity rules that control how pods are assigned + to particular nodes. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches all + objects with implicit weight 0 (i.e. it's a no-op). A null + preferred scheduling term matches no objects (i.e. is also + a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The + terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node has pods which matches the corresponding podAffinityTerm; + the node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may not + try to eventually evict the pod from its node. When there + are multiple elements, the lists of nodes corresponding to + each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some other + pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the anti-affinity expressions specified by this + field, but it may choose a node that violates one or more + of the expressions. The node that is most preferred is the + one with the greatest sum of weights, i.e. for each node that + meets all of the scheduling requirements (resource request, + requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field + and adding "weight" to the sum if the node has pods which + matches the corresponding podAffinityTerm; the node(s) with + the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will not + be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + clusterMasterRef: + description: ClusterMasterRef refers to a Splunk Enterprise indexer + cluster managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + defaults: + description: Inline map of default.yml overrides used to initialize + the environment + type: string + defaultsUrl: + description: Full path or URL for one or more default.yml files, separated + by commas + type: string + ephemeralStorage: + description: If true, ephemeral (emptyDir) storage will be used for + /opt/splunk/etc and /opt/splunk/var volumes + type: boolean + etcStorage: + description: Storage capacity to request for /opt/splunk/etc persistent + volume claims (default=”1Gi”) + type: string + image: + description: Image to use for Splunk pod containers (overrides RELATED_IMAGE_SPLUNK_ENTERPRISE + environment variables) + type: string + imagePullPolicy: + description: 'Sets pull policy for all images (either “Always” or the + default: “IfNotPresent”)' + enum: + - Always + - IfNotPresent + type: string + licenseMasterRef: + description: LicenseMasterRef refers to a Splunk Enterprise license + master managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + licenseUrl: + description: Full path or URL for a Splunk Enterprise license file + type: string + resources: + description: resource requirements for the pod containers + properties: + limits: + additionalProperties: + type: string + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + type: string + 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. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + schedulerName: + description: Name of Scheduler to use for pod placement (defaults to + “default-scheduler”) + type: string + serviceTemplate: + description: ServiceTemplate is a template used to create Kubernetes + services + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the + latest internal value, and may reject unrecognized values. More + info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + spec: + description: Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + clusterIP: + description: 'clusterIP is the IP address of the service and + is usually assigned randomly by the master. If an address + is specified manually and is not in use by others, it will + be allocated to the service; otherwise, creation of the service + will fail. This field can not be changed through updates. + Valid values are "None", empty string (""), or a valid IP + address. "None" can be specified for headless services when + proxying is not required. Only applies to types ClusterIP, + NodePort, and LoadBalancer. Ignored if type is ExternalName. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + externalIPs: + description: externalIPs is a list of IP addresses for which + nodes in the cluster will also accept traffic for this service. These + IPs are not managed by Kubernetes. The user is responsible + for ensuring that traffic arrives at a node with this IP. A + common example is external load-balancers that are not part + of the Kubernetes system. + items: + type: string + type: array + externalName: + description: externalName is the external reference that kubedns + or equivalent will return as a CNAME record for this service. + No proxying will be involved. Must be a valid RFC-1123 hostname + (https://tools.ietf.org/html/rfc1123) and requires Type to + be ExternalName. + type: string + externalTrafficPolicy: + description: externalTrafficPolicy denotes if this Service desires + to route external traffic to node-local or cluster-wide endpoints. + "Local" preserves the client source IP and avoids a second + hop for LoadBalancer and Nodeport type services, but risks + potentially imbalanced traffic spreading. "Cluster" obscures + the client source IP and may cause a second hop to another + node, but should have good overall load-spreading. + type: string + healthCheckNodePort: + description: healthCheckNodePort specifies the healthcheck nodePort + for the service. If not specified, HealthCheckNodePort is + created by the service api backend with the allocated nodePort. + Will use user-specified nodePort value if specified by the + client. Only effects when Type is set to LoadBalancer and + ExternalTrafficPolicy is set to Local. + format: int32 + type: integer + ipFamily: + description: ipFamily specifies whether this Service has a preference + for a particular IP family (e.g. IPv4 vs. IPv6). If a specific + IP family is requested, the clusterIP field will be allocated + from that family, if it is available in the cluster. If no + IP family is requested, the cluster's primary IP family will + be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, + externalIPs) and controllers which allocate external load-balancers + should use the same IP family. Endpoints for this Service + will be of this family. This field is immutable after creation. + Assigning a ServiceIPFamily not available in the cluster (e.g. + IPv6 in IPv4 only cluster) is an error condition and will + fail during clusterIP assignment. + type: string + loadBalancerIP: + description: 'Only applies to Service Type: LoadBalancer LoadBalancer + will get created with the IP specified in this field. This + feature depends on whether the underlying cloud-provider supports + specifying the loadBalancerIP when a load balancer is created. + This field will be ignored if the cloud-provider does not + support the feature.' + type: string + loadBalancerSourceRanges: + description: 'If specified and supported by the platform, this + will restrict traffic through the cloud-provider load-balancer + will be restricted to the specified client IPs. This field + will be ignored if the cloud-provider does not support the + feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/' + items: + type: string + type: array + ports: + description: 'The list of ports that are exposed by this service. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + items: + description: ServicePort contains information on service's + port. + properties: + name: + description: The name of this port within the service. + This must be a DNS_LABEL. All ports within a ServiceSpec + must have unique names. When considering the endpoints + for a Service, this must match the 'name' field in the + EndpointPort. Optional if only one ServicePort is defined + on this service. + type: string + nodePort: + description: 'The port on each node on which this service + is exposed when type=NodePort or LoadBalancer. Usually + assigned by the system. If specified, it will be allocated + to the service if unused or else creation of the service + will fail. Default is to auto-allocate a port if the + ServiceType of this Service requires one. More info: + https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: 'Number or name of the port to access on + the pods targeted by the service. Number must be in + the range 1 to 65535. Name must be an IANA_SVC_NAME. + If this is a string, it will be looked up as a named + port in the target Pod''s container ports. If this is + not specified, the value of the ''port'' field is used + (an identity map). This field is ignored for services + with clusterIP=None, and should be omitted or set equal + to the ''port'' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + publishNotReadyAddresses: + description: publishNotReadyAddresses, when set to true, indicates + that DNS implementations must publish the notReadyAddresses + of subsets for the Endpoints associated with the Service. + The default value is false. The primary use case for setting + this field is to use a StatefulSet's Headless Service to propagate + SRV records for its Pods without respect to their readiness + for purpose of peer discovery. + type: boolean + selector: + additionalProperties: + type: string + description: 'Route service traffic to pods with label keys + and values matching this selector. If empty or not present, + the service is assumed to have an external process managing + its endpoints, which Kubernetes will not modify. Only applies + to types ClusterIP, NodePort, and LoadBalancer. Ignored if + type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/' + type: object + sessionAffinity: + description: 'Supports "ClientIP" and "None". Used to maintain + session affinity. Enable client IP based session affinity. + Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations of Client + IP based session affinity. + properties: + timeoutSeconds: + description: timeoutSeconds specifies the seconds of + ClientIP type session sticky time. The value must + be >0 && <=86400(for 1 day) if ServiceAffinity == + "ClientIP". Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: 'type determines how the Service is exposed. Defaults + to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, + and LoadBalancer. "ExternalName" maps to the specified externalName. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or + if that is not specified, by manual construction of an Endpoints + object. If clusterIP is "None", no virtual IP is allocated + and the endpoints are published as a set of endpoints rather + than a stable IP. "NodePort" builds on ClusterIP and allocates + a port on every node which routes to the clusterIP. "LoadBalancer" + builds on NodePort and creates an external load-balancer (if + supported in the current cloud) which routes to the clusterIP. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types' + type: string + type: object + status: + description: 'Most recently observed status of the service. Populated + by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + properties: + loadBalancer: + description: LoadBalancer contains the current status of the + load-balancer, if one is present. + properties: + ingress: + description: Ingress is a list containing ingress points + for the load-balancer. Traffic intended for the service + should be sent to these ingress points. + items: + description: 'LoadBalancerIngress represents the status + of a load-balancer ingress point: traffic intended for + the service should be sent to an ingress point.' + properties: + hostname: + description: Hostname is set for load-balancer ingress + points that are DNS based (typically AWS load-balancers) + type: string + ip: + description: IP is set for load-balancer ingress points + that are IP based (typically GCE or OpenStack load-balancers) + type: string + type: object + type: array + type: object + type: object + type: object + storageClassName: + description: Name of StorageClass to use for persistent volume claims + type: string + tolerations: + description: Pod's tolerations for Kubernetes node's taint + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, operator + must be Exists; this combination means to match all values and + all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. Exists + is equivalent to wildcard for value, so that a pod can tolerate + all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the + toleration (which must be of effect NoExecute, otherwise this + field is ignored) tolerates the taint. By default, it is not + set, which means tolerate the taint forever (do not evict). + Zero and negative values will be treated as 0 (evict immediately) + by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise + just a regular string. + type: string + type: object + type: array + varStorage: + description: Storage capacity to request for /opt/splunk/var persistent + volume claims (default=”50Gi”) + type: string + volumes: + description: List of one or more Kubernetes volumes. These will be mounted + in all pod containers as as /mnt/ + items: + description: Volume represents a named volume in a pod that may be + accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents an AWS Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'Specify "true" to force and set the ReadOnly + property in VolumeMounts to "true". If omitted, the default + is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent disk resource in + AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: AzureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'Host Caching mode: None, Read Only, Read Write.' + type: string + diskName: + description: The Name of the data disk in the blob storage + type: string + diskURI: + description: The URI the data disk in the blob storage + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'Expected values Shared: multiple blob disks + per storage account Dedicated: single blob disk per storage + account Managed: azure managed data disk (only in managed + availability set). defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: AzureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret that contains Azure Storage + Account Name and Key + type: string + shareName: + description: Share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: CephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: 'Required: Monitors is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'Optional: Used as the mounted root, rather than + the full Ceph tree, default is /' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile is the path to key ring + for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef is reference to the authentication + secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + 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 + user: + description: 'Optional: User is the rados user name, default + is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'Cinder represents a cinder volume attached and mounted + on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to a secret object containing + parameters used to connect to OpenStack.' + 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 + volumeID: + description: 'volume id used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: ConfigMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. 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 + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. 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: 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: Specify whether the ConfigMap or its keys must + be defined + type: boolean + type: object + csi: + description: CSI (Container Storage Interface) represents storage + that is handled by an external CSI driver (Alpha feature). + properties: + driver: + description: Driver is the name of the CSI driver that handles + this volume. Consult with your admin for the correct name + as registered in the cluster. + type: string + fsType: + description: Filesystem type to mount. Ex. "ext4", "xfs", + "ntfs". If not provided, the empty value is passed to the + associated CSI driver which will determine the default filesystem + to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef is a reference to the secret + object containing sensitive information to pass to the CSI + driver to complete the CSI NodePublishVolume and NodeUnpublishVolume + calls. This field is optional, and may be empty if no secret + is required. If the secret object contains more than one + secret, all secret references are passed. + 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 + readOnly: + description: Specifies a read-only configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: VolumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: DownwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. 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 + items: + description: Items is a list of downward API volume 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 + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. 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: + description: Specifies the output format of the + exposed resources, defaults to "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'EmptyDir represents a temporary directory that shares + a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'What type of storage medium should back this + directory. The default is "" which means to use the node''s + default medium. Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + description: 'Total amount of local storage required for this + EmptyDir volume. The size limit is also applicable for memory + medium. The maximum usage on memory medium EmptyDir would + be the minimum value between the SizeLimit specified here + and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + type: string + type: object + fc: + description: FC represents a Fibre Channel resource that is attached + to a kubelet's host machine and then exposed to the pod. + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + lun: + description: 'Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target worldwide names (WWNs)' + items: + type: string + type: array + wwids: + description: 'Optional: FC volume world wide identifiers (wwids) + Either wwids or combination of targetWWNs and lun must be + set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: FlexVolume represents a generic volume resource that + is provisioned/attached using an exec based plugin. + properties: + driver: + description: Driver is the name of the driver to use for this + volume. + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". The default filesystem depends on FlexVolume + script. + type: string + options: + additionalProperties: + type: string + description: 'Optional: Extra command options if any.' + type: object + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef is reference to the secret + object containing sensitive information to pass to the plugin + scripts. This may be empty if no secret object is specified. + If the secret object contains more than one secret, all + secrets are passed to the plugin scripts.' + 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 + required: + - driver + type: object + flocker: + description: Flocker represents a Flocker volume attached to a + kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: Name of the dataset stored as metadata -> name + on the dataset for Flocker should be considered as deprecated + type: string + datasetUUID: + description: UUID of the dataset. This is unique identifier + of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'GCEPersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'Unique name of the PD resource in GCE. Used + to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'GitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated. To provision a + container with a git repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then mount the EmptyDir into + the Pod''s container.' + properties: + directory: + description: Target directory name. Must not contain or start + with '..'. If '.' is supplied, the volume directory will + be the git repository. Otherwise, if specified, the volume + will contain the git repository in the subdirectory with + the given name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'Glusterfs represents a Glusterfs mount on the host + that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'EndpointsName is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs volume path. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'HostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + This is generally used for system agents or other privileged + things that are allowed to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host directory + mounts and who can/can not mount host directories as read/write.' + properties: + path: + description: 'Path of the directory on the host. If the path + is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume Defaults to "" More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'ISCSI represents an ISCSI Disk resource that is + attached to a kubelet''s host machine and then exposed to the + pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: whether support iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: whether support iSCSI Session CHAP authentication + type: boolean + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator Name. If initiatorName + is specified with iscsiInterface simultaneously, new iSCSI + interface : will be created + for the connection. + type: string + iqn: + description: Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iSCSI Interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: iSCSI Target Portal List. The portal is either + an IP or ip_addr:port if the port is other than default + (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI target and initiator authentication + 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 + targetPortal: + description: iSCSI Target Portal. The Portal is either an + IP or ip_addr:port if the port is other than default (typically + TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'Volume''s name. Must be a DNS_LABEL and unique within + the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS mount on the host that shares + a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'Path that is exported by the NFS server. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will force the NFS export to be + mounted with read-only permissions. Defaults to false. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname or IP address of the + NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource represents a reference + to a PersistentVolumeClaim in the same namespace. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'ClaimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: PhotonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: ID that identifies Photon Controller persistent + disk + type: string + required: + - pdID + type: object + portworxVolume: + description: PortworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: FSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: Items for all in one resources secrets, configmaps, + and downward API + properties: + defaultMode: + description: Mode bits to use on created files by default. + Must be a value between 0 and 0777. 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: list of volume projections + items: + description: Projection that may be projected along with + other supported volume types + properties: + configMap: + description: information about the configMap data to + project + properties: + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. 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: 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: Specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + downwardAPI: + description: 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 + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. 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: + description: Specifies the output format + of the exposed resources, defaults to + "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: information about the secret data to project + properties: + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. 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: 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: Specify whether the Secret or its key + must be defined + type: boolean + type: object + serviceAccountToken: + description: 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 + required: + - sources + type: object + quobyte: + description: Quobyte represents a Quobyte mount on the host that + shares a pod's lifetime + properties: + group: + description: Group to map volume access to Default is no group + type: string + readOnly: + description: ReadOnly here will force the Quobyte volume to + be mounted with read-only permissions. Defaults to false. + type: boolean + registry: + description: Registry represents a single or multiple Quobyte + Registry services specified as a string as host:port pair + (multiple entries are separated with commas) which acts + as the central registry for volumes + type: string + tenant: + description: Tenant owning the given Quobyte volume in the + Backend Used with dynamically provisioned Quobyte volumes, + value is set by the plugin + type: string + user: + description: User to map volume access to Defaults to serivceaccount + user + type: string + volume: + description: Volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'RBD represents a Rados Block Device mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + image: + description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'The rados pool name. Default is rbd. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name of the authentication secret + for RBDUser. If provided overrides keyring. Default is nil. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + 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 + user: + description: 'The rados user name. Default is admin. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: ScaleIO represents a ScaleIO persistent volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: The host address of the ScaleIO API Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO Protection Domain for + the configured storage. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references to the secret for ScaleIO + user and other sensitive information. If this is not provided, + Login operation will fail. + 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 + sslEnabled: + description: Flag to enable/disable SSL communication with + Gateway, default false + type: boolean + storageMode: + description: Indicates whether the storage for a volume should + be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage Pool associated with the + protection domain. + type: string + system: + description: The name of the storage system as configured + in ScaleIO. + type: string + volumeName: + description: The name of a volume already created in the ScaleIO + system that is associated with this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'Secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. 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 + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. 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: 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 + optional: + description: Specify whether the Secret or its keys must be + defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace to + use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: StorageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies the secret to use for obtaining + the StorageOS API credentials. If not specified, default + values will be attempted. + 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 + volumeName: + description: VolumeName is the human-readable name of the + StorageOS volume. Volume names are only unique within a + namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies the scope of the volume + within StorageOS. If no namespace is specified then the + Pod's namespace will be used. This allows the Kubernetes + name scoping to be mirrored within StorageOS for tighter + integration. Set VolumeName to any name to override the + default behaviour. Set to "default" if you are not using + namespaces within StorageOS. Namespaces that do not pre-exist + within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: VsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based Management (SPBM) profile + ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based Management (SPBM) profile + name. + type: string + volumePath: + description: Path that identifies vSphere volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + status: + description: LicenseMasterStatus defines the observed state of a Splunk + Enterprise license master. + properties: + phase: + description: current phase of the license master + enum: + - Pending + - Ready + - Updating + - ScalingUp + - ScalingDown + - Terminating + - Error + type: string + type: object + type: object + version: v1beta1 + versions: + - name: v1beta1 + served: true + storage: true + - name: v1alpha3 + served: true + storage: false + - name: v1alpha2 + served: true + storage: false diff --git a/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_searchheadclusters_crd.yaml b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_searchheadclusters_crd.yaml new file mode 100644 index 000000000..77d75f9aa --- /dev/null +++ b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_searchheadclusters_crd.yaml @@ -0,0 +1,2394 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: searchheadclusters.enterprise.splunk.com +spec: + additionalPrinterColumns: + - JSONPath: .status.phase + description: Status of search head cluster + name: Phase + type: string + - JSONPath: .status.deployerPhase + description: Status of the deployer + name: Deployer + type: string + - JSONPath: .status.replicas + description: Desired number of search head cluster members + name: Desired + type: integer + - JSONPath: .status.readyReplicas + description: Current number of ready search head cluster members + name: Ready + type: integer + - JSONPath: .metadata.creationTimestamp + description: Age of search head cluster + name: Age + type: date + group: enterprise.splunk.com + names: + kind: SearchHeadCluster + listKind: SearchHeadClusterList + plural: searchheadclusters + shortNames: + - shc + singular: searchheadcluster + scope: Namespaced + subresources: + scale: + labelSelectorPath: .status.selector + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} + validation: + openAPIV3Schema: + description: SearchHeadCluster is the Schema for a Splunk Enterprise search + head cluster + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: SearchHeadClusterSpec defines the desired state of a Splunk + Enterprise search head cluster + properties: + Mock: + description: Mock to differentiate between UTs and actual reconcile + type: boolean + affinity: + description: Kubernetes Affinity rules that control how pods are assigned + to particular nodes. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches all + objects with implicit weight 0 (i.e. it's a no-op). A null + preferred scheduling term matches no objects (i.e. is also + a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The + terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node has pods which matches the corresponding podAffinityTerm; + the node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may not + try to eventually evict the pod from its node. When there + are multiple elements, the lists of nodes corresponding to + each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some other + pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the anti-affinity expressions specified by this + field, but it may choose a node that violates one or more + of the expressions. The node that is most preferred is the + one with the greatest sum of weights, i.e. for each node that + meets all of the scheduling requirements (resource request, + requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field + and adding "weight" to the sum if the node has pods which + matches the corresponding podAffinityTerm; the node(s) with + the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will not + be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + clusterMasterRef: + description: ClusterMasterRef refers to a Splunk Enterprise indexer + cluster managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + defaults: + description: Inline map of default.yml overrides used to initialize + the environment + type: string + defaultsUrl: + description: Full path or URL for one or more default.yml files, separated + by commas + type: string + ephemeralStorage: + description: If true, ephemeral (emptyDir) storage will be used for + /opt/splunk/etc and /opt/splunk/var volumes + type: boolean + etcStorage: + description: Storage capacity to request for /opt/splunk/etc persistent + volume claims (default=”1Gi”) + type: string + image: + description: Image to use for Splunk pod containers (overrides RELATED_IMAGE_SPLUNK_ENTERPRISE + environment variables) + type: string + imagePullPolicy: + description: 'Sets pull policy for all images (either “Always” or the + default: “IfNotPresent”)' + enum: + - Always + - IfNotPresent + type: string + licenseMasterRef: + description: LicenseMasterRef refers to a Splunk Enterprise license + master managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + licenseUrl: + description: Full path or URL for a Splunk Enterprise license file + type: string + replicas: + description: Number of search head pods; a search head cluster will + be created if > 1 + format: int32 + type: integer + resources: + description: resource requirements for the pod containers + properties: + limits: + additionalProperties: + type: string + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + type: string + 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. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + schedulerName: + description: Name of Scheduler to use for pod placement (defaults to + “default-scheduler”) + type: string + serviceTemplate: + description: ServiceTemplate is a template used to create Kubernetes + services + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the + latest internal value, and may reject unrecognized values. More + info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + spec: + description: Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + clusterIP: + description: 'clusterIP is the IP address of the service and + is usually assigned randomly by the master. If an address + is specified manually and is not in use by others, it will + be allocated to the service; otherwise, creation of the service + will fail. This field can not be changed through updates. + Valid values are "None", empty string (""), or a valid IP + address. "None" can be specified for headless services when + proxying is not required. Only applies to types ClusterIP, + NodePort, and LoadBalancer. Ignored if type is ExternalName. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + externalIPs: + description: externalIPs is a list of IP addresses for which + nodes in the cluster will also accept traffic for this service. These + IPs are not managed by Kubernetes. The user is responsible + for ensuring that traffic arrives at a node with this IP. A + common example is external load-balancers that are not part + of the Kubernetes system. + items: + type: string + type: array + externalName: + description: externalName is the external reference that kubedns + or equivalent will return as a CNAME record for this service. + No proxying will be involved. Must be a valid RFC-1123 hostname + (https://tools.ietf.org/html/rfc1123) and requires Type to + be ExternalName. + type: string + externalTrafficPolicy: + description: externalTrafficPolicy denotes if this Service desires + to route external traffic to node-local or cluster-wide endpoints. + "Local" preserves the client source IP and avoids a second + hop for LoadBalancer and Nodeport type services, but risks + potentially imbalanced traffic spreading. "Cluster" obscures + the client source IP and may cause a second hop to another + node, but should have good overall load-spreading. + type: string + healthCheckNodePort: + description: healthCheckNodePort specifies the healthcheck nodePort + for the service. If not specified, HealthCheckNodePort is + created by the service api backend with the allocated nodePort. + Will use user-specified nodePort value if specified by the + client. Only effects when Type is set to LoadBalancer and + ExternalTrafficPolicy is set to Local. + format: int32 + type: integer + ipFamily: + description: ipFamily specifies whether this Service has a preference + for a particular IP family (e.g. IPv4 vs. IPv6). If a specific + IP family is requested, the clusterIP field will be allocated + from that family, if it is available in the cluster. If no + IP family is requested, the cluster's primary IP family will + be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, + externalIPs) and controllers which allocate external load-balancers + should use the same IP family. Endpoints for this Service + will be of this family. This field is immutable after creation. + Assigning a ServiceIPFamily not available in the cluster (e.g. + IPv6 in IPv4 only cluster) is an error condition and will + fail during clusterIP assignment. + type: string + loadBalancerIP: + description: 'Only applies to Service Type: LoadBalancer LoadBalancer + will get created with the IP specified in this field. This + feature depends on whether the underlying cloud-provider supports + specifying the loadBalancerIP when a load balancer is created. + This field will be ignored if the cloud-provider does not + support the feature.' + type: string + loadBalancerSourceRanges: + description: 'If specified and supported by the platform, this + will restrict traffic through the cloud-provider load-balancer + will be restricted to the specified client IPs. This field + will be ignored if the cloud-provider does not support the + feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/' + items: + type: string + type: array + ports: + description: 'The list of ports that are exposed by this service. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + items: + description: ServicePort contains information on service's + port. + properties: + name: + description: The name of this port within the service. + This must be a DNS_LABEL. All ports within a ServiceSpec + must have unique names. When considering the endpoints + for a Service, this must match the 'name' field in the + EndpointPort. Optional if only one ServicePort is defined + on this service. + type: string + nodePort: + description: 'The port on each node on which this service + is exposed when type=NodePort or LoadBalancer. Usually + assigned by the system. If specified, it will be allocated + to the service if unused or else creation of the service + will fail. Default is to auto-allocate a port if the + ServiceType of this Service requires one. More info: + https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: 'Number or name of the port to access on + the pods targeted by the service. Number must be in + the range 1 to 65535. Name must be an IANA_SVC_NAME. + If this is a string, it will be looked up as a named + port in the target Pod''s container ports. If this is + not specified, the value of the ''port'' field is used + (an identity map). This field is ignored for services + with clusterIP=None, and should be omitted or set equal + to the ''port'' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + publishNotReadyAddresses: + description: publishNotReadyAddresses, when set to true, indicates + that DNS implementations must publish the notReadyAddresses + of subsets for the Endpoints associated with the Service. + The default value is false. The primary use case for setting + this field is to use a StatefulSet's Headless Service to propagate + SRV records for its Pods without respect to their readiness + for purpose of peer discovery. + type: boolean + selector: + additionalProperties: + type: string + description: 'Route service traffic to pods with label keys + and values matching this selector. If empty or not present, + the service is assumed to have an external process managing + its endpoints, which Kubernetes will not modify. Only applies + to types ClusterIP, NodePort, and LoadBalancer. Ignored if + type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/' + type: object + sessionAffinity: + description: 'Supports "ClientIP" and "None". Used to maintain + session affinity. Enable client IP based session affinity. + Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations of Client + IP based session affinity. + properties: + timeoutSeconds: + description: timeoutSeconds specifies the seconds of + ClientIP type session sticky time. The value must + be >0 && <=86400(for 1 day) if ServiceAffinity == + "ClientIP". Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: 'type determines how the Service is exposed. Defaults + to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, + and LoadBalancer. "ExternalName" maps to the specified externalName. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or + if that is not specified, by manual construction of an Endpoints + object. If clusterIP is "None", no virtual IP is allocated + and the endpoints are published as a set of endpoints rather + than a stable IP. "NodePort" builds on ClusterIP and allocates + a port on every node which routes to the clusterIP. "LoadBalancer" + builds on NodePort and creates an external load-balancer (if + supported in the current cloud) which routes to the clusterIP. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types' + type: string + type: object + status: + description: 'Most recently observed status of the service. Populated + by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + properties: + loadBalancer: + description: LoadBalancer contains the current status of the + load-balancer, if one is present. + properties: + ingress: + description: Ingress is a list containing ingress points + for the load-balancer. Traffic intended for the service + should be sent to these ingress points. + items: + description: 'LoadBalancerIngress represents the status + of a load-balancer ingress point: traffic intended for + the service should be sent to an ingress point.' + properties: + hostname: + description: Hostname is set for load-balancer ingress + points that are DNS based (typically AWS load-balancers) + type: string + ip: + description: IP is set for load-balancer ingress points + that are IP based (typically GCE or OpenStack load-balancers) + type: string + type: object + type: array + type: object + type: object + type: object + sparkImage: + description: Image to use for Spark pod containers (overrides RELATED_IMAGE_SPLUNK_SPARK + environment variables) + type: string + sparkRef: + description: SparkRef refers to a Spark cluster managed by the operator + within Kubernetes When defined, Data Fabric Search (DFS) will be enabled + and configured to use the Spark cluster. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + storageClassName: + description: Name of StorageClass to use for persistent volume claims + type: string + tolerations: + description: Pod's tolerations for Kubernetes node's taint + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, operator + must be Exists; this combination means to match all values and + all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. Exists + is equivalent to wildcard for value, so that a pod can tolerate + all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the + toleration (which must be of effect NoExecute, otherwise this + field is ignored) tolerates the taint. By default, it is not + set, which means tolerate the taint forever (do not evict). + Zero and negative values will be treated as 0 (evict immediately) + by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise + just a regular string. + type: string + type: object + type: array + varStorage: + description: Storage capacity to request for /opt/splunk/var persistent + volume claims (default=”50Gi”) + type: string + volumes: + description: List of one or more Kubernetes volumes. These will be mounted + in all pod containers as as /mnt/ + items: + description: Volume represents a named volume in a pod that may be + accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents an AWS Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'Specify "true" to force and set the ReadOnly + property in VolumeMounts to "true". If omitted, the default + is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent disk resource in + AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: AzureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'Host Caching mode: None, Read Only, Read Write.' + type: string + diskName: + description: The Name of the data disk in the blob storage + type: string + diskURI: + description: The URI the data disk in the blob storage + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'Expected values Shared: multiple blob disks + per storage account Dedicated: single blob disk per storage + account Managed: azure managed data disk (only in managed + availability set). defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: AzureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret that contains Azure Storage + Account Name and Key + type: string + shareName: + description: Share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: CephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: 'Required: Monitors is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'Optional: Used as the mounted root, rather than + the full Ceph tree, default is /' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile is the path to key ring + for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef is reference to the authentication + secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + 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 + user: + description: 'Optional: User is the rados user name, default + is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'Cinder represents a cinder volume attached and mounted + on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to a secret object containing + parameters used to connect to OpenStack.' + 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 + volumeID: + description: 'volume id used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: ConfigMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. 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 + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. 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: 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: Specify whether the ConfigMap or its keys must + be defined + type: boolean + type: object + csi: + description: CSI (Container Storage Interface) represents storage + that is handled by an external CSI driver (Alpha feature). + properties: + driver: + description: Driver is the name of the CSI driver that handles + this volume. Consult with your admin for the correct name + as registered in the cluster. + type: string + fsType: + description: Filesystem type to mount. Ex. "ext4", "xfs", + "ntfs". If not provided, the empty value is passed to the + associated CSI driver which will determine the default filesystem + to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef is a reference to the secret + object containing sensitive information to pass to the CSI + driver to complete the CSI NodePublishVolume and NodeUnpublishVolume + calls. This field is optional, and may be empty if no secret + is required. If the secret object contains more than one + secret, all secret references are passed. + 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 + readOnly: + description: Specifies a read-only configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: VolumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: DownwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. 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 + items: + description: Items is a list of downward API volume 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 + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. 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: + description: Specifies the output format of the + exposed resources, defaults to "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'EmptyDir represents a temporary directory that shares + a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'What type of storage medium should back this + directory. The default is "" which means to use the node''s + default medium. Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + description: 'Total amount of local storage required for this + EmptyDir volume. The size limit is also applicable for memory + medium. The maximum usage on memory medium EmptyDir would + be the minimum value between the SizeLimit specified here + and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + type: string + type: object + fc: + description: FC represents a Fibre Channel resource that is attached + to a kubelet's host machine and then exposed to the pod. + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + lun: + description: 'Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target worldwide names (WWNs)' + items: + type: string + type: array + wwids: + description: 'Optional: FC volume world wide identifiers (wwids) + Either wwids or combination of targetWWNs and lun must be + set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: FlexVolume represents a generic volume resource that + is provisioned/attached using an exec based plugin. + properties: + driver: + description: Driver is the name of the driver to use for this + volume. + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". The default filesystem depends on FlexVolume + script. + type: string + options: + additionalProperties: + type: string + description: 'Optional: Extra command options if any.' + type: object + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef is reference to the secret + object containing sensitive information to pass to the plugin + scripts. This may be empty if no secret object is specified. + If the secret object contains more than one secret, all + secrets are passed to the plugin scripts.' + 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 + required: + - driver + type: object + flocker: + description: Flocker represents a Flocker volume attached to a + kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: Name of the dataset stored as metadata -> name + on the dataset for Flocker should be considered as deprecated + type: string + datasetUUID: + description: UUID of the dataset. This is unique identifier + of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'GCEPersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'Unique name of the PD resource in GCE. Used + to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'GitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated. To provision a + container with a git repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then mount the EmptyDir into + the Pod''s container.' + properties: + directory: + description: Target directory name. Must not contain or start + with '..'. If '.' is supplied, the volume directory will + be the git repository. Otherwise, if specified, the volume + will contain the git repository in the subdirectory with + the given name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'Glusterfs represents a Glusterfs mount on the host + that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'EndpointsName is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs volume path. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'HostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + This is generally used for system agents or other privileged + things that are allowed to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host directory + mounts and who can/can not mount host directories as read/write.' + properties: + path: + description: 'Path of the directory on the host. If the path + is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume Defaults to "" More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'ISCSI represents an ISCSI Disk resource that is + attached to a kubelet''s host machine and then exposed to the + pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: whether support iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: whether support iSCSI Session CHAP authentication + type: boolean + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator Name. If initiatorName + is specified with iscsiInterface simultaneously, new iSCSI + interface : will be created + for the connection. + type: string + iqn: + description: Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iSCSI Interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: iSCSI Target Portal List. The portal is either + an IP or ip_addr:port if the port is other than default + (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI target and initiator authentication + 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 + targetPortal: + description: iSCSI Target Portal. The Portal is either an + IP or ip_addr:port if the port is other than default (typically + TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'Volume''s name. Must be a DNS_LABEL and unique within + the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS mount on the host that shares + a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'Path that is exported by the NFS server. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will force the NFS export to be + mounted with read-only permissions. Defaults to false. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname or IP address of the + NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource represents a reference + to a PersistentVolumeClaim in the same namespace. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'ClaimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: PhotonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: ID that identifies Photon Controller persistent + disk + type: string + required: + - pdID + type: object + portworxVolume: + description: PortworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: FSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: Items for all in one resources secrets, configmaps, + and downward API + properties: + defaultMode: + description: Mode bits to use on created files by default. + Must be a value between 0 and 0777. 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: list of volume projections + items: + description: Projection that may be projected along with + other supported volume types + properties: + configMap: + description: information about the configMap data to + project + properties: + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. 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: 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: Specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + downwardAPI: + description: 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 + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. 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: + description: Specifies the output format + of the exposed resources, defaults to + "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: information about the secret data to project + properties: + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. 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: 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: Specify whether the Secret or its key + must be defined + type: boolean + type: object + serviceAccountToken: + description: 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 + required: + - sources + type: object + quobyte: + description: Quobyte represents a Quobyte mount on the host that + shares a pod's lifetime + properties: + group: + description: Group to map volume access to Default is no group + type: string + readOnly: + description: ReadOnly here will force the Quobyte volume to + be mounted with read-only permissions. Defaults to false. + type: boolean + registry: + description: Registry represents a single or multiple Quobyte + Registry services specified as a string as host:port pair + (multiple entries are separated with commas) which acts + as the central registry for volumes + type: string + tenant: + description: Tenant owning the given Quobyte volume in the + Backend Used with dynamically provisioned Quobyte volumes, + value is set by the plugin + type: string + user: + description: User to map volume access to Defaults to serivceaccount + user + type: string + volume: + description: Volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'RBD represents a Rados Block Device mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + image: + description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'The rados pool name. Default is rbd. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name of the authentication secret + for RBDUser. If provided overrides keyring. Default is nil. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + 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 + user: + description: 'The rados user name. Default is admin. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: ScaleIO represents a ScaleIO persistent volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: The host address of the ScaleIO API Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO Protection Domain for + the configured storage. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references to the secret for ScaleIO + user and other sensitive information. If this is not provided, + Login operation will fail. + 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 + sslEnabled: + description: Flag to enable/disable SSL communication with + Gateway, default false + type: boolean + storageMode: + description: Indicates whether the storage for a volume should + be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage Pool associated with the + protection domain. + type: string + system: + description: The name of the storage system as configured + in ScaleIO. + type: string + volumeName: + description: The name of a volume already created in the ScaleIO + system that is associated with this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'Secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. 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 + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. 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: 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 + optional: + description: Specify whether the Secret or its keys must be + defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace to + use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: StorageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies the secret to use for obtaining + the StorageOS API credentials. If not specified, default + values will be attempted. + 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 + volumeName: + description: VolumeName is the human-readable name of the + StorageOS volume. Volume names are only unique within a + namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies the scope of the volume + within StorageOS. If no namespace is specified then the + Pod's namespace will be used. This allows the Kubernetes + name scoping to be mirrored within StorageOS for tighter + integration. Set VolumeName to any name to override the + default behaviour. Set to "default" if you are not using + namespaces within StorageOS. Namespaces that do not pre-exist + within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: VsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based Management (SPBM) profile + ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based Management (SPBM) profile + name. + type: string + volumePath: + description: Path that identifies vSphere volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + status: + description: SearchHeadClusterStatus defines the observed state of a Splunk + Enterprise search head cluster + properties: + adminPasswordChangedSecrets: + additionalProperties: + type: boolean + description: Holds secrets whose admin password has changed + type: object + adminSecretChangedFlag: + description: Indicates when the admin password has been changed for + a peer + items: + type: boolean + type: array + captain: + description: name or label of the search head captain + type: string + captainReady: + description: true if the search head cluster's captain is ready to service + requests + type: boolean + deployerPhase: + description: current phase of the deployer + enum: + - Pending + - Ready + - Updating + - ScalingUp + - ScalingDown + - Terminating + - Error + type: string + initialized: + description: true if the search head cluster has finished initialization + type: boolean + maintenanceMode: + description: true if the search head cluster is in maintenance mode + type: boolean + members: + description: status of each search head cluster member + items: + description: SearchHeadClusterMemberStatus is used to track the status + of each search head cluster member + properties: + active_historical_search_count: + description: Number of currently running historical searches. + type: integer + active_realtime_search_count: + description: Number of currently running realtime searches. + type: integer + adhoc_searchhead: + description: Flag that indicates if this member can run scheduled + searches. + type: boolean + is_registered: + description: Indicates if this member is registered with the searchhead + cluster captain. + type: boolean + name: + description: Name of the search head cluster member + type: string + status: + description: Indicates the status of the member. + type: string + type: object + type: array + minPeersJoined: + description: true if the minimum number of search head cluster members + have joined + type: boolean + namespace_scoped_secret_resource_version: + description: Indicates resource version of namespace scoped secret + type: string + phase: + description: current phase of the search head cluster + enum: + - Pending + - Ready + - Updating + - ScalingUp + - ScalingDown + - Terminating + - Error + type: string + readyReplicas: + description: current number of ready search head cluster members + format: int32 + type: integer + replicas: + description: desired number of search head cluster members + format: int32 + type: integer + selector: + description: selector for pods, used by HorizontalPodAutoscaler + type: string + shcSecretChangedFlag: + description: Indicates when the shc_secret has been changed for a peer + items: + type: boolean + type: array + skip_recheck_update: + description: Indicates if we need to recheck the revision update on + pods + type: boolean + type: object + type: object + version: v1beta1 + versions: + - name: v1beta1 + served: true + storage: true + - name: v1alpha3 + served: true + storage: false + - name: v1alpha2 + served: true + storage: false diff --git a/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_sparks_crd.yaml b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_sparks_crd.yaml new file mode 100644 index 000000000..063d16f26 --- /dev/null +++ b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_sparks_crd.yaml @@ -0,0 +1,996 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: sparks.enterprise.splunk.com +spec: + additionalPrinterColumns: + - JSONPath: .status.phase + description: Status of Spark workers + name: Phase + type: string + - JSONPath: .status.masterPhase + description: Status of Spark master + name: Master + type: string + - JSONPath: .status.replicas + description: Number of desired Spark workers + name: Desired + type: integer + - JSONPath: .status.readyReplicas + description: Current number of ready Spark workers + name: Ready + type: integer + - JSONPath: .metadata.creationTimestamp + description: Age of Spark cluster + name: Age + type: date + group: enterprise.splunk.com + names: + kind: Spark + listKind: SparkList + plural: sparks + singular: spark + scope: Namespaced + subresources: + scale: + labelSelectorPath: .status.selector + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} + validation: + openAPIV3Schema: + description: Spark is the Schema for a Spark cluster + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: SparkSpec defines the desired state of a Spark cluster + properties: + affinity: + description: Kubernetes Affinity rules that control how pods are assigned + to particular nodes. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches all + objects with implicit weight 0 (i.e. it's a no-op). A null + preferred scheduling term matches no objects (i.e. is also + a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The + terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node has pods which matches the corresponding podAffinityTerm; + the node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may not + try to eventually evict the pod from its node. When there + are multiple elements, the lists of nodes corresponding to + each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some other + pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the anti-affinity expressions specified by this + field, but it may choose a node that violates one or more + of the expressions. The node that is most preferred is the + one with the greatest sum of weights, i.e. for each node that + meets all of the scheduling requirements (resource request, + requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field + and adding "weight" to the sum if the node has pods which + matches the corresponding podAffinityTerm; the node(s) with + the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will not + be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + image: + description: Image to use for Splunk pod containers (overrides RELATED_IMAGE_SPLUNK_ENTERPRISE + environment variables) + type: string + imagePullPolicy: + description: 'Sets pull policy for all images (either “Always” or the + default: “IfNotPresent”)' + enum: + - Always + - IfNotPresent + type: string + replicas: + description: Number of spark worker pods + format: int32 + type: integer + resources: + description: resource requirements for the pod containers + properties: + limits: + additionalProperties: + type: string + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + type: string + 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. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + schedulerName: + description: Name of Scheduler to use for pod placement (defaults to + “default-scheduler”) + type: string + serviceTemplate: + description: ServiceTemplate is a template used to create Kubernetes + services + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the + latest internal value, and may reject unrecognized values. More + info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + spec: + description: Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + clusterIP: + description: 'clusterIP is the IP address of the service and + is usually assigned randomly by the master. If an address + is specified manually and is not in use by others, it will + be allocated to the service; otherwise, creation of the service + will fail. This field can not be changed through updates. + Valid values are "None", empty string (""), or a valid IP + address. "None" can be specified for headless services when + proxying is not required. Only applies to types ClusterIP, + NodePort, and LoadBalancer. Ignored if type is ExternalName. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + externalIPs: + description: externalIPs is a list of IP addresses for which + nodes in the cluster will also accept traffic for this service. These + IPs are not managed by Kubernetes. The user is responsible + for ensuring that traffic arrives at a node with this IP. A + common example is external load-balancers that are not part + of the Kubernetes system. + items: + type: string + type: array + externalName: + description: externalName is the external reference that kubedns + or equivalent will return as a CNAME record for this service. + No proxying will be involved. Must be a valid RFC-1123 hostname + (https://tools.ietf.org/html/rfc1123) and requires Type to + be ExternalName. + type: string + externalTrafficPolicy: + description: externalTrafficPolicy denotes if this Service desires + to route external traffic to node-local or cluster-wide endpoints. + "Local" preserves the client source IP and avoids a second + hop for LoadBalancer and Nodeport type services, but risks + potentially imbalanced traffic spreading. "Cluster" obscures + the client source IP and may cause a second hop to another + node, but should have good overall load-spreading. + type: string + healthCheckNodePort: + description: healthCheckNodePort specifies the healthcheck nodePort + for the service. If not specified, HealthCheckNodePort is + created by the service api backend with the allocated nodePort. + Will use user-specified nodePort value if specified by the + client. Only effects when Type is set to LoadBalancer and + ExternalTrafficPolicy is set to Local. + format: int32 + type: integer + ipFamily: + description: ipFamily specifies whether this Service has a preference + for a particular IP family (e.g. IPv4 vs. IPv6). If a specific + IP family is requested, the clusterIP field will be allocated + from that family, if it is available in the cluster. If no + IP family is requested, the cluster's primary IP family will + be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, + externalIPs) and controllers which allocate external load-balancers + should use the same IP family. Endpoints for this Service + will be of this family. This field is immutable after creation. + Assigning a ServiceIPFamily not available in the cluster (e.g. + IPv6 in IPv4 only cluster) is an error condition and will + fail during clusterIP assignment. + type: string + loadBalancerIP: + description: 'Only applies to Service Type: LoadBalancer LoadBalancer + will get created with the IP specified in this field. This + feature depends on whether the underlying cloud-provider supports + specifying the loadBalancerIP when a load balancer is created. + This field will be ignored if the cloud-provider does not + support the feature.' + type: string + loadBalancerSourceRanges: + description: 'If specified and supported by the platform, this + will restrict traffic through the cloud-provider load-balancer + will be restricted to the specified client IPs. This field + will be ignored if the cloud-provider does not support the + feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/' + items: + type: string + type: array + ports: + description: 'The list of ports that are exposed by this service. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + items: + description: ServicePort contains information on service's + port. + properties: + name: + description: The name of this port within the service. + This must be a DNS_LABEL. All ports within a ServiceSpec + must have unique names. When considering the endpoints + for a Service, this must match the 'name' field in the + EndpointPort. Optional if only one ServicePort is defined + on this service. + type: string + nodePort: + description: 'The port on each node on which this service + is exposed when type=NodePort or LoadBalancer. Usually + assigned by the system. If specified, it will be allocated + to the service if unused or else creation of the service + will fail. Default is to auto-allocate a port if the + ServiceType of this Service requires one. More info: + https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: 'Number or name of the port to access on + the pods targeted by the service. Number must be in + the range 1 to 65535. Name must be an IANA_SVC_NAME. + If this is a string, it will be looked up as a named + port in the target Pod''s container ports. If this is + not specified, the value of the ''port'' field is used + (an identity map). This field is ignored for services + with clusterIP=None, and should be omitted or set equal + to the ''port'' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + publishNotReadyAddresses: + description: publishNotReadyAddresses, when set to true, indicates + that DNS implementations must publish the notReadyAddresses + of subsets for the Endpoints associated with the Service. + The default value is false. The primary use case for setting + this field is to use a StatefulSet's Headless Service to propagate + SRV records for its Pods without respect to their readiness + for purpose of peer discovery. + type: boolean + selector: + additionalProperties: + type: string + description: 'Route service traffic to pods with label keys + and values matching this selector. If empty or not present, + the service is assumed to have an external process managing + its endpoints, which Kubernetes will not modify. Only applies + to types ClusterIP, NodePort, and LoadBalancer. Ignored if + type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/' + type: object + sessionAffinity: + description: 'Supports "ClientIP" and "None". Used to maintain + session affinity. Enable client IP based session affinity. + Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations of Client + IP based session affinity. + properties: + timeoutSeconds: + description: timeoutSeconds specifies the seconds of + ClientIP type session sticky time. The value must + be >0 && <=86400(for 1 day) if ServiceAffinity == + "ClientIP". Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: 'type determines how the Service is exposed. Defaults + to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, + and LoadBalancer. "ExternalName" maps to the specified externalName. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or + if that is not specified, by manual construction of an Endpoints + object. If clusterIP is "None", no virtual IP is allocated + and the endpoints are published as a set of endpoints rather + than a stable IP. "NodePort" builds on ClusterIP and allocates + a port on every node which routes to the clusterIP. "LoadBalancer" + builds on NodePort and creates an external load-balancer (if + supported in the current cloud) which routes to the clusterIP. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types' + type: string + type: object + status: + description: 'Most recently observed status of the service. Populated + by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + properties: + loadBalancer: + description: LoadBalancer contains the current status of the + load-balancer, if one is present. + properties: + ingress: + description: Ingress is a list containing ingress points + for the load-balancer. Traffic intended for the service + should be sent to these ingress points. + items: + description: 'LoadBalancerIngress represents the status + of a load-balancer ingress point: traffic intended for + the service should be sent to an ingress point.' + properties: + hostname: + description: Hostname is set for load-balancer ingress + points that are DNS based (typically AWS load-balancers) + type: string + ip: + description: IP is set for load-balancer ingress points + that are IP based (typically GCE or OpenStack load-balancers) + type: string + type: object + type: array + type: object + type: object + type: object + tolerations: + description: Pod's tolerations for Kubernetes node's taint + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, operator + must be Exists; this combination means to match all values and + all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. Exists + is equivalent to wildcard for value, so that a pod can tolerate + all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the + toleration (which must be of effect NoExecute, otherwise this + field is ignored) tolerates the taint. By default, it is not + set, which means tolerate the taint forever (do not evict). + Zero and negative values will be treated as 0 (evict immediately) + by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise + just a regular string. + type: string + type: object + type: array + type: object + status: + description: SparkStatus defines the observed state of a Spark cluster + properties: + masterPhase: + description: current phase of the spark master + enum: + - Pending + - Ready + - Updating + - ScalingUp + - ScalingDown + - Terminating + - Error + type: string + phase: + description: current phase of the spark workers + enum: + - Pending + - Ready + - Updating + - ScalingUp + - ScalingDown + - Terminating + - Error + type: string + readyReplicas: + description: current number of ready spark workers + format: int32 + type: integer + replicas: + description: number of desired spark workers + format: int32 + type: integer + selector: + description: selector for pods, used by HorizontalPodAutoscaler + type: string + type: object + type: object + version: v1beta1 + versions: + - name: v1beta1 + served: true + storage: true + - name: v1alpha3 + served: true + storage: false + - name: v1alpha2 + served: true + storage: false diff --git a/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_standalones_crd.yaml b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_standalones_crd.yaml new file mode 100644 index 000000000..044014cd5 --- /dev/null +++ b/deploy/olm-catalog/splunk/0.2.1/enterprise.splunk.com_standalones_crd.yaml @@ -0,0 +1,2518 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: standalones.enterprise.splunk.com +spec: + additionalPrinterColumns: + - JSONPath: .status.phase + description: Status of standalone instances + name: Phase + type: string + - JSONPath: .status.replicas + description: Number of desired standalone instances + name: Desired + type: integer + - JSONPath: .status.readyReplicas + description: Current number of ready standalone instances + name: Ready + type: integer + - JSONPath: .metadata.creationTimestamp + description: Age of standalone resource + name: Age + type: date + group: enterprise.splunk.com + names: + kind: Standalone + listKind: StandaloneList + plural: standalones + singular: standalone + scope: Namespaced + subresources: + scale: + labelSelectorPath: .status.selector + specReplicasPath: .spec.replicas + statusReplicasPath: .status.replicas + status: {} + validation: + openAPIV3Schema: + description: Standalone is the Schema for a Splunk Enterprise standalone instances. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: StandaloneSpec defines the desired state of a Splunk Enterprise + standalone instances. + properties: + Mock: + description: Mock to differentiate between UTs and actual reconcile + type: boolean + affinity: + description: Kubernetes Affinity rules that control how pods are assigned + to particular nodes. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches all + objects with implicit weight 0 (i.e. it's a no-op). A null + preferred scheduling term matches no objects (i.e. is also + a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The + terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: An array of string values. If the + operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be + empty. If the operator is Gt or Lt, the values + array must have a single element, which will + be interpreted as an integer. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the affinity expressions specified by this field, + but it may choose a node that violates one or more of the + expressions. The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node that meets + all of the scheduling requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating through + the elements of this field and adding "weight" to the sum + if the node has pods which matches the corresponding podAffinityTerm; + the node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may not + try to eventually evict the pod from its node. When there + are multiple elements, the lists of nodes corresponding to + each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some other + pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes + that satisfy the anti-affinity expressions specified by this + field, but it may choose a node that violates one or more + of the expressions. The node that is most preferred is the + one with the greatest sum of weights, i.e. for each node that + meets all of the scheduling requirements (resource request, + requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field + and adding "weight" to the sum if the node has pods which + matches the corresponding podAffinityTerm; the node(s) with + the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement is + a selector that contains values, a key, and + an operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If + the operator is Exists or DoesNotExist, + the values array must be empty. This array + is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey matches + that of any node on which any of the selected pods + is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will not + be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms must + be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) that + this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of pods + is running + properties: + labelSelector: + description: A label query over a set of resources, in + this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of any + node on which any of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + clusterMasterRef: + description: ClusterMasterRef refers to a Splunk Enterprise indexer + cluster managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + defaults: + description: Inline map of default.yml overrides used to initialize + the environment + type: string + defaultsUrl: + description: Full path or URL for one or more default.yml files, separated + by commas + type: string + ephemeralStorage: + description: If true, ephemeral (emptyDir) storage will be used for + /opt/splunk/etc and /opt/splunk/var volumes + type: boolean + etcStorage: + description: Storage capacity to request for /opt/splunk/etc persistent + volume claims (default=”1Gi”) + type: string + image: + description: Image to use for Splunk pod containers (overrides RELATED_IMAGE_SPLUNK_ENTERPRISE + environment variables) + type: string + imagePullPolicy: + description: 'Sets pull policy for all images (either “Always” or the + default: “IfNotPresent”)' + enum: + - Always + - IfNotPresent + type: string + licenseMasterRef: + description: LicenseMasterRef refers to a Splunk Enterprise license + master managed by the operator within Kubernetes + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + licenseUrl: + description: Full path or URL for a Splunk Enterprise license file + type: string + replicas: + description: Number of standalone pods + format: int32 + type: integer + resources: + description: resource requirements for the pod containers + properties: + limits: + additionalProperties: + type: string + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + type: string + 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. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + schedulerName: + description: Name of Scheduler to use for pod placement (defaults to + “default-scheduler”) + type: string + serviceTemplate: + description: ServiceTemplate is a template used to create Kubernetes + services + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the + latest internal value, and may reject unrecognized values. More + info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + spec: + description: Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + clusterIP: + description: 'clusterIP is the IP address of the service and + is usually assigned randomly by the master. If an address + is specified manually and is not in use by others, it will + be allocated to the service; otherwise, creation of the service + will fail. This field can not be changed through updates. + Valid values are "None", empty string (""), or a valid IP + address. "None" can be specified for headless services when + proxying is not required. Only applies to types ClusterIP, + NodePort, and LoadBalancer. Ignored if type is ExternalName. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + externalIPs: + description: externalIPs is a list of IP addresses for which + nodes in the cluster will also accept traffic for this service. These + IPs are not managed by Kubernetes. The user is responsible + for ensuring that traffic arrives at a node with this IP. A + common example is external load-balancers that are not part + of the Kubernetes system. + items: + type: string + type: array + externalName: + description: externalName is the external reference that kubedns + or equivalent will return as a CNAME record for this service. + No proxying will be involved. Must be a valid RFC-1123 hostname + (https://tools.ietf.org/html/rfc1123) and requires Type to + be ExternalName. + type: string + externalTrafficPolicy: + description: externalTrafficPolicy denotes if this Service desires + to route external traffic to node-local or cluster-wide endpoints. + "Local" preserves the client source IP and avoids a second + hop for LoadBalancer and Nodeport type services, but risks + potentially imbalanced traffic spreading. "Cluster" obscures + the client source IP and may cause a second hop to another + node, but should have good overall load-spreading. + type: string + healthCheckNodePort: + description: healthCheckNodePort specifies the healthcheck nodePort + for the service. If not specified, HealthCheckNodePort is + created by the service api backend with the allocated nodePort. + Will use user-specified nodePort value if specified by the + client. Only effects when Type is set to LoadBalancer and + ExternalTrafficPolicy is set to Local. + format: int32 + type: integer + ipFamily: + description: ipFamily specifies whether this Service has a preference + for a particular IP family (e.g. IPv4 vs. IPv6). If a specific + IP family is requested, the clusterIP field will be allocated + from that family, if it is available in the cluster. If no + IP family is requested, the cluster's primary IP family will + be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, + externalIPs) and controllers which allocate external load-balancers + should use the same IP family. Endpoints for this Service + will be of this family. This field is immutable after creation. + Assigning a ServiceIPFamily not available in the cluster (e.g. + IPv6 in IPv4 only cluster) is an error condition and will + fail during clusterIP assignment. + type: string + loadBalancerIP: + description: 'Only applies to Service Type: LoadBalancer LoadBalancer + will get created with the IP specified in this field. This + feature depends on whether the underlying cloud-provider supports + specifying the loadBalancerIP when a load balancer is created. + This field will be ignored if the cloud-provider does not + support the feature.' + type: string + loadBalancerSourceRanges: + description: 'If specified and supported by the platform, this + will restrict traffic through the cloud-provider load-balancer + will be restricted to the specified client IPs. This field + will be ignored if the cloud-provider does not support the + feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/' + items: + type: string + type: array + ports: + description: 'The list of ports that are exposed by this service. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + items: + description: ServicePort contains information on service's + port. + properties: + name: + description: The name of this port within the service. + This must be a DNS_LABEL. All ports within a ServiceSpec + must have unique names. When considering the endpoints + for a Service, this must match the 'name' field in the + EndpointPort. Optional if only one ServicePort is defined + on this service. + type: string + nodePort: + description: 'The port on each node on which this service + is exposed when type=NodePort or LoadBalancer. Usually + assigned by the system. If specified, it will be allocated + to the service if unused or else creation of the service + will fail. Default is to auto-allocate a port if the + ServiceType of this Service requires one. More info: + https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: 'Number or name of the port to access on + the pods targeted by the service. Number must be in + the range 1 to 65535. Name must be an IANA_SVC_NAME. + If this is a string, it will be looked up as a named + port in the target Pod''s container ports. If this is + not specified, the value of the ''port'' field is used + (an identity map). This field is ignored for services + with clusterIP=None, and should be omitted or set equal + to the ''port'' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + publishNotReadyAddresses: + description: publishNotReadyAddresses, when set to true, indicates + that DNS implementations must publish the notReadyAddresses + of subsets for the Endpoints associated with the Service. + The default value is false. The primary use case for setting + this field is to use a StatefulSet's Headless Service to propagate + SRV records for its Pods without respect to their readiness + for purpose of peer discovery. + type: boolean + selector: + additionalProperties: + type: string + description: 'Route service traffic to pods with label keys + and values matching this selector. If empty or not present, + the service is assumed to have an external process managing + its endpoints, which Kubernetes will not modify. Only applies + to types ClusterIP, NodePort, and LoadBalancer. Ignored if + type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/' + type: object + sessionAffinity: + description: 'Supports "ClientIP" and "None". Used to maintain + session affinity. Enable client IP based session affinity. + Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies' + type: string + sessionAffinityConfig: + description: sessionAffinityConfig contains the configurations + of session affinity. + properties: + clientIP: + description: clientIP contains the configurations of Client + IP based session affinity. + properties: + timeoutSeconds: + description: timeoutSeconds specifies the seconds of + ClientIP type session sticky time. The value must + be >0 && <=86400(for 1 day) if ServiceAffinity == + "ClientIP". Default value is 10800(for 3 hours). + format: int32 + type: integer + type: object + type: object + type: + description: 'type determines how the Service is exposed. Defaults + to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, + and LoadBalancer. "ExternalName" maps to the specified externalName. + "ClusterIP" allocates a cluster-internal IP address for load-balancing + to endpoints. Endpoints are determined by the selector or + if that is not specified, by manual construction of an Endpoints + object. If clusterIP is "None", no virtual IP is allocated + and the endpoints are published as a set of endpoints rather + than a stable IP. "NodePort" builds on ClusterIP and allocates + a port on every node which routes to the clusterIP. "LoadBalancer" + builds on NodePort and creates an external load-balancer (if + supported in the current cloud) which routes to the clusterIP. + More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types' + type: string + type: object + status: + description: 'Most recently observed status of the service. Populated + by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + properties: + loadBalancer: + description: LoadBalancer contains the current status of the + load-balancer, if one is present. + properties: + ingress: + description: Ingress is a list containing ingress points + for the load-balancer. Traffic intended for the service + should be sent to these ingress points. + items: + description: 'LoadBalancerIngress represents the status + of a load-balancer ingress point: traffic intended for + the service should be sent to an ingress point.' + properties: + hostname: + description: Hostname is set for load-balancer ingress + points that are DNS based (typically AWS load-balancers) + type: string + ip: + description: IP is set for load-balancer ingress points + that are IP based (typically GCE or OpenStack load-balancers) + type: string + type: object + type: array + type: object + type: object + type: object + smartstore: + description: Splunk Smartstore configuration. Refer to indexes.conf.spec + and server.conf.spec on docs.splunk.com + properties: + cacheManager: + description: Defines Cache manager settings + properties: + evictionPadding: + description: Additional size beyond 'minFreeSize' before eviction + kicks in + type: integer + evictionPolicy: + description: Eviction policy to use + type: string + hotlistBloomFilterRecencyHours: + description: Time period relative to the bucket's age, during + which the bloom filter file is protected from cache eviction + type: integer + hotlistRecencySecs: + description: Time period relative to the bucket's age, during + which the bucket is protected from cache eviction + type: integer + maxCacheSize: + description: Max cache size per partition + type: integer + maxConcurrentDownloads: + description: Maximum number of buckets that can be downloaded + from remote storage in parallel + type: integer + maxConcurrentUploads: + description: Maximum number of buckets that can be uploaded + to remote storage in parallel + type: integer + type: object + defaults: + description: Default configuration for indexes + properties: + maxGlobalDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of space for warm and cold buckets of an index + type: integer + maxGlobalRawDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of cumulative space for warm and cold buckets of an index + type: integer + volumeName: + description: Remote Volume name + type: string + type: object + indexes: + description: List of Splunk indexes + items: + description: IndexSpec defines Splunk index name and storage path + properties: + hotlistBloomFilterRecencyHours: + description: Time period relative to the bucket's age, during + which the bloom filter file is protected from cache eviction + type: integer + hotlistRecencySecs: + description: Time period relative to the bucket's age, during + which the bucket is protected from cache eviction + type: integer + maxGlobalDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of space for warm and cold buckets of an index + type: integer + maxGlobalRawDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of cumulative space for warm and cold buckets of an index + type: integer + name: + description: Splunk index name + type: string + remotePath: + description: Index location relative to the remote volume + path + type: string + volumeName: + description: Remote Volume name + type: string + type: object + type: array + volumes: + description: List of remote storage volumes + items: + description: VolumeSpec defines remote volume name and remote + volume URI + properties: + endpoint: + description: Remote volume URI + type: string + name: + description: Remote volume name + type: string + path: + description: Remote volume path + type: string + secretRef: + description: Secret object name + type: string + type: object + type: array + type: object + sparkImage: + description: Image to use for Spark pod containers (overrides RELATED_IMAGE_SPLUNK_SPARK + environment variables) + type: string + sparkRef: + description: SparkRef refers to a Spark cluster managed by the operator + within Kubernetes When defined, Data Fabric Search (DFS) will be enabled + and configured to use the Spark cluster. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an + entire object, this string should contain a valid JSON/Go field + access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen only + to have some well-defined way of referencing a part of an object. + TODO: this design is not final and this field is subject to change + in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is + made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + storageClassName: + description: Name of StorageClass to use for persistent volume claims + type: string + tolerations: + description: Pod's tolerations for Kubernetes node's taint + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, operator + must be Exists; this combination means to match all values and + all keys. + type: string + operator: + description: Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. Exists + is equivalent to wildcard for value, so that a pod can tolerate + all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time the + toleration (which must be of effect NoExecute, otherwise this + field is ignored) tolerates the taint. By default, it is not + set, which means tolerate the taint forever (do not evict). + Zero and negative values will be treated as 0 (evict immediately) + by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise + just a regular string. + type: string + type: object + type: array + varStorage: + description: Storage capacity to request for /opt/splunk/var persistent + volume claims (default=”50Gi”) + type: string + volumes: + description: List of one or more Kubernetes volumes. These will be mounted + in all pod containers as as /mnt/ + items: + description: Volume represents a named volume in a pod that may be + accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents an AWS Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'Specify "true" to force and set the ReadOnly + property in VolumeMounts to "true". If omitted, the default + is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent disk resource in + AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: AzureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'Host Caching mode: None, Read Only, Read Write.' + type: string + diskName: + description: The Name of the data disk in the blob storage + type: string + diskURI: + description: The URI the data disk in the blob storage + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'Expected values Shared: multiple blob disks + per storage account Dedicated: single blob disk per storage + account Managed: azure managed data disk (only in managed + availability set). defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: AzureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret that contains Azure Storage + Account Name and Key + type: string + shareName: + description: Share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: CephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: 'Required: Monitors is a collection of Ceph monitors + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'Optional: Used as the mounted root, rather than + the full Ceph tree, default is /' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile is the path to key ring + for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef is reference to the authentication + secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + 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 + user: + description: 'Optional: User is the rados user name, default + is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'Cinder represents a cinder volume attached and mounted + on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Examples: "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. More + info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to a secret object containing + parameters used to connect to OpenStack.' + 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 + volumeID: + description: 'volume id used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: ConfigMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. 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 + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. 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: 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: Specify whether the ConfigMap or its keys must + be defined + type: boolean + type: object + csi: + description: CSI (Container Storage Interface) represents storage + that is handled by an external CSI driver (Alpha feature). + properties: + driver: + description: Driver is the name of the CSI driver that handles + this volume. Consult with your admin for the correct name + as registered in the cluster. + type: string + fsType: + description: Filesystem type to mount. Ex. "ext4", "xfs", + "ntfs". If not provided, the empty value is passed to the + associated CSI driver which will determine the default filesystem + to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef is a reference to the secret + object containing sensitive information to pass to the CSI + driver to complete the CSI NodePublishVolume and NodeUnpublishVolume + calls. This field is optional, and may be empty if no secret + is required. If the secret object contains more than one + secret, all secret references are passed. + 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 + readOnly: + description: Specifies a read-only configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: VolumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: DownwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. 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 + items: + description: Items is a list of downward API volume 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 + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. 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: + description: Specifies the output format of the + exposed resources, defaults to "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'EmptyDir represents a temporary directory that shares + a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'What type of storage medium should back this + directory. The default is "" which means to use the node''s + default medium. Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + description: 'Total amount of local storage required for this + EmptyDir volume. The size limit is also applicable for memory + medium. The maximum usage on memory medium EmptyDir would + be the minimum value between the SizeLimit specified here + and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + type: string + type: object + fc: + description: FC represents a Fibre Channel resource that is attached + to a kubelet's host machine and then exposed to the pod. + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + lun: + description: 'Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target worldwide names (WWNs)' + items: + type: string + type: array + wwids: + description: 'Optional: FC volume world wide identifiers (wwids) + Either wwids or combination of targetWWNs and lun must be + set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: FlexVolume represents a generic volume resource that + is provisioned/attached using an exec based plugin. + properties: + driver: + description: Driver is the name of the driver to use for this + volume. + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". The default filesystem depends on FlexVolume + script. + type: string + options: + additionalProperties: + type: string + description: 'Optional: Extra command options if any.' + type: object + readOnly: + description: 'Optional: Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef is reference to the secret + object containing sensitive information to pass to the plugin + scripts. This may be empty if no secret object is specified. + If the secret object contains more than one secret, all + secrets are passed to the plugin scripts.' + 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 + required: + - driver + type: object + flocker: + description: Flocker represents a Flocker volume attached to a + kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: Name of the dataset stored as metadata -> name + on the dataset for Flocker should be considered as deprecated + type: string + datasetUUID: + description: UUID of the dataset. This is unique identifier + of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'GCEPersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + partition: + description: 'The partition in the volume that you want to + mount. If omitted, the default is to mount by volume name. + Examples: For volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for /dev/sda is + "0" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'Unique name of the PD resource in GCE. Used + to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'GitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated. To provision a + container with a git repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then mount the EmptyDir into + the Pod''s container.' + properties: + directory: + description: Target directory name. Must not contain or start + with '..'. If '.' is supplied, the volume directory will + be the git repository. Otherwise, if specified, the volume + will contain the git repository in the subdirectory with + the given name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'Glusterfs represents a Glusterfs mount on the host + that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'EndpointsName is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs volume path. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to false. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'HostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + This is generally used for system agents or other privileged + things that are allowed to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host directory + mounts and who can/can not mount host directories as read/write.' + properties: + path: + description: 'Path of the directory on the host. If the path + is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume Defaults to "" More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'ISCSI represents an ISCSI Disk resource that is + attached to a kubelet''s host machine and then exposed to the + pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: whether support iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: whether support iSCSI Session CHAP authentication + type: boolean + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator Name. If initiatorName + is specified with iscsiInterface simultaneously, new iSCSI + interface : will be created + for the connection. + type: string + iqn: + description: Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iSCSI Interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: iSCSI Target Portal List. The portal is either + an IP or ip_addr:port if the port is other than default + (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI target and initiator authentication + 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 + targetPortal: + description: iSCSI Target Portal. The Portal is either an + IP or ip_addr:port if the port is other than default (typically + TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'Volume''s name. Must be a DNS_LABEL and unique within + the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS mount on the host that shares + a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'Path that is exported by the NFS server. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will force the NFS export to be + mounted with read-only permissions. Defaults to false. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname or IP address of the + NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource represents a reference + to a PersistentVolumeClaim in the same namespace. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'ClaimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: PhotonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: ID that identifies Photon Controller persistent + disk + type: string + required: + - pdID + type: object + portworxVolume: + description: PortworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: FSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: Items for all in one resources secrets, configmaps, + and downward API + properties: + defaultMode: + description: Mode bits to use on created files by default. + Must be a value between 0 and 0777. 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: list of volume projections + items: + description: Projection that may be projected along with + other supported volume types + properties: + configMap: + description: information about the configMap data to + project + properties: + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. 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: 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: Specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + downwardAPI: + description: 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 + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. 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: + description: Specifies the output format + of the exposed resources, defaults to + "1" + type: string + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: information about the secret data to project + properties: + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on + this file, must be a value between 0 and + 0777. 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: 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: Specify whether the Secret or its key + must be defined + type: boolean + type: object + serviceAccountToken: + description: 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 + required: + - sources + type: object + quobyte: + description: Quobyte represents a Quobyte mount on the host that + shares a pod's lifetime + properties: + group: + description: Group to map volume access to Default is no group + type: string + readOnly: + description: ReadOnly here will force the Quobyte volume to + be mounted with read-only permissions. Defaults to false. + type: boolean + registry: + description: Registry represents a single or multiple Quobyte + Registry services specified as a string as host:port pair + (multiple entries are separated with commas) which acts + as the central registry for volumes + type: string + tenant: + description: Tenant owning the given Quobyte volume in the + Backend Used with dynamically provisioned Quobyte volumes, + value is set by the plugin + type: string + user: + description: User to map volume access to Defaults to serivceaccount + user + type: string + volume: + description: Volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'RBD represents a Rados Block Device mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. More info: + https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from compromising + the machine' + type: string + image: + description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'The rados pool name. Default is rbd. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name of the authentication secret + for RBDUser. If provided overrides keyring. Default is nil. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + 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 + user: + description: 'The rados user name. Default is admin. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: ScaleIO represents a ScaleIO persistent volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: The host address of the ScaleIO API Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO Protection Domain for + the configured storage. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references to the secret for ScaleIO + user and other sensitive information. If this is not provided, + Login operation will fail. + 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 + sslEnabled: + description: Flag to enable/disable SSL communication with + Gateway, default false + type: boolean + storageMode: + description: Indicates whether the storage for a volume should + be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage Pool associated with the + protection domain. + type: string + system: + description: The name of the storage system as configured + in ScaleIO. + type: string + volumeName: + description: The name of a volume already created in the ScaleIO + system that is associated with this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'Secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a value between 0 and 0777. Defaults + to 0644. 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 + items: + description: 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: The key to project. + type: string + mode: + description: 'Optional: mode bits to use on this file, + must be a value between 0 and 0777. 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: 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 + optional: + description: Specify whether the Secret or its keys must be + defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace to + use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: StorageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies the secret to use for obtaining + the StorageOS API credentials. If not specified, default + values will be attempted. + 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 + volumeName: + description: VolumeName is the human-readable name of the + StorageOS volume. Volume names are only unique within a + namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies the scope of the volume + within StorageOS. If no namespace is specified then the + Pod's namespace will be used. This allows the Kubernetes + name scoping to be mirrored within StorageOS for tighter + integration. Set VolumeName to any name to override the + default behaviour. Set to "default" if you are not using + namespaces within StorageOS. Namespaces that do not pre-exist + within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: VsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based Management (SPBM) profile + ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based Management (SPBM) profile + name. + type: string + volumePath: + description: Path that identifies vSphere volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + status: + description: StandaloneStatus defines the observed state of a Splunk Enterprise + standalone instances. + properties: + phase: + description: current phase of the standalone instances + enum: + - Pending + - Ready + - Updating + - ScalingUp + - ScalingDown + - Terminating + - Error + type: string + readyReplicas: + description: current number of ready standalone instances + format: int32 + type: integer + replicas: + description: number of desired standalone instances + format: int32 + type: integer + resourceRevMap: + additionalProperties: + type: string + description: Resource Revision tracker + type: object + selector: + description: selector for pods, used by HorizontalPodAutoscaler + type: string + smartstore: + description: Splunk Smartstore configuration. Refer to indexes.conf.spec + and server.conf.spec on docs.splunk.com + properties: + cacheManager: + description: Defines Cache manager settings + properties: + evictionPadding: + description: Additional size beyond 'minFreeSize' before eviction + kicks in + type: integer + evictionPolicy: + description: Eviction policy to use + type: string + hotlistBloomFilterRecencyHours: + description: Time period relative to the bucket's age, during + which the bloom filter file is protected from cache eviction + type: integer + hotlistRecencySecs: + description: Time period relative to the bucket's age, during + which the bucket is protected from cache eviction + type: integer + maxCacheSize: + description: Max cache size per partition + type: integer + maxConcurrentDownloads: + description: Maximum number of buckets that can be downloaded + from remote storage in parallel + type: integer + maxConcurrentUploads: + description: Maximum number of buckets that can be uploaded + to remote storage in parallel + type: integer + type: object + defaults: + description: Default configuration for indexes + properties: + maxGlobalDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of space for warm and cold buckets of an index + type: integer + maxGlobalRawDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of cumulative space for warm and cold buckets of an index + type: integer + volumeName: + description: Remote Volume name + type: string + type: object + indexes: + description: List of Splunk indexes + items: + description: IndexSpec defines Splunk index name and storage path + properties: + hotlistBloomFilterRecencyHours: + description: Time period relative to the bucket's age, during + which the bloom filter file is protected from cache eviction + type: integer + hotlistRecencySecs: + description: Time period relative to the bucket's age, during + which the bucket is protected from cache eviction + type: integer + maxGlobalDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of space for warm and cold buckets of an index + type: integer + maxGlobalRawDataSizeMB: + description: MaxGlobalDataSizeMB defines the maximum amount + of cumulative space for warm and cold buckets of an index + type: integer + name: + description: Splunk index name + type: string + remotePath: + description: Index location relative to the remote volume + path + type: string + volumeName: + description: Remote Volume name + type: string + type: object + type: array + volumes: + description: List of remote storage volumes + items: + description: VolumeSpec defines remote volume name and remote + volume URI + properties: + endpoint: + description: Remote volume URI + type: string + name: + description: Remote volume name + type: string + path: + description: Remote volume path + type: string + secretRef: + description: Secret object name + type: string + type: object + type: array + type: object + type: object + type: object + version: v1beta1 + versions: + - name: v1beta1 + served: true + storage: true + - name: v1alpha3 + served: true + storage: false + - name: v1alpha2 + served: true + storage: false diff --git a/deploy/olm-catalog/splunk/0.2.1/splunk.v0.2.1.clusterserviceversion.yaml b/deploy/olm-catalog/splunk/0.2.1/splunk.v0.2.1.clusterserviceversion.yaml new file mode 100644 index 000000000..1888cd640 --- /dev/null +++ b/deploy/olm-catalog/splunk/0.2.1/splunk.v0.2.1.clusterserviceversion.yaml @@ -0,0 +1,261 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + annotations: + alm-examples: |- + [{ + "apiVersion": "enterprise.splunk.com/v1beta1", + "kind": "IndexerCluster", + "metadata": { + "name": "example", + "finalizers": [ "enterprise.splunk.com/delete-pvc" ] + }, + "spec": { + "replicas": 1 + } + }, + { + "apiVersion": "enterprise.splunk.com/v1beta1", + "kind": "LicenseMaster", + "metadata": { + "name": "example", + "finalizers": [ "enterprise.splunk.com/delete-pvc" ] + }, + "spec": {} + }, + { + "apiVersion": "enterprise.splunk.com/v1beta1", + "kind": "SearchHeadCluster", + "metadata": { + "name": "example", + "finalizers": [ "enterprise.splunk.com/delete-pvc" ] + }, + "spec": { + "replicas": 1 + } + }, + { + "apiVersion": "enterprise.splunk.com/v1beta1", + "kind": "Spark", + "metadata": { + "name": "example" + }, + "spec": { + "replicas": 1 + } + }, + { + "apiVersion": "enterprise.splunk.com/v1beta1", + "kind": "Standalone", + "metadata": { + "name": "example", + "finalizers": [ "enterprise.splunk.com/delete-pvc" ] + }, + "spec": {} + }] + capabilities: Basic Install + name: splunk.v0.2.1 + namespace: placeholder +spec: + apiservicedefinitions: {} + customresourcedefinitions: + owned: + - description: ClusterMaster is the Schema for the clustermasters API + kind: ClusterMaster + name: clustermasters.enterprise.splunk.com + version: v1beta1 + resources: + - kind: StatefulSets + version: apps/v1 + - kind: Deployments + version: apps/v1 + - kind: Pods + version: v1 + - kind: Services + version: v1 + - kind: ConfigMaps + version: v1 + - kind: Secrets + version: v1 + displayName: IndexerCluster + - description: IndexerCluster is the Schema for a Splunk Enterprise indexer cluster + kind: IndexerCluster + name: indexerclusters.enterprise.splunk.com + version: v1beta1 + resources: + - kind: StatefulSets + version: apps/v1 + - kind: Deployments + version: apps/v1 + - kind: Pods + version: v1 + - kind: Services + version: v1 + - kind: ConfigMaps + version: v1 + - kind: Secrets + version: v1 + displayName: LicenseMaster + - description: LicenseMaster is the Schema for a Splunk Enterprise license master. + kind: LicenseMaster + name: licensemasters.enterprise.splunk.com + version: v1beta1 + resources: + - kind: StatefulSets + version: apps/v1 + - kind: Deployments + version: apps/v1 + - kind: Pods + version: v1 + - kind: Services + version: v1 + - kind: ConfigMaps + version: v1 + - kind: Secrets + version: v1 + displayName: SearchHeadCluster + - description: SearchHeadCluster is the Schema for a Splunk Enterprise search + head cluster + kind: SearchHeadCluster + name: searchheadclusters.enterprise.splunk.com + version: v1beta1 + resources: + - kind: StatefulSets + version: apps/v1 + - kind: Deployments + version: apps/v1 + - kind: Pods + version: v1 + - kind: Services + version: v1 + - kind: ConfigMaps + version: v1 + - kind: Secrets + version: v1 + displayName: Spark + - description: Spark is the Schema for a Spark cluster + kind: Spark + name: sparks.enterprise.splunk.com + version: v1beta1 + resources: + - kind: StatefulSets + version: apps/v1 + - kind: Deployments + version: apps/v1 + - kind: Pods + version: v1 + - kind: Services + version: v1 + - kind: ConfigMaps + version: v1 + - kind: Secrets + version: v1 + displayName: Standalone + - description: Standalone is the Schema for a Splunk Enterprise standalone instances. + kind: Standalone + name: standalones.enterprise.splunk.com + version: v1beta1 + description: Placeholder description + displayName: Splunk + install: + spec: + deployments: + - name: splunk-operator + spec: + replicas: 1 + selector: + matchLabels: + name: splunk-operator + strategy: {} + template: + metadata: + labels: + name: splunk-operator + spec: + containers: + - env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.annotations['olm.targetNamespaces'] + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: OPERATOR_NAME + value: splunk-operator + - name: RELATED_IMAGE_SPLUNK_ENTERPRISE + value: docker.io/splunk/splunk:8.1.1 + - name: RELATED_IMAGE_SPLUNK_SPARK + value: docker.io/splunk/spark:0.0.2 + image: docker.io/splunk/splunk-operator:0.2.1 + imagePullPolicy: IfNotPresent + name: splunk-operator + resources: {} + serviceAccountName: splunk-operator + permissions: + - rules: + - apiGroups: + - "" + resources: + - services + - endpoints + - persistentvolumeclaims + - configmaps + - secrets + - pods + - pods/exec + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - events + verbs: + - get + - list + - watch + - apiGroups: + - apps + resources: + - deployments + - daemonsets + - replicasets + - statefulsets + verbs: + - create + - delete + - deletecollection + - get + - list + - patch + - update + - watch + - apiGroups: + - enterprise.splunk.com + resources: + - '*' + verbs: + - '*' + serviceAccountName: splunk-operator + strategy: deployment + installModes: + - supported: true + type: OwnNamespace + - supported: true + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + maturity: alpha + provider: {} + replaces: splunk.v0.0.0 + version: 0.2.1 diff --git a/deploy/operator.yaml b/deploy/operator.yaml index 148e1db65..701e6ba53 100644 --- a/deploy/operator.yaml +++ b/deploy/operator.yaml @@ -30,6 +30,6 @@ spec: - name: OPERATOR_NAME value: "splunk-operator" - name: RELATED_IMAGE_SPLUNK_ENTERPRISE - value: "docker.io/splunk/splunk:8.1.0" + value: "docker.io/splunk/splunk:8.1.1" - name: RELATED_IMAGE_SPLUNK_SPARK value: "docker.io/splunk/spark:0.0.2" diff --git a/docs/ChangeLog.md b/docs/ChangeLog.md index ccc47a374..a74fc71aa 100644 --- a/docs/ChangeLog.md +++ b/docs/ChangeLog.md @@ -1,5 +1,22 @@ # Splunk Operator for Kubernetes Change Log +## 0.2.1 Beta() +* This release depends upon changes made concurrently in the Splunk Enterprise container images. You must use the latest splunk/splunk:edge nightly image with it, or alternatively any release version 8.1.0 or later + +* CSPL-529 - Fixed an issue where deletion of ClusterMaster would result in the deletion of indexer PVCs + +* CSPL 466 - Fixed an issue where Indexer Cluster created with peers < SF, RF would result in an infinite reconcile loop of the operator + +* CSPL-532 - Fixed a race condition where changing the idxc.secret on the global secret object could result in an infinite loop of container restarts + +* Increased code coverage + +* CSPL-534 - Fixed unnecessary pod recycles on scale up/down + +* CSPL-592 - Initiate a pod recycle on change of environment variables of containers + +* CSPL-658 - Fixed an issue where Indexer states go from configured to new in the Monitoring Console + ## 0.2.0 Beta (2020-10-15) * This release depends upon changes made concurrently in the Splunk Enterprise container images. You must use the latest splunk/splunk:edge nightly image with it, or alternatively any release version 8.1.0 or later. diff --git a/docs/Install.md b/docs/Install.md index eb03fbbed..5dc746ecf 100644 --- a/docs/Install.md +++ b/docs/Install.md @@ -78,7 +78,7 @@ EOF ## Private Registries -*Note: The `splunk/splunk:8.1.0` image is rather large, so we strongly +*Note: The `splunk/splunk:8.1.0` image (or later) is rather large, so we strongly recommend copying this to a private registry or directly onto your Kubernetes workers as per the [Required Images Documentation](Images.md), and following these instructions before creating any large Splunk deployments.* @@ -92,7 +92,7 @@ it to a private registry, you will need to edit the image parameter in the image: splunk/splunk-operator ``` -If you are using a private registry for the `splunk/splunk:8.1.0` and +If you are using a private registry for the `splunk/splunk:8.1.0` (or later) and `splunk/spark` (used by DFS) images, you should modify the `RELATED_IMAGE_SPLUNK_ENTERPRISE` and `RELATED_IMAGE_SPLUNK_SPARK` environment variables in `splunk-operator.yaml` to point @@ -100,7 +100,7 @@ to the appropriate locations. ```yaml - name: RELATED_IMAGE_SPLUNK_ENTERPRISE - value: "splunk/splunk:8.1.0" + value: "splunk/splunk:8.1.0" (or later) - name: RELATED_IMAGE_SPLUNK_SPARK value: "splunk/spark" ``` diff --git a/docs/README.md b/docs/README.md index e86d60c9e..8634a8df0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -97,7 +97,7 @@ special considerations, including the use of private image registries, installation at cluster scope, and installing as a regular user (who is not a Kubernetes cluster administrator). -*Note: The `splunk/splunk:8.1.0` image is rather large, so we strongly +*Note: The `splunk/splunk:8.1.0` image (or later) is rather large, so we strongly recommend copying this to a private registry or directly onto your Kubernetes workers as per the [Required Images Documentation](Images.md), and following the [Advanced Installation Instructions](Install.md), diff --git a/version/version.go b/version/version.go index 14c9d1830..36ec83b1d 100644 --- a/version/version.go +++ b/version/version.go @@ -16,5 +16,5 @@ package version var ( // Version of splunk-operator - Version = "0.2.0" + Version = "0.2.1" ) From 6fa43d9be09f815c80e577b98d05d3656fcd11e0 Mon Sep 17 00:00:00 2001 From: akondur Date: Tue, 15 Dec 2020 16:42:41 -0800 Subject: [PATCH 38/38] Addressing review comments, changed github link to point to 0.2.1 --- docs/ChangeLog.md | 6 +++--- docs/Install.md | 8 ++++---- docs/README.md | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/ChangeLog.md b/docs/ChangeLog.md index a74fc71aa..a9b41904c 100644 --- a/docs/ChangeLog.md +++ b/docs/ChangeLog.md @@ -3,9 +3,9 @@ ## 0.2.1 Beta() * This release depends upon changes made concurrently in the Splunk Enterprise container images. You must use the latest splunk/splunk:edge nightly image with it, or alternatively any release version 8.1.0 or later -* CSPL-529 - Fixed an issue where deletion of ClusterMaster would result in the deletion of indexer PVCs +* CSPL-529 - Fixed incorrect deletion of Indexer PVCs upon deletion of ClusterMaster -* CSPL 466 - Fixed an issue where Indexer Cluster created with peers < SF, RF would result in an infinite reconcile loop of the operator +* CSPL 466 - Fixed infinite reconcile loop of the Operator when an Indexer Cluster is created with peers < SF, RF * CSPL-532 - Fixed a race condition where changing the idxc.secret on the global secret object could result in an infinite loop of container restarts @@ -15,7 +15,7 @@ * CSPL-592 - Initiate a pod recycle on change of environment variables of containers -* CSPL-658 - Fixed an issue where Indexer states go from configured to new in the Monitoring Console +* CSPL-658 - Fixed incorrect change of Indexer state from Configured to New in the Monitoring Console ## 0.2.0 Beta (2020-10-15) * This release depends upon changes made concurrently in the Splunk Enterprise container images. You must use the latest splunk/splunk:edge nightly image with it, or alternatively any release version 8.1.0 or later. diff --git a/docs/Install.md b/docs/Install.md index 5dc746ecf..f3469c4b7 100644 --- a/docs/Install.md +++ b/docs/Install.md @@ -11,7 +11,7 @@ Splunk Operator (as described below), please download a local copy of the installation YAML and open it in your favorite editor. ``` -wget -O splunk-operator.yaml https://github.com/splunk/splunk-operator/releases/download/0.2.0/splunk-operator-install.yaml +wget -O splunk-operator.yaml https://github.com/splunk/splunk-operator/releases/download/0.2.1/splunk-operator-install.yaml ``` @@ -23,14 +23,14 @@ installed by regular users within their own namespaces. If you are not an administrator, you can have someone else create these objects for you by running ``` -kubectl apply -f https://github.com/splunk/splunk-operator/releases/download/0.2.0/splunk-operator-crds.yaml +kubectl apply -f https://github.com/splunk/splunk-operator/releases/download/0.2.1/splunk-operator-crds.yaml ``` You should then be able download and use the following YAML to install the operator within your own namespace: ``` -wget -O splunk-operator.yaml https://github.com/splunk/splunk-operator/releases/download/0.2.0/splunk-operator-noadmin.yaml +wget -O splunk-operator.yaml https://github.com/splunk/splunk-operator/releases/download/0.2.1/splunk-operator-noadmin.yaml kubectl config set-context --current --namespace= kubectl apply -f splunk-operator.yaml ``` @@ -43,7 +43,7 @@ objects for all the namespaces of your cluster, you can use the alternative cluster scope installation YAML: ``` -wget -O splunk-operator.yaml https://github.com/splunk/splunk-operator/releases/download/0.2.0/splunk-operator-cluster.yaml +wget -O splunk-operator.yaml https://github.com/splunk/splunk-operator/releases/download/0.2.1/splunk-operator-cluster.yaml ``` When running at cluster scope, you will need to bind the diff --git a/docs/README.md b/docs/README.md index 8634a8df0..61ad4df70 100644 --- a/docs/README.md +++ b/docs/README.md @@ -86,7 +86,7 @@ information. Most users can install and start the Splunk Operator by just running ``` -kubectl apply -f https://github.com/splunk/splunk-operator/releases/download/0.2.0/splunk-operator-install.yaml +kubectl apply -f https://github.com/splunk/splunk-operator/releases/download/0.2.1/splunk-operator-install.yaml ``` Users of Red Hat OpenShift should read the additional @@ -120,7 +120,7 @@ kubectl delete searchheadclusters --all kubectl delete clustermasters --all kubectl delete indexerclusters --all kubectl delete spark --all -kubectl delete -f https://github.com/splunk/splunk-operator/releases/download/0.2.0/splunk-operator-install.yaml +kubectl delete -f https://github.com/splunk/splunk-operator/releases/download/0.2.1/splunk-operator-install.yaml ```