From 80aa03ea654fc3abb5264ad58c3aefc29c674c55 Mon Sep 17 00:00:00 2001 From: amrita Date: Thu, 12 Dec 2024 15:57:43 +0545 Subject: [PATCH 1/5] initial commit --- tests/ociswrapper/wrapper/handlers/handler.go | 33 +++++++++++++++++++ tests/ociswrapper/wrapper/wrapper.go | 1 + 2 files changed, 34 insertions(+) diff --git a/tests/ociswrapper/wrapper/handlers/handler.go b/tests/ociswrapper/wrapper/handlers/handler.go index c2bd1cde55a..3fdee8398d8 100644 --- a/tests/ociswrapper/wrapper/handlers/handler.go +++ b/tests/ociswrapper/wrapper/handlers/handler.go @@ -202,3 +202,36 @@ func CommandHandler(res http.ResponseWriter, req *http.Request) { exitCode, output := ocis.RunCommand(command, stdIn) sendCmdResponse(res, exitCode, output) } + +func OcisServiceHandler(res http.ResponseWriter, req *http.Request) { + + if req.Method != http.MethodPost || req.Method != http.MethodDelete { + sendResponse(res, http.StatusMethodNotAllowed, "") + return + } + + serviceName := strings.TrimPrefix(req.URL.Path, "/services/") + + if serviceName == "" { + sendResponse(res, http.StatusUnprocessableEntity, "Service name is required") + return + } + + // add script to check whether ocis runs without that service or not + if !ocis.IsOcisRunning() { + sendResponse(res, http.StatusPreconditionFailed, "oCIS server is not running") + return + } + + common.Wg.Add(1) + // start ocis service with env to set env false + go ocis.Start(nil) + + success, message := ocis.WaitForConnection() + if success { + sendResponse(res, http.StatusOK, message) + return + } + + sendResponse(res, http.StatusInternalServerError, message) +} diff --git a/tests/ociswrapper/wrapper/wrapper.go b/tests/ociswrapper/wrapper/wrapper.go index 35b7873134a..2c28077cd35 100644 --- a/tests/ociswrapper/wrapper/wrapper.go +++ b/tests/ociswrapper/wrapper/wrapper.go @@ -27,6 +27,7 @@ func Start(port string) { mux.HandleFunc("/command", handlers.CommandHandler) mux.HandleFunc("/stop", handlers.StopOcisHandler) mux.HandleFunc("/start", handlers.StartOcisHandler) + mux.HandleFunc("/services/", handlers.OcisServiceHandler) httpServer.Handler = mux From 4f8cf2604481266681515f5f85ab336b8514cac3 Mon Sep 17 00:00:00 2001 From: amrita Date: Mon, 16 Dec 2024 17:21:24 +0545 Subject: [PATCH 2/5] stops ocis and runs ocis with ocis_Exclude_service env --- tests/ociswrapper/ocis/ocis.go | 87 +++++++++++++++++++ tests/ociswrapper/wrapper/handlers/handler.go | 40 ++++----- tests/ociswrapper/wrapper/wrapper.go | 2 +- 3 files changed, 106 insertions(+), 23 deletions(-) diff --git a/tests/ociswrapper/ocis/ocis.go b/tests/ociswrapper/ocis/ocis.go index aa4a69dd997..44e85ebd3bd 100644 --- a/tests/ociswrapper/ocis/ocis.go +++ b/tests/ociswrapper/ocis/ocis.go @@ -271,3 +271,90 @@ func RunCommand(command string, inputs []string) (int, string) { return c.ProcessState.ExitCode(), cmdOutput } + +func runOcisService(service string) { + // wait for the log scanner to finish + var wg sync.WaitGroup + wg.Add(2) + + stopSignal = false + if retryCount == 0 { + defer common.Wg.Done() + } + + cmd = exec.Command(config.Get("bin"), service , "server") +// output, err := cmd.CombinedOutput() +// if err != nil { +// fmt.Printf("Error running service %s: %v\nOutput: %s\n", service, err, output) +// return +// } +// fmt.Printf("Service %s started successfully: %s\n", service, output) + + logs, err := cmd.StderrPipe() + if err != nil { + log.Panic(err) + } + output, err := cmd.StdoutPipe() + if err != nil { + log.Panic(err) + } + + err = cmd.Start() + if err != nil { + log.Panic(err) + } + + logScanner := bufio.NewScanner(logs) + outputScanner := bufio.NewScanner(output) + outChan := make(chan string) + + // Read the logs when the 'ocis server' command is running + go func() { + defer wg.Done() + for logScanner.Scan() { + outChan <- logScanner.Text() + } + }() + + go func() { + defer wg.Done() + for outputScanner.Scan() { + outChan <- outputScanner.Text() + } + }() + + // Fetch logs from the channel and print them + go func() { + for s := range outChan { + fmt.Println(s) + } + }() + + if err := cmd.Wait(); err != nil { + if exitErr, ok := err.(*exec.ExitError); ok { + status := exitErr.Sys().(syscall.WaitStatus) + // retry only if oCIS server exited with code > 0 + // -1 exit code means that the process was killed by a signal (syscall.SIGINT) + if status.ExitStatus() > 0 && !stopSignal { + waitUntilCompleteShutdown() + + log.Println(fmt.Sprintf("oCIS service server exited with code %v", status.ExitStatus())) + + // retry to start oCIS server + retryCount++ + maxRetry, _ := strconv.Atoi(config.Get("retry")) + if retryCount <= maxRetry { + wg.Wait() + close(outChan) + log.Println(fmt.Sprintf("Retry starting oCIS server... (retry %v)", retryCount)) + // wait 500 milliseconds before retrying + time.Sleep(500 * time.Millisecond) + runOcisService(service) + return + } + } + } + } + wg.Wait() + close(outChan) +} diff --git a/tests/ociswrapper/wrapper/handlers/handler.go b/tests/ociswrapper/wrapper/handlers/handler.go index 3fdee8398d8..3d53db839c4 100644 --- a/tests/ociswrapper/wrapper/handlers/handler.go +++ b/tests/ociswrapper/wrapper/handlers/handler.go @@ -204,34 +204,30 @@ func CommandHandler(res http.ResponseWriter, req *http.Request) { } func OcisServiceHandler(res http.ResponseWriter, req *http.Request) { - - if req.Method != http.MethodPost || req.Method != http.MethodDelete { - sendResponse(res, http.StatusMethodNotAllowed, "") - return - } - - serviceName := strings.TrimPrefix(req.URL.Path, "/services/") - - if serviceName == "" { - sendResponse(res, http.StatusUnprocessableEntity, "Service name is required") + if req.Method != http.MethodPost && req.Method != http.MethodDelete { + sendResponse(res, http.StatusMethodNotAllowed, "Method not allowed") return } - // add script to check whether ocis runs without that service or not - if !ocis.IsOcisRunning() { - sendResponse(res, http.StatusPreconditionFailed, "oCIS server is not running") + envBody, err := parseJsonBody(req.Body) + if err != nil { + sendResponse(res, http.StatusMethodNotAllowed, "Invalid json body") return - } + } - common.Wg.Add(1) - // start ocis service with env to set env false - go ocis.Start(nil) + var envMap []string + for key, value := range envBody { + envMap = append(envMap, fmt.Sprintf("%s=%v", key, value)) + } + ocis.EnvConfigs = append(ocis.EnvConfigs, envMap...) - success, message := ocis.WaitForConnection() - if success { - sendResponse(res, http.StatusOK, message) - return + if req.Method == http.MethodPost { + success, _ := ocis.Restart(ocis.EnvConfigs) + if success { + sendResponse(res, http.StatusOK, "oCIS configured successfully") + return + } + sendResponse(res, http.StatusInternalServerError, "Failed to restart oCIS with new configuration") } - sendResponse(res, http.StatusInternalServerError, message) } diff --git a/tests/ociswrapper/wrapper/wrapper.go b/tests/ociswrapper/wrapper/wrapper.go index 2c28077cd35..fda2604d3c0 100644 --- a/tests/ociswrapper/wrapper/wrapper.go +++ b/tests/ociswrapper/wrapper/wrapper.go @@ -27,7 +27,7 @@ func Start(port string) { mux.HandleFunc("/command", handlers.CommandHandler) mux.HandleFunc("/stop", handlers.StopOcisHandler) mux.HandleFunc("/start", handlers.StartOcisHandler) - mux.HandleFunc("/services/", handlers.OcisServiceHandler) + mux.HandleFunc("/services", handlers.OcisServiceHandler) httpServer.Handler = mux From 670155790535c52fde01f97058488700c9bac71e Mon Sep 17 00:00:00 2001 From: amrita Date: Tue, 17 Dec 2024 17:17:51 +0545 Subject: [PATCH 3/5] rerun remaining --- .../TestHelpers/OcisConfigHelper.php | 18 ++ tests/acceptance/bootstrap/FeatureContext.php | 2 +- .../bootstrap/OcisConfigContext.php | 18 ++ .../serviceAvailabilityCheck.feature | 12 +- .../cliCommands/backupConsistency.feature | 4 +- tests/ociswrapper/ocis/ocis.go | 222 +++++++++--------- tests/ociswrapper/wrapper/handlers/handler.go | 62 ++++- tests/ociswrapper/wrapper/wrapper.go | 2 +- 8 files changed, 210 insertions(+), 130 deletions(-) diff --git a/tests/acceptance/TestHelpers/OcisConfigHelper.php b/tests/acceptance/TestHelpers/OcisConfigHelper.php index 5d3ac479af3..df839bc45b7 100644 --- a/tests/acceptance/TestHelpers/OcisConfigHelper.php +++ b/tests/acceptance/TestHelpers/OcisConfigHelper.php @@ -120,4 +120,22 @@ public static function startOcis(): ResponseInterface { $url = self::getWrapperUrl() . "/start"; return self::sendRequest($url, "POST"); } + + /** + * this method stops the running oCIS instance, + * restarts it while excluding specific services, + * and then starts the excluded services separately. + * + * @param string $service + * + * @return ResponseInterface + * @throws GuzzleException + */ + public static function startService(string $service): ResponseInterface { + $envs = [ + "OCIS_LOG_LEVEL" => "info", + ]; + $url = self::getWrapperUrl() . "/services/" . $service; + return self::sendRequest($url, "POST", \json_encode($envs)); + } } diff --git a/tests/acceptance/bootstrap/FeatureContext.php b/tests/acceptance/bootstrap/FeatureContext.php index 505080f5f36..3f858872e97 100644 --- a/tests/acceptance/bootstrap/FeatureContext.php +++ b/tests/acceptance/bootstrap/FeatureContext.php @@ -613,7 +613,7 @@ public static function logStep(BeforeStepScope $scope): void { * NOTE: This method is called after each scenario having the @env-config tag * This ensures that the server is running for clean-up purposes * - * @AfterScenario @env-config + * @AfterScenario @backup-consistency * * @return void */ diff --git a/tests/acceptance/bootstrap/OcisConfigContext.php b/tests/acceptance/bootstrap/OcisConfigContext.php index 85f20c871e6..187528864c9 100644 --- a/tests/acceptance/bootstrap/OcisConfigContext.php +++ b/tests/acceptance/bootstrap/OcisConfigContext.php @@ -191,6 +191,24 @@ public function theConfigHasBeenSetToValue(TableNode $table): void { ); } + /** + * @Given the ocis server has served service :service separately + * + * @param string $service + * + * @return void + * @throws GuzzleException + */ + public function theOcisServerHasExcludedService(string $service) { + $response = OcisConfigHelper::startService($service); + + Assert::assertEquals( + 200, + $response->getStatusCode(), + "Failed to set config" + ); + } + /** * @AfterScenario @env-config * diff --git a/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature b/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature index 6fffb3e8104..ff36e69576e 100644 --- a/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature +++ b/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature @@ -113,7 +113,7 @@ Feature: service health check Then the HTTP status code of responses on all endpoints should be "200" @issue-10661 - Scenario: check default services readiness + Scenario: check default services readiness (graph, idp, proxy) When a user requests these URLs with "GET" and no authentication | endpoint | service | | http://%base_url_hostname%:9124/readyz | graph | @@ -122,7 +122,7 @@ Feature: service health check Then the HTTP status code of responses on all endpoints should be "200" @env-config @issue-10661 - Scenario: check extra services readiness + Scenario: check auth-bearer service readiness Given the following configs have been set: | config | value | | OCIS_ADD_RUN_SERVICES | auth-bearer | @@ -131,3 +131,11 @@ Feature: service health check | endpoint | service | | http://%base_url_hostname%:9149/readyz | auth-bearer | Then the HTTP status code of responses on all endpoints should be "200" + + @env-config + Scenario: check services health while running separately + Given the ocis server has served service "storage-users" separately + When a user requests these URLs with "GET" and no authentication + | endpoint | service | + | http://%base_url_hostname%:9159/healthz | storage-users | + Then the HTTP status code of responses on all endpoints should be "200" diff --git a/tests/acceptance/features/cliCommands/backupConsistency.feature b/tests/acceptance/features/cliCommands/backupConsistency.feature index e061b4fb8be..b21aa996041 100644 --- a/tests/acceptance/features/cliCommands/backupConsistency.feature +++ b/tests/acceptance/features/cliCommands/backupConsistency.feature @@ -1,4 +1,4 @@ -@env-config +@env-config @backup-consistency Feature: backup consistency As a user I want to check my data for inconsistencies @@ -53,4 +53,4 @@ Feature: backup consistency And the administrator has started the server When user "Alice" gets the number of versions of file "/textfile0.txt" Then the HTTP status code should be "207" - And the number of versions should be "2" \ No newline at end of file + And the number of versions should be "2" diff --git a/tests/ociswrapper/ocis/ocis.go b/tests/ociswrapper/ocis/ocis.go index 44e85ebd3bd..9be3b03b41f 100644 --- a/tests/ociswrapper/ocis/ocis.go +++ b/tests/ociswrapper/ocis/ocis.go @@ -7,6 +7,9 @@ import ( "fmt" "io" "net/http" + "ociswrapper/common" + "ociswrapper/log" + "ociswrapper/ocis/config" "os" "os/exec" "strconv" @@ -15,10 +18,6 @@ import ( "syscall" "time" - "ociswrapper/common" - "ociswrapper/log" - "ociswrapper/ocis/config" - "github.com/creack/pty" ) @@ -26,118 +25,41 @@ var cmd *exec.Cmd var retryCount = 0 var stopSignal = false var EnvConfigs = []string{} +var runningCommands = make(map[string]int) // Maps unique IDs to PIDs func Start(envMap []string) { - // wait for the log scanner to finish - var wg sync.WaitGroup - wg.Add(2) - - stopSignal = false - if retryCount == 0 { - defer common.Wg.Done() - } - - cmd = exec.Command(config.Get("bin"), "server") - if envMap == nil { - cmd.Env = append(os.Environ(), EnvConfigs...) - } else { - cmd.Env = append(os.Environ(), envMap...) - } - - logs, err := cmd.StderrPipe() - if err != nil { - log.Panic(err) - } - output, err := cmd.StdoutPipe() - if err != nil { - log.Panic(err) - } - - err = cmd.Start() - if err != nil { - log.Panic(err) - } - - logScanner := bufio.NewScanner(logs) - outputScanner := bufio.NewScanner(output) - outChan := make(chan string) - - // Read the logs when the 'ocis server' command is running - go func() { - defer wg.Done() - for logScanner.Scan() { - outChan <- logScanner.Text() - } - }() - - go func() { - defer wg.Done() - for outputScanner.Scan() { - outChan <- outputScanner.Text() - } - }() - - // Fetch logs from the channel and print them - go func() { - for s := range outChan { - fmt.Println(s) - } - }() - - if err := cmd.Wait(); err != nil { - if exitErr, ok := err.(*exec.ExitError); ok { - status := exitErr.Sys().(syscall.WaitStatus) - // retry only if oCIS server exited with code > 0 - // -1 exit code means that the process was killed by a signal (syscall.SIGINT) - if status.ExitStatus() > 0 && !stopSignal { - waitUntilCompleteShutdown() - - log.Println(fmt.Sprintf("oCIS server exited with code %v", status.ExitStatus())) - - // retry to start oCIS server - retryCount++ - maxRetry, _ := strconv.Atoi(config.Get("retry")) - if retryCount <= maxRetry { - wg.Wait() - close(outChan) - log.Println(fmt.Sprintf("Retry starting oCIS server... (retry %v)", retryCount)) - // wait 500 milliseconds before retrying - time.Sleep(500 * time.Millisecond) - Start(envMap) - return - } - } - } - } - wg.Wait() - close(outChan) + StartService("", envMap) } func Stop() (bool, string) { + log.Println(fmt.Sprintf("Stop ocis check cmd %s\n", cmd)) log.Println("Stopping oCIS server...") stopSignal = true - if cmd == nil { - return true, "oCIS server is not running" + for listservice, pid := range runningCommands { + log.Println(fmt.Sprintf("Services running before terminating: %s with process and id: %v\n", listservice, pid)) + process, err := os.FindProcess(pid) + err = process.Signal(syscall.SIGINT) + if err != nil { + if !strings.HasSuffix(err.Error(), "process already finished") { + log.Fatalln(err) + } else { + return true, "oCIS server is already stopped" + } + } + process.Wait() } - err := cmd.Process.Signal(syscall.SIGINT) - if err != nil { - if !strings.HasSuffix(err.Error(), "process already finished") { - log.Fatalln(err) - } else { - return true, "oCIS server is already stopped" - } - } - cmd.Process.Wait() - success, message := waitUntilCompleteShutdown() - cmd = nil + success, message := waitUntilCompleteShutdown() return success, message } func Restart(envMap []string) (bool, string) { - Stop() + log.Println(fmt.Sprintf("Restarting ocis check cmd %s\n", cmd)) + log.Println(fmt.Sprintf("Restaring ocis with rollback os environ %s\n", envMap)) + log.Println(fmt.Sprintf("OS environment: %s\n", os.Environ())) + go Stop() log.Println("Restarting oCIS server...") common.Wg.Add(1) @@ -214,6 +136,7 @@ func WaitForConnection() (bool, string) { } func waitUntilCompleteShutdown() (bool, string) { + log.Println("Process found. Waiting... waitUntilCompleteShutdown") timeout := 30 * time.Second startTime := time.Now() @@ -272,7 +195,22 @@ func RunCommand(command string, inputs []string) (int, string) { return c.ProcessState.ExitCode(), cmdOutput } -func runOcisService(service string) { +func RunOcisService(service string, envMap []string) { + log.Println(fmt.Sprintf("Environment variable envMap: %s\n", envMap)) + StartService(service, envMap) +} + +// startService is a common function for starting a service (ocis or other) +func StartService(service string, envMap []string) { + log.Println(fmt.Sprintf("Start service: %s with Environment variable envMap: %s\n", service, envMap)) + // Initialize command args based on service presence + cmdArgs := []string{"server"} // Default command args + + if service != "" { + // Directly append service if provided + cmdArgs = append([]string{service}, cmdArgs...) + } + // wait for the log scanner to finish var wg sync.WaitGroup wg.Add(2) @@ -282,13 +220,19 @@ func runOcisService(service string) { defer common.Wg.Done() } - cmd = exec.Command(config.Get("bin"), service , "server") -// output, err := cmd.CombinedOutput() -// if err != nil { -// fmt.Printf("Error running service %s: %v\nOutput: %s\n", service, err, output) -// return -// } -// fmt.Printf("Service %s started successfully: %s\n", service, output) + // Initialize the command + cmd = exec.Command(config.Get("bin"), cmdArgs...) + + // Use the provided envMap if not empty, otherwise use EnvConfigs + if len(envMap) == 0 { + cmd.Env = append(os.Environ(), EnvConfigs...) + log.Println(fmt.Sprintf("OS environment variables while running ocis service: %s\n", cmd.Env)) + log.Println(fmt.Sprintf("OS environment: %s\n", os.Environ())) + } else { + cmd.Env = append(os.Environ(), envMap...) + log.Println(fmt.Sprintf("OS environment variables while running ocis service: %s\n", cmd.Env)) + log.Println(fmt.Sprintf("OS environment: %s\n", os.Environ())) + } logs, err := cmd.StderrPipe() if err != nil { @@ -299,7 +243,11 @@ func runOcisService(service string) { log.Panic(err) } +// log.Println(fmt.Sprintf("command env used to start service %s\n", cmd.Env)) +// log.Println(fmt.Sprintf("command used to start service %s\n", cmd)) + err = cmd.Start() + if err != nil { log.Panic(err) } @@ -308,6 +256,19 @@ func runOcisService(service string) { outputScanner := bufio.NewScanner(output) outChan := make(chan string) + // If service is an empty string, set the PID for "ocis" + if service == "" { + runningCommands["ocis"] = cmd.Process.Pid + } else { + runningCommands[service] = cmd.Process.Pid + } + + log.Println("Started oCIS processes:") + for listservice, pid := range runningCommands { + log.Println(fmt.Sprintf("Service started: %s with process and id: %v\n", listservice, pid)) + + } + // Read the logs when the 'ocis server' command is running go func() { defer wg.Done() @@ -338,7 +299,7 @@ func runOcisService(service string) { if status.ExitStatus() > 0 && !stopSignal { waitUntilCompleteShutdown() - log.Println(fmt.Sprintf("oCIS service server exited with code %v", status.ExitStatus())) + log.Println(fmt.Sprintf("oCIS server exited with code %v", status.ExitStatus())) // retry to start oCIS server retryCount++ @@ -349,12 +310,51 @@ func runOcisService(service string) { log.Println(fmt.Sprintf("Retry starting oCIS server... (retry %v)", retryCount)) // wait 500 milliseconds before retrying time.Sleep(500 * time.Millisecond) - runOcisService(service) + StartService(service, envMap) return } } } } + + log.Println(fmt.Sprintf(" ---- ocis start service ending line---- %s\n", cmd)) wg.Wait() close(outChan) } + +// Stop oCIS service or a specific service by its unique identifier +func StopService(service string) (bool, string) { + for listservice, pid := range runningCommands { + log.Println(fmt.Sprintf("Services running before terminating: %s with process and id: %v\n", listservice, pid)) + } + + pid, exists := runningCommands[service] + log.Println(fmt.Sprintf("Services process id to terminate: %s\n", pid)) + if !exists { + return false, fmt.Sprintf("Service %s is not running", service) + } + + // Find the process by PID and send SIGINT to stop it + process, err := os.FindProcess(pid) + log.Println(fmt.Sprintf("Found process to terminate in os: %s\n", pid)) + + if err != nil { + log.Println(fmt.Sprintf("Failed to find process: %v", err)) + return false, fmt.Sprintf("Failed to find process with ID %d", pid) + } + + err = process.Signal(syscall.SIGINT) + log.Println("Process terminated using signal") + if err != nil { + log.Println(fmt.Sprintf("Failed to send signal: %v", err)) + return false, fmt.Sprintf("Failed to stop service with PID %d", pid) + } + time.Sleep(30 * time.Second) + process.Wait() + log.Println("Process terminating process.wait") + delete(runningCommands, service) + for listservice, pid := range runningCommands { + log.Println(fmt.Sprintf("Service list after deleteing %s service. list contain service: %s with process and id: %v\n", service, listservice, pid)) + } + return true, fmt.Sprintf("Service %s stopped successfully", service) +} diff --git a/tests/ociswrapper/wrapper/handlers/handler.go b/tests/ociswrapper/wrapper/handlers/handler.go index 3d53db839c4..34298dfaeb4 100644 --- a/tests/ociswrapper/wrapper/handlers/handler.go +++ b/tests/ociswrapper/wrapper/handlers/handler.go @@ -5,10 +5,12 @@ import ( "errors" "fmt" "io" + "log" "net/http" "ociswrapper/common" "ociswrapper/ocis" "os" + "strings" ) type BasicResponse struct { @@ -113,7 +115,8 @@ func RollbackHandler(res http.ResponseWriter, req *http.Request) { var message string ocis.EnvConfigs = []string{} - success, _ := ocis.Restart(os.Environ()) + log.Printf(fmt.Sprintf("os Environ when rollback %s", os.Environ)) + success, _ := ocis.Restart(ocis.EnvConfigs) if success { message = "oCIS configuration rolled back successfully" sendResponse(res, http.StatusOK, message) @@ -209,25 +212,58 @@ func OcisServiceHandler(res http.ResponseWriter, req *http.Request) { return } - envBody, err := parseJsonBody(req.Body) - if err != nil { - sendResponse(res, http.StatusMethodNotAllowed, "Invalid json body") + serviceName := strings.TrimPrefix(req.URL.Path, "/services/") + + if serviceName == "" { + sendResponse(res, http.StatusUnprocessableEntity, "Service name not specified") return } - var envMap []string - for key, value := range envBody { - envMap = append(envMap, fmt.Sprintf("%s=%v", key, value)) - } - ocis.EnvConfigs = append(ocis.EnvConfigs, envMap...) + envMap := []string{fmt.Sprintf("OCIS_EXCLUDE_RUN_SERVICES=%s", serviceName)} if req.Method == http.MethodPost { - success, _ := ocis.Restart(ocis.EnvConfigs) + // restart oCIS without service that need to start separately + success, _ := ocis.Restart(envMap) + if success { + // Clear `EnvConfigs` to prevent persistence of temporary changes + log.Println(fmt.Sprintf("Environment Config when service Post request has been hit: %s\n", ocis.EnvConfigs)) + + var envBody map[string]interface{} + var envMap []string + + if req.Body != nil && req.ContentLength > 0 { + var err error + envBody, err = parseJsonBody(req.Body) + if err != nil { + sendResponse(res, http.StatusBadRequest, "Invalid json body") + return + } + } + + for key, value := range envBody { + envMap = append(envMap, fmt.Sprintf("%s=%v", key, value)) + } + + log.Println(fmt.Sprintf("serviceName to start: %s\n", serviceName)) + + go ocis.RunOcisService(serviceName, envMap) + success, _ := ocis.WaitForConnection() + if success { + sendResponse(res, http.StatusOK, fmt.Sprintf("oCIS service %s started successfully", serviceName)) + return + } + } + + sendResponse(res, http.StatusInternalServerError, fmt.Sprintf("Failed to restart oCIS without service %s", serviceName)) + } + + if req.Method == http.MethodDelete { + success, message := ocis.StopService(serviceName) if success { - sendResponse(res, http.StatusOK, "oCIS configured successfully") - return + sendResponse(res, http.StatusOK, fmt.Sprintf("oCIS service %s stopped successfully", serviceName)) + } else { + sendResponse(res, http.StatusInternalServerError, message) } - sendResponse(res, http.StatusInternalServerError, "Failed to restart oCIS with new configuration") } } diff --git a/tests/ociswrapper/wrapper/wrapper.go b/tests/ociswrapper/wrapper/wrapper.go index fda2604d3c0..2c28077cd35 100644 --- a/tests/ociswrapper/wrapper/wrapper.go +++ b/tests/ociswrapper/wrapper/wrapper.go @@ -27,7 +27,7 @@ func Start(port string) { mux.HandleFunc("/command", handlers.CommandHandler) mux.HandleFunc("/stop", handlers.StopOcisHandler) mux.HandleFunc("/start", handlers.StartOcisHandler) - mux.HandleFunc("/services", handlers.OcisServiceHandler) + mux.HandleFunc("/services/", handlers.OcisServiceHandler) httpServer.Handler = mux From a5ef4e28ee1eadd1ae9ee5d2615781e0e3199bb6 Mon Sep 17 00:00:00 2001 From: amrita Date: Mon, 6 Jan 2025 17:42:28 +0545 Subject: [PATCH 4/5] add docs and refactor isocisrunning --- .../TestHelpers/OcisConfigHelper.php | 5 +- tests/acceptance/bootstrap/FeatureContext.php | 2 +- .../cliCommands/backupConsistency.feature | 2 +- tests/ociswrapper/README.md | 28 +++++ tests/ociswrapper/ocis/ocis.go | 102 ++++++------------ tests/ociswrapper/wrapper/handlers/handler.go | 3 +- 6 files changed, 65 insertions(+), 77 deletions(-) diff --git a/tests/acceptance/TestHelpers/OcisConfigHelper.php b/tests/acceptance/TestHelpers/OcisConfigHelper.php index df839bc45b7..830af52c624 100644 --- a/tests/acceptance/TestHelpers/OcisConfigHelper.php +++ b/tests/acceptance/TestHelpers/OcisConfigHelper.php @@ -132,10 +132,7 @@ public static function startOcis(): ResponseInterface { * @throws GuzzleException */ public static function startService(string $service): ResponseInterface { - $envs = [ - "OCIS_LOG_LEVEL" => "info", - ]; $url = self::getWrapperUrl() . "/services/" . $service; - return self::sendRequest($url, "POST", \json_encode($envs)); + return self::sendRequest($url, "POST"); } } diff --git a/tests/acceptance/bootstrap/FeatureContext.php b/tests/acceptance/bootstrap/FeatureContext.php index 3f858872e97..505080f5f36 100644 --- a/tests/acceptance/bootstrap/FeatureContext.php +++ b/tests/acceptance/bootstrap/FeatureContext.php @@ -613,7 +613,7 @@ public static function logStep(BeforeStepScope $scope): void { * NOTE: This method is called after each scenario having the @env-config tag * This ensures that the server is running for clean-up purposes * - * @AfterScenario @backup-consistency + * @AfterScenario @env-config * * @return void */ diff --git a/tests/acceptance/features/cliCommands/backupConsistency.feature b/tests/acceptance/features/cliCommands/backupConsistency.feature index b21aa996041..a83a5d147d4 100644 --- a/tests/acceptance/features/cliCommands/backupConsistency.feature +++ b/tests/acceptance/features/cliCommands/backupConsistency.feature @@ -1,4 +1,4 @@ -@env-config @backup-consistency +@env-config Feature: backup consistency As a user I want to check my data for inconsistencies diff --git a/tests/ociswrapper/README.md b/tests/ociswrapper/README.md index 6186e272678..b0c0a63cb8e 100644 --- a/tests/ociswrapper/README.md +++ b/tests/ociswrapper/README.md @@ -124,3 +124,31 @@ Also, see `./bin/ociswrapper help` for more information. - `200 OK` - oCIS server is stopped - `500 Internal Server Error` - Unable to stop oCIS server + +6. `POST /services/` + + Restart oCIS with service excluded and start excluded oCIS service individually, not covered by the oCIS supervisor. + + Body of the request should be a JSON object with the following structure: + + ```json + { + "ENV_KEY1": "value1", + "ENV_KEY2": "value2" + } + ``` + + Returns: + + - `200 OK` - oCIS server is stopped + - `500 Internal Server Error` - Unable to stop oCIS server + +7. `DELETE /services/` + + Stop individually running oCIS service + + Returns: + + - `200 OK` - command is successfully executed + - `400 Bad Request` - request body is not a valid JSON object + - `500 Internal Server Error` diff --git a/tests/ociswrapper/ocis/ocis.go b/tests/ociswrapper/ocis/ocis.go index 9be3b03b41f..8ca3e1d7824 100644 --- a/tests/ociswrapper/ocis/ocis.go +++ b/tests/ociswrapper/ocis/ocis.go @@ -7,9 +7,6 @@ import ( "fmt" "io" "net/http" - "ociswrapper/common" - "ociswrapper/log" - "ociswrapper/ocis/config" "os" "os/exec" "strconv" @@ -18,6 +15,10 @@ import ( "syscall" "time" + "ociswrapper/common" + "ociswrapper/log" + "ociswrapper/ocis/config" + "github.com/creack/pty" ) @@ -25,41 +26,28 @@ var cmd *exec.Cmd var retryCount = 0 var stopSignal = false var EnvConfigs = []string{} -var runningCommands = make(map[string]int) // Maps unique IDs to PIDs +var runningServices = make(map[string]int) func Start(envMap []string) { StartService("", envMap) } func Stop() (bool, string) { - log.Println(fmt.Sprintf("Stop ocis check cmd %s\n", cmd)) log.Println("Stopping oCIS server...") stopSignal = true - for listservice, pid := range runningCommands { - log.Println(fmt.Sprintf("Services running before terminating: %s with process and id: %v\n", listservice, pid)) - process, err := os.FindProcess(pid) - err = process.Signal(syscall.SIGINT) - if err != nil { - if !strings.HasSuffix(err.Error(), "process already finished") { - log.Fatalln(err) - } else { - return true, "oCIS server is already stopped" - } - } - process.Wait() + for service := range runningServices { + StopService(service) } - cmd = nil success, message := waitUntilCompleteShutdown() + + cmd = nil return success, message } func Restart(envMap []string) (bool, string) { - log.Println(fmt.Sprintf("Restarting ocis check cmd %s\n", cmd)) - log.Println(fmt.Sprintf("Restaring ocis with rollback os environ %s\n", envMap)) - log.Println(fmt.Sprintf("OS environment: %s\n", os.Environ())) - go Stop() + Stop() log.Println("Restarting oCIS server...") common.Wg.Add(1) @@ -69,10 +57,16 @@ func Restart(envMap []string) (bool, string) { } func IsOcisRunning() bool { - if cmd != nil { - return cmd.Process.Pid > 0 + if runningServices["ocis"] == 0 { + return false + } + + _, err := os.FindProcess(runningServices["ocis"]) + if err != nil { + delete(runningServices, "ocis") + return false } - return false + return true } func waitAllServices(startTime time.Time, timeout time.Duration) { @@ -136,7 +130,6 @@ func WaitForConnection() (bool, string) { } func waitUntilCompleteShutdown() (bool, string) { - log.Println("Process found. Waiting... waitUntilCompleteShutdown") timeout := 30 * time.Second startTime := time.Now() @@ -207,11 +200,9 @@ func StartService(service string, envMap []string) { cmdArgs := []string{"server"} // Default command args if service != "" { - // Directly append service if provided cmdArgs = append([]string{service}, cmdArgs...) } - // wait for the log scanner to finish var wg sync.WaitGroup wg.Add(2) @@ -220,18 +211,12 @@ func StartService(service string, envMap []string) { defer common.Wg.Done() } - // Initialize the command cmd = exec.Command(config.Get("bin"), cmdArgs...) - // Use the provided envMap if not empty, otherwise use EnvConfigs if len(envMap) == 0 { cmd.Env = append(os.Environ(), EnvConfigs...) - log.Println(fmt.Sprintf("OS environment variables while running ocis service: %s\n", cmd.Env)) - log.Println(fmt.Sprintf("OS environment: %s\n", os.Environ())) } else { cmd.Env = append(os.Environ(), envMap...) - log.Println(fmt.Sprintf("OS environment variables while running ocis service: %s\n", cmd.Env)) - log.Println(fmt.Sprintf("OS environment: %s\n", os.Environ())) } logs, err := cmd.StderrPipe() @@ -243,9 +228,6 @@ func StartService(service string, envMap []string) { log.Panic(err) } -// log.Println(fmt.Sprintf("command env used to start service %s\n", cmd.Env)) -// log.Println(fmt.Sprintf("command used to start service %s\n", cmd)) - err = cmd.Start() if err != nil { @@ -256,17 +238,14 @@ func StartService(service string, envMap []string) { outputScanner := bufio.NewScanner(output) outChan := make(chan string) - // If service is an empty string, set the PID for "ocis" if service == "" { - runningCommands["ocis"] = cmd.Process.Pid + runningServices["ocis"] = cmd.Process.Pid } else { - runningCommands[service] = cmd.Process.Pid + runningServices[service] = cmd.Process.Pid } - log.Println("Started oCIS processes:") - for listservice, pid := range runningCommands { - log.Println(fmt.Sprintf("Service started: %s with process and id: %v\n", listservice, pid)) - + for listservice, pid := range runningServices { + log.Println(fmt.Sprintf("Service started: %s with process and id: %v\n", listservice, pid)) } // Read the logs when the 'ocis server' command is running @@ -316,45 +295,30 @@ func StartService(service string, envMap []string) { } } } - - log.Println(fmt.Sprintf(" ---- ocis start service ending line---- %s\n", cmd)) wg.Wait() close(outChan) } // Stop oCIS service or a specific service by its unique identifier func StopService(service string) (bool, string) { - for listservice, pid := range runningCommands { - log.Println(fmt.Sprintf("Services running before terminating: %s with process and id: %v\n", listservice, pid)) - } - - pid, exists := runningCommands[service] - log.Println(fmt.Sprintf("Services process id to terminate: %s\n", pid)) + pid, exists := runningServices[service] if !exists { return false, fmt.Sprintf("Service %s is not running", service) } - // Find the process by PID and send SIGINT to stop it process, err := os.FindProcess(pid) - log.Println(fmt.Sprintf("Found process to terminate in os: %s\n", pid)) - if err != nil { - log.Println(fmt.Sprintf("Failed to find process: %v", err)) - return false, fmt.Sprintf("Failed to find process with ID %d", pid) + return false, fmt.Sprintf("Failed to find service %s process running with ID %d", service, pid) } - err = process.Signal(syscall.SIGINT) - log.Println("Process terminated using signal") - if err != nil { - log.Println(fmt.Sprintf("Failed to send signal: %v", err)) - return false, fmt.Sprintf("Failed to stop service with PID %d", pid) - } - time.Sleep(30 * time.Second) - process.Wait() - log.Println("Process terminating process.wait") - delete(runningCommands, service) - for listservice, pid := range runningCommands { - log.Println(fmt.Sprintf("Service list after deleteing %s service. list contain service: %s with process and id: %v\n", service, listservice, pid)) + pKillError := process.Signal(syscall.SIGINT) + if pKillError != nil { + return false, fmt.Sprintf("Failed to stop service with process id %d", pid) + } else { + delete(runningServices, service) + + log.Println(fmt.Sprintf("oCIS service %s has been stopped successfully", service)) } + return true, fmt.Sprintf("Service %s stopped successfully", service) } diff --git a/tests/ociswrapper/wrapper/handlers/handler.go b/tests/ociswrapper/wrapper/handlers/handler.go index 34298dfaeb4..f0b0c3660bc 100644 --- a/tests/ociswrapper/wrapper/handlers/handler.go +++ b/tests/ociswrapper/wrapper/handlers/handler.go @@ -9,7 +9,6 @@ import ( "net/http" "ociswrapper/common" "ociswrapper/ocis" - "os" "strings" ) @@ -115,7 +114,7 @@ func RollbackHandler(res http.ResponseWriter, req *http.Request) { var message string ocis.EnvConfigs = []string{} - log.Printf(fmt.Sprintf("os Environ when rollback %s", os.Environ)) + success, _ := ocis.Restart(ocis.EnvConfigs) if success { message = "oCIS configuration rolled back successfully" From cf15b25cbac88076b883876adea8b0cc0e40b944 Mon Sep 17 00:00:00 2001 From: amrita Date: Mon, 13 Jan 2025 14:05:33 +0545 Subject: [PATCH 5/5] review addressed --- .../bootstrap/OcisConfigContext.php | 6 ++-- .../serviceAvailabilityCheck.feature | 21 ++++++----- tests/ociswrapper/README.md | 4 +-- tests/ociswrapper/ocis/ocis.go | 16 +++------ tests/ociswrapper/wrapper/handlers/handler.go | 36 ++++++------------- tests/ociswrapper/wrapper/wrapper.go | 2 +- 6 files changed, 31 insertions(+), 54 deletions(-) diff --git a/tests/acceptance/bootstrap/OcisConfigContext.php b/tests/acceptance/bootstrap/OcisConfigContext.php index 187528864c9..01d10cceeea 100644 --- a/tests/acceptance/bootstrap/OcisConfigContext.php +++ b/tests/acceptance/bootstrap/OcisConfigContext.php @@ -192,20 +192,20 @@ public function theConfigHasBeenSetToValue(TableNode $table): void { } /** - * @Given the ocis server has served service :service separately + * @Given the administrator has started service :service separately * * @param string $service * * @return void * @throws GuzzleException */ - public function theOcisServerHasExcludedService(string $service) { + public function theAdministratorHasStartedServiceSeparately(string $service): void { $response = OcisConfigHelper::startService($service); Assert::assertEquals( 200, $response->getStatusCode(), - "Failed to set config" + "Failed to start service $service." ); } diff --git a/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature b/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature index ff36e69576e..b7c3cf877ff 100644 --- a/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature +++ b/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature @@ -97,16 +97,15 @@ Feature: service health check @env-config Scenario: check extra services readiness Given the following configs have been set: - | config | value | - | OCIS_ADD_RUN_SERVICES | audit,auth-app,auth-bearer,policies,invitations | - | AUDIT_DEBUG_ADDR | 0.0.0.0:9229 | - | AUTH_APP_DEBUG_ADDR | 0.0.0.0:9245 | - | AUTH_BEARER_DEBUG_ADDR | 0.0.0.0:9149 | - | POLICIES_DEBUG_ADDR | 0.0.0.0:9129 | - | INVITATIONS_DEBUG_ADDR | 0.0.0.0:9269 | + | config | value | + | OCIS_ADD_RUN_SERVICES | auth-app,policies,invitations | + | AUDIT_DEBUG_ADDR | 0.0.0.0:9229 | + | AUTH_APP_DEBUG_ADDR | 0.0.0.0:9245 | + | AUTH_BEARER_DEBUG_ADDR | 0.0.0.0:9149 | + | POLICIES_DEBUG_ADDR | 0.0.0.0:9129 | + | INVITATIONS_DEBUG_ADDR | 0.0.0.0:9269 | When a user requests these URLs with "GET" and no authentication | endpoint | service | - | http://%base_url_hostname%:9229/readyz | audit | | http://%base_url_hostname%:9245/readyz | auth-app | | http://%base_url_hostname%:9269/readyz | invitations | | http://%base_url_hostname%:9129/readyz | policies | @@ -134,8 +133,8 @@ Feature: service health check @env-config Scenario: check services health while running separately - Given the ocis server has served service "storage-users" separately + Given the administrator has started service "audit" separately When a user requests these URLs with "GET" and no authentication - | endpoint | service | - | http://%base_url_hostname%:9159/healthz | storage-users | + | endpoint | service | + | http://%base_url_hostname%:9229/healthz | audit | Then the HTTP status code of responses on all endpoints should be "200" diff --git a/tests/ociswrapper/README.md b/tests/ociswrapper/README.md index b0c0a63cb8e..676cd2b30d2 100644 --- a/tests/ociswrapper/README.md +++ b/tests/ociswrapper/README.md @@ -125,7 +125,7 @@ Also, see `./bin/ociswrapper help` for more information. - `200 OK` - oCIS server is stopped - `500 Internal Server Error` - Unable to stop oCIS server -6. `POST /services/` +6. `POST /services/{service-name}` Restart oCIS with service excluded and start excluded oCIS service individually, not covered by the oCIS supervisor. @@ -143,7 +143,7 @@ Also, see `./bin/ociswrapper help` for more information. - `200 OK` - oCIS server is stopped - `500 Internal Server Error` - Unable to stop oCIS server -7. `DELETE /services/` +7. `DELETE /services/{service-name}` Stop individually running oCIS service diff --git a/tests/ociswrapper/ocis/ocis.go b/tests/ociswrapper/ocis/ocis.go index 8ca3e1d7824..348a22c9b9a 100644 --- a/tests/ociswrapper/ocis/ocis.go +++ b/tests/ociswrapper/ocis/ocis.go @@ -29,6 +29,7 @@ var EnvConfigs = []string{} var runningServices = make(map[string]int) func Start(envMap []string) { + log.Println("Starting oCIS service........") StartService("", envMap) } @@ -188,21 +189,14 @@ func RunCommand(command string, inputs []string) (int, string) { return c.ProcessState.ExitCode(), cmdOutput } -func RunOcisService(service string, envMap []string) { - log.Println(fmt.Sprintf("Environment variable envMap: %s\n", envMap)) - StartService(service, envMap) -} - -// startService is a common function for starting a service (ocis or other) func StartService(service string, envMap []string) { - log.Println(fmt.Sprintf("Start service: %s with Environment variable envMap: %s\n", service, envMap)) // Initialize command args based on service presence cmdArgs := []string{"server"} // Default command args if service != "" { cmdArgs = append([]string{service}, cmdArgs...) } - + // wait for the log scanner to finish var wg sync.WaitGroup wg.Add(2) @@ -244,8 +238,8 @@ func StartService(service string, envMap []string) { runningServices[service] = cmd.Process.Pid } - for listservice, pid := range runningServices { - log.Println(fmt.Sprintf("Service started: %s with process and id: %v\n", listservice, pid)) + for listService, pid := range runningServices { + log.Println(fmt.Sprintf("%s service started with process id %v\n", listService, pid)) } // Read the logs when the 'ocis server' command is running @@ -303,7 +297,7 @@ func StartService(service string, envMap []string) { func StopService(service string) (bool, string) { pid, exists := runningServices[service] if !exists { - return false, fmt.Sprintf("Service %s is not running", service) + return false, fmt.Sprintf("Running service doesn't not include %s service", service) } process, err := os.FindProcess(pid) diff --git a/tests/ociswrapper/wrapper/handlers/handler.go b/tests/ociswrapper/wrapper/handlers/handler.go index f0b0c3660bc..4e8122b85d6 100644 --- a/tests/ociswrapper/wrapper/handlers/handler.go +++ b/tests/ociswrapper/wrapper/handlers/handler.go @@ -9,7 +9,6 @@ import ( "net/http" "ociswrapper/common" "ociswrapper/ocis" - "strings" ) type BasicResponse struct { @@ -115,7 +114,7 @@ func RollbackHandler(res http.ResponseWriter, req *http.Request) { var message string ocis.EnvConfigs = []string{} - success, _ := ocis.Restart(ocis.EnvConfigs) + success, _ := ocis.Restart([]string{}) if success { message = "oCIS configuration rolled back successfully" sendResponse(res, http.StatusOK, message) @@ -206,29 +205,15 @@ func CommandHandler(res http.ResponseWriter, req *http.Request) { } func OcisServiceHandler(res http.ResponseWriter, req *http.Request) { - if req.Method != http.MethodPost && req.Method != http.MethodDelete { - sendResponse(res, http.StatusMethodNotAllowed, "Method not allowed") - return - } - - serviceName := strings.TrimPrefix(req.URL.Path, "/services/") - - if serviceName == "" { - sendResponse(res, http.StatusUnprocessableEntity, "Service name not specified") - return - } - + serviceName := req.PathValue("service") envMap := []string{fmt.Sprintf("OCIS_EXCLUDE_RUN_SERVICES=%s", serviceName)} if req.Method == http.MethodPost { // restart oCIS without service that need to start separately success, _ := ocis.Restart(envMap) if success { - // Clear `EnvConfigs` to prevent persistence of temporary changes - log.Println(fmt.Sprintf("Environment Config when service Post request has been hit: %s\n", ocis.EnvConfigs)) - var envBody map[string]interface{} - var envMap []string + var serviceEnvMap []string if req.Body != nil && req.ContentLength > 0 { var err error @@ -240,29 +225,28 @@ func OcisServiceHandler(res http.ResponseWriter, req *http.Request) { } for key, value := range envBody { - envMap = append(envMap, fmt.Sprintf("%s=%v", key, value)) + serviceEnvMap = append(serviceEnvMap, fmt.Sprintf("%s=%v", key, value)) } - log.Println(fmt.Sprintf("serviceName to start: %s\n", serviceName)) + log.Println(fmt.Sprintf("Starting oCIS service %s......", serviceName)) - go ocis.RunOcisService(serviceName, envMap) + go ocis.StartService(serviceName, serviceEnvMap) success, _ := ocis.WaitForConnection() if success { sendResponse(res, http.StatusOK, fmt.Sprintf("oCIS service %s started successfully", serviceName)) return } } - sendResponse(res, http.StatusInternalServerError, fmt.Sprintf("Failed to restart oCIS without service %s", serviceName)) - } - - if req.Method == http.MethodDelete { + return + } else if req.Method == http.MethodDelete { success, message := ocis.StopService(serviceName) if success { sendResponse(res, http.StatusOK, fmt.Sprintf("oCIS service %s stopped successfully", serviceName)) } else { sendResponse(res, http.StatusInternalServerError, message) } + return } - + sendResponse(res, http.StatusMethodNotAllowed, "Invalid method requested") } diff --git a/tests/ociswrapper/wrapper/wrapper.go b/tests/ociswrapper/wrapper/wrapper.go index 2c28077cd35..8c14006adf0 100644 --- a/tests/ociswrapper/wrapper/wrapper.go +++ b/tests/ociswrapper/wrapper/wrapper.go @@ -27,7 +27,7 @@ func Start(port string) { mux.HandleFunc("/command", handlers.CommandHandler) mux.HandleFunc("/stop", handlers.StopOcisHandler) mux.HandleFunc("/start", handlers.StartOcisHandler) - mux.HandleFunc("/services/", handlers.OcisServiceHandler) + mux.HandleFunc("/services/{service}", handlers.OcisServiceHandler) httpServer.Handler = mux