diff --git a/pkg/splunk/controller/statefulset.go b/pkg/splunk/controller/statefulset.go index b70814168..d4a4496e5 100644 --- a/pkg/splunk/controller/statefulset.go +++ b/pkg/splunk/controller/statefulset.go @@ -282,3 +282,17 @@ func GetStatefulSetByName(c splcommon.ControllerClient, namespacedName types.Nam return &statefulset, nil } + +// IsStatefulSetScalingUp checks if we are currently scaling up +func IsStatefulSetScalingUp(client splcommon.ControllerClient, cr splcommon.MetaObject, name string, desiredReplicas int32) (bool, error) { + scopedLog := log.WithName("isScalingUp").WithValues("name", cr.GetName(), "namespace", cr.GetNamespace()) + + namespacedName := types.NamespacedName{Namespace: cr.GetNamespace(), Name: name} + current, err := GetStatefulSetByName(client, namespacedName) + if err != nil { + scopedLog.Error(err, "Unable to get current stateful set", "name", namespacedName) + return false, err + } + + return *current.Spec.Replicas < desiredReplicas, nil +} diff --git a/pkg/splunk/controller/statefulset_test.go b/pkg/splunk/controller/statefulset_test.go index 353d812b7..a6b1af709 100644 --- a/pkg/splunk/controller/statefulset_test.go +++ b/pkg/splunk/controller/statefulset_test.go @@ -208,3 +208,39 @@ func TestGetStatefulSetByName(t *testing.T) { t.Errorf(err.Error()) } } + +func TestIsStatefulSetScalingUp(t *testing.T) { + var replicas int32 = 1 + statefulSetName := "splunk-stand1-standalone" + + cr := enterprisev1.Standalone{ + ObjectMeta: metav1.ObjectMeta{ + Name: "stand1", + Namespace: "test", + }, + } + + current := &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: statefulSetName, + Namespace: "test", + }, + Spec: appsv1.StatefulSetSpec{ + Replicas: &replicas, + }, + } + + c := spltest.NewMockClient() + + *current.Spec.Replicas = 2 + _, err := IsStatefulSetScalingUp(c, &cr, statefulSetName, replicas) + if err == nil { + t.Errorf("IsStatefulSetScalingUp should have returned error as we have not yet added statefulset to client.") + } + + c.AddObject(current) + _, err = IsStatefulSetScalingUp(c, &cr, statefulSetName, replicas) + if err != nil { + t.Errorf("IsStatefulSetScalingUp should not have returned error") + } +} diff --git a/pkg/splunk/enterprise/standalone.go b/pkg/splunk/enterprise/standalone.go index 7af897b69..f6d6a844f 100644 --- a/pkg/splunk/enterprise/standalone.go +++ b/pkg/splunk/enterprise/standalone.go @@ -123,6 +123,35 @@ func ApplyStandalone(client splcommon.ControllerClient, cr *enterprisev1.Standal return result, err } + // If we are using appFramework and are scaling up, we should re-populate the + // configMap with all the appSource entries. This is done so that the new pods + // that come up now will have the complete list of all the apps and then can + // download and install all the apps. + // TODO: Improve this logic so that we only recycle the new pod/replica + // and not all the existing pods. + if len(cr.Spec.AppFrameworkConfig.AppSources) != 0 && cr.Spec.Replicas > 1 { + + statefulsetName := GetSplunkStatefulsetName(SplunkStandalone, cr.GetName()) + + isScalingUp, err := splctrl.IsStatefulSetScalingUp(client, cr, statefulsetName, cr.Spec.Replicas) + if err != nil { + return result, err + } else if isScalingUp { + // if we are indeed scaling up, then mark the deploy status to Pending + // for all the app sources so that we add all the app sources in configMap. + appStatusContext := cr.Status.AppContext + for appSrc := range appStatusContext.AppsSrcDeployStatus { + changeAppSrcDeployInfoStatus(appSrc, appStatusContext.AppsSrcDeployStatus, enterprisev1.RepoStateActive, enterprisev1.DeployStatusComplete, enterprisev1.DeployStatusPending) + } + + // Now apply the configMap will full app listing. + _, _, err = ApplyAppListingConfigMap(client, cr, &cr.Spec.AppFrameworkConfig, appStatusContext.AppsSrcDeployStatus) + if err != nil { + return result, err + } + } + } + // create or update statefulset statefulSet, err := getStandaloneStatefulSet(client, cr) if err != nil { diff --git a/pkg/splunk/enterprise/standalone_test.go b/pkg/splunk/enterprise/standalone_test.go index 041aa25dc..5a18bfcd8 100644 --- a/pkg/splunk/enterprise/standalone_test.go +++ b/pkg/splunk/enterprise/standalone_test.go @@ -361,6 +361,68 @@ func TestAppFrameworkApplyStandaloneShouldNotFail(t *testing.T) { } } +func TestAppFrameworkApplyStandaloneScalingUpShouldNotFail(t *testing.T) { + cr := enterprisev1.Standalone{ + ObjectMeta: metav1.ObjectMeta{ + Name: "standalone", + Namespace: "test", + }, + Spec: enterprisev1.StandaloneSpec{ + Replicas: 1, + AppFrameworkConfig: enterprisev1.AppFrameworkSpec{ + VolList: []enterprisev1.VolumeSpec{ + {Name: "msos_s2s3_vol", Endpoint: "https://s3-eu-west-2.amazonaws.com", Path: "testbucket-rs-london", SecretRef: "s3-secret", Type: "s3", Provider: "aws"}, + }, + AppSources: []enterprisev1.AppSourceSpec{ + {Name: "adminApps", + Location: "adminAppsRepo", + AppSourceDefaultSpec: enterprisev1.AppSourceDefaultSpec{ + VolName: "msos_s2s3_vol", + Scope: "local"}, + }, + {Name: "securityApps", + Location: "securityAppsRepo", + AppSourceDefaultSpec: enterprisev1.AppSourceDefaultSpec{ + VolName: "msos_s2s3_vol", + Scope: "local"}, + }, + {Name: "authenticationApps", + Location: "authenticationAppsRepo", + AppSourceDefaultSpec: enterprisev1.AppSourceDefaultSpec{ + VolName: "msos_s2s3_vol", + Scope: "local"}, + }, + }, + }, + }, + } + + client := spltest.NewMockClient() + + // Create namespace scoped secret + _, err := splutil.ApplyNamespaceScopedSecretObject(client, "test") + if err != nil { + t.Errorf(err.Error()) + } + + // Create S3 secret + s3Secret := spltest.GetMockS3SecretKeys("s3-secret") + + client.AddObject(&s3Secret) + + _, err = ApplyStandalone(client, &cr) + if err != nil { + t.Errorf("ApplyStandalone should be successful") + } + + // now scale up + cr.Spec.Replicas = 2 + _, err = ApplyStandalone(client, &cr) + if err != nil { + t.Errorf("ApplyStandalone should be successful") + } +} + func TestStandaloneGetAppsListForAWSS3ClientShouldNotFail(t *testing.T) { cr := enterprisev1.Standalone{ ObjectMeta: metav1.ObjectMeta{