Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
bed6401
Added changeAnnotation method
tgarg-splunk Jun 14, 2023
7de6f36
Refined changeClusterManagerAnnotations
tgarg-splunk Jun 15, 2023
f4453a1
test case for upgrade scenario
vivekr-splunk Jun 15, 2023
32c385d
Modified kuttl cases
tgarg-splunk Jun 21, 2023
8efb456
Added kuttl tests; Updated LicenseMaster
tgarg-splunk Jun 22, 2023
3c75c99
Fixed uninstall kuttl test
tgarg-splunk Jun 22, 2023
ee474fc
Fixed unit test
tgarg-splunk Jun 22, 2023
35a2eb0
Removed changeAnnotation from licenseMaster
tgarg-splunk Jun 23, 2023
d9540fd
Added branch in int-tests
tgarg-splunk Jun 23, 2023
5a17a5f
Completed code coverage tests
tgarg-splunk Jun 26, 2023
706dc96
Added upgradeScenario and related methods for CM
tgarg-splunk Jun 15, 2023
c5af670
Added label selectors to get Current Image
tgarg-splunk Jun 15, 2023
237ecdf
Changed pod.Spec to pod.Status
tgarg-splunk Jun 15, 2023
5966d87
Added changeAnnotations for MC
tgarg-splunk Jun 21, 2023
b827bd9
Added kuttl test cases
tgarg-splunk Jun 22, 2023
a1159a8
Fixed unit test
tgarg-splunk Jun 23, 2023
3aa032b
Fixed SmartStore unit test
tgarg-splunk Jun 23, 2023
f0e73c8
Added code coverage test
tgarg-splunk Jun 26, 2023
53f6f68
using fake client instead of mock
vivekr-splunk Jun 27, 2023
8301fd9
removed creating statefulset and service
vivekr-splunk Jun 27, 2023
8353d25
Corrected LMCurrentImage method
tgarg-splunk Jun 26, 2023
dab93db
Completed Coverage tests for CM
tgarg-splunk Jun 27, 2023
fb77880
Refined changeClusterManagerAnnotations
tgarg-splunk Jun 15, 2023
b6c70d5
test case for upgrade scenario
vivekr-splunk Jun 15, 2023
6957966
Modified kuttl cases
tgarg-splunk Jun 21, 2023
70c73c2
Added kuttl tests; Updated LicenseMaster
tgarg-splunk Jun 22, 2023
4a945eb
Fixed unit test
tgarg-splunk Jun 22, 2023
a2c9f6d
Removed changeAnnotation from licenseMaster
tgarg-splunk Jun 23, 2023
86baa21
Completed code coverage tests
tgarg-splunk Jun 26, 2023
b24fca7
Resolved all conflict issues
tgarg-splunk Jun 28, 2023
1d0cc57
Added comments
tgarg-splunk Jun 28, 2023
10cc0b6
Updated upgradeScenario to check if statefulSet exists
tgarg-splunk Jun 30, 2023
27ddd67
Fixed Unit tests
tgarg-splunk Jun 30, 2023
0378e94
Added common APIs, changed upgrade condition
tgarg-splunk Jul 10, 2023
a116e9c
Added only warning if annotation not found
tgarg-splunk Jul 10, 2023
3695b41
Add warning
tgarg-splunk Jul 10, 2023
80e6acc
Updated upgradeCondition
tgarg-splunk Jul 11, 2023
3db7d4c
updated changeAnnotation to work with no ref
tgarg-splunk Jul 11, 2023
1c1531a
Fixed unit tests
tgarg-splunk Jul 11, 2023
f9c171f
Handled not found error
tgarg-splunk Jul 11, 2023
6c6b995
Added MC functions
tgarg-splunk Jul 12, 2023
e2e4433
Removed blank lines; handled errors in changeAnnotation
tgarg-splunk Jul 12, 2023
fe1d66f
Only call changeAnnotation if LM is ready
tgarg-splunk Jul 12, 2023
4e983b4
Handled errors
tgarg-splunk Jul 12, 2023
4515880
Removed redundant checks
tgarg-splunk Jul 12, 2023
b2d7bc1
Return if CM list is empty
tgarg-splunk Jul 13, 2023
0d178a1
removed superfluous nil err check
tgarg-splunk Jul 13, 2023
77f9a74
Removed branch from workflow
tgarg-splunk Jul 14, 2023
f6323dc
Merge branch 'CSPL-2094-LM-upgrade-strategy' into cspl-2343
tgarg-splunk Jul 14, 2023
130c778
Added branch to workflow
tgarg-splunk Jul 14, 2023
077f130
Fixed comment
tgarg-splunk Jul 17, 2023
52914dd
Fixed unit test
tgarg-splunk Jul 17, 2023
be99408
Merge branch 'feature-level2' into cspl-2343
tgarg-splunk Jul 17, 2023
26efb68
Improved comment for the upgrade condition
tgarg-splunk Jul 20, 2023
5a0dc92
Removed branch from workflow
tgarg-splunk Jul 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions pkg/splunk/enterprise/clustermanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,12 @@ func ApplyClusterManager(ctx context.Context, client splcommon.ControllerClient,

finalResult := handleAppFrameworkActivity(ctx, client, cr, &cr.Status.AppContext, &cr.Spec.AppFrameworkConfig)
result = *finalResult

// trigger MonitoringConsole reconcile by changing the splunk/image-tag annotation
err = changeMonitoringConsoleAnnotations(ctx, client, cr)
if err != nil {
return result, err
}
}
// RequeueAfter if greater than 0, tells the Controller to requeue the reconcile key after the Duration.
// Implies that Requeue is true, there is no need to set Requeue to true at the same time as RequeueAfter.
Expand Down Expand Up @@ -497,7 +503,8 @@ func isClusterManagerReadyForUpgrade(ctx context.Context, c splcommon.Controller
return false, err
}

