mirror of
https://github.com/MengMengCode/CLICD.git
synced 2026-08-05 05:36:07 +08:00
Fix some problem.
This commit is contained in:
@@ -0,0 +1,104 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"clicd/internal/config"
|
||||||
|
"clicd/internal/kvm"
|
||||||
|
"clicd/internal/lxc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CaptureRuntimeRestoreState records which managed workloads are actually
|
||||||
|
// running before the CLICD service exits. On the next host boot, only those
|
||||||
|
// workloads are started again.
|
||||||
|
func CaptureRuntimeRestoreState() {
|
||||||
|
if config.AppConfig == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lxcManager := lxc.NewManager()
|
||||||
|
kvmManager := kvm.NewManager()
|
||||||
|
changed := false
|
||||||
|
|
||||||
|
for i := range config.AppConfig.Containers {
|
||||||
|
c := &config.AppConfig.Containers[i]
|
||||||
|
status, err := runtimeStatus(*c, lxcManager, kvmManager)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Warning: failed to capture runtime state for %s: %v\n", c.Name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
restore := status == "running"
|
||||||
|
if c.RestoreOnHostBoot != restore {
|
||||||
|
c.RestoreOnHostBoot = restore
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
if status != "" && c.Status != status {
|
||||||
|
c.Status = status
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if changed {
|
||||||
|
if err := config.SaveConfig(); err != nil {
|
||||||
|
fmt.Printf("Warning: failed to save host boot restore state: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartHostBootRestore() {
|
||||||
|
go RestoreHostBootState()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RestoreHostBootState() {
|
||||||
|
if config.AppConfig == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
|
lxcManager := lxc.NewManager()
|
||||||
|
kvmManager := kvm.NewManager()
|
||||||
|
containers := append([]config.Container(nil), config.AppConfig.Containers...)
|
||||||
|
|
||||||
|
for _, c := range containers {
|
||||||
|
if !c.RestoreOnHostBoot {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if c.PolicyBlocked {
|
||||||
|
fmt.Printf("Skipping host boot restore for %s: policy blocked\n", c.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if lxc.IsExpired(c) {
|
||||||
|
fmt.Printf("Skipping host boot restore for %s: expired at %s\n", c.Name, c.ExpiresAt)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
status, err := runtimeStatus(c, lxcManager, kvmManager)
|
||||||
|
if err == nil && status == "running" {
|
||||||
|
config.UpdateContainerStatusAndRestore(c.ID, "running", true)
|
||||||
|
if !c.IsKVM() {
|
||||||
|
_ = lxcManager.ApplyPortMappings(c.ID)
|
||||||
|
} else {
|
||||||
|
_ = lxc.NewManager().ApplyPortMappings(c.ID)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Restoring workload after host boot: %s (ID=%d)\n", c.Name, c.ID)
|
||||||
|
if c.IsKVM() {
|
||||||
|
if err := kvmManager.StartContainer(c.ID); err != nil {
|
||||||
|
fmt.Printf("Warning: failed to restore KVM %s: %v\n", c.Name, err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := lxcManager.StartContainer(c.ID); err != nil {
|
||||||
|
fmt.Printf("Warning: failed to restore LXC %s: %v\n", c.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lxc.EnsureAllRunningPortMappings()
|
||||||
|
}
|
||||||
|
|
||||||
|
func runtimeStatus(c config.Container, lxcManager *lxc.Manager, kvmManager *kvm.Manager) (string, error) {
|
||||||
|
if c.IsKVM() {
|
||||||
|
return kvmManager.GetContainerStatus(c.VirshName())
|
||||||
|
}
|
||||||
|
return lxcManager.GetContainerStatus(c.LxcName())
|
||||||
|
}
|
||||||
@@ -128,6 +128,7 @@ type Container struct {
|
|||||||
IOReadMBps int `json:"io_read_mbps"`
|
IOReadMBps int `json:"io_read_mbps"`
|
||||||
IOWriteMBps int `json:"io_write_mbps"`
|
IOWriteMBps int `json:"io_write_mbps"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
|
RestoreOnHostBoot bool `json:"restore_on_host_boot,omitempty"`
|
||||||
IP string `json:"ip"`
|
IP string `json:"ip"`
|
||||||
LANIPv4Mode string `json:"lan_ipv4_mode,omitempty"`
|
LANIPv4Mode string `json:"lan_ipv4_mode,omitempty"`
|
||||||
LANInterface string `json:"lan_interface,omitempty"`
|
LANInterface string `json:"lan_interface,omitempty"`
|
||||||
@@ -1218,6 +1219,23 @@ func UpdateContainerStatus(id int, status string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UpdateContainerStatusAndRestore(id int, status string, restoreOnHostBoot bool) {
|
||||||
|
c := FindContainer(id)
|
||||||
|
if c != nil {
|
||||||
|
c.Status = status
|
||||||
|
c.RestoreOnHostBoot = restoreOnHostBoot
|
||||||
|
SaveConfig()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetContainerRestoreOnHostBoot(id int, restore bool) {
|
||||||
|
c := FindContainer(id)
|
||||||
|
if c != nil {
|
||||||
|
c.RestoreOnHostBoot = restore
|
||||||
|
SaveConfig()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func SetContainerPolicyBlock(id int, blocked bool, reason string) {
|
func SetContainerPolicyBlock(id int, blocked bool, reason string) {
|
||||||
c := FindContainer(id)
|
c := FindContainer(id)
|
||||||
if c == nil {
|
if c == nil {
|
||||||
|
|||||||
@@ -209,6 +209,7 @@ func ensureSchema() error {
|
|||||||
io_read_mbps INTEGER NOT NULL DEFAULT 0,
|
io_read_mbps INTEGER NOT NULL DEFAULT 0,
|
||||||
io_write_mbps INTEGER NOT NULL DEFAULT 0,
|
io_write_mbps INTEGER NOT NULL DEFAULT 0,
|
||||||
status TEXT,
|
status TEXT,
|
||||||
|
restore_on_host_boot INTEGER NOT NULL DEFAULT 0,
|
||||||
ip TEXT,
|
ip TEXT,
|
||||||
lan_ipv4_mode TEXT,
|
lan_ipv4_mode TEXT,
|
||||||
lan_interface TEXT,
|
lan_interface TEXT,
|
||||||
@@ -459,6 +460,7 @@ func ensureSchemaMigrations() error {
|
|||||||
{"containers", "firewall_rules", "TEXT"},
|
{"containers", "firewall_rules", "TEXT"},
|
||||||
{"containers", "allowed_image_ids", "TEXT"},
|
{"containers", "allowed_image_ids", "TEXT"},
|
||||||
{"containers", "image_limit_configured", "INTEGER NOT NULL DEFAULT 0"},
|
{"containers", "image_limit_configured", "INTEGER NOT NULL DEFAULT 0"},
|
||||||
|
{"containers", "restore_on_host_boot", "INTEGER NOT NULL DEFAULT 0"},
|
||||||
{"containers", "lan_ipv4_mode", "TEXT"},
|
{"containers", "lan_ipv4_mode", "TEXT"},
|
||||||
{"containers", "lan_interface", "TEXT"},
|
{"containers", "lan_interface", "TEXT"},
|
||||||
{"containers", "lan_ipv4_address", "TEXT NOT NULL DEFAULT ''"},
|
{"containers", "lan_ipv4_address", "TEXT NOT NULL DEFAULT ''"},
|
||||||
@@ -739,20 +741,20 @@ func saveContainers(tx *sql.Tx) error {
|
|||||||
monthly_traffic_gb, traffic_mode, traffic_in_gb,
|
monthly_traffic_gb, traffic_mode, traffic_in_gb,
|
||||||
traffic_out_gb, traffic_used_rx, traffic_used_tx, traffic_reset_date,
|
traffic_out_gb, traffic_used_rx, traffic_used_tx, traffic_reset_date,
|
||||||
io_speed_mbps, io_read_mbps, io_write_mbps,
|
io_speed_mbps, io_read_mbps, io_write_mbps,
|
||||||
status, ip, lan_ipv4_mode, lan_interface, lan_ipv4_address, lan_ipv4_prefix_len, lan_ipv4_gateway,
|
status, restore_on_host_boot, ip, lan_ipv4_mode, lan_interface, lan_ipv4_address, lan_ipv4_prefix_len, lan_ipv4_gateway,
|
||||||
ipv6, ipv6_prefix_len, ipv6_interface, vnc_port, ssh_port, ssh_password,
|
ipv6, ipv6_prefix_len, ipv6_interface, vnc_port, ssh_port, ssh_password,
|
||||||
ssh_host_key, port_mapping_limit, snapshot_limit, created_at, expires_at,
|
ssh_host_key, port_mapping_limit, snapshot_limit, created_at, expires_at,
|
||||||
snapshot_schedule_enabled, snapshot_schedule_interval_hours, snapshot_schedule_time,
|
snapshot_schedule_enabled, snapshot_schedule_interval_hours, snapshot_schedule_time,
|
||||||
snapshot_schedule_last_run, snapshot_schedule_next_run, snapshot_schedule_created_by,
|
snapshot_schedule_last_run, snapshot_schedule_next_run, snapshot_schedule_created_by,
|
||||||
policy_blocked, policy_blocked_reason, policy_blocked_at,
|
policy_blocked, policy_blocked_reason, policy_blocked_at,
|
||||||
firewall_enabled, firewall_default_action, firewall_rules, allowed_image_ids, image_limit_configured
|
firewall_enabled, firewall_default_action, firewall_rules, allowed_image_ids, image_limit_configured
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
c.ID, c.UUID, c.Name, c.Virtualization, c.LXCName, c.KVMName, c.DiskImage, c.MACAddress, c.Template,
|
c.ID, c.UUID, c.Name, c.Virtualization, c.LXCName, c.KVMName, c.DiskImage, c.MACAddress, c.Template,
|
||||||
c.VCPU, c.RAMMB, c.DiskGB, c.NetworkBWMbps, c.NetworkDownMbps, c.NetworkUpMbps,
|
c.VCPU, c.RAMMB, c.DiskGB, c.NetworkBWMbps, c.NetworkDownMbps, c.NetworkUpMbps,
|
||||||
c.MonthlyTrafficGB, c.TrafficMode, c.TrafficInGB,
|
c.MonthlyTrafficGB, c.TrafficMode, c.TrafficInGB,
|
||||||
c.TrafficOutGB, c.TrafficUsedRX, c.TrafficUsedTX, c.TrafficResetDate,
|
c.TrafficOutGB, c.TrafficUsedRX, c.TrafficUsedTX, c.TrafficResetDate,
|
||||||
c.IOSpeedMBps, c.IOReadMBps, c.IOWriteMBps,
|
c.IOSpeedMBps, c.IOReadMBps, c.IOWriteMBps,
|
||||||
c.Status, c.IP, c.LANIPv4Mode, c.LANInterface, c.LANIPv4Address, c.LANIPv4PrefixLen, c.LANIPv4Gateway,
|
c.Status, boolInt(c.RestoreOnHostBoot), c.IP, c.LANIPv4Mode, c.LANInterface, c.LANIPv4Address, c.LANIPv4PrefixLen, c.LANIPv4Gateway,
|
||||||
c.IPv6, c.IPv6PrefixLen, c.IPv6Interface, c.VNCPort, c.SSHPort, c.SSHPassword,
|
c.IPv6, c.IPv6PrefixLen, c.IPv6Interface, c.VNCPort, c.SSHPort, c.SSHPassword,
|
||||||
c.SSHHostKey, c.PortMappingLimit, c.SnapshotLimit, c.CreatedAt, c.ExpiresAt,
|
c.SSHHostKey, c.PortMappingLimit, c.SnapshotLimit, c.CreatedAt, c.ExpiresAt,
|
||||||
boolInt(c.SnapshotScheduleEnabled), c.SnapshotScheduleIntervalHours, c.SnapshotScheduleTime,
|
boolInt(c.SnapshotScheduleEnabled), c.SnapshotScheduleIntervalHours, c.SnapshotScheduleTime,
|
||||||
@@ -960,7 +962,7 @@ func loadContainers() ([]Container, error) {
|
|||||||
monthly_traffic_gb, traffic_mode, traffic_in_gb,
|
monthly_traffic_gb, traffic_mode, traffic_in_gb,
|
||||||
traffic_out_gb, traffic_used_rx, traffic_used_tx, traffic_reset_date,
|
traffic_out_gb, traffic_used_rx, traffic_used_tx, traffic_reset_date,
|
||||||
io_speed_mbps, io_read_mbps, io_write_mbps,
|
io_speed_mbps, io_read_mbps, io_write_mbps,
|
||||||
status, ip, lan_ipv4_mode, lan_interface, lan_ipv4_address, lan_ipv4_prefix_len, lan_ipv4_gateway,
|
status, restore_on_host_boot, ip, lan_ipv4_mode, lan_interface, lan_ipv4_address, lan_ipv4_prefix_len, lan_ipv4_gateway,
|
||||||
ipv6, ipv6_prefix_len, ipv6_interface, vnc_port, ssh_port, ssh_password,
|
ipv6, ipv6_prefix_len, ipv6_interface, vnc_port, ssh_port, ssh_password,
|
||||||
ssh_host_key, port_mapping_limit, snapshot_limit, created_at, expires_at,
|
ssh_host_key, port_mapping_limit, snapshot_limit, created_at, expires_at,
|
||||||
snapshot_schedule_enabled, snapshot_schedule_interval_hours, snapshot_schedule_time,
|
snapshot_schedule_enabled, snapshot_schedule_interval_hours, snapshot_schedule_time,
|
||||||
@@ -976,7 +978,7 @@ func loadContainers() ([]Container, error) {
|
|||||||
result := []Container{}
|
result := []Container{}
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var c Container
|
var c Container
|
||||||
var scheduleEnabled, policyBlocked, firewallEnabled, imageLimitConfigured int
|
var scheduleEnabled, policyBlocked, firewallEnabled, imageLimitConfigured, restoreOnHostBoot int
|
||||||
var firewallDefaultAction string
|
var firewallDefaultAction string
|
||||||
var firewallRulesJSON, allowedImageIDs sql.NullString
|
var firewallRulesJSON, allowedImageIDs sql.NullString
|
||||||
var lanIPv4Mode, lanInterface sql.NullString
|
var lanIPv4Mode, lanInterface sql.NullString
|
||||||
@@ -988,7 +990,7 @@ func loadContainers() ([]Container, error) {
|
|||||||
&c.MonthlyTrafficGB, &c.TrafficMode, &c.TrafficInGB,
|
&c.MonthlyTrafficGB, &c.TrafficMode, &c.TrafficInGB,
|
||||||
&c.TrafficOutGB, &c.TrafficUsedRX, &c.TrafficUsedTX, &c.TrafficResetDate,
|
&c.TrafficOutGB, &c.TrafficUsedRX, &c.TrafficUsedTX, &c.TrafficResetDate,
|
||||||
&c.IOSpeedMBps, &c.IOReadMBps, &c.IOWriteMBps,
|
&c.IOSpeedMBps, &c.IOReadMBps, &c.IOWriteMBps,
|
||||||
&c.Status, &c.IP, &lanIPv4Mode, &lanInterface, &lanIPv4Address, &lanIPv4PrefixLen, &lanIPv4Gateway,
|
&c.Status, &restoreOnHostBoot, &c.IP, &lanIPv4Mode, &lanInterface, &lanIPv4Address, &lanIPv4PrefixLen, &lanIPv4Gateway,
|
||||||
&c.IPv6, &c.IPv6PrefixLen, &c.IPv6Interface, &c.VNCPort, &c.SSHPort, &c.SSHPassword,
|
&c.IPv6, &c.IPv6PrefixLen, &c.IPv6Interface, &c.VNCPort, &c.SSHPort, &c.SSHPassword,
|
||||||
&c.SSHHostKey, &c.PortMappingLimit, &c.SnapshotLimit, &c.CreatedAt, &c.ExpiresAt,
|
&c.SSHHostKey, &c.PortMappingLimit, &c.SnapshotLimit, &c.CreatedAt, &c.ExpiresAt,
|
||||||
&scheduleEnabled, &c.SnapshotScheduleIntervalHours, &c.SnapshotScheduleTime,
|
&scheduleEnabled, &c.SnapshotScheduleIntervalHours, &c.SnapshotScheduleTime,
|
||||||
@@ -1006,6 +1008,7 @@ func loadContainers() ([]Container, error) {
|
|||||||
}
|
}
|
||||||
c.LANIPv4Gateway = lanIPv4Gateway.String
|
c.LANIPv4Gateway = lanIPv4Gateway.String
|
||||||
c.SnapshotScheduleEnabled = scheduleEnabled != 0
|
c.SnapshotScheduleEnabled = scheduleEnabled != 0
|
||||||
|
c.RestoreOnHostBoot = restoreOnHostBoot != 0
|
||||||
c.PolicyBlocked = policyBlocked != 0
|
c.PolicyBlocked = policyBlocked != 0
|
||||||
c.FirewallEnabled = firewallEnabled != 0
|
c.FirewallEnabled = firewallEnabled != 0
|
||||||
c.FirewallDefaultAction = normalizeFirewallDefaultAction(firewallDefaultAction)
|
c.FirewallDefaultAction = normalizeFirewallDefaultAction(firewallDefaultAction)
|
||||||
|
|||||||
@@ -649,7 +649,7 @@ func (m *Manager) StartContainer(id int) error {
|
|||||||
if !isWindows && c.IP != "" {
|
if !isWindows && c.IP != "" {
|
||||||
m.waitForCloudInitReady(name, c.IP, c.SSHPassword)
|
m.waitForCloudInitReady(name, c.IP, c.SSHPassword)
|
||||||
}
|
}
|
||||||
config.UpdateContainerStatus(id, "running")
|
config.UpdateContainerStatusAndRestore(id, "running", true)
|
||||||
if c.IPv6 != "" || len(c.IPv6Addresses) > 0 {
|
if c.IPv6 != "" || len(c.IPv6Addresses) > 0 {
|
||||||
if err := m.applyIPv6Runtime(c); err != nil {
|
if err := m.applyIPv6Runtime(c); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -728,13 +728,13 @@ func (m *Manager) StopContainer(id int) error {
|
|||||||
name := c.VirshName()
|
name := c.VirshName()
|
||||||
status, _ := m.GetContainerStatus(name)
|
status, _ := m.GetContainerStatus(name)
|
||||||
if status != "running" {
|
if status != "running" {
|
||||||
config.UpdateContainerStatus(id, "stopped")
|
config.UpdateContainerStatusAndRestore(id, "stopped", false)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
exec.Command("virsh", "shutdown", name).Run()
|
exec.Command("virsh", "shutdown", name).Run()
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
if status, _ := m.GetContainerStatus(name); status != "running" {
|
if status, _ := m.GetContainerStatus(name); status != "running" {
|
||||||
config.UpdateContainerStatus(id, "stopped")
|
config.UpdateContainerStatusAndRestore(id, "stopped", false)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
@@ -743,7 +743,7 @@ func (m *Manager) StopContainer(id int) error {
|
|||||||
if output, err := cmd.CombinedOutput(); err != nil {
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
return fmt.Errorf("virsh destroy failed: %v, output: %s", err, string(output))
|
return fmt.Errorf("virsh destroy failed: %v, output: %s", err, string(output))
|
||||||
}
|
}
|
||||||
config.UpdateContainerStatus(id, "stopped")
|
config.UpdateContainerStatusAndRestore(id, "stopped", false)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1730,7 +1730,7 @@ func (m *Manager) StartContainer(id int) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
config.UpdateContainerStatus(id, "running")
|
config.UpdateContainerStatusAndRestore(id, "running", true)
|
||||||
|
|
||||||
var ip string
|
var ip string
|
||||||
for retry := 0; retry < 10; retry++ {
|
for retry := 0; retry < 10; retry++ {
|
||||||
@@ -1951,7 +1951,7 @@ func (m *Manager) StopContainer(id int) error {
|
|||||||
|
|
||||||
status, _ := m.GetContainerStatus(lxcName)
|
status, _ := m.GetContainerStatus(lxcName)
|
||||||
if status != "running" {
|
if status != "running" {
|
||||||
config.UpdateContainerStatus(id, "stopped")
|
config.UpdateContainerStatusAndRestore(id, "stopped", false)
|
||||||
m.CleanPortMappings(id)
|
m.CleanPortMappings(id)
|
||||||
CleanFirewallRules(id)
|
CleanFirewallRules(id)
|
||||||
m.cleanupBandwidthLimit(lxcName)
|
m.cleanupBandwidthLimit(lxcName)
|
||||||
@@ -1966,13 +1966,13 @@ func (m *Manager) StopContainer(id int) error {
|
|||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(string(output), "not running") {
|
if strings.Contains(string(output), "not running") {
|
||||||
config.UpdateContainerStatus(id, "stopped")
|
config.UpdateContainerStatusAndRestore(id, "stopped", false)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("failed to stop container: %v, output: %s", err, string(output))
|
return fmt.Errorf("failed to stop container: %v, output: %s", err, string(output))
|
||||||
}
|
}
|
||||||
|
|
||||||
config.UpdateContainerStatus(id, "stopped")
|
config.UpdateContainerStatusAndRestore(id, "stopped", false)
|
||||||
fmt.Printf("Container %d (%s) stopped\n", id, c.Name)
|
fmt.Printf("Container %d (%s) stopped\n", id, c.Name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"clicd/internal/api"
|
"clicd/internal/api"
|
||||||
"clicd/internal/cli"
|
"clicd/internal/cli"
|
||||||
@@ -16,6 +19,8 @@ import (
|
|||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var shutdownCaptureOnce sync.Once
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
isTerminal := term.IsTerminal(int(os.Stdin.Fd()))
|
isTerminal := term.IsTerminal(int(os.Stdin.Fd()))
|
||||||
|
|
||||||
@@ -44,6 +49,8 @@ func main() {
|
|||||||
_ = cfg
|
_ = cfg
|
||||||
|
|
||||||
if isServerMode || (!isTerminal && !isCliMode) {
|
if isServerMode || (!isTerminal && !isCliMode) {
|
||||||
|
installShutdownStateCapture()
|
||||||
|
|
||||||
// Restore persisted state
|
// Restore persisted state
|
||||||
api.RestoreTasks()
|
api.RestoreTasks()
|
||||||
api.RestoreLoginLogs()
|
api.RestoreLoginLogs()
|
||||||
@@ -75,6 +82,7 @@ func main() {
|
|||||||
|
|
||||||
// Clean up stale container configs (LXC dir was deleted but config remains)
|
// Clean up stale container configs (LXC dir was deleted but config remains)
|
||||||
config.CleanStaleContainers()
|
config.CleanStaleContainers()
|
||||||
|
api.StartHostBootRestore()
|
||||||
lxc.EnsureAllRunningPortMappings()
|
lxc.EnsureAllRunningPortMappings()
|
||||||
|
|
||||||
// Pre-warm SSH for containers already running after host boot or service restart.
|
// Pre-warm SSH for containers already running after host boot or service restart.
|
||||||
@@ -97,6 +105,17 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func installShutdownStateCapture() {
|
||||||
|
signals := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
go func() {
|
||||||
|
sig := <-signals
|
||||||
|
fmt.Fprintf(os.Stderr, "Received %s, capturing workload restore state...\n", sig)
|
||||||
|
shutdownCaptureOnce.Do(api.CaptureRuntimeRestoreState)
|
||||||
|
os.Exit(0)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
func isWebPanelSystemdRunning() bool {
|
func isWebPanelSystemdRunning() bool {
|
||||||
cmd := exec.Command("systemctl", "is-active", "clicd")
|
cmd := exec.Command("systemctl", "is-active", "clicd")
|
||||||
output, err := cmd.Output()
|
output, err := cmd.Output()
|
||||||
|
|||||||
Reference in New Issue
Block a user