mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
Server: Move process handling and shutdown to separate package #4767
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -2,16 +2,15 @@ package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||
"github.com/photoprism/photoprism/internal/server/process"
|
||||
)
|
||||
|
||||
// StopServer shuts down the server.
|
||||
// StopServer initiates a server restart if the user is authorized.
|
||||
//
|
||||
// POST /api/v1/server/stop
|
||||
func StopServer(router *gin.RouterGroup) {
|
||||
@@ -25,16 +24,11 @@ func StopServer(router *gin.RouterGroup) {
|
||||
return
|
||||
}
|
||||
|
||||
process, err := os.FindProcess(os.Getpid())
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, NewResponse(http.StatusInternalServerError, err, ""))
|
||||
return
|
||||
} else {
|
||||
c.JSON(http.StatusOK, conf.Options())
|
||||
}
|
||||
|
||||
if err = process.Signal(syscall.SIGTERM); err != nil {
|
||||
log.Errorf("server: %s", err)
|
||||
}
|
||||
// Trigger restart.
|
||||
//
|
||||
// Note that this requires an entrypoint script or other process to
|
||||
// spawns a new instance when the server exists with status code 1.
|
||||
c.JSON(http.StatusOK, conf.Options())
|
||||
process.Restart()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/photoprism/photoprism/internal/mutex"
|
||||
"github.com/photoprism/photoprism/internal/photoprism/backup"
|
||||
"github.com/photoprism/photoprism/internal/server"
|
||||
"github.com/photoprism/photoprism/internal/server/process"
|
||||
"github.com/photoprism/photoprism/internal/workers"
|
||||
"github.com/photoprism/photoprism/internal/workers/auto"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
@@ -144,9 +145,9 @@ func startAction(ctx *cli.Context) error {
|
||||
// Start auto-indexing background worker.
|
||||
auto.Start(conf)
|
||||
|
||||
// Wait for signal to initiate server shutdown.
|
||||
signal.Notify(server.Signal, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR1)
|
||||
sig := <-server.Signal
|
||||
// Wait for signal to trigger server shutdown or restart.
|
||||
signal.Notify(process.Signal, os.Interrupt, syscall.SIGTERM, syscall.SIGUSR1)
|
||||
sig := <-process.Signal
|
||||
|
||||
// Stop all background activity.
|
||||
auto.Shutdown()
|
||||
@@ -165,7 +166,10 @@ func startAction(ctx *cli.Context) error {
|
||||
time.Sleep(2 * time.Second)
|
||||
conf.Shutdown()
|
||||
|
||||
// Don't exit with 0 if SIGUSR1 was received to avoid restarts.
|
||||
// Exit with status code 1 if the shutdown was initiated with SIGUSR1 to request a restart.
|
||||
//
|
||||
// Note that this requires an entrypoint script or other process to
|
||||
// spawns a new instance when the server exists with status code 1.
|
||||
if sig == syscall.SIGUSR1 {
|
||||
os.Exit(1)
|
||||
return nil
|
||||
|
||||
14
internal/server/fail.go
Normal file
14
internal/server/fail.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/photoprism/photoprism/internal/server/process"
|
||||
)
|
||||
|
||||
// Fail logs an error and then initiates a server shutdown.
|
||||
func Fail(err string, params ...interface{}) {
|
||||
if err != "" {
|
||||
log.Errorf(err, params...)
|
||||
}
|
||||
|
||||
process.Shutdown()
|
||||
}
|
||||
27
internal/server/process/pid.go
Normal file
27
internal/server/process/pid.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
)
|
||||
|
||||
// ID is the process ID under which the server is running.
|
||||
var ID = os.Getpid()
|
||||
|
||||
// WritePID writes the process ID to a file, if specified.
|
||||
func WritePID(fileName string) error {
|
||||
if fileName == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if pidDir := filepath.Dir(fileName); !fs.Writable(pidDir) {
|
||||
return fmt.Errorf("%s is not writable", pidDir)
|
||||
} else if err := fs.WriteString(fileName, fmt.Sprintf("%d", ID)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
14
internal/server/process/pid_test.go
Normal file
14
internal/server/process/pid_test.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestID(t *testing.T) {
|
||||
t.Run("Matches", func(t *testing.T) {
|
||||
assert.Equal(t, os.Getpid(), ID)
|
||||
})
|
||||
}
|
||||
25
internal/server/process/process.go
Normal file
25
internal/server/process/process.go
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
Package process provides server process information and handling.
|
||||
|
||||
Copyright (c) 2018 - 2025 PhotoPrism UG. All rights reserved.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
|
||||
<https://docs.photoprism.app/license/agpl>
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
The AGPL is supplemented by our Trademark and Brand Guidelines,
|
||||
which describe how our Brand Assets may be used:
|
||||
<https://www.photoprism.app/trademark>
|
||||
|
||||
Feel free to send an email to hello@photoprism.app if you have questions,
|
||||
want to support our work, or just want to say hello.
|
||||
|
||||
Additional information can be found in our Developer Guide:
|
||||
<https://docs.photoprism.app/developer-guide/>
|
||||
*/
|
||||
package process
|
||||
22
internal/server/process/signal.go
Normal file
22
internal/server/process/signal.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Signal channel for initiating the server shutdown.
|
||||
var Signal = make(chan os.Signal)
|
||||
|
||||
// Restart gracefully restarts the server.
|
||||
//
|
||||
// Note that this requires an entrypoint script or other process to
|
||||
// spawns a new instance when the server exists with status code 1.
|
||||
func Restart() {
|
||||
Signal <- syscall.SIGUSR1
|
||||
}
|
||||
|
||||
// Shutdown gracefully stops the server.
|
||||
func Shutdown() {
|
||||
Signal <- syscall.SIGTERM
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Signal channel for initiating the shutdown.
|
||||
var Signal = make(chan os.Signal)
|
||||
|
||||
// Fail reports an error and shuts down the server.
|
||||
func Fail(err string, params ...interface{}) {
|
||||
if err != "" {
|
||||
log.Errorf(err, params...)
|
||||
}
|
||||
|
||||
Shutdown()
|
||||
}
|
||||
|
||||
// Shutdown gracefully stops the server.
|
||||
func Shutdown() {
|
||||
Signal <- syscall.SIGINT
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/server/process"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/net/header"
|
||||
@@ -33,6 +34,9 @@ func Start(ctx context.Context, conf *config.Config) {
|
||||
|
||||
start := time.Now()
|
||||
|
||||
// Log the server process ID for troubleshooting purposes.
|
||||
log.Infof("server: started as pid %d", process.ID)
|
||||
|
||||
// Set web server mode.
|
||||
if conf.HttpMode() != "" {
|
||||
gin.SetMode(conf.HttpMode())
|
||||
|
||||
2
scripts/dist/cmd.sh
vendored
2
scripts/dist/cmd.sh
vendored
@@ -88,7 +88,7 @@ echo "backup path...: ${PHOTOPRISM_BACKUP_PATH:-default}"
|
||||
echo "import path...: ${PHOTOPRISM_IMPORT_PATH:-default}"
|
||||
echo "originals path: ${PHOTOPRISM_ORIGINALS_PATH:-default}"
|
||||
|
||||
# error code of the last executed command
|
||||
# exit status code of the last command executed
|
||||
ret=0
|
||||
|
||||
# change to another user and group on request
|
||||
|
||||
Reference in New Issue
Block a user