// check if an image upgrade is happening and whether the ClusterManager is ready for the upgrade
// check if an image upgrade is happening and whether LM has finished updating yet, return false to stop
// further reconcile operations on CM until LM is ready
if (cr.Spec.Image != cmImage) && (licenseManager.Status.Phase != enterpriseApi.PhaseReady || lmImage != cr.Spec.Image) {
return false, nil
}
Expand Down Expand Up @@ -539,7 +546,6 @@ func changeClusterManagerAnnotations(ctx context.Context, c splcommon.Controller
}
return err
}

if len(objectList.Items) == 0 {
return nil
}
Expand Down
9 changes: 7 additions & 2 deletions pkg/splunk/enterprise/clustermanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,9 +531,14 @@ func TestApplyClusterManagerWithSmartstore(t *testing.T) {
runtime.InNamespace("test"),
runtime.MatchingLabels(labels),
}
listOpts1 := []runtime.ListOption{
runtime.InNamespace("test"),
}
listmockCall := []spltest.MockFuncCall{
{ListOpts: listOpts}}
createCalls := map[string][]spltest.MockFuncCall{"Get": funcCalls, "Create": {funcCalls[7], funcCalls[10], funcCalls[12]}, "List": {listmockCall[0], listmockCall[0]}, "Update": {funcCalls[0], funcCalls[3], funcCalls[13]}}
{ListOpts: listOpts},
{ListOpts: listOpts1},
}
createCalls := map[string][]spltest.MockFuncCall{"Get": funcCalls, "Create": {funcCalls[7], funcCalls[10], funcCalls[12]}, "List": {listmockCall[0], listmockCall[0], listmockCall[1]}, "Update": {funcCalls[0], funcCalls[3], funcCalls[13]}}
updateCalls := map[string][]spltest.MockFuncCall{"Get": updateFuncCalls, "Update": {funcCalls[8]}, "List": {listmockCall[0]}}

