Skip to content

Commit 37b2374

Browse files
committed
Initial changes to support deployer spec in SHC CRD
1 parent 0e066ac commit 37b2374

File tree

8 files changed

+189
-2
lines changed

8 files changed

+189
-2
lines changed

api/v4/searchheadcluster_types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ type SearchHeadClusterSpec struct {
4444

4545
// Splunk Enterprise App repository. Specifies remote App location and scope for Splunk App management
4646
AppFrameworkConfig AppFrameworkSpec `json:"appRepo,omitempty"`
47+
48+
// Splunk Deployer resource spec
49+
DeployerResourceSpec corev1.ResourceRequirements `json:"deployerResourceSpec,omitempty"`
4750
}
4851

4952
// SearchHeadClusterMemberStatus is used to track the status of each search head cluster member

api/v4/zz_generated.deepcopy.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/enterprise.splunk.com_searchheadclusters.yaml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5295,6 +5295,59 @@ spec:
52955295
will be installed on the CM, standalone, search head deployer
52965296
or license manager instance.
52975297
type: string
5298+
deployerResourceSpec:
5299+
description: Splunk Deployer resource spec
5300+
properties:
5301+
claims:
5302+
description: |-
5303+
Claims lists the names of resources, defined in spec.resourceClaims,
5304+
that are used by this container.
5305+
5306+
This is an alpha field and requires enabling the
5307+
DynamicResourceAllocation feature gate.
5308+
5309+
This field is immutable.
5310+
items:
5311+
description: ResourceClaim references one entry in PodSpec.ResourceClaims.
5312+
properties:
5313+
name:
5314+
description: |-
5315+
Name must match the name of one entry in pod.spec.resourceClaims of
5316+
the Pod where this field is used. It makes that resource available
5317+
inside a container.
5318+
type: string
5319+
required:
5320+
- name
5321+
type: object
5322+
type: array
5323+
x-kubernetes-list-map-keys:
5324+
- name
5325+
x-kubernetes-list-type: map
5326+
limits:
5327+
additionalProperties:
5328+
anyOf:
5329+
- type: integer
5330+
- type: string
5331+
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
5332+
x-kubernetes-int-or-string: true
5333+
description: |-
5334+
Limits describes the maximum amount of compute resources allowed.
5335+
More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
5336+
type: object
5337+
requests:
5338+
additionalProperties:
5339+
anyOf:
5340+
- type: integer
5341+
- type: string
5342+
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
5343+
x-kubernetes-int-or-string: true
5344+
description: |-
5345+
Requests describes the minimum amount of compute resources required.
5346+
If Requests is omitted for a container, it defaults to Limits if that is explicitly specified,
5347+
otherwise to an implementation-defined value.
5348+
More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
5349+
type: object
5350+
type: object
52985351
etcVolumeStorageConfig:
52995352
description: Storage configuration for /opt/splunk/etc volume
53005353
properties:

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ require (
1313
github.com/google/uuid v1.6.0
1414
github.com/joho/godotenv v1.5.1
1515
github.com/minio/minio-go/v7 v7.0.16
16-
github.com/onsi/ginkgo/v2 v2.21.0
17-
github.com/onsi/gomega v1.35.1
16+
github.com/onsi/ginkgo/v2 v2.22.0
17+
github.com/onsi/gomega v1.36.1
1818
github.com/pkg/errors v0.9.1
1919
github.com/prometheus/client_golang v1.14.0
2020
github.com/stretchr/testify v1.9.0

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,8 +291,12 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
291291
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
292292
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
293293
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
294+
github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
295+
github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
294296
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
295297
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
298+
github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
299+
github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
296300
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
297301
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
298302
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

pkg/splunk/enterprise/searchheadcluster.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,13 +651,31 @@ func getSearchHeadStatefulSet(ctx context.Context, client splcommon.ControllerCl
651651
return ss, nil
652652
}
653653

654+
// CSPL-3652 Configure deployer resources if configured
655+
// Use default otherwise
656+
func setDeployerResources(cr *enterpriseApi.SearchHeadCluster, podTemplate *corev1.PodTemplateSpec) {
657+
depRes := cr.Spec.DeployerResourceSpec
658+
for i := range podTemplate.Spec.Containers {
659+
if len(depRes.Requests) != 0 {
660+
podTemplate.Spec.Containers[i].Resources.Requests = cr.Spec.DeployerResourceSpec.Requests
661+
}
662+
663+
if len(depRes.Limits) != 0 {
664+
podTemplate.Spec.Containers[i].Resources.Limits = cr.Spec.DeployerResourceSpec.Limits
665+
}
666+
}
667+
}
668+
654669
// getDeployerStatefulSet returns a Kubernetes StatefulSet object for a Splunk Enterprise license manager.
655670
func getDeployerStatefulSet(ctx context.Context, client splcommon.ControllerClient, cr *enterpriseApi.SearchHeadCluster) (*appsv1.StatefulSet, error) {
656671
ss, err := getSplunkStatefulSet(ctx, client, cr, &cr.Spec.CommonSplunkSpec, SplunkDeployer, 1, getSearchHeadExtraEnv(cr, cr.Spec.Replicas))
657672
if err != nil {
658673
return ss, err
659674
}
660675

676+
// CSPL-3562 - Set deployer resources if configured
677+
setDeployerResources(cr, &ss.Spec.Template)
678+
661679
// Setup App framework staging volume for apps
662680
setupAppsStagingVolume(ctx, client, cr, &ss.Spec.Template, &cr.Spec.AppFrameworkConfig)
663681

test/custom_resource_crud/manager_custom_resource_crud_c3_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,67 @@ var _ = Describe("Crcrud test for SVA C3", func() {
158158
})
159159
})
160160

