github.com/verrazzano/verrazzano-monitoring-operator@v0.0.30/verrazzano-backup-hook/opensearch/opensearch_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 opensearch_test 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "github.com/stretchr/testify/assert" 10 "github.com/verrazzano/verrazzano-monitoring-operator/verrazzano-backup-hook/constants" 11 "github.com/verrazzano/verrazzano-monitoring-operator/verrazzano-backup-hook/log" 12 "github.com/verrazzano/verrazzano-monitoring-operator/verrazzano-backup-hook/opensearch" 13 "github.com/verrazzano/verrazzano-monitoring-operator/verrazzano-backup-hook/types" 14 "go.uber.org/zap" 15 "net/http" 16 "net/http/httptest" 17 "os" 18 "strings" 19 "testing" 20 ) 21 22 const ( 23 healthURL = "/_cluster/health" 24 secureSettingsURL = "/_nodes/reload_secure_settings" 25 dataStreamsURL = "/_data_stream" 26 snapshotURL = "/_snapshot" 27 timeOutGlobal = "10m" 28 ) 29 30 func logHelper() (*zap.SugaredLogger, string) { 31 file, err := os.CreateTemp(os.TempDir(), fmt.Sprintf("verrazzano-%s-hook-*.log", strings.ToLower("TEST"))) 32 if err != nil { 33 fmt.Printf("Unable to create temp file") 34 os.Exit(1) 35 } 36 defer file.Close() 37 log, _ := log.Logger(file.Name()) 38 return log, file.Name() 39 } 40 41 var ( 42 httpServer *httptest.Server 43 openSearch opensearch.Opensearch 44 ) 45 46 func mockEnsureOpenSearchIsReachable(error bool, w http.ResponseWriter, r *http.Request) { 47 fmt.Println("Reachable ...") 48 w.Header().Add("Content-Type", constants.HTTPContentType) 49 if error { 50 w.WriteHeader(http.StatusGatewayTimeout) 51 } else { 52 w.WriteHeader(http.StatusOK) 53 } 54 var osinfo types.OpenSearchClusterInfo 55 osinfo.ClusterName = "foo" 56 json.NewEncoder(w).Encode(osinfo) 57 } 58 59 func mockEnsureOpenSearchIsHealthy(error bool, w http.ResponseWriter, r *http.Request) { 60 fmt.Println("Healthy ...") 61 w.Header().Add("Content-Type", constants.HTTPContentType) 62 var oshealth types.OpenSearchHealthResponse 63 oshealth.ClusterName = "bar" 64 if error { 65 w.WriteHeader(http.StatusGatewayTimeout) 66 oshealth.Status = "red" 67 } else { 68 w.WriteHeader(http.StatusOK) 69 oshealth.Status = "green" 70 } 71 json.NewEncoder(w).Encode(oshealth) 72 } 73 74 func mockOpenSearchOperationResponse(error bool, w http.ResponseWriter, r *http.Request) { 75 fmt.Println("Snapshot register ...") 76 w.Header().Add("Content-Type", constants.HTTPContentType) 77 var registerResponse types.OpenSearchOperationResponse 78 if error { 79 w.WriteHeader(http.StatusGatewayTimeout) 80 registerResponse.Acknowledged = false 81 } else { 82 w.WriteHeader(http.StatusOK) 83 registerResponse.Acknowledged = true 84 } 85 86 json.NewEncoder(w).Encode(registerResponse) 87 } 88 89 func mockReloadOpensearchSecureSettings(error bool, w http.ResponseWriter, r *http.Request) { 90 fmt.Println("Reload secure settings") 91 w.Header().Add("Content-Type", constants.HTTPContentType) 92 var reloadsettings types.OpenSearchSecureSettingsReloadStatus 93 w.WriteHeader(http.StatusOK) 94 reloadsettings.ClusterNodes.Total = 3 95 if error { 96 reloadsettings.ClusterNodes.Failed = 1 97 reloadsettings.ClusterNodes.Successful = 3 98 } else { 99 reloadsettings.ClusterNodes.Failed = 0 100 reloadsettings.ClusterNodes.Successful = 3 101 } 102 json.NewEncoder(w).Encode(reloadsettings) 103 } 104 105 func mockTriggerSnapshotRepository(error bool, w http.ResponseWriter, r *http.Request) { 106 fmt.Println("Snapshot ...") 107 w.Header().Add("Content-Type", constants.HTTPContentType) 108 109 if error { 110 w.WriteHeader(http.StatusGatewayTimeout) 111 } else { 112 w.WriteHeader(http.StatusOK) 113 } 114 115 switch r.Method { 116 case http.MethodPost: 117 var triggerSnapshot types.OpenSearchSnapshotResponse 118 triggerSnapshot.Accepted = true 119 json.NewEncoder(w).Encode(triggerSnapshot) 120 121 case http.MethodGet: 122 var snapshotInfo types.OpenSearchSnapshotStatus 123 var snapshots []types.Snapshot 124 var snapshot types.Snapshot 125 snapshot.Snapshot = "foo" 126 snapshot.State = constants.OpenSearchSnapShotSuccess 127 snapshot.Indices = []string{"alpha", "beta", "gamma"} 128 snapshot.DataStreams = []string{"mono", "di", "tri"} 129 snapshots = append(snapshots, snapshot) 130 snapshotInfo.Snapshots = snapshots 131 json.NewEncoder(w).Encode(snapshotInfo) 132 } 133 134 } 135 136 func mockRestoreProgress(w http.ResponseWriter, r *http.Request) { 137 fmt.Println("Restore progress ...") 138 w.Header().Add("Content-Type", constants.HTTPContentType) 139 w.WriteHeader(http.StatusOK) 140 var dsInfo types.OpenSearchDataStreams 141 var arrayDs []types.DataStreams 142 var ds types.DataStreams 143 ds.Name = "foo" 144 ds.Status = constants.DataStreamGreen 145 arrayDs = append(arrayDs, ds) 146 ds.Name = "bar" 147 arrayDs = append(arrayDs, ds) 148 dsInfo.DataStreams = arrayDs 149 json.NewEncoder(w).Encode(dsInfo) 150 151 } 152 153 func TestMain(m *testing.M) { 154 log, f := logHelper() 155 defer os.Remove(f) 156 157 conData := types.ConnectionData{ 158 BackupName: "mango", 159 VeleroTimeout: "1s", 160 } 161 162 fmt.Println("Starting mock server") 163 httpServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 164 switch strings.TrimSpace(r.URL.Path) { 165 case "/": 166 mockEnsureOpenSearchIsReachable(false, w, r) 167 case healthURL: 168 mockEnsureOpenSearchIsHealthy(false, w, r) 169 case fmt.Sprintf("%s/%s", snapshotURL, constants.OpenSearchSnapShotRepoName), fmt.Sprintf("%s/*", dataStreamsURL), "/*": 170 mockOpenSearchOperationResponse(false, w, r) 171 case secureSettingsURL: 172 mockReloadOpensearchSecureSettings(false, w, r) 173 case fmt.Sprintf("/_snapshot/%s/%s", constants.OpenSearchSnapShotRepoName, "mango"), fmt.Sprintf("/_snapshot/%s/%s/_restore", constants.OpenSearchSnapShotRepoName, "mango"): 174 mockTriggerSnapshotRepository(false, w, r) 175 case dataStreamsURL: 176 mockRestoreProgress(w, r) 177 178 default: 179 http.NotFoundHandler().ServeHTTP(w, r) 180 } 181 })) 182 defer httpServer.Close() 183 184 fmt.Println("mock opensearch handler") 185 openSearch = opensearch.New(httpServer.URL, timeOutGlobal, http.DefaultClient, &conData, log) 186 187 fmt.Println("Start tests") 188 m.Run() 189 } 190 191 // Test_EnsureOpenSearchIsReachable tests the EnsureOpenSearchIsReachable method for the following use case. 192 // GIVEN OpenSearch object 193 // WHEN invoked with OpenSearch URL 194 // THEN verifies whether OpenSearch is reachable or not 195 func Test_EnsureOpenSearchIsReachable(t *testing.T) { 196 log, f := logHelper() 197 defer os.Remove(f) 198 199 server1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 200 switch strings.TrimSpace(r.URL.Path) { 201 case "/": 202 mockEnsureOpenSearchIsReachable(false, w, r) 203 default: 204 http.NotFoundHandler().ServeHTTP(w, r) 205 } 206 })) 207 defer server1.Close() 208 209 conData := types.ConnectionData{ 210 BackupName: "mango", 211 VeleroTimeout: "1s", 212 } 213 o := opensearch.New(server1.URL, timeOutGlobal, http.DefaultClient, &conData, log) 214 err := o.EnsureOpenSearchIsReachable() 215 assert.Nil(t, err) 216 217 server2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 218 switch strings.TrimSpace(r.URL.Path) { 219 case "/": 220 mockEnsureOpenSearchIsReachable(true, w, r) 221 default: 222 http.NotFoundHandler().ServeHTTP(w, r) 223 } 224 })) 225 defer server2.Close() 226 227 o = opensearch.New(server2.URL, timeOutGlobal, http.DefaultClient, &conData, log) 228 err = o.EnsureOpenSearchIsReachable() 229 assert.Nil(t, err) 230 231 } 232 233 // Test_EnsureOpenSearchIsHealthy tests the EnsureOpenSearchIsHealthy method for the following use case. 234 // GIVEN OpenSearch object 235 // WHEN invoked with snapshot name 236 // THEN checks if opensearch cluster is healthy 237 func Test_EnsureOpenSearchIsHealthy(t *testing.T) { 238 log, f := logHelper() 239 defer os.Remove(f) 240 241 server1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 242 switch strings.TrimSpace(r.URL.Path) { 243 case healthURL: 244 mockEnsureOpenSearchIsHealthy(false, w, r) 245 default: 246 http.NotFoundHandler().ServeHTTP(w, r) 247 } 248 })) 249 defer server1.Close() 250 251 conData := types.ConnectionData{ 252 BackupName: "mango", 253 VeleroTimeout: "1s", 254 } 255 o := opensearch.New(server1.URL, timeOutGlobal, http.DefaultClient, &conData, log) 256 err := o.EnsureOpenSearchIsHealthy() 257 assert.Nil(t, err) 258 259 server2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 260 switch strings.TrimSpace(r.URL.Path) { 261 case healthURL: 262 mockEnsureOpenSearchIsHealthy(true, w, r) 263 default: 264 http.NotFoundHandler().ServeHTTP(w, r) 265 } 266 })) 267 defer server2.Close() 268 269 o = opensearch.New(server2.URL, timeOutGlobal, http.DefaultClient, &conData, log) 270 err = o.EnsureOpenSearchIsHealthy() 271 assert.NotNil(t, err) 272 } 273 274 // Test_EnsureOpenSearchIsHealthy tests the EnsureOpenSearchIsHealthy method for the following use case. 275 // GIVEN OpenSearch object 276 // WHEN invoked with snapshot name 277 // THEN checks if opensearch cluster is healthy 278 func Test_RegisterSnapshotRepository(t *testing.T) { 279 log, f := logHelper() 280 defer os.Remove(f) 281 282 server1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 283 switch strings.TrimSpace(r.URL.Path) { 284 case fmt.Sprintf("%s/%s", snapshotURL, constants.OpenSearchSnapShotRepoName): 285 mockOpenSearchOperationResponse(false, w, r) 286 default: 287 http.NotFoundHandler().ServeHTTP(w, r) 288 } 289 })) 290 defer server1.Close() 291 292 var objsecret types.ObjectStoreSecret 293 objsecret.SecretName = "alpha" 294 objsecret.SecretKey = "cloud" 295 objsecret.ObjectAccessKey = "alphalapha" 296 objsecret.ObjectSecretKey = "betabetabeta" 297 298 conData := types.ConnectionData{ 299 BackupName: "mango", 300 VeleroTimeout: "1s", 301 RegionName: "region", 302 Endpoint: constants.OpenSearchURL, 303 Secret: objsecret, 304 } 305 306 o := opensearch.New(server1.URL, timeOutGlobal, http.DefaultClient, &conData, log) 307 err := o.RegisterSnapshotRepository() 308 assert.Nil(t, err) 309 310 server2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 311 switch strings.TrimSpace(r.URL.Path) { 312 case fmt.Sprintf("%s/%s", snapshotURL, constants.OpenSearchSnapShotRepoName): 313 mockOpenSearchOperationResponse(true, w, r) 314 default: 315 http.NotFoundHandler().ServeHTTP(w, r) 316 } 317 })) 318 defer server2.Close() 319 320 o = opensearch.New(server2.URL, timeOutGlobal, http.DefaultClient, &conData, log) 321 err = o.RegisterSnapshotRepository() 322 assert.NotNil(t, err) 323 324 } 325 326 // Test_ReloadOpensearchSecureSettings tests the ReloadOpensearchSecureSettings method for the following use case. 327 // GIVEN OpenSearch object 328 // WHEN invoked with snapshot name 329 // THEN updates opensearch keystore creds 330 func Test_ReloadOpensearchSecureSettings(t *testing.T) { 331 log, f := logHelper() 332 defer os.Remove(f) 333 server1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 334 switch strings.TrimSpace(r.URL.Path) { 335 case secureSettingsURL: 336 mockReloadOpensearchSecureSettings(false, w, r) 337 default: 338 http.NotFoundHandler().ServeHTTP(w, r) 339 } 340 })) 341 defer server1.Close() 342 343 conData := types.ConnectionData{ 344 BackupName: "mango", 345 VeleroTimeout: "1s", 346 RegionName: "region", 347 } 348 o := opensearch.New(server1.URL, timeOutGlobal, http.DefaultClient, &conData, log) 349 err := o.ReloadOpensearchSecureSettings() 350 assert.Nil(t, err) 351 352 server2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 353 switch strings.TrimSpace(r.URL.Path) { 354 case secureSettingsURL: 355 mockReloadOpensearchSecureSettings(true, w, r) 356 default: 357 http.NotFoundHandler().ServeHTTP(w, r) 358 } 359 })) 360 defer server2.Close() 361 o = opensearch.New(server2.URL, timeOutGlobal, http.DefaultClient, &conData, log) 362 err = o.ReloadOpensearchSecureSettings() 363 assert.NotNil(t, err) 364 } 365 366 // TestTriggerSnapshot tests the TriggerSnapshot method for the following use case. 367 // GIVEN OpenSearch object 368 // WHEN invoked with snapshot name 369 // THEN creates a snapshot in object store 370 func Test_TriggerSnapshot(t *testing.T) { 371 log, f := logHelper() 372 defer os.Remove(f) 373 374 server1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 375 switch strings.TrimSpace(r.URL.Path) { 376 case fmt.Sprintf("%s/%s/%s", snapshotURL, constants.OpenSearchSnapShotRepoName, "mango"): 377 mockTriggerSnapshotRepository(false, w, r) 378 default: 379 http.NotFoundHandler().ServeHTTP(w, r) 380 } 381 })) 382 defer server1.Close() 383 384 conData := types.ConnectionData{ 385 BackupName: "mango", 386 VeleroTimeout: "1s", 387 RegionName: "region", 388 } 389 o := opensearch.New(server1.URL, timeOutGlobal, http.DefaultClient, &conData, log) 390 err := o.TriggerSnapshot() 391 assert.Nil(t, err) 392 393 server2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 394 switch strings.TrimSpace(r.URL.Path) { 395 case fmt.Sprintf("%s/%s/%s", snapshotURL, constants.OpenSearchSnapShotRepoName, "mango"): 396 mockTriggerSnapshotRepository(true, w, r) 397 default: 398 http.NotFoundHandler().ServeHTTP(w, r) 399 } 400 })) 401 defer server2.Close() 402 o = opensearch.New(server2.URL, timeOutGlobal, http.DefaultClient, &conData, log) 403 err = o.TriggerSnapshot() 404 assert.NotNil(t, err) 405 406 } 407 408 // TestCheckSnapshotProgress tests the CheckSnapshotProgress method for the following use case. 409 // GIVEN OpenSearch object 410 // WHEN invoked with snapshot name 411 // THEN tracks snapshot progress towards completion 412 func TestCheckSnapshotProgress(t *testing.T) { 413 log, f := logHelper() 414 defer os.Remove(f) 415 416 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 417 switch strings.TrimSpace(r.URL.Path) { 418 case fmt.Sprintf("%s/%s/%s", snapshotURL, constants.OpenSearchSnapShotRepoName, "mango"): 419 mockTriggerSnapshotRepository(false, w, r) 420 default: 421 http.NotFoundHandler().ServeHTTP(w, r) 422 } 423 })) 424 defer server.Close() 425 426 conData := types.ConnectionData{ 427 BackupName: "mango", 428 VeleroTimeout: "1s", 429 RegionName: "region", 430 } 431 o := opensearch.New(server.URL, timeOutGlobal, http.DefaultClient, &conData, log) 432 err := o.CheckSnapshotProgress() 433 assert.Nil(t, err) 434 } 435 436 // Test_DeleteDataStreams tests the DeleteData method for the following use case. 437 // GIVEN OpenSearch object 438 // WHEN invoked with logger 439 // THEN deletes data from Opensearch cluster 440 func Test_DeleteDataStreams(t *testing.T) { 441 log, f := logHelper() 442 defer os.Remove(f) 443 444 server1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 445 switch strings.TrimSpace(r.URL.Path) { 446 case fmt.Sprintf("%s/*", dataStreamsURL), "/*": 447 mockOpenSearchOperationResponse(false, w, r) 448 default: 449 http.NotFoundHandler().ServeHTTP(w, r) 450 } 451 })) 452 defer server1.Close() 453 454 conData := types.ConnectionData{ 455 BackupName: "mango", 456 VeleroTimeout: "1s", 457 RegionName: "region", 458 } 459 o := opensearch.New(server1.URL, timeOutGlobal, http.DefaultClient, &conData, log) 460 err := o.DeleteData() 461 assert.Nil(t, err) 462 463 server2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 464 switch strings.TrimSpace(r.URL.Path) { 465 case fmt.Sprintf("%s/*", dataStreamsURL), "/*": 466 mockOpenSearchOperationResponse(true, w, r) 467 default: 468 http.NotFoundHandler().ServeHTTP(w, r) 469 } 470 })) 471 defer server2.Close() 472 473 o = opensearch.New(server2.URL, timeOutGlobal, http.DefaultClient, &conData, log) 474 err = o.DeleteData() 475 assert.NotNil(t, err) 476 } 477 478 // Test_TriggerSnapshot tests the TriggerRestore method for the following use case. 479 // GIVEN OpenSearch object 480 // WHEN invoked with snapshot name 481 // THEN creates a restore from object store from given snapshot name 482 func Test_TriggerRestore(t *testing.T) { 483 log, f := logHelper() 484 defer os.Remove(f) 485 486 server1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 487 switch strings.TrimSpace(r.URL.Path) { 488 case fmt.Sprintf("%s/%s/%s/_restore", snapshotURL, constants.OpenSearchSnapShotRepoName, "mango"): 489 mockTriggerSnapshotRepository(false, w, r) 490 default: 491 http.NotFoundHandler().ServeHTTP(w, r) 492 } 493 })) 494 defer server1.Close() 495 496 conData := types.ConnectionData{ 497 BackupName: "mango", 498 VeleroTimeout: "1s", 499 RegionName: "region", 500 } 501 o := opensearch.New(server1.URL, timeOutGlobal, http.DefaultClient, &conData, log) 502 err := o.TriggerRestore() 503 assert.Nil(t, err) 504 505 server2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 506 switch strings.TrimSpace(r.URL.Path) { 507 case fmt.Sprintf("%s/%s/%s/_restore", snapshotURL, constants.OpenSearchSnapShotRepoName, "mango"): 508 mockTriggerSnapshotRepository(true, w, r) 509 default: 510 http.NotFoundHandler().ServeHTTP(w, r) 511 } 512 })) 513 defer server2.Close() 514 o = opensearch.New(server2.URL, timeOutGlobal, http.DefaultClient, &conData, log) 515 err = o.TriggerRestore() 516 assert.NotNil(t, err) 517 } 518 519 // Test_CheckRestoreProgress tests the CheckRestoreProgress method for the following use case. 520 // GIVEN OpenSearch object 521 // WHEN invoked with snapshot name 522 // THEN tracks snapshot restore towards completion 523 func Test_CheckRestoreProgress(t *testing.T) { 524 log, f := logHelper() 525 defer os.Remove(f) 526 527 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 528 switch strings.TrimSpace(r.URL.Path) { 529 case dataStreamsURL: 530 mockRestoreProgress(w, r) 531 default: 532 http.NotFoundHandler().ServeHTTP(w, r) 533 } 534 })) 535 defer server.Close() 536 537 conData := types.ConnectionData{ 538 BackupName: "mango", 539 VeleroTimeout: "1s", 540 RegionName: "region", 541 } 542 o := opensearch.New(server.URL, timeOutGlobal, http.DefaultClient, &conData, log) 543 err := o.CheckRestoreProgress() 544 assert.Nil(t, err) 545 } 546 547 // Test_Backup tests the Backup method for the following use case. 548 // GIVEN OpenSearch object 549 // WHEN invoked with snapshot name 550 // THEN takes the opensearch backup 551 func Test_Backup(t *testing.T) { 552 err := openSearch.Backup() 553 assert.Nil(t, err) 554 } 555 556 // Test_Restore tests the Restore method for the following use case. 557 // GIVEN OpenSearch object 558 // WHEN invoked with snapshot name 559 // THEN restores the opensearch from a given backup 560 func Test_Restore(t *testing.T) { 561 err := openSearch.Restore() 562 assert.Nil(t, err) 563 }