current := enterpriseApi.ClusterManager{
Expand Down
68 changes: 68 additions & 0 deletions pkg/splunk/enterprise/monitoringconsole.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ func ApplyMonitoringConsole(ctx context.Context, client splcommon.ControllerClie
return result, err
}

// check if the Monitoring Console is ready for version upgrade, if required
continueReconcile, err := isMonitoringConsoleReadyForUpgrade(ctx, client, cr)
if err != nil || !continueReconcile {
return result, err
}

mgr := splctrl.DefaultStatefulSetPodManager{}
phase, err := mgr.Update(ctx, client, statefulSet, 1)
if err != nil {
Expand Down Expand Up @@ -357,6 +363,65 @@ func DeleteURLsConfigMap(revised *corev1.ConfigMap, crName string, newURLs []cor
}
}

// isMonitoringConsoleReadyForUpgrade checks if MonitoringConsole can be upgraded if a version upgrade is in-progress
// No-operation otherwise; returns bool, err accordingly
func isMonitoringConsoleReadyForUpgrade(ctx context.Context, c splcommon.ControllerClient, cr *enterpriseApi.MonitoringConsole) (bool, error) {
reqLogger := log.FromContext(ctx)
scopedLog := reqLogger.WithName("isMonitoringConsoleReadyForUpgrade").WithValues("name", cr.GetName(), "namespace", cr.GetNamespace())
eventPublisher, _ := newK8EventPublisher(c, cr)

// check if a LicenseManager is attached to the instance
clusterManagerRef := cr.Spec.ClusterManagerRef
if clusterManagerRef.Name == "" {
return true, nil
}

namespacedName := types.NamespacedName{
Namespace: cr.GetNamespace(),
Name: GetSplunkStatefulsetName(SplunkMonitoringConsole, cr.GetName()),
}

// check if the stateful set is created at this instance
statefulSet := &appsv1.StatefulSet{}
err := c.Get(ctx, namespacedName, statefulSet)
if err != nil && k8serrors.IsNotFound(err) {
return true, nil
}

namespacedName = types.NamespacedName{Namespace: cr.GetNamespace(), Name: clusterManagerRef.Name}
clusterManager := &enterpriseApi.ClusterManager{}

// get the cluster manager referred in monitoring console
err = c.Get(ctx, namespacedName, clusterManager)
if err != nil {
eventPublisher.Warning(ctx, "isMonitoringConsoleReadyForUpgrade", fmt.Sprintf("Could not find the Cluster Manager. Reason %v", err))
scopedLog.Error(err, "Unable to get clusterManager")
return true, err
}

cmImage, err := getCurrentImage(ctx, c, cr, SplunkClusterManager)
if err != nil {
eventPublisher.Warning(ctx, "isMonitoringConsoleReadyForUpgrade", fmt.Sprintf("Could not get the Cluster Manager Image. Reason %v", err))
scopedLog.Error(err, "Unable to get clusterManager current image")
return false, err
}

mcImage, err := getCurrentImage(ctx, c, cr, SplunkMonitoringConsole)
if err != nil {
eventPublisher.Warning(ctx, "isMonitoringConsolerReadyForUpgrade", fmt.Sprintf("Could not get the Monitoring Console Image. Reason %v", err))
scopedLog.Error(err, "Unable to get monitoring console current image")
return false, err
}

// check if an image upgrade is happening and whether CM has finished updating yet, return false to stop
// further reconcile operations on MC until CM is ready
if (cr.Spec.Image != mcImage) && (clusterManager.Status.Phase != enterpriseApi.PhaseReady || cmImage != cr.Spec.Image) {
return false, nil
}

return true, nil
}

// changeMonitoringConsoleAnnotations updates the splunk/image-tag field of the MonitoringConsole annotations to trigger the reconcile loop
// on update, and returns error if something is wrong.
func changeMonitoringConsoleAnnotations(ctx context.Context, client splcommon.ControllerClient, cr *enterpriseApi.ClusterManager) error {
Expand Down Expand Up @@ -391,6 +456,9 @@ func changeMonitoringConsoleAnnotations(ctx context.Context, client splcommon.Co
}
return err
}
if len(objectList.Items) == 0 {
return nil
}

// check if instance has the required ClusterManagerRef
for _, mc := range objectList.Items {
Expand Down
170 changes: 170 additions & 0 deletions pkg/splunk/enterprise/monitoringconsole_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1100,3 +1100,173 @@ func TestGetMonitoringConsoleList(t *testing.T) {
t.Errorf("Got wrong number of IndexerCluster objects. Expected=%d, Got=%d", 1, numOfObjects)
}
}

func TestIsMonitoringConsoleReadyForUpgrade(t *testing.T) {
ctx := context.TODO()

builder := fake.NewClientBuilder()
client := builder.Build()
utilruntime.Must(enterpriseApi.AddToScheme(clientgoscheme.Scheme))

// Create Cluster Manager
cm := enterpriseApi.ClusterManager{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Namespace: "test",
},
Spec: enterpriseApi.ClusterManagerSpec{
CommonSplunkSpec: enterpriseApi.CommonSplunkSpec{
Spec: enterpriseApi.Spec{
ImagePullPolicy: "Always",
Image: "splunk/splunk:latest",
},
Volumes: []corev1.Volume{},
MonitoringConsoleRef: corev1.ObjectReference{
Name: "test",
},
},
},
}

err := client.Create(ctx, &cm)
_, err = ApplyClusterManager(ctx, client, &cm)
if err != nil {
t.Errorf("applyClusterManager should not have returned error; err=%v", err)
}
cm.Status.Phase = enterpriseApi.PhaseReady
err = client.Status().Update(ctx, &cm)
if err != nil {
t.Errorf("Unexpected status update %v", err)
debug.PrintStack()
}

// Create Monitoring Console
mc := enterpriseApi.MonitoringConsole{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Namespace: "test",
},
Spec: enterpriseApi.MonitoringConsoleSpec{
CommonSplunkSpec: enterpriseApi.CommonSplunkSpec{
Spec: enterpriseApi.Spec{
ImagePullPolicy: "Always",
Image: "splunk/splunk:latest",
},
Volumes: []corev1.Volume{},
ClusterManagerRef: corev1.ObjectReference{
Name: "test",
},
},
},
}

