Skip to content

Commit

Permalink
Multisite / multipart IndexerCluster - Return an error when cluster m…
Browse files Browse the repository at this point in the history
…aster located in a different namespace and document the limitation
  • Loading branch information
romain-bellanger committed Aug 16, 2020
1 parent d65b975 commit 80523ac
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 5 deletions.
2 changes: 2 additions & 0 deletions docs/MultisiteExamples.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ Advantages:
- some operations are performed per site which mitigates the risk of impact on the whole cluster (e.g. Splunk upgrades, scaling up resources)
- specific indexer services are created per site allowing to send events to the indexers located in the same zone, avoiding possible cost of cross-zone traffic. Indexer discovery from cluster-master can do this for forwarders, but this solution also covers http/HEC traffic

Limitation: all the IndexerCluster resources must be located in the same namespace

#### Deploy the cluster-master

Note: the image version is defined in these resources as this allows to control the upgrade cycle
Expand Down
10 changes: 7 additions & 3 deletions pkg/splunk/enterprise/indexercluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func ApplyIndexerCluster(client splcommon.ControllerClient, cr *enterprisev1.Ind
scopedLog := log.WithName("ApplyIndexerCluster").WithValues("name", cr.GetName(), "namespace", cr.GetNamespace())

// validate and updates defaults for CR
err := validateIndexerClusterSpec(&cr.Spec)
err := validateIndexerClusterSpec(cr)
if err != nil {
return result, err
}
Expand Down Expand Up @@ -322,9 +322,13 @@ func getClusterMasterStatefulSet(cr *enterprisev1.IndexerCluster) (*appsv1.State
}

// validateIndexerClusterSpec checks validity and makes default updates to a IndexerClusterSpec, and returns error if something is wrong.
func validateIndexerClusterSpec(spec *enterprisev1.IndexerClusterSpec) error {
func validateIndexerClusterSpec(cr *enterprisev1.IndexerCluster) error {
// IndexerCluster must support 0 replicas for multisite / multipart clusters
return validateCommonSplunkSpec(&spec.CommonSplunkSpec)
// Multisite / multipart clusters: can't reference a cluster master located in another namespace because of Service and Secret limitations
if len(cr.Spec.IndexerClusterRef.Namespace) > 0 && cr.Spec.IndexerClusterRef.Namespace != cr.GetNamespace() {
return fmt.Errorf("Multisite cluster does not support cluster master to be located in a different namespace")
}
return validateCommonSplunkSpec(&cr.Spec.CommonSplunkSpec)
}

// getIndexerExtraEnv returns extra environment variables used by search head clusters
Expand Down
9 changes: 7 additions & 2 deletions pkg/splunk/enterprise/indexercluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ func TestGetIndexerStatefulSet(t *testing.T) {

test := func(want string) {
f := func() (interface{}, error) {
if err := validateIndexerClusterSpec(&cr.Spec); err != nil {
if err := validateIndexerClusterSpec(&cr); err != nil {
t.Errorf("validateIndexerClusterSpec() returned error: %v", err)
}
return getIndexerStatefulSet(&cr)
Expand All @@ -374,6 +374,11 @@ func TestGetIndexerStatefulSet(t *testing.T) {
cr.Spec.IndexerClusterRef.Name = "master1"
// Multi-part differences: part-of labels / selector, secret name, SPLUNK_CLUSTER_MASTER_URL variable
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-master1-indexer-secrets","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_DECLARATVE_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.IndexerClusterRef.Namespace = "other"
if err := validateIndexerClusterSpec(&cr); err == nil {
t.Errorf("validateIndexerClusterSpec() error expected on multisite IndexerCluster referencing a cluster master located in a different namespace")
}
}

func TestGetClusterMasterStatefulSet(t *testing.T) {
Expand All @@ -386,7 +391,7 @@ func TestGetClusterMasterStatefulSet(t *testing.T) {

test := func(want string) {
f := func() (interface{}, error) {
if err := validateIndexerClusterSpec(&cr.Spec); err != nil {
if err := validateIndexerClusterSpec(&cr); err != nil {
t.Errorf("validateIndexerClusterSpec() returned error: %v", err)
}
return getClusterMasterStatefulSet(&cr)
Expand Down

0 comments on commit 80523ac

Please sign in to comment.