-
Notifications
You must be signed in to change notification settings - Fork 222
T3 (#809) & T4 (#811): Added native migration/rollback executables #822
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
GOKULRAJ136
wants to merge
4
commits into
mosip:develop
Choose a base branch
from
GOKULRAJ136:T4-811-RC
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| # Go source is LF by convention (gofmt/goimports emit LF). Pin it so Windows | ||
| # checkouts don't introduce CRLF, which would trip gofmt in golangci-lint. | ||
| *.go text eol=lf | ||
| go.mod text eol=lf | ||
| go.sum text eol=lf |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # build outputs | ||
| *.exe | ||
| # runtime log written by rollback.exe/migration.exe | ||
| *.log |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| # Lint config for the native upgrade executables (migration.exe / rollback.exe). | ||
| # Mirrors the mosip/esignet develop-go service config for consistency across the | ||
| # org's Go code; only the goimports local-prefix differs (our module path). | ||
| # | ||
| # Not required to build/test - go vet (in build.ps1) is the essential gate. This | ||
| # adds the stricter golangci-lint pass used in CI. Run locally with: | ||
| # golangci-lint run (https://golangci-lint.run) | ||
| version: "2" | ||
|
|
||
| linters: | ||
| default: standard | ||
| enable: | ||
| - errorlint | ||
| - gocritic | ||
| - misspell | ||
| - revive | ||
|
|
||
| formatters: | ||
| enable: | ||
| - gofmt | ||
| - goimports | ||
| settings: | ||
| goimports: | ||
| local-prefixes: | ||
| - github.com/mosip/registration-client/native |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| # Native migration tooling (Go) — `migration.exe` / `rollback.exe` | ||
|
|
||
| Native Windows executables for the Java 11 → 21 JRE migration (the upgrade-revamp | ||
| feature). They perform the JRE swap **outside the JVM**, because the swap | ||
| deletes/replaces the very `jre/` a running JVM would hold open (Windows locks | ||
| in-use files). `_launcher.jar` starts them and exits the JVM first. | ||
|
|
||
| | Output | Role | Status | | ||
| |--------|------|--------| | ||
| | `rollback.exe` | Restore the pre-migration state after a failed/aborted migration | **Logic complete** (`cmd/rollback`, 4 tests) | | ||
| | `migration.exe` | Perform the Java 11 → 21 JRE swap | **Logic complete** (`cmd/migration`, 6 tests; auto-invokes `rollback.exe` on failure) | | ||
|
|
||
| ## Why Go | ||
| - Single **static `.exe`, no runtime dependency** (the hard requirement — these run when the JRE is mid-swap/absent). | ||
| - File ops are stdlib (`os`, `path/filepath`); the operator dialog is a one-line `user32!MessageBoxW` syscall — no GUI toolkit. | ||
| - Cross-compiles to Windows from any OS; small, self-contained binary. | ||
| - (Comparison candidate vs GraalVM `native-image`, which keeps the language Java but adds the GraalVM + MSVC toolchain. Same logic either way.) | ||
|
|
||
| ## Layout | ||
| Two thin `cmd/` entrypoints build the two exes; **both migration and rollback | ||
| logic live together in a single file**, `internal/upgrade/upgrade.go`. | ||
| ``` | ||
| native/ | ||
| go.mod | ||
| cmd/ | ||
| rollback/ rollback.exe - thin main(): dialog/exit/log -> upgrade.Rollback | ||
| main.go | ||
| migration/ migration.exe - thin main(): dialog/exit/log + invokeRollback -> upgrade.Migrate | ||
| main.go | ||
| internal/ | ||
| upgrade/ Migrate(base) + Rollback(base) - BOTH flows in upgrade.go | ||
| upgrade.go | ||
| migrate_test.go / rollback_test.go / helpers_test.go (6 + 4 tests) | ||
| appenv/ resolve app root from the exe location (not CWD) | ||
| fsops/ idempotent move/remove/clean/restore (+ CopyFile, Unzip) | ||
| jre/ detect JRE feature version from <jre>/release | ||
| ui/ MessageBoxW (Windows) + stdout stub (dev) | ||
| ``` | ||
|
|
||
| ## `migration.exe` behaviour (design step 4) | ||
| Idempotent/resumable. Against the app root: | ||
| 1. extract `.artifacts/jre21.zip → jre21_temp/` (only if missing — done first so a failed extract never destroys `jre/`) | ||
| 2. back up `jre/ → jre11/` (once, only when on JRE 11) | ||
| 3. promote `jre21_temp/ → jre/` (delete old `jre/`, rename) | ||
| 4. reset `lib/` to just `_launcher.jar` (new jars arrive from `.TEMP/` via `run.bat` on restart) | ||
| 5. restore `run.bat` from `.artifacts/` | ||
| 6. success dialog — **on any failure, auto-invoke `rollback.exe` and exit non-zero** (design Scenarios 1 & 5) | ||
|
|
||
| ## `rollback.exe` behaviour (design step 4a) | ||
| Idempotent, safe to re-run. Against the app root: | ||
| 1. `jre11/ → jre/` (skip if no backup — never deletes `jre/` without a replacement) | ||
| 2. remove `jre21_temp/` | ||
| 3. `run.bat_jre11 → run.bat` (skip if no backup) | ||
| 4. empty `.TEMP/`, remove `.artifacts/` (re-downloaded on retry — design P8 / N12 / Scenario 5) | ||
| 5. completion dialog | ||
| 6. **never touches `./MANIFEST.MF`** (preserves the version mismatch for retry) — asserted via a before/after fingerprint | ||
|
|
||
| ## Build + test (one step) | ||
| ```powershell | ||
| ./build.ps1 # go vet + go test, then build both .exe into ./dist | ||
| ./build.ps1 -TestOnly # vet + test only | ||
| ``` | ||
|
|
||
| Or by hand: | ||
| ```bash | ||
| go vet ./... | ||
| go test ./... # runs on any OS (ui has a non-Windows stub) | ||
|
|
||
| # build ON or FOR Windows; -H=windowsgui = no console window | ||
| go build -ldflags "-H=windowsgui" -o dist/rollback.exe ./cmd/rollback | ||
| go build -ldflags "-H=windowsgui" -o dist/migration.exe ./cmd/migration | ||
|
|
||
| # cross-compile from Linux/macOS: | ||
| GOOS=windows GOARCH=amd64 go build -ldflags "-H=windowsgui" -o dist/rollback.exe ./cmd/rollback | ||
| GOOS=windows GOARCH=amd64 go build -ldflags "-H=windowsgui" -o dist/migration.exe ./cmd/migration | ||
| ``` | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # Build + verify the T3/T4 native upgrade executables (migration.exe / rollback.exe). | ||
| # | ||
| # ./build.ps1 # vet + test, then build both .exe into ./dist | ||
| # ./build.ps1 -TestOnly # vet + test only (no .exe produced) | ||
| # | ||
| # Requires the Go toolchain on PATH (https://go.dev/dl/ or: winget install GoLang.Go). | ||
| # The .exe still needs Authenticode signing before release (see README.md). | ||
| [CmdletBinding()] | ||
| param( | ||
| [switch]$TestOnly | ||
| ) | ||
|
|
||
| $ErrorActionPreference = 'Stop' | ||
| Set-Location -Path $PSScriptRoot | ||
|
|
||
| if (-not (Get-Command go -ErrorAction SilentlyContinue)) { | ||
| throw "Go toolchain not found on PATH. Install it (winget install GoLang.Go) and re-run." | ||
| } | ||
| Write-Host ("Using {0}" -f (go version)) | ||
|
|
||
| Write-Host "`n== go vet ==" -ForegroundColor Cyan | ||
| go vet ./... | ||
| if ($LASTEXITCODE -ne 0) { throw "go vet failed" } | ||
|
|
||
| Write-Host "`n== go test ==" -ForegroundColor Cyan | ||
| go test ./... | ||
| if ($LASTEXITCODE -ne 0) { throw "go test failed" } | ||
|
|
||
| if ($TestOnly) { | ||
| Write-Host "`nTests passed (TestOnly: no binaries built)." -ForegroundColor Green | ||
| return | ||
| } | ||
|
|
||
| $dist = Join-Path $PSScriptRoot 'dist' | ||
| New-Item -ItemType Directory -Force -Path $dist | Out-Null | ||
|
|
||
| # -H=windowsgui => no console window pops up when run.bat / _launcher.jar launches it. | ||
| $ldflags = '-H=windowsgui' | ||
| $env:GOOS = 'windows' | ||
| $env:GOARCH = 'amd64' | ||
|
|
||
| foreach ($cmd in @('rollback', 'migration')) { | ||
| $out = Join-Path $dist "$cmd.exe" | ||
| Write-Host ("`n== build {0}.exe ==" -f $cmd) -ForegroundColor Cyan | ||
| go build -ldflags $ldflags -o $out "./cmd/$cmd" | ||
| if ($LASTEXITCODE -ne 0) { throw "build $cmd failed" } | ||
| Write-Host (" -> {0}" -f $out) | ||
| } | ||
|
|
||
| Write-Host "`nDone. Sign the binaries in ./dist before release (see README.md)." -ForegroundColor Green |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| // This Source Code Form is subject to the terms of the Mozilla Public | ||
| // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
|
||
| // Command migration is the native executable: migration.exe. | ||
| // | ||
| // Thin entrypoint over the shared upgrade logic. It performs the Java 11 -> 21 | ||
| // JRE swap from outside the JVM (the swap deletes the very jre/ a JVM would hold | ||
| // open). It is launched by _launcher.jar (which then exits the JVM) and is | ||
| // idempotent/resumable. On failure it auto-invokes rollback.exe (T4). | ||
| // | ||
| // The migration + rollback logic both live in ../../internal/upgrade. | ||
| package main | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "log" | ||
| "os" | ||
| "os/exec" | ||
| "path/filepath" | ||
|
|
||
| "github.com/mosip/registration-client/native/internal/appenv" | ||
| "github.com/mosip/registration-client/native/internal/fsops" | ||
| "github.com/mosip/registration-client/native/internal/ui" | ||
| "github.com/mosip/registration-client/native/internal/upgrade" | ||
| ) | ||
|
|
||
| const ( | ||
| dialogTitle = "Registration Client - JRE Migration" | ||
| successMessage = "JRE migration complete. Please start the application using run.bat." | ||
| ) | ||
|
|
||
| func main() { os.Exit(run()) } | ||
|
|
||
| // run performs the migration and returns the process exit code. Keeping os.Exit | ||
| // in main (not here) lets the deferred log-file close actually run. | ||
| func run() int { | ||
| base, err := appenv.AppRoot() | ||
| if err != nil { | ||
| ui.ShowError(dialogTitle, "Could not determine the application directory: "+err.Error()) | ||
| return 1 | ||
| } | ||
|
|
||
| if f, e := os.OpenFile(filepath.Join(base, "migration.log"), | ||
| os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644); e == nil { | ||
| defer func() { _ = f.Close() }() | ||
| log.SetOutput(f) | ||
| } | ||
| log.Printf("migration started, base=%s", base) | ||
|
|
||
| if err := upgrade.Migrate(base); err != nil { | ||
| log.Printf("migration FAILED: %v", err) | ||
| // Design step 4 / Scenarios 1 & 5: on failure, auto-invoke rollback.exe, | ||
| // which restores the previous state and shows its own dialog. | ||
| if rbErr := invokeRollback(base); rbErr != nil { | ||
| log.Printf("could not launch rollback.exe: %v", rbErr) | ||
| ui.ShowError(dialogTitle, | ||
| "Migration failed: "+err.Error()+"\n\nRollback could not be started: "+rbErr.Error()) | ||
| } | ||
| return 1 | ||
| } | ||
|
|
||
| log.Printf("migration completed successfully") | ||
| ui.ShowInfo(dialogTitle, successMessage) | ||
| return 0 | ||
| } | ||
|
|
||
| // invokeRollback fire-and-forget launches rollback.exe from the app root. | ||
| func invokeRollback(base string) error { | ||
| rb := filepath.Join(base, "rollback.exe") | ||
| if !fsops.Exists(rb) { | ||
| return fmt.Errorf("rollback.exe not found at %s", rb) | ||
| } | ||
| log.Printf("auto-invoking rollback.exe") | ||
| return exec.Command(rb).Start() | ||
| } | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| // This Source Code Form is subject to the terms of the Mozilla Public | ||
| // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
|
||
| // Command rollback is the native executable: rollback.exe. | ||
| // | ||
| // Thin entrypoint over the shared upgrade logic. It restores the Registration | ||
| // Client to its pre-migration state after a failed or aborted Java 11 -> 21 | ||
| // migration. It is invoked by migration.exe on failure, by _launcher.jar on an | ||
| // inconsistent-JRE startup, or manually by an operator. It is fully idempotent | ||
| // and never touches ./MANIFEST.MF, so the version mismatch is preserved and the | ||
| // upgrade can be retried later. | ||
| // | ||
| // The migration + rollback logic both live in ../../internal/upgrade. | ||
| package main | ||
|
|
||
| import ( | ||
| "log" | ||
| "os" | ||
| "path/filepath" | ||
|
|
||
| "github.com/mosip/registration-client/native/internal/appenv" | ||
| "github.com/mosip/registration-client/native/internal/ui" | ||
| "github.com/mosip/registration-client/native/internal/upgrade" | ||
| ) | ||
|
|
||
| const ( | ||
| dialogTitle = "Registration Client - Rollback" | ||
| successMessage = "Rollback complete. Application restored to previous state. Please start using run.bat." | ||
| ) | ||
|
|
||
| func main() { os.Exit(run()) } | ||
|
|
||
| // run performs the rollback and returns the process exit code. Keeping os.Exit | ||
| // in main (not here) lets the deferred log-file close actually run. | ||
| func run() int { | ||
| base, err := appenv.AppRoot() | ||
| if err != nil { | ||
| ui.ShowError(dialogTitle, "Could not determine the application directory: "+err.Error()) | ||
| return 1 | ||
| } | ||
|
|
||
| // Best-effort file log in the app root (the windowsgui build has no console). | ||
| if f, e := os.OpenFile(filepath.Join(base, "rollback.log"), | ||
| os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644); e == nil { | ||
| defer func() { _ = f.Close() }() | ||
| log.SetOutput(f) | ||
| } | ||
| log.Printf("rollback started, base=%s", base) | ||
|
|
||
| if err := upgrade.Rollback(base); err != nil { | ||
| log.Printf("rollback FAILED: %v", err) | ||
| ui.ShowError(dialogTitle, | ||
| "Rollback failed: "+err.Error()+"\n\nYou can retry by running rollback.exe again.") | ||
| return 1 | ||
| } | ||
|
|
||
| log.Printf("rollback completed successfully") | ||
| ui.ShowInfo(dialogTitle, successMessage) | ||
| return 0 | ||
| } | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| module github.com/mosip/registration-client/native | ||
|
|
||
| go 1.21 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| // This Source Code Form is subject to the terms of the Mozilla Public | ||
| // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
|
||
| // Package appenv resolves the Registration Client application root directory. | ||
| // | ||
| // The migration/rollback executables live in the app root (next to run.bat, | ||
| // jre/, lib/, ./MANIFEST.MF). They may be launched by run.bat, by migration.exe, | ||
| // or manually by an operator, so the working directory is not reliable. We resolve | ||
| // the app root from the executable's own location instead. | ||
| package appenv | ||
|
|
||
| import ( | ||
| "os" | ||
| "path/filepath" | ||
| ) | ||
|
|
||
| // AppRoot returns the directory containing the running executable, with symlinks | ||
| // resolved. This is the Registration Client application root. | ||
| func AppRoot() (string, error) { | ||
| exe, err := os.Executable() | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
| if resolved, err := filepath.EvalSymlinks(exe); err == nil { | ||
| exe = resolved | ||
| } | ||
| return filepath.Dir(exe), nil | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.