platform: modularize api/gui, add docs-tests-web foundation, and refresh root config
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (transportSystemdBackend) Cleanup(client TransportClient) transportBackendActionResult {
|
||||
unit := transportBackendUnit(client)
|
||||
if !validSystemdUnitName(unit) {
|
||||
return transportBackendActionResult{
|
||||
OK: false,
|
||||
Code: "TRANSPORT_BACKEND_UNIT_REQUIRED",
|
||||
Message: "valid systemd unit is required for cleanup",
|
||||
ExitCode: -1,
|
||||
}
|
||||
}
|
||||
|
||||
units := []string{unit}
|
||||
if transportSystemdSingBoxUsesTemplateInstance(client, unit) {
|
||||
units = transportSystemdAppendUniqueUnits(units, transportSystemdLegacySingBoxUnitCandidates(client, unit))
|
||||
}
|
||||
if transportDNSTTSSHTunnelEnabled(client) {
|
||||
sshUnit := transportDNSTTSSHUnit(client)
|
||||
if !validSystemdUnitName(sshUnit) {
|
||||
return transportBackendActionResult{
|
||||
OK: false,
|
||||
Code: "TRANSPORT_BACKEND_UNIT_REQUIRED",
|
||||
Message: "valid config.ssh_unit is required for cleanup",
|
||||
ExitCode: -1,
|
||||
}
|
||||
}
|
||||
units = append(units, sshUnit)
|
||||
}
|
||||
|
||||
ownedUnits := make([]string, 0, len(units))
|
||||
out := make([]string, 0, len(units)*2)
|
||||
errOut := make([]string, 0, len(units)*2)
|
||||
|
||||
for _, u := range units {
|
||||
path := transportSystemdUnitPath(u)
|
||||
dropInMode := false
|
||||
dropInDir := ""
|
||||
if transportSystemdSingBoxUsesTemplateInstance(client, u) {
|
||||
path = transportSystemdUnitDropInPath(u, transportSingBoxInstanceDropIn)
|
||||
dropInMode = true
|
||||
dropInDir = transportSystemdUnitDropInDir(u)
|
||||
}
|
||||
owned, err := transportSystemdUnitOwnedByClient(path, client.ID)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
errOut = append(errOut, fmt.Sprintf("%s ownership check failed: %v", u, err))
|
||||
continue
|
||||
}
|
||||
if !owned {
|
||||
// Safety: never touch units we did not provision for this client id.
|
||||
continue
|
||||
}
|
||||
ownedUnits = append(ownedUnits, u)
|
||||
|
||||
stdout, stderr, code, runErr := transportRunCommand(transportBackendActionTimeout, "systemctl", "stop", u)
|
||||
if s := strings.TrimSpace(stdout); s != "" {
|
||||
out = append(out, u+" stop: "+s)
|
||||
}
|
||||
if s := strings.TrimSpace(stderr); s != "" {
|
||||
errOut = append(errOut, u+" stop: "+s)
|
||||
}
|
||||
if runErr != nil || code != 0 {
|
||||
errOut = append(errOut, fmt.Sprintf("%s stop failed (exit=%d)", u, code))
|
||||
}
|
||||
|
||||
stdout, stderr, code, runErr = transportRunCommand(transportBackendActionTimeout, "systemctl", "disable", u)
|
||||
if s := strings.TrimSpace(stdout); s != "" {
|
||||
out = append(out, u+" disable: "+s)
|
||||
}
|
||||
if s := strings.TrimSpace(stderr); s != "" {
|
||||
errOut = append(errOut, u+" disable: "+s)
|
||||
}
|
||||
if runErr != nil || code != 0 {
|
||||
errOut = append(errOut, fmt.Sprintf("%s disable failed (exit=%d)", u, code))
|
||||
}
|
||||
|
||||
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
|
||||
errOut = append(errOut, fmt.Sprintf("%s remove failed: %v", u, err))
|
||||
}
|
||||
if dropInMode && strings.TrimSpace(dropInDir) != "" {
|
||||
if err := os.Remove(dropInDir); err != nil && !os.IsNotExist(err) {
|
||||
msg := strings.ToLower(strings.TrimSpace(err.Error()))
|
||||
if !strings.Contains(msg, "directory not empty") {
|
||||
errOut = append(errOut, fmt.Sprintf("%s drop-in dir remove failed: %v", u, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(ownedUnits) > 0 {
|
||||
stdout, stderr, code, runErr := transportRunCommand(transportBackendActionTimeout, "systemctl", "daemon-reload")
|
||||
if s := strings.TrimSpace(stdout); s != "" {
|
||||
out = append(out, "daemon-reload: "+s)
|
||||
}
|
||||
if s := strings.TrimSpace(stderr); s != "" {
|
||||
errOut = append(errOut, "daemon-reload: "+s)
|
||||
}
|
||||
if runErr != nil || code != 0 {
|
||||
errOut = append(errOut, fmt.Sprintf("daemon-reload failed (exit=%d)", code))
|
||||
}
|
||||
|
||||
for _, u := range ownedUnits {
|
||||
_, stderr, code, runErr := transportRunCommand(transportBackendActionTimeout, "systemctl", "reset-failed", u)
|
||||
if s := strings.TrimSpace(stderr); s != "" {
|
||||
errOut = append(errOut, u+" reset-failed: "+s)
|
||||
}
|
||||
if runErr != nil || code != 0 {
|
||||
errOut = append(errOut, fmt.Sprintf("%s reset-failed failed (exit=%d)", u, code))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if transportNetnsEnabled(client) {
|
||||
msg, err := transportCleanupNetnsForClient(client)
|
||||
if s := strings.TrimSpace(msg); s != "" {
|
||||
out = append(out, "netns: "+s)
|
||||
}
|
||||
if err != nil {
|
||||
errOut = append(errOut, "netns: "+err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("cleanup done for %d managed units", len(ownedUnits))
|
||||
if len(ownedUnits) == 0 {
|
||||
msg = "no managed unit artifacts found"
|
||||
}
|
||||
if len(errOut) > 0 {
|
||||
return transportBackendActionResult{
|
||||
OK: false,
|
||||
Code: "TRANSPORT_BACKEND_CLEANUP_FAILED",
|
||||
Message: msg,
|
||||
ExitCode: -1,
|
||||
Stdout: strings.Join(out, "\n"),
|
||||
Stderr: strings.Join(errOut, "\n"),
|
||||
Retryable: true,
|
||||
}
|
||||
}
|
||||
return transportBackendActionResult{
|
||||
OK: true,
|
||||
ExitCode: 0,
|
||||
Message: msg,
|
||||
Stdout: strings.Join(out, "\n"),
|
||||
}
|
||||
}
|
||||
|
||||
func transportSystemdAppendUniqueUnits(dst []string, candidates []string) []string {
|
||||
for _, candidate := range candidates {
|
||||
u := strings.TrimSpace(candidate)
|
||||
if u == "" {
|
||||
continue
|
||||
}
|
||||
already := false
|
||||
for _, existing := range dst {
|
||||
if strings.EqualFold(strings.TrimSpace(existing), u) {
|
||||
already = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !already {
|
||||
dst = append(dst, u)
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
Reference in New Issue
Block a user