github.com/verrazzano/verrazzano-monitoring-operator@v0.0.30/pkg/resources/statefulsets/plan_test.go (about)

     1  // Copyright (C) 2022, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package statefulsets
     5  
     6  import (
     7  	"github.com/stretchr/testify/assert"
     8  	"github.com/verrazzano/verrazzano-monitoring-operator/pkg/config"
     9  	"github.com/verrazzano/verrazzano-monitoring-operator/pkg/constants"
    10  	"github.com/verrazzano/verrazzano-monitoring-operator/pkg/resources"
    11  	"github.com/verrazzano/verrazzano-monitoring-operator/pkg/util/logs/vzlog"
    12  	appsv1 "k8s.io/api/apps/v1"
    13  	corev1 "k8s.io/api/core/v1"
    14  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    15  	"testing"
    16  )
    17  
    18  func createTestSTS(name string, replicas int32) *appsv1.StatefulSet {
    19  	return &appsv1.StatefulSet{
    20  		ObjectMeta: metav1.ObjectMeta{
    21  			Name: name,
    22  		},
    23  		Spec: appsv1.StatefulSetSpec{
    24  			Replicas: &replicas,
    25  		},
    26  		Status: appsv1.StatefulSetStatus{
    27  			ReadyReplicas: replicas,
    28  		},
    29  	}
    30  }
    31  
    32  func TestCreatePlan(t *testing.T) {
    33  	log := vzlog.DefaultLogger()
    34  	var tests = []struct {
    35  		name         string
    36  		existingList []*appsv1.StatefulSet
    37  		expectedList []*appsv1.StatefulSet
    38  		plan         *StatefulSetPlan
    39  	}{
    40  		{
    41  			"should delete nodes when expected is empty",
    42  			[]*appsv1.StatefulSet{
    43  				createTestSTS("foo", 1),
    44  			},
    45  			[]*appsv1.StatefulSet{},
    46  			&StatefulSetPlan{
    47  				ExistingCluster: true,
    48  				BounceNodes:     true,
    49  				Delete: []*appsv1.StatefulSet{
    50  					createTestSTS("foo", 1),
    51  				},
    52  			},
    53  		},
    54  		{
    55  			"should bounce nodes when scaling up single node cluster",
    56  			[]*appsv1.StatefulSet{
    57  				createTestSTS("foo", 1),
    58  			},
    59  			[]*appsv1.StatefulSet{
    60  				createTestSTS("foo", 3),
    61  			},
    62  			&StatefulSetPlan{
    63  				ExistingCluster: true,
    64  				BounceNodes:     true,
    65  				Update: []*appsv1.StatefulSet{
    66  					createTestSTS("foo", 3),
    67  				},
    68  			},
    69  		},
    70  		{
    71  			"do nothing when expected and existing are the same",
    72  			[]*appsv1.StatefulSet{
    73  				createTestSTS("foo", 1),
    74  				createTestSTS("bar", 2),
    75  			},
    76  			[]*appsv1.StatefulSet{
    77  				createTestSTS("foo", 1),
    78  				createTestSTS("bar", 2),
    79  			},
    80  			&StatefulSetPlan{ExistingCluster: true},
    81  		},
    82  		{
    83  			"create when expected, but not existing",
    84  			nil,
    85  			[]*appsv1.StatefulSet{
    86  				createTestSTS("foo", 1),
    87  				createTestSTS("bar", 2),
    88  			},
    89  			&StatefulSetPlan{
    90  				ExistingCluster: false,
    91  				Create: []*appsv1.StatefulSet{
    92  					createTestSTS("foo", 1),
    93  					createTestSTS("bar", 2),
    94  				},
    95  			},
    96  		},
    97  		{
    98  			"delete when no longer expected",
    99  			[]*appsv1.StatefulSet{
   100  				createTestSTS("foo", 1),
   101  				createTestSTS("bar", 2),
   102  			},
   103  			nil,
   104  			&StatefulSetPlan{
   105  				ExistingCluster: true,
   106  				Delete: []*appsv1.StatefulSet{
   107  					createTestSTS("foo", 1),
   108  					createTestSTS("bar", 2),
   109  				},
   110  			},
   111  		},
   112  		{
   113  			"update when existing and expected are both present, and there is a change, and scaling is allowed",
   114  			[]*appsv1.StatefulSet{
   115  				createTestSTS("foo", 3),
   116  			},
   117  			[]*appsv1.StatefulSet{
   118  				createTestSTS("foo", 4),
   119  			},
   120  			&StatefulSetPlan{
   121  				ExistingCluster: true,
   122  				Update: []*appsv1.StatefulSet{
   123  					createTestSTS("foo", 4),
   124  				},
   125  			},
   126  		},
   127  		{
   128  			"update allowed when existing cluster is down",
   129  			[]*appsv1.StatefulSet{
   130  				createTestSTS("foo", 0),
   131  			},
   132  			[]*appsv1.StatefulSet{
   133  				createTestSTS("foo", 3),
   134  			},
   135  			&StatefulSetPlan{
   136  				ExistingCluster: false,
   137  				Update: []*appsv1.StatefulSet{
   138  					createTestSTS("foo", 3),
   139  				},
   140  			},
   141  		},
   142  		{
   143  			"don't update if the scaling would cause cluster downtime",
   144  			[]*appsv1.StatefulSet{
   145  				createTestSTS("foo", 3),
   146  			},
   147  			[]*appsv1.StatefulSet{
   148  				createTestSTS("foo", 2),
   149  			},
   150  			&StatefulSetPlan{ExistingCluster: true},
   151  		},
   152  		{
   153  			"don't delete if the scaling would cause cluster downtime",
   154  			[]*appsv1.StatefulSet{
   155  				createTestSTS("foo", 1),
   156  				createTestSTS("bar", 2),
   157  			},
   158  			[]*appsv1.StatefulSet{
   159  				createTestSTS("foo", 1),
   160  			},
   161  			&StatefulSetPlan{ExistingCluster: true},
   162  		},
   163  		{
   164  			"scaling should be allowed on single node clusters",
   165  			[]*appsv1.StatefulSet{
   166  				createTestSTS("foo", 1),
   167  			},
   168  			[]*appsv1.StatefulSet{
   169  				createTestSTS("foo", 1),
   170  			},
   171  			&StatefulSetPlan{
   172  				BounceNodes:     true,
   173  				ExistingCluster: true,
   174  			},
   175  		},
   176  		{
   177  			"changing single node cluster name is not allowed",
   178  			[]*appsv1.StatefulSet{
   179  				createTestSTS("foo", 1),
   180  			},
   181  			[]*appsv1.StatefulSet{
   182  				createTestSTS("bar", 1),
   183  			},
   184  			&StatefulSetPlan{
   185  				BounceNodes:     true,
   186  				ExistingCluster: true,
   187  				Create: []*appsv1.StatefulSet{
   188  					createTestSTS("bar", 1),
   189  				},
   190  			},
   191  		},
   192  	}
   193  
   194  	for _, tt := range tests {
   195  		t.Run(tt.name, func(t *testing.T) {
   196  			actualPlan := CreatePlan(log, tt.existingList, tt.expectedList)
   197  			assert.Equal(t, len(tt.plan.Create), len(actualPlan.Create))
   198  			assert.Equal(t, len(tt.plan.Update), len(actualPlan.Update))
   199  			assert.Equal(t, len(tt.plan.Delete), len(actualPlan.Delete))
   200  			assert.Equal(t, tt.plan.ExistingCluster, actualPlan.ExistingCluster)
   201  			assert.Equal(t, tt.plan.BounceNodes, actualPlan.BounceNodes)
   202  		})
   203  	}
   204  }
   205  
   206  func TestCopyFromContainers(t *testing.T) {
   207  	nodeRoleVar := "node.roles"
   208  	existing := createTestSTS("foo", 1)
   209  	existing.Spec = appsv1.StatefulSetSpec{
   210  		VolumeClaimTemplates: []corev1.PersistentVolumeClaim{
   211  			{
   212  				ObjectMeta: metav1.ObjectMeta{
   213  					Name: "test",
   214  				},
   215  			},
   216  		},
   217  		Template: corev1.PodTemplateSpec{
   218  			Spec: corev1.PodSpec{
   219  				Containers: []corev1.Container{
   220  					{
   221  						Name: config.ElasticsearchMaster.Name,
   222  						Env: []corev1.EnvVar{
   223  							{
   224  								Name:  constants.ClusterInitialMasterNodes,
   225  								Value: "z",
   226  							},
   227  							{
   228  								Name:  nodeRoleVar,
   229  								Value: "a",
   230  							},
   231  						},
   232  					},
   233  				},
   234  			},
   235  		},
   236  	}
   237  	expected := createTestSTS("foo", 1)
   238  	expected.Spec = appsv1.StatefulSetSpec{
   239  		Template: corev1.PodTemplateSpec{
   240  			Spec: corev1.PodSpec{
   241  				Containers: []corev1.Container{
   242  					{
   243  						Name: config.ElasticsearchMaster.Name,
   244  						Env: []corev1.EnvVar{
   245  							{
   246  								Name:  "x",
   247  								Value: "y",
   248  							},
   249  							{
   250  								Name:  nodeRoleVar,
   251  								Value: "b",
   252  							},
   253  						},
   254  					},
   255  				},
   256  			},
   257  		},
   258  	}
   259  
   260  	assert.NotEqualValues(t, existing.Spec.VolumeClaimTemplates, expected.Spec.VolumeClaimTemplates)
   261  	existingInitialClusterMasters := resources.GetEnvVar(&existing.Spec.Template.Spec.Containers[0], constants.ClusterInitialMasterNodes)
   262  	expectedInitialClusterMasters := resources.GetEnvVar(&expected.Spec.Template.Spec.Containers[0], constants.ClusterInitialMasterNodes)
   263  	assert.NotEqualValues(t, existingInitialClusterMasters, expectedInitialClusterMasters)
   264  	existingNodeRoles := resources.GetEnvVar(&existing.Spec.Template.Spec.Containers[0], nodeRoleVar)
   265  	expectedNodeRoles := resources.GetEnvVar(&expected.Spec.Template.Spec.Containers[0], nodeRoleVar)
   266  	assert.NotEqualValues(t, existingNodeRoles, expectedNodeRoles)
   267  	CopyFromExisting(expected, existing)
   268  
   269  	assert.EqualValues(t, existing.Spec.VolumeClaimTemplates, expected.Spec.VolumeClaimTemplates)
   270  	existingInitialClusterMasters = resources.GetEnvVar(&existing.Spec.Template.Spec.Containers[0], constants.ClusterInitialMasterNodes)
   271  	expectedInitialClusterMasters = resources.GetEnvVar(&expected.Spec.Template.Spec.Containers[0], constants.ClusterInitialMasterNodes)
   272  	assert.EqualValues(t, existingInitialClusterMasters, expectedInitialClusterMasters)
   273  	existingNodeRoles = resources.GetEnvVar(&existing.Spec.Template.Spec.Containers[0], nodeRoleVar)
   274  	expectedNodeRoles = resources.GetEnvVar(&expected.Spec.Template.Spec.Containers[0], nodeRoleVar)
   275  	assert.EqualValues(t, existingNodeRoles, expectedNodeRoles)
   276  }