diff --git a/pkg/splunk/enterprise/configuration.go b/pkg/splunk/enterprise/configuration.go index 58baef702..c0891824c 100644 --- a/pkg/splunk/enterprise/configuration.go +++ b/pkg/splunk/enterprise/configuration.go @@ -425,7 +425,7 @@ func getSplunkStatefulSet(client splcommon.ControllerClient, cr splcommon.MetaOb } // update statefulset's pod template with common splunk pod config - updateSplunkPodTemplateWithConfig(&statefulSet.Spec.Template, cr, spec, instanceType, extraEnv, statefulSetSecret.GetName()) + updateSplunkPodTemplateWithConfig(client, &statefulSet.Spec.Template, cr, spec, instanceType, extraEnv, statefulSetSecret.GetName()) // make Splunk Enterprise object the owner statefulSet.SetOwnerReferences(append(statefulSet.GetOwnerReferences(), splcommon.AsOwner(cr, true))) @@ -434,7 +434,7 @@ func getSplunkStatefulSet(client splcommon.ControllerClient, cr splcommon.MetaOb } // updateSplunkPodTemplateWithConfig modifies the podTemplateSpec object based on configuration of the Splunk Enterprise resource. -func updateSplunkPodTemplateWithConfig(podTemplateSpec *corev1.PodTemplateSpec, cr splcommon.MetaObject, spec *enterprisev1.CommonSplunkSpec, instanceType InstanceType, extraEnv []corev1.EnvVar, secretToMount string) { +func updateSplunkPodTemplateWithConfig(client splcommon.ControllerClient, podTemplateSpec *corev1.PodTemplateSpec, cr splcommon.MetaObject, spec *enterprisev1.CommonSplunkSpec, instanceType InstanceType, extraEnv []corev1.EnvVar, secretToMount string) { // Add custom ports to splunk containers if spec.ServiceTemplate.Spec.Ports != nil { diff --git a/pkg/splunk/enterprise/finalizers_test.go b/pkg/splunk/enterprise/finalizers_test.go index 692838b3f..3d46450ef 100644 --- a/pkg/splunk/enterprise/finalizers_test.go +++ b/pkg/splunk/enterprise/finalizers_test.go @@ -98,7 +98,7 @@ func splunkDeletionTester(t *testing.T, cr splcommon.MetaObject, delete func(spl {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.Deployment-test-splunk-test-monitoring-console"}, + {MetaName: "*v1.StatefulSet-test-splunk-test-monitoring-console"}, } mockCalls["Create"] = []spltest.MockFuncCall{ {MetaName: "*v1.Secret-test-splunk-test-secret"}, @@ -106,7 +106,7 @@ func splunkDeletionTester(t *testing.T, cr splcommon.MetaObject, delete func(spl {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.Deployment-test-splunk-test-monitoring-console"}, + {MetaName: "*v1.StatefulSet-test-splunk-test-monitoring-console"}, } } else { mockCalls["Update"] = []spltest.MockFuncCall{ diff --git a/pkg/splunk/enterprise/licensemaster_test.go b/pkg/splunk/enterprise/licensemaster_test.go index 545454958..fee1ffd2f 100644 --- a/pkg/splunk/enterprise/licensemaster_test.go +++ b/pkg/splunk/enterprise/licensemaster_test.go @@ -38,7 +38,7 @@ func TestApplyLicenseMaster(t *testing.T) { {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.Deployment-test-splunk-test-monitoring-console"}, + {MetaName: "*v1.StatefulSet-test-splunk-test-monitoring-console"}, {MetaName: "*v1.Service-test-splunk-stack1-license-master-service"}, {MetaName: "*v1.Secret-test-splunk-test-secret"}, {MetaName: "*v1.Secret-test-splunk-stack1-license-master-secret-v1"}, @@ -54,6 +54,7 @@ func TestApplyLicenseMaster(t *testing.T) { } listmockCall := []spltest.MockFuncCall{ {ListOpts: listOpts}} + createCalls := map[string][]spltest.MockFuncCall{"Get": funcCalls, "Create": {funcCalls[0], funcCalls[3], funcCalls[4], funcCalls[5], funcCalls[6], funcCalls[8], funcCalls[9], funcCalls[11], funcCalls[12]}, "List": {listmockCall[0], listmockCall[0]}, "Update": {funcCalls[0]}} updateCalls := map[string][]spltest.MockFuncCall{"Get": {funcCalls[0], funcCalls[2], funcCalls[3], funcCalls[4], funcCalls[5], funcCalls[6], funcCalls[7], funcCalls[8], funcCalls[9], funcCalls[10], funcCalls[11], funcCalls[12]}, "Update": {funcCalls[8], funcCalls[12]}, "List": {listmockCall[0], listmockCall[0]}} current := enterprisev1.LicenseMaster{ diff --git a/pkg/splunk/enterprise/monitoringconsole.go b/pkg/splunk/enterprise/monitoringconsole.go index 23f55b81e..15ec495d4 100644 --- a/pkg/splunk/enterprise/monitoringconsole.go +++ b/pkg/splunk/enterprise/monitoringconsole.go @@ -15,12 +15,7 @@ package enterprise import ( - "bytes" "context" - "crypto/md5" - "encoding/hex" - "fmt" - "io" "reflect" "sort" "strings" @@ -35,12 +30,9 @@ import ( "k8s.io/apimachinery/pkg/types" ) -// ApplyMonitoringConsole creates the deployment for monitoring console deployment of Splunk Enterprise. +// ApplyMonitoringConsole creates the statefulset for monitoring console statefulset of Splunk Enterprise. func ApplyMonitoringConsole(client splcommon.ControllerClient, cr splcommon.MetaObject, spec enterprisev1.CommonSplunkSpec, extraEnv []corev1.EnvVar) error { - var secrets *corev1.Secret - var err error - - secrets, err = splutil.GetLatestVersionedSecret(client, cr, cr.GetNamespace(), GetSplunkMonitoringConsoleDeploymentName(SplunkMonitoringConsole, cr.GetNamespace())) + secrets, err := splutil.GetLatestVersionedSecret(client, cr, cr.GetNamespace(), GetSplunkStatefulsetName(SplunkMonitoringConsole, cr.GetNamespace())) if err != nil { return err } @@ -74,29 +66,22 @@ func ApplyMonitoringConsole(client splcommon.ControllerClient, cr splcommon.Meta return err } - //configMapHash to trigger the configMap change in monitoring console deployment - configMapHash, err := getConfigMapHash(client, cr.GetNamespace()) + statefulsetMC, err := getMonitoringConsoleStatefulSet(client, cr, &spec, SplunkMonitoringConsole, secretName) if err != nil { return err } - //configMapHash should never be nil, just adding extra check - if configMapHash == "" { - return err - } - - deploymentMC, err := getMonitoringConsoleDeployment(cr, &spec, SplunkMonitoringConsole, configMapHash, secretName) + mgr := splctrl.DefaultStatefulSetPodManager{} + _, err = mgr.Update(client, statefulsetMC, 1) if err != nil { return err } - _, err = splctrl.ApplyDeployment(client, deploymentMC) - return err } -// GetMonitoringConsoleDeployment returns a Kubernetes Deployment object for Splunk Enterprise monitoring console instance. -func getMonitoringConsoleDeployment(cr splcommon.MetaObject, spec *enterprisev1.CommonSplunkSpec, instanceType InstanceType, configMapHash string, secretName string) (*appsv1.Deployment, error) { +// getMonitoringConsoleStatefulSet returns a Kubernetes Statefulset object for Splunk Enterprise monitoring console instance. +func getMonitoringConsoleStatefulSet(client splcommon.ControllerClient, cr splcommon.MetaObject, spec *enterprisev1.CommonSplunkSpec, instanceType InstanceType, secretName string) (*appsv1.StatefulSet, error) { var partOfIdentifier string // there will be always 1 replica of monitoring console replicas := int32(1) @@ -113,21 +98,26 @@ func getMonitoringConsoleDeployment(cr splcommon.MetaObject, spec *enterprisev1. labels[k] = v } - //create deployment configuration - deploymentMC := &appsv1.Deployment{ + //create statefulset configuration + statefulSet := &appsv1.StatefulSet{ TypeMeta: metav1.TypeMeta{ - Kind: "Deployment", + Kind: "StatefulSet", APIVersion: "apps/v1", }, ObjectMeta: metav1.ObjectMeta{ - Name: GetSplunkMonitoringConsoleDeploymentName(instanceType, cr.GetNamespace()), + Name: GetSplunkStatefulsetName(instanceType, cr.GetNamespace()), Namespace: cr.GetNamespace(), }, - Spec: appsv1.DeploymentSpec{ + Spec: appsv1.StatefulSetSpec{ Selector: &metav1.LabelSelector{ MatchLabels: selectLabels, }, - Replicas: &replicas, + ServiceName: GetSplunkServiceName(instanceType, cr.GetNamespace(), true), + Replicas: &replicas, + PodManagementPolicy: appsv1.ParallelPodManagement, + UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ + Type: appsv1.OnDeleteStatefulSetStrategyType, + }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: labels, @@ -159,18 +149,26 @@ func getMonitoringConsoleDeployment(cr splcommon.MetaObject, spec *enterprisev1. }, } - env := []corev1.EnvVar{ - {Name: "SPLUNK_CONFIGMAP_HASH", - Value: configMapHash, - }, - } + env := []corev1.EnvVar{} + // append labels and annotations from parent - splcommon.AppendParentMeta(deploymentMC.Spec.Template.GetObjectMeta(), cr.GetObjectMeta()) + splcommon.AppendParentMeta(statefulSet.Spec.Template.GetObjectMeta(), cr.GetObjectMeta()) // update statefulset's pod template with common splunk pod config - updateSplunkPodTemplateWithConfig(&deploymentMC.Spec.Template, cr, spec, instanceType, env, secretName) + updateSplunkPodTemplateWithConfig(client, &statefulSet.Spec.Template, cr, spec, instanceType, env, secretName) - return deploymentMC, nil + //update podTemplate annotation with configMap resource version + var monitoringConsoleConfigMap *corev1.ConfigMap + if cr.GetObjectMeta().GetDeletionTimestamp() != nil { + monitoringConsoleConfigMap, _ = ApplyMonitoringConsoleEnvConfigMap(client, cr.GetNamespace(), cr.GetName(), env, false) + } else { + monitoringConsoleConfigMap, _ = ApplyMonitoringConsoleEnvConfigMap(client, cr.GetNamespace(), cr.GetName(), env, true) + } + statefulSet.Spec.Template.ObjectMeta.Annotations[monitoringConsoleConfigRev] = monitoringConsoleConfigMap.ResourceVersion + + statefulSet.SetOwnerReferences(append(statefulSet.GetOwnerReferences(), splcommon.AsOwner(cr, false))) + + return statefulSet, nil } //ApplyMonitoringConsoleEnvConfigMap creates or updates a Kubernetes ConfigMap for extra env for monitoring console pod @@ -298,43 +296,3 @@ func DeleteURLsConfigMap(revised *corev1.ConfigMap, crName string, newURLs []cor } } } - -func getConfigMapHash(client splcommon.ControllerClient, namespace string) (string, error) { - - namespacedName := types.NamespacedName{Namespace: namespace, Name: GetSplunkMonitoringconsoleConfigMapName(namespace, SplunkMonitoringConsole)} - var current corev1.ConfigMap - - err := client.Get(context.TODO(), namespacedName, ¤t) - // if configMap doens't exist and we try to get configMap hash just return with empty string - if err != nil { - return "", err - } - - contents := current.Data - - hash := md5.New() - b := new(bytes.Buffer) - - //check key sorting. Collect keys in array and sort. Then use that sorted keys to append - var keys []string - - for k := range contents { - keys = append(keys, k) - } - - //sort keys here - sort.Strings(keys) - - //now iterate through the map in sorted order - for _, k := range keys { - fmt.Fprintf(b, "%s=\"%s\"\n", k, contents[k]) - } - - if _, err := io.Copy(hash, b); err != nil { - return "", err - } - - hashInBytes := hash.Sum(nil)[:16] - returnMD5String := hex.EncodeToString(hashInBytes) - return returnMD5String, nil -} diff --git a/pkg/splunk/enterprise/monitoringconsole_test.go b/pkg/splunk/enterprise/monitoringconsole_test.go index 642293c28..a1336eb32 100644 --- a/pkg/splunk/enterprise/monitoringconsole_test.go +++ b/pkg/splunk/enterprise/monitoringconsole_test.go @@ -44,7 +44,7 @@ func TestApplyMonitoringConsole(t *testing.T) { {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.Deployment-test-splunk-test-monitoring-console"}, + {MetaName: "*v1.StatefulSet-test-splunk-test-monitoring-console"}, } labels := map[string]string{ "app.kubernetes.io/component": "versionedSecrets", @@ -57,7 +57,7 @@ func TestApplyMonitoringConsole(t *testing.T) { listmockCall := []spltest.MockFuncCall{ {ListOpts: listOpts}} - createCalls := map[string][]spltest.MockFuncCall{"Get": funcCalls, "Create": {funcCalls[1], funcCalls[2], funcCalls[3], funcCalls[4], funcCalls[6]}, "List": {listmockCall[0]}} + createCalls := map[string][]spltest.MockFuncCall{"Get": funcCalls, "Create": {funcCalls[1], funcCalls[2], funcCalls[3], funcCalls[5], funcCalls[6]}, "List": {listmockCall[0]}} updateCalls := map[string][]spltest.MockFuncCall{"Get": funcCalls, "Update": {funcCalls[6]}, "List": {listmockCall[0]}} c := spltest.NewMockClient() diff --git a/pkg/splunk/enterprise/names.go b/pkg/splunk/enterprise/names.go index 9c21b47ef..1d6619bb3 100644 --- a/pkg/splunk/enterprise/names.go +++ b/pkg/splunk/enterprise/names.go @@ -49,6 +49,9 @@ const ( // identifier used for S3 secret key s3SecretKey = "s3_secret_key" + + //identifier for monitoring console configMap revision + monitoringConsoleConfigRev = "monitoringConsoleConfigRev" ) // GetSplunkDeploymentName uses a template to name a Kubernetes Deployment for Splunk instances. @@ -61,11 +64,6 @@ func GetSplunkStatefulsetName(instanceType InstanceType, identifier string) stri return fmt.Sprintf(statefulSetTemplateStr, identifier, instanceType) } -// GetSplunkMonitoringConsoleDeploymentName uses a template to name a Kubernetes Deployment for Splunk MC instance. -func GetSplunkMonitoringConsoleDeploymentName(instanceType InstanceType, identifier string) string { - return fmt.Sprintf(deploymentTemplateStr, identifier, instanceType) -} - // GetSplunkStatefulsetPodName uses a template to name a specific pod within a Kubernetes StatefulSet for Splunk instances. func GetSplunkStatefulsetPodName(instanceType InstanceType, identifier string, index int32) string { return fmt.Sprintf(statefulSetPodTemplateStr, identifier, instanceType, index) @@ -91,7 +89,7 @@ func GetSplunkDefaultsName(identifier string, instanceType InstanceType) string // GetSplunkMonitoringconsoleConfigMapName uses a template to name a Kubernetes ConfigMap for a SplunkEnterprise resource. func GetSplunkMonitoringconsoleConfigMapName(identifier string, instanceType InstanceType) string { - return fmt.Sprintf(deploymentTemplateStr, identifier, instanceType.ToKind()) + return fmt.Sprintf(statefulSetTemplateStr, identifier, instanceType.ToKind()) } // GetSplunkSmartstoreConfigMapName uses a template to name a Kubernetes ConfigMap for a SplunkEnterprise resource. diff --git a/pkg/splunk/enterprise/names_test.go b/pkg/splunk/enterprise/names_test.go index 7f9dafd46..613570dcb 100644 --- a/pkg/splunk/enterprise/names_test.go +++ b/pkg/splunk/enterprise/names_test.go @@ -35,14 +35,6 @@ func TestGetSplunkStatefulsetName(t *testing.T) { } } -func TestGetSplunkMonitoringConsoleDeploymentName(t *testing.T) { - got := GetSplunkMonitoringConsoleDeploymentName(SplunkMonitoringConsole, "t2") - want := "splunk-t2-monitoring-console" - if got != want { - t.Errorf("GetSplunkMonitoringConsoleDeploymentName(\"%s\",\"%s\") = %s; want %s", SplunkMonitoringConsole.ToString(), "t2", got, want) - } -} - func TestGetSplunkStatefulsetPodName(t *testing.T) { got := GetSplunkStatefulsetPodName(SplunkSearchHead, "t3", 2) want := "splunk-t3-search-head-2" diff --git a/pkg/splunk/enterprise/searchheadcluster_test.go b/pkg/splunk/enterprise/searchheadcluster_test.go index d3c6496c4..da23cde45 100644 --- a/pkg/splunk/enterprise/searchheadcluster_test.go +++ b/pkg/splunk/enterprise/searchheadcluster_test.go @@ -43,7 +43,7 @@ func TestApplySearchHeadCluster(t *testing.T) { {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.Deployment-test-splunk-test-monitoring-console"}, + {MetaName: "*v1.StatefulSet-test-splunk-test-monitoring-console"}, {MetaName: "*v1.Service-test-splunk-stack1-search-head-headless"}, {MetaName: "*v1.Service-test-splunk-stack1-search-head-service"}, {MetaName: "*v1.Service-test-splunk-stack1-deployer-service"}, diff --git a/pkg/splunk/enterprise/standalone_test.go b/pkg/splunk/enterprise/standalone_test.go index 328d63c73..73c6ba19d 100644 --- a/pkg/splunk/enterprise/standalone_test.go +++ b/pkg/splunk/enterprise/standalone_test.go @@ -39,7 +39,7 @@ func TestApplyStandalone(t *testing.T) { {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.Deployment-test-splunk-test-monitoring-console"}, + {MetaName: "*v1.StatefulSet-test-splunk-test-monitoring-console"}, {MetaName: "*v1.Service-test-splunk-stack1-standalone-headless"}, {MetaName: "*v1.Service-test-splunk-stack1-standalone-service"}, {MetaName: "*v1.Secret-test-splunk-test-secret"}, @@ -100,7 +100,7 @@ func TestApplyStandaloneWithSmartstore(t *testing.T) { {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.Deployment-test-splunk-test-monitoring-console"}, + {MetaName: "*v1.StatefulSet-test-splunk-test-monitoring-console"}, {MetaName: "*v1.Service-test-splunk-stack1-standalone-headless"}, {MetaName: "*v1.Service-test-splunk-stack1-standalone-service"}, {MetaName: "*v1.Secret-test-splunk-test-secret"},