github.com/verrazzano/verrazzano@v1.7.1/tools/psr/backend/workers/opensearch/restart/restart_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 restart 5 6 import ( 7 "strings" 8 "testing" 9 10 "github.com/verrazzano/verrazzano/pkg/constants" 11 "github.com/verrazzano/verrazzano/pkg/log/vzlog" 12 "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1alpha1" 13 "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1beta1" 14 vpoFakeClient "github.com/verrazzano/verrazzano/platform-operator/clientset/versioned/fake" 15 "github.com/verrazzano/verrazzano/tools/psr/backend/config" 16 "github.com/verrazzano/verrazzano/tools/psr/backend/osenv" 17 "github.com/verrazzano/verrazzano/tools/psr/backend/pkg/k8sclient" 18 opensearchpsr "github.com/verrazzano/verrazzano/tools/psr/backend/pkg/opensearch" 19 20 "github.com/stretchr/testify/assert" 21 appsv1 "k8s.io/api/apps/v1" 22 corev1 "k8s.io/api/core/v1" 23 k8sapiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/apimachinery/pkg/runtime" 26 "k8s.io/apimachinery/pkg/types" 27 crtFakeClient "sigs.k8s.io/controller-runtime/pkg/client/fake" 28 ) 29 30 type fakeEnv struct { 31 data map[string]string 32 } 33 34 type fakePsrClient struct { 35 psrClient *k8sclient.PsrClient 36 } 37 38 // TestGetters tests the worker getters 39 // GIVEN a worker 40 // 41 // WHEN the getter methods are calls 42 // THEN ensure that the correct results are returned 43 func TestGetters(t *testing.T) { 44 origFunc := overridePsrClient() 45 defer func() { 46 funcNewPsrClient = origFunc 47 }() 48 49 envMap := map[string]string{ 50 openSearchTier: opensearchpsr.MasterTier, 51 } 52 f := fakeEnv{data: envMap} 53 saveEnv := osenv.GetEnvFunc 54 osenv.GetEnvFunc = f.GetEnv 55 defer func() { 56 osenv.GetEnvFunc = saveEnv 57 }() 58 59 w, err := NewRestartWorker() 60 assert.NoError(t, err) 61 62 wd := w.GetWorkerDesc() 63 assert.Equal(t, config.WorkerTypeOpsRestart, wd.WorkerType) 64 assert.Equal(t, "Worker to restart pods in the specified OpenSearch tier", wd.Description) 65 assert.Equal(t, metricsPrefix, wd.MetricsPrefix) 66 67 logged := w.WantLoopInfoLogged() 68 assert.False(t, logged) 69 } 70 71 // TestGetEnvDescList tests the GetEnvDescList method 72 // GIVEN a worker 73 // 74 // WHEN the GetEnvDescList methods is called 75 // THEN ensure that the correct results are returned 76 func TestGetEnvDescList(t *testing.T) { 77 origFunc := overridePsrClient() 78 defer func() { 79 funcNewPsrClient = origFunc 80 }() 81 82 envMap := map[string]string{ 83 openSearchTier: opensearchpsr.MasterTier, 84 } 85 f := fakeEnv{data: envMap} 86 saveEnv := osenv.GetEnvFunc 87 osenv.GetEnvFunc = f.GetEnv 88 defer func() { 89 osenv.GetEnvFunc = saveEnv 90 }() 91 92 tests := []struct { 93 name string 94 key string 95 defval string 96 required bool 97 }{ 98 {name: "1", 99 key: openSearchTier, 100 defval: "", 101 required: true, 102 }, 103 } 104 105 for _, test := range tests { 106 t.Run(test.name, func(t *testing.T) { 107 w, err := NewRestartWorker() 108 assert.NoError(t, err) 109 el := w.GetEnvDescList() 110 for _, e := range el { 111 if e.Key == test.key { 112 assert.Equal(t, test.defval, e.DefaultVal) 113 assert.Equal(t, test.required, e.Required) 114 } 115 } 116 }) 117 } 118 } 119 120 // TestGetMetricDescList tests the GetEnvDescList method 121 // GIVEN a worker 122 // 123 // WHEN the GetEnvDescList methods is called 124 // THEN ensure that the correct results are returned 125 func TestGetMetricDescList(t *testing.T) { 126 origFunc := overridePsrClient() 127 defer func() { 128 funcNewPsrClient = origFunc 129 }() 130 131 envMap := map[string]string{ 132 openSearchTier: opensearchpsr.MasterTier, 133 } 134 f := fakeEnv{data: envMap} 135 saveEnv := osenv.GetEnvFunc 136 osenv.GetEnvFunc = f.GetEnv 137 defer func() { 138 osenv.GetEnvFunc = saveEnv 139 }() 140 141 tests := []struct { 142 name string 143 fqName string 144 help string 145 label string 146 }{ 147 {name: "1", fqName: metricsPrefix + "_pod_restart_count", help: "The total number of OpenSearch pod restarts", label: `opensearch_tier="master"`}, 148 {name: "2", fqName: metricsPrefix + "_pod_restart_time_nanoseconds", help: "The number of nanoseconds elapsed to restart the OpenSearch pod", label: `opensearch_tier="master"`}, 149 } 150 for _, test := range tests { 151 t.Run(test.name, func(t *testing.T) { 152 wi, err := NewRestartWorker() 153 w := wi.(worker) 154 assert.NoError(t, err) 155 dl := w.GetMetricDescList() 156 var found int 157 for _, d := range dl { 158 s := d.String() 159 if strings.Contains(s, test.fqName) && strings.Contains(s, test.help) { 160 found++ 161 } 162 } 163 assert.Equal(t, 1, found) 164 }) 165 } 166 } 167 168 // TestGetMetricList tests the GetMetricList method 169 // GIVEN a worker 170 // 171 // WHEN the GetMetricList methods is called 172 // THEN ensure that the correct results are returned 173 func TestGetMetricList(t *testing.T) { 174 origFunc := overridePsrClient() 175 defer func() { 176 funcNewPsrClient = origFunc 177 }() 178 179 envMap := map[string]string{ 180 openSearchTier: opensearchpsr.MasterTier, 181 } 182 f := fakeEnv{data: envMap} 183 saveEnv := osenv.GetEnvFunc 184 osenv.GetEnvFunc = f.GetEnv 185 defer func() { 186 osenv.GetEnvFunc = saveEnv 187 }() 188 189 tests := []struct { 190 name string 191 fqName string 192 help string 193 }{ 194 {name: "1", fqName: metricsPrefix + "_pod_restart_count", help: "The total number of OpenSearch pod restarts"}, 195 {name: "2", fqName: metricsPrefix + "_pod_restart_time_nanoseconds", help: "The number of nanoseconds elapsed to restart the OpenSearch pod"}, 196 } 197 for _, test := range tests { 198 t.Run(test.name, func(t *testing.T) { 199 wi, err := NewRestartWorker() 200 w := wi.(worker) 201 assert.NoError(t, err) 202 ml := w.GetMetricList() 203 var found int 204 for _, m := range ml { 205 s := m.Desc().String() 206 if strings.Contains(s, test.fqName) && strings.Contains(s, test.help) { 207 found++ 208 } 209 } 210 assert.Equal(t, 1, found) 211 }) 212 } 213 } 214 215 // TestDoWork tests the DoWork method 216 // GIVEN a worker 217 // 218 // WHEN the DoWork methods is called 219 // THEN ensure that the correct results are returned 220 func TestDoWork(t *testing.T) { 221 readyState := "ready" 222 notReadyState := "notready" 223 podExistsState := "podexists" 224 podUID := "poduid" 225 226 tests := []struct { 227 name string 228 tier string 229 state string 230 }{ 231 { 232 name: "master-ready", 233 tier: opensearchpsr.MasterTier, 234 state: readyState, 235 }, 236 { 237 name: "data-ready", 238 tier: opensearchpsr.DataTier, 239 state: readyState, 240 }, 241 { 242 name: "ingest-ready", 243 tier: opensearchpsr.IngestTier, 244 state: readyState, 245 }, 246 { 247 name: "master-not-ready", 248 tier: opensearchpsr.MasterTier, 249 state: notReadyState, 250 }, 251 { 252 name: "data-not-ready", 253 tier: opensearchpsr.DataTier, 254 state: notReadyState, 255 }, 256 { 257 name: "ingest-not-ready", 258 tier: opensearchpsr.IngestTier, 259 state: notReadyState, 260 }, 261 { 262 name: "master-pod-exists", 263 tier: opensearchpsr.MasterTier, 264 state: podExistsState, 265 }, 266 { 267 name: "data-pod-exists", 268 tier: opensearchpsr.DataTier, 269 state: podExistsState, 270 }, 271 { 272 name: "ingest-pod-exists", 273 tier: opensearchpsr.IngestTier, 274 state: podExistsState, 275 }, 276 } 277 for _, test := range tests { 278 t.Run(test.name, func(t *testing.T) { 279 envMap := map[string]string{ 280 openSearchTier: test.tier, 281 } 282 f := fakeEnv{data: envMap} 283 saveEnv := osenv.GetEnvFunc 284 osenv.GetEnvFunc = f.GetEnv 285 defer func() { 286 osenv.GetEnvFunc = saveEnv 287 }() 288 289 // Setup fake VZ client 290 cr := &v1beta1.Verrazzano{ 291 ObjectMeta: metav1.ObjectMeta{ 292 Name: "testVZ", 293 }, 294 } 295 vzclient := vpoFakeClient.NewSimpleClientset(cr) 296 297 // Setup fake K8s client 298 podLabels := getTierLabels(test.tier) 299 masterSTSLabel := map[string]string{"verrazzano-component": "opensearch"} 300 scheme := runtime.NewScheme() 301 _ = corev1.AddToScheme(scheme) 302 _ = k8sapiext.AddToScheme(scheme) 303 _ = v1alpha1.AddToScheme(scheme) 304 _ = appsv1.AddToScheme(scheme) 305 builder := crtFakeClient.NewClientBuilder().WithScheme(scheme) 306 if test.state == readyState { 307 builder = builder.WithObjects(initFakePodWithLabels(podLabels)).WithLists(initReadyDeployments(podLabels), initReadyStatefulSets(masterSTSLabel)) 308 } else if test.state == notReadyState { 309 builder = builder.WithLists(initNotReadyDeployments(podLabels), initNotReadyStatefulSets(masterSTSLabel)) 310 } else { 311 builder = builder.WithObjects(initFakePodWithLabels(podLabels)).WithLists(initReadyDeployments(podLabels), initReadyStatefulSets(masterSTSLabel)) 312 } 313 crtClient := builder.Build() 314 315 // Load the PsrClient with both fake clients 316 psrClient := fakePsrClient{ 317 psrClient: &k8sclient.PsrClient{ 318 CrtlRuntime: crtClient, 319 VzInstall: vzclient, 320 }, 321 } 322 origFc := funcNewPsrClient 323 defer func() { 324 funcNewPsrClient = origFc 325 }() 326 funcNewPsrClient = psrClient.NewPsrClient 327 328 // Create worker and call dowork 329 wi, err := NewRestartWorker() 330 assert.NoError(t, err) 331 w := wi.(worker) 332 err = config.PsrEnv.LoadFromEnv(w.GetEnvDescList()) 333 assert.NoError(t, err) 334 if test.state == podExistsState { 335 w.restartedPodUID = types.UID(podUID) 336 } 337 err = w.DoWork(config.CommonConfig{ 338 WorkerType: "restart", 339 }, vzlog.DefaultLogger()) 340 if test.state == readyState { 341 assert.NoError(t, err) 342 assert.True(t, w.restartStartTime > 0) 343 assert.Equal(t, w.restartedPodUID, types.UID(podUID)) 344 } else if test.state == notReadyState { 345 assert.Error(t, err) 346 assert.False(t, w.restartStartTime > 0) 347 assert.NotEqual(t, w.restartedPodUID, types.UID(podUID)) 348 } else { 349 assert.Error(t, err) 350 assert.False(t, w.restartStartTime > 0) 351 assert.Equal(t, w.restartedPodUID, types.UID(podUID)) 352 } 353 }) 354 } 355 } 356 357 // initFakePodWithLabels inits a fake Pod with specified image and labels 358 func initFakePodWithLabels(labels map[string]string) *corev1.Pod { 359 return &corev1.Pod{ 360 ObjectMeta: metav1.ObjectMeta{ 361 Name: "testPod", 362 Namespace: "verrazzano-system", 363 Labels: labels, 364 UID: "poduid", 365 }, 366 } 367 } 368 369 func initReadyDeployments(labels map[string]string) *appsv1.DeploymentList { 370 return &appsv1.DeploymentList{ 371 Items: []appsv1.Deployment{ 372 { 373 ObjectMeta: metav1.ObjectMeta{ 374 Name: "vmi-system-es-ingest", 375 Namespace: constants.VerrazzanoSystemNamespace, 376 Labels: labels, 377 }, 378 Status: appsv1.DeploymentStatus{ 379 Replicas: 3, 380 ReadyReplicas: 3, 381 }, 382 }, 383 { 384 ObjectMeta: metav1.ObjectMeta{ 385 Name: "vmi-system-es-data-0", 386 Namespace: constants.VerrazzanoSystemNamespace, 387 Labels: labels, 388 }, 389 Status: appsv1.DeploymentStatus{ 390 Replicas: 1, 391 ReadyReplicas: 1, 392 }, 393 }, 394 { 395 ObjectMeta: metav1.ObjectMeta{ 396 Name: "vmi-system-es-data-1", 397 Namespace: constants.VerrazzanoSystemNamespace, 398 Labels: labels, 399 }, 400 Status: appsv1.DeploymentStatus{ 401 Replicas: 1, 402 ReadyReplicas: 1, 403 }, 404 }, 405 }, 406 } 407 } 408 409 func initNotReadyDeployments(labels map[string]string) *appsv1.DeploymentList { 410 return &appsv1.DeploymentList{ 411 Items: []appsv1.Deployment{ 412 { 413 ObjectMeta: metav1.ObjectMeta{ 414 Name: "vmi-system-es-ingest", 415 Namespace: constants.VerrazzanoSystemNamespace, 416 Labels: labels, 417 }, 418 Status: appsv1.DeploymentStatus{ 419 Replicas: 2, 420 ReadyReplicas: 3, 421 }, 422 }, 423 { 424 ObjectMeta: metav1.ObjectMeta{ 425 Name: "vmi-system-es-data-0", 426 Namespace: constants.VerrazzanoSystemNamespace, 427 Labels: labels, 428 }, 429 Status: appsv1.DeploymentStatus{ 430 Replicas: 1, 431 ReadyReplicas: 1, 432 }, 433 }, 434 { 435 ObjectMeta: metav1.ObjectMeta{ 436 Name: "vmi-system-es-data-1", 437 Namespace: constants.VerrazzanoSystemNamespace, 438 Labels: labels, 439 }, 440 Status: appsv1.DeploymentStatus{ 441 Replicas: 0, 442 ReadyReplicas: 1, 443 }, 444 }, 445 }, 446 } 447 } 448 449 func initReadyStatefulSets(labels map[string]string) *appsv1.StatefulSetList { 450 return &appsv1.StatefulSetList{ 451 Items: []appsv1.StatefulSet{ 452 { 453 ObjectMeta: metav1.ObjectMeta{ 454 Name: "vmi-system-es-master", 455 Namespace: constants.VerrazzanoSystemNamespace, 456 Labels: labels, 457 }, 458 Status: appsv1.StatefulSetStatus{ 459 Replicas: 3, 460 ReadyReplicas: 3, 461 }, 462 }, 463 }, 464 } 465 } 466 467 func initNotReadyStatefulSets(labels map[string]string) *appsv1.StatefulSetList { 468 return &appsv1.StatefulSetList{ 469 Items: []appsv1.StatefulSet{ 470 { 471 ObjectMeta: metav1.ObjectMeta{ 472 Name: "vmi-system-es-master", 473 Namespace: constants.VerrazzanoSystemNamespace, 474 Labels: labels, 475 }, 476 Status: appsv1.StatefulSetStatus{ 477 Replicas: 2, 478 ReadyReplicas: 3, 479 }, 480 }, 481 }, 482 } 483 } 484 485 func (f *fakeEnv) GetEnv(key string) string { 486 return f.data[key] 487 } 488 489 func (f *fakePsrClient) NewPsrClient() (k8sclient.PsrClient, error) { 490 return *f.psrClient, nil 491 } 492 493 func getTierLabels(tier string) map[string]string { 494 switch tier { 495 case opensearchpsr.MasterTier: 496 return map[string]string{"opensearch.verrazzano.io/role-master": "true"} 497 case opensearchpsr.DataTier: 498 return map[string]string{"opensearch.verrazzano.io/role-data": "true"} 499 case opensearchpsr.IngestTier: 500 return map[string]string{"opensearch.verrazzano.io/role-ingest": "true"} 501 default: 502 return nil 503 } 504 } 505 506 func overridePsrClient() func() (k8sclient.PsrClient, error) { 507 f := fakePsrClient{ 508 psrClient: &k8sclient.PsrClient{}, 509 } 510 origFc := funcNewPsrClient 511 funcNewPsrClient = f.NewPsrClient 512 return origFc 513 }