This is an automated email from the ASF dual-hosted git repository.

hsluoyz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/casbin-gateway.git


The following commit(s) were added to refs/heads/master by this push:
     new 8ba981c  fix(update): recognise the Gateway an update renamed when 
claiming the port (#227)
8ba981c is described below

commit 8ba981c7a11488e839b173efb4997e17362334fe
Author: qioo777 <[email protected]>
AuthorDate: Tue Sep 15 21:20:27 2026 +0800

    fix(update): recognise the Gateway an update renamed when claiming the port 
(#227)
---
 util/port.go      | 21 +++++++++++++++++----
 util/port_test.go | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 68 insertions(+), 4 deletions(-)

diff --git a/util/port.go b/util/port.go
index 8e4e824..e4e5d6a 100644
--- a/util/port.go
+++ b/util/port.go
@@ -49,6 +49,11 @@ const (
        // waiting, which costs a netstat each time.
        portStopInterval  = 2 * time.Second
        portClaimInterval = 250 * time.Millisecond
+       // updateBackupSuffix and updateFailedSuffix are what an update renames 
the
+       // executable it replaces to. They mirror the version package's own
+       // constants, which cannot be imported here: that package imports this 
one.
+       updateBackupSuffix = ".old"
+       updateFailedSuffix = ".failed"
 )
 
 // ListenTcp binds a TCP listener on all interfaces for the given port. Callers
@@ -217,16 +222,24 @@ func isSelfExecutable(name string) bool {
        return mine == other
 }
 
-// executableBaseName drops the directory and the ".exe" suffix and lowercases
-// the rest, so that "C:\Program Files\Casbin-Gateway.exe" and
-// "casbin-gateway" are recognised as the same program.
+// executableBaseName drops the directory, the suffix an update renamed the 
file
+// to and the ".exe" suffix, and lowercases the rest, so that
+// "C:\Program Files\Casbin-Gateway.exe" and "casbin-gateway" are recognised as
+// the same program.
+//
+// The renamed suffix matters because a process is named after the file it runs
+// from as that file is now: an update renames the executable it is replacing
+// before the new Gateway starts, so the one still holding the port is reported
+// as "casbin-gateway.exe.old".
 func executableBaseName(value string) string {
        value = strings.TrimSpace(value)
        if value == "" {
                return ""
        }
 
-       return strings.TrimSuffix(strings.ToLower(filepath.Base(value)), ".exe")
+       name := strings.ToLower(filepath.Base(value))
+       name = strings.TrimSuffix(strings.TrimSuffix(name, updateFailedSuffix), 
updateBackupSuffix)
+       return strings.TrimSuffix(name, ".exe")
 }
 
 // findListenerPid returns the pid of a process listening on the port, or 0 
when
diff --git a/util/port_test.go b/util/port_test.go
index cae82cb..1b0feea 100644
--- a/util/port_test.go
+++ b/util/port_test.go
@@ -18,6 +18,7 @@ import (
        "net"
        "os"
        "os/exec"
+       "path/filepath"
        "strings"
        "testing"
 )
@@ -60,6 +61,56 @@ func TestParseNetstatPid(t *testing.T) {
        }
 }
 
+// TestExecutableBaseName covers the names the same program can be reported
+// under, including the ones an update leaves behind.
+func TestExecutableBaseName(t *testing.T) {
+       tests := []struct {
+               name  string
+               value string
+               want  string
+       }{
+               {"plain name", "casbin-gateway", "casbin-gateway"},
+               {"windows path", `C:\Program Files\Casbin-Gateway.exe`, 
"casbin-gateway"},
+               {"replaced by an update", "casbin-gateway.exe.old", 
"casbin-gateway"},
+               {"replaced by an update off windows", "casbin-gateway.old", 
"casbin-gateway"},
+               {"set aside by a rollback", "casbin-gateway.exe.failed", 
"casbin-gateway"},
+               {"another program", "nginx.exe", "nginx"},
+               {"nothing to name", "  ", ""},
+       }
+
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       if got := executableBaseName(tt.value); got != tt.want {
+                               t.Errorf("executableBaseName(%q) = %q, want 
%q", tt.value, got, tt.want)
+                       }
+               })
+       }
+}
+
+// TestIsSelfExecutableAcrossAnUpdate is the check a restart into an update
+// depends on. The update renames the executable it replaces before the new
+// Gateway starts, and a process is named after the file it runs from as that
+// file is now, so the Gateway still holding the port is reported under the
+// renamed name. Taking that for another program is what made the restarted
+// Gateway give up the port and exit instead of taking it over.
+func TestIsSelfExecutableAcrossAnUpdate(t *testing.T) {
+       self, err := os.Executable()
+       if err != nil {
+               t.Fatalf("os.Executable() error: %v", err)
+       }
+       name := filepath.Base(self)
+
+       for _, suffix := range []string{"", ".old", ".failed"} {
+               if !isSelfExecutable(name + suffix) {
+                       t.Errorf("isSelfExecutable(%q) = false, want true", 
name+suffix)
+               }
+       }
+
+       if isSelfExecutable("nginx.exe") {
+               t.Errorf(`isSelfExecutable("nginx.exe") = true, want false`)
+       }
+}
+
 // TestDescribePortHolder checks the whole lookup against a port this test is
 // holding itself. The platform commands it shells out to are not guaranteed to
 // exist, so an empty answer is tolerated: the description is only ever a hint.


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to