diff --git a/internal/commands/asca/asca_test.go b/internal/commands/asca/asca_test.go index 9fc1b2d24..363ea71a3 100644 --- a/internal/commands/asca/asca_test.go +++ b/internal/commands/asca/asca_test.go @@ -21,14 +21,14 @@ func TestInstallOrUpgrade_firstInstallation_Success(t *testing.T) { func firstInstallation() error { os.RemoveAll(ascaconfig.Params.WorkingDir()) - _, err := osinstaller.InstallOrUpgrade(&ascaconfig.Params) + _, err := osinstaller.InstallOrUpgrade(&ascaconfig.Params, nil) return err } func TestInstallOrUpgrade_installationIsUpToDate_Success(t *testing.T) { err := firstInstallation() assert.NilError(t, err, "Error on first installation of asca") - _, err = osinstaller.InstallOrUpgrade(&ascaconfig.Params) + _, err = osinstaller.InstallOrUpgrade(&ascaconfig.Params, nil) assert.NilError(t, err, "Error when not need to upgrade") } @@ -36,7 +36,7 @@ func TestInstallOrUpgrade_installationIsNotUpToDate_Success(t *testing.T) { err := firstInstallation() assert.NilError(t, err, "Error on first installation of asca") changeHashFile() - _, err = osinstaller.InstallOrUpgrade(&ascaconfig.Params) + _, err = osinstaller.InstallOrUpgrade(&ascaconfig.Params, nil) assert.NilError(t, err, "Error when need to upgrade") fileExists, _ := osinstaller.FileExists(ascaconfig.Params.ExecutableFilePath()) assert.Assert(t, fileExists, "Executable file not found") diff --git a/internal/commands/scarealtime/sca-realtime.go b/internal/commands/scarealtime/sca-realtime.go index 197603117..495cd85b2 100644 --- a/internal/commands/scarealtime/sca-realtime.go +++ b/internal/commands/scarealtime/sca-realtime.go @@ -75,7 +75,7 @@ func RunScaRealtime(scaRealTimeWrapper wrappers.ScaRealTimeWrapper) func(*cobra. fmt.Println("Running SCA Realtime...") // Handle SCA Resolver. Checks if it already exists and if it is in the latest version - _, err = osinstaller.InstallOrUpgrade(&scaconfig.Params) + _, err = osinstaller.InstallOrUpgrade(&scaconfig.Params, nil) if err != nil { return err } diff --git a/internal/services/asca.go b/internal/services/asca.go index fb55c664e..9bb61b74c 100644 --- a/internal/services/asca.go +++ b/internal/services/asca.go @@ -200,13 +200,11 @@ func manageASCAInstallation(ascaParams AscaScanParams, ascaWrappers AscaWrappers _ = ascaWrappers.ASCAWrapper.ShutDown() return err } - newInstallation, err := osinstaller.InstallOrUpgrade(&ascaconfig.Params) + _, err := osinstaller.InstallOrUpgrade(&ascaconfig.Params, ascaWrappers.ASCAWrapper) if err != nil { return err } - if newInstallation { - _ = ascaWrappers.ASCAWrapper.ShutDown() - } + } return nil } diff --git a/internal/services/osinstaller/os-installer.go b/internal/services/osinstaller/os-installer.go index 82994a457..f686cbf9c 100644 --- a/internal/services/osinstaller/os-installer.go +++ b/internal/services/osinstaller/os-installer.go @@ -9,9 +9,11 @@ import ( "net/http" "os" "path/filepath" + "time" "github.com/checkmarx/ast-cli/internal/logger" "github.com/checkmarx/ast-cli/internal/wrappers" + grpcs "github.com/checkmarx/ast-cli/internal/wrappers/grpcs" "github.com/pkg/errors" ) @@ -52,7 +54,7 @@ func downloadFile(downloadURLPath, filePath string) error { // InstallOrUpgrade Checks the version according to the hash file, // downloads the RealTime installation if the version is not up-to-date, // Extracts the RealTime installation according to the operating system type -func InstallOrUpgrade(installationConfiguration *InstallationConfiguration) (NewSuccessfulInstallation, error) { +func InstallOrUpgrade(installationConfiguration *InstallationConfiguration, ascaWrapper grpcs.AscaWrapper) (NewSuccessfulInstallation, error) { logger.PrintIfVerbose("Handling RealTime Installation...") if downloadNotNeeded(installationConfiguration) { logger.PrintIfVerbose("RealTime installation already exists and is up to date. Skipping download.") @@ -77,6 +79,10 @@ func InstallOrUpgrade(installationConfiguration *InstallationConfiguration) (New return false, err } + if ascaWrapper != nil { + shutDownAndWait(ascaWrapper) + } + // Unzip or extract downloaded zip depending on which OS is running err = UnzipOrExtractFiles(installationConfiguration) if err != nil { @@ -167,3 +173,27 @@ func downloadHashFile(hashURL, zipFileNameHash string) error { return nil } + +// shutDownAndWait sends a shutdown signal and polls until the service is no longer reachable, +// ensuring the process has released its file handles before the caller replaces the binary. +func shutDownAndWait(ascaWrapper grpcs.AscaWrapper) { + const ( + maxAttempts = 20 + pollInterval = 500 * time.Millisecond + ) + + logger.PrintIfVerbose("Shutting down Vorpal service before replacing binary...") + _ = ascaWrapper.ShutDown() + + port := ascaWrapper.GetPort() + for i := 0; i < maxAttempts; i++ { + // ConfigurePort resets the cached 'serving' flag, forcing a live connection attempt. + ascaWrapper.ConfigurePort(port) + if err := ascaWrapper.HealthCheck(); err != nil { + logger.PrintIfVerbose("Vorpal service has stopped.") + return + } + time.Sleep(pollInterval) + } + logger.PrintIfVerbose("Timed out waiting for Vorpal service to stop; proceeding anyway.") +}