err = client.Create(ctx, &mc)
_, err = ApplyMonitoringConsole(ctx, client, &mc)
if err != nil {
t.Errorf("applyMonitoringConsole should not have returned error; err=%v", err)
}

mc.Spec.Image = "splunk2"
cm.Spec.Image = "splunk2"
_, err = ApplyClusterManager(ctx, client, &cm)

monitoringConsole := &enterpriseApi.MonitoringConsole{}
namespacedName := types.NamespacedName{
Name: cm.Name,
Namespace: cm.Namespace,
}
err = client.Get(ctx, namespacedName, monitoringConsole)
if err != nil {
t.Errorf("isMonitoringConsoleReadyForUpgrade should not have returned error=%v", err)
}

check, err := isMonitoringConsoleReadyForUpgrade(ctx, client, monitoringConsole)

if err != nil {
t.Errorf("Unexpected upgradeScenario error %v", err)
}

if !check {
t.Errorf("isMonitoringConsoleReadyForUpgrade: MC should be ready for upgrade")
}
}

func TestChangeMonitoringConsoleAnnotations(t *testing.T) {
ctx := context.TODO()

builder := fake.NewClientBuilder()
client := builder.Build()
utilruntime.Must(enterpriseApi.AddToScheme(clientgoscheme.Scheme))

// define CM and MC
cm := &enterpriseApi.ClusterManager{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Namespace: "test",
},
Spec: enterpriseApi.ClusterManagerSpec{
CommonSplunkSpec: enterpriseApi.CommonSplunkSpec{
Spec: enterpriseApi.Spec{
ImagePullPolicy: "Always",
},
Volumes: []corev1.Volume{},
},
},
}

mc := &enterpriseApi.MonitoringConsole{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Namespace: "test",
},
Spec: enterpriseApi.MonitoringConsoleSpec{
CommonSplunkSpec: enterpriseApi.CommonSplunkSpec{
Spec: enterpriseApi.Spec{
ImagePullPolicy: "Always",
},
Volumes: []corev1.Volume{},
ClusterManagerRef: corev1.ObjectReference{
Name: "test",
},
},
},
}
cm.Spec.Image = "splunk/splunk:latest"

// Create the instances
client.Create(ctx, cm)
_, err := ApplyClusterManager(ctx, client, cm)
if err != nil {
t.Errorf("applyClusterManager should not have returned error; err=%v", err)
}
cm.Status.Phase = enterpriseApi.PhaseReady
err = client.Status().Update(ctx, cm)
if err != nil {
t.Errorf("Unexpected update pod %v", err)
debug.PrintStack()
}
client.Create(ctx, mc)
_, err = ApplyMonitoringConsole(ctx, client, mc)
if err != nil {
t.Errorf("applyMonitoringConsole should not have returned error; err=%v", err)
}

err = changeMonitoringConsoleAnnotations(ctx, client, cm)
if err != nil {
t.Errorf("changeMonitoringConsoleAnnotations should not have returned error=%v", err)
}
monitoringConsole := &enterpriseApi.MonitoringConsole{}
namespacedName := types.NamespacedName{
Name: cm.Name,
Namespace: cm.Namespace,
}
err = client.Get(ctx, namespacedName, monitoringConsole)
if err != nil {
t.Errorf("changeMonitoringConsoleAnnotations should not have returned error=%v", err)
}

annotations := monitoringConsole.GetAnnotations()
if annotations["splunk/image-tag"] != cm.Spec.Image {
t.Errorf("changeMonitoringConsoleAnnotations should have set the checkUpdateImage annotation field to the current image")
}
}