161+
Context("Search Head Cluster", func() {
162+
It("managercrcrud, smoke, c3: can deploy Search Head Cluster with Deployer resource spec configured", func() {
163+
shcName := fmt.Sprintf("%s-shc", deployment.GetName())
164+
_, err := deployment.DeploySearchHeadCluster(ctx, shcName, "", "", "", "")
165+
if err != nil {
166+
Expect(err).To(Succeed(), "Unable to deploy Search Head Cluster", "Shc", shcName)
167+
}
168+
169+
// Verify CPU limits on Search Heads and deployer before updating CR
170+
searchHeadCount := 3
171+
for i := 0; i < searchHeadCount; i++ {
172+
SearchHeadPodName := fmt.Sprintf(testenv.SearchHeadPod, deployment.GetName(), i)
173+
testenv.VerifyCPULimits(deployment, testcaseEnvInst.GetName(), SearchHeadPodName, defaultCPULimits)
174+
}
175+
176+
DeployerPodName := fmt.Sprintf(testenv.DeployerPod, deployment.GetName())
177+
testenv.VerifyCPULimits(deployment, testcaseEnvInst.GetName(), DeployerPodName, defaultCPULimits)
178+
179+
shc := &enterpriseApi.SearchHeadCluster{}
180+
err = deployment.GetInstance(ctx, shcName, shc)
181+
Expect(err).To(Succeed(), "Unable to fetch Search Head Cluster deployment")
182+
183+
// Assign new resources for deployer pod only
184+
newCPULimits = "4"
185+
newCPURequests := "2"
186+
newMemoryLimits := "14Gi"
187+
newMemoryRequests := "12Gi"
188+
189+
depResSpec := corev1.ResourceRequirements{
190+
Requests: corev1.ResourceList{
191+
"cpu": resource.MustParse(newCPURequests),
192+
"memory": resource.MustParse(newMemoryRequests),
193+
},
194+
Limits: corev1.ResourceList{
195+
"cpu": resource.MustParse(newCPULimits),
196+
"memory": resource.MustParse(newMemoryLimits),
197+
},
198+
}
199+
shc.Spec.DeployerResourceSpec = depResSpec
200+
201+
err = deployment.UpdateCR(ctx, shc)
202+
Expect(err).To(Succeed(), "Unable to deploy Search Head Cluster with updated CR")
203+
204+
// Verify Search Head Cluster is updating
205+
testenv.VerifySearchHeadClusterPhase(ctx, deployment, testcaseEnvInst, enterpriseApi.PhaseUpdating)
206+
207+
// Verify Search Head go to ready state
208+
testenv.SearchHeadClusterReady(ctx, deployment, testcaseEnvInst)
209+
210+
// Verify CPU limits on Search Heads - Should be same as before
211+
searchHeadCount = 3
212+
for i := 0; i < searchHeadCount; i++ {
213+
SearchHeadPodName := fmt.Sprintf(testenv.SearchHeadPod, deployment.GetName(), i)
214+
testenv.VerifyCPULimits(deployment, testcaseEnvInst.GetName(), SearchHeadPodName, defaultCPULimits)
215+
}
216+
217+
// Verify modified deployer spec
218+
testenv.VerifyResourceConstraints(deployment, testcaseEnvInst.GetName(), DeployerPodName, depResSpec)
219+
})
220+
})
221+
161222
Context("Clustered deployment (C3 - clustered indexer, search head cluster)", func() {
162223
It("managercrcrud, integration, c3: can verify IDXC, CM and SHC PVCs are correctly deleted after the CRs deletion", func() {
163224

test/testenv/verificationutils.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"time"
2626

2727
gomega "github.com/onsi/gomega"
28+
corev1 "k8s.io/api/core/v1"
2829

2930
enterpriseApiV3 "github.com/splunk/splunk-operator/api/v3"
3031
enterpriseApi "github.com/splunk/splunk-operator/api/v4"
@@ -41,6 +42,10 @@ type PodDetailsStruct struct {
4142
CPU string `json:"cpu"`
4243
Memory string `json:"memory"`
4344
} `json:"limits"`
45+
Requests struct {
46+
CPU string `json:"cpu"`
47+
Memory string `json:"memory"`
48+
} `json:"requests"`
4449
} `json:"resources"`
4550
}
4651
ServiceAccount string `json:"serviceAccount"`
@@ -641,6 +646,48 @@ func VerifyCPULimits(deployment *Deployment, ns string, podName string, expected
641646
}, deployment.GetTimeout(), PollInterval).Should(gomega.Equal(true))
642647
}
643648

649+
// VerifyResourceConstraints verifies value of CPU limits is as expected
650+
func VerifyResourceConstraints(deployment *Deployment, ns string, podName string, res corev1.ResourceRequirements) {
651+
gomega.Eventually(func() bool {
652+
output, err := exec.Command("kubectl", "get", "pods", "-n", ns, podName, "-o", "json").Output()
653+
if err != nil {
654+
cmd := fmt.Sprintf("kubectl get pods -n %s %s -o json", ns, podName)
655+
logf.Log.Error(err, "Failed to execute command", "command", cmd)
656+
return false
657+
}
658+
restResponse := PodDetailsStruct{}
659+
err = json.Unmarshal([]byte(output), &restResponse)
660+
if err != nil {
661+
logf.Log.Error(err, "Failed to parse JSON")
662+
return false
663+
}
664+
result := false
665+
666+
for i := 0; i < len(restResponse.Spec.Containers); i++ {
667+
if strings.Contains(restResponse.Spec.Containers[i].Resources.Limits.CPU, res.Limits.Cpu().String()) {
668+
result = true
669+
logf.Log.Info("Verifying CPU limits: ", "POD", podName, "FOUND", restResponse.Spec.Containers[0].Resources.Limits.CPU, "EXPECTED", res.Limits.Cpu().String())
670+
}
671+
672+
if strings.Contains(restResponse.Spec.Containers[i].Resources.Limits.Memory, res.Limits.Memory().String()) {
673+
result = true
674+
logf.Log.Info("Verifying Memory limits: ", "POD", podName, "FOUND", restResponse.Spec.Containers[i].Resources.Limits.Memory, "EXPECTED", res.Limits.Memory().String())
675+
}
676+
677+
if strings.Contains(restResponse.Spec.Containers[i].Resources.Requests.CPU, res.Requests.Cpu().String()) {
678+
result = true
679+
logf.Log.Info("Verifying CPU limits: ", "POD", podName, "FOUND", restResponse.Spec.Containers[i].Resources.Requests.CPU, "EXPECTED", res.Requests.Cpu().String())
680+
}
681+
682+
if strings.Contains(restResponse.Spec.Containers[i].Resources.Requests.Memory, res.Requests.Memory().String()) {
683+
result = true
684+
logf.Log.Info("Verifying CPU limits: ", "POD", podName, "FOUND", restResponse.Spec.Containers[i].Resources.Requests.Memory, "EXPECTED", res.Requests.Memory().String())
685+
}
686+
}
687+
return result
688+
}, deployment.GetTimeout(), PollInterval).Should(gomega.Equal(true))
689+
}
690+
644691
// VerifyClusterManagerPhase verify phase of cluster manager
645692
func VerifyClusterManagerPhase(ctx context.Context, deployment *Deployment, testenvInstance *TestCaseEnv, phase enterpriseApi.Phase) {
646693
cm := &enterpriseApi.ClusterManager{}

0 commit comments

Comments
 (0)