mirror of
https://github.com/MengMengCode/CLICD.git
synced 2026-08-06 05:52:19 +08:00
Sync CLI and Web container state
This commit is contained in:
@@ -62,3 +62,4 @@ backend/tmp/
|
|||||||
# OS
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
linux.txt
|
||||||
|
|||||||
@@ -19,9 +19,13 @@ func Run() {
|
|||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
if _, err := config.InitConfig(); err != nil {
|
||||||
|
fmt.Printf("Failed to reload config: %v\n", err)
|
||||||
|
waitEnter(reader)
|
||||||
|
}
|
||||||
clearScreen()
|
clearScreen()
|
||||||
printMenu()
|
printMenu()
|
||||||
fmt.Print("\nSelect action [1-10,0/q]: ")
|
fmt.Print("\nSelect action [1-11,0/q]: ")
|
||||||
input, _ := reader.ReadString('\n')
|
input, _ := reader.ReadString('\n')
|
||||||
input = strings.TrimSpace(input)
|
input = strings.TrimSpace(input)
|
||||||
|
|
||||||
@@ -63,6 +67,10 @@ func Run() {
|
|||||||
cliToggleWebPanel()
|
cliToggleWebPanel()
|
||||||
waitEnter(reader)
|
waitEnter(reader)
|
||||||
case "10":
|
case "10":
|
||||||
|
clearScreen()
|
||||||
|
cliImportExistingContainers()
|
||||||
|
waitEnter(reader)
|
||||||
|
case "11":
|
||||||
clearScreen()
|
clearScreen()
|
||||||
cliUninstall(reader)
|
cliUninstall(reader)
|
||||||
return
|
return
|
||||||
@@ -105,7 +113,8 @@ func printMenu() {
|
|||||||
fmt.Println(" 7. Reinstall container")
|
fmt.Println(" 7. Reinstall container")
|
||||||
fmt.Println(" 8. Reset web admin password")
|
fmt.Println(" 8. Reset web admin password")
|
||||||
fmt.Printf(" 9. %s web panel\n", webStatus)
|
fmt.Printf(" 9. %s web panel\n", webStatus)
|
||||||
fmt.Println(" 10. Uninstall CLICD")
|
fmt.Println(" 10. Import existing ct-* containers")
|
||||||
|
fmt.Println(" 11. Uninstall CLICD")
|
||||||
fmt.Println(" 0. System info")
|
fmt.Println(" 0. System info")
|
||||||
fmt.Println(" q. Quit")
|
fmt.Println(" q. Quit")
|
||||||
}
|
}
|
||||||
@@ -179,6 +188,7 @@ func cliCreateContainer(reader *bufio.Reader) {
|
|||||||
if container != nil {
|
if container != nil {
|
||||||
fmt.Printf("SSH: root / %s, port %d -> 22\n", container.SSHPassword, container.SSHPort)
|
fmt.Printf("SSH: root / %s, port %d -> 22\n", container.SSHPassword, container.SSHPort)
|
||||||
}
|
}
|
||||||
|
restartWebPanelForConfigChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
func cliStartContainer(reader *bufio.Reader) {
|
func cliStartContainer(reader *bufio.Reader) {
|
||||||
@@ -232,6 +242,7 @@ func cliDeleteContainer(reader *bufio.Reader) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Printf("Container %s deleted\n", name)
|
fmt.Printf("Container %s deleted\n", name)
|
||||||
|
restartWebPanelForConfigChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
func cliReinstallContainer(reader *bufio.Reader) {
|
func cliReinstallContainer(reader *bufio.Reader) {
|
||||||
@@ -263,6 +274,7 @@ func cliReinstallContainer(reader *bufio.Reader) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Printf("Container %s reinstalled\n", name)
|
fmt.Printf("Container %s reinstalled\n", name)
|
||||||
|
restartWebPanelForConfigChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
func cliResetPassword(reader *bufio.Reader) {
|
func cliResetPassword(reader *bufio.Reader) {
|
||||||
@@ -281,7 +293,8 @@ func cliResetPassword(reader *bufio.Reader) {
|
|||||||
fmt.Printf("Reset failed: %v\n", err)
|
fmt.Printf("Reset failed: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Println("Admin password reset. Restart the web service for it to take effect.")
|
fmt.Println("Admin password reset.")
|
||||||
|
restartWebPanelForConfigChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
func cliToggleWebPanel() {
|
func cliToggleWebPanel() {
|
||||||
@@ -316,6 +329,28 @@ func isWebPanelRunning() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cliImportExistingContainers() {
|
||||||
|
fmt.Println("\n--- Import existing CLICD containers ---")
|
||||||
|
fmt.Println("This imports LXC containers named ct-{id} from /var/lib/lxc into CLICD config.")
|
||||||
|
fmt.Println("Containers with names like ubuntu or alpine are skipped because CLICD requires ct-{id}.")
|
||||||
|
|
||||||
|
imported, err := manager.ImportExistingClicdContainers()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Import failed: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(imported) == 0 {
|
||||||
|
fmt.Println("No new ct-* containers found to import.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Imported %d container(s):\n", len(imported))
|
||||||
|
for _, c := range imported {
|
||||||
|
fmt.Printf(" [%d] %s [%s]\n", c.ID, c.Name, c.Status)
|
||||||
|
}
|
||||||
|
restartWebPanelForConfigChange()
|
||||||
|
}
|
||||||
|
|
||||||
func cliUninstall(reader *bufio.Reader) {
|
func cliUninstall(reader *bufio.Reader) {
|
||||||
fmt.Println("\n--- Uninstall CLICD ---")
|
fmt.Println("\n--- Uninstall CLICD ---")
|
||||||
fmt.Println("This removes the CLICD service and /usr/local/bin/clicd.")
|
fmt.Println("This removes the CLICD service and /usr/local/bin/clicd.")
|
||||||
@@ -421,6 +456,14 @@ func runQuiet(name string, args ...string) {
|
|||||||
_ = exec.Command(name, args...).Run()
|
_ = exec.Command(name, args...).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func restartWebPanelForConfigChange() {
|
||||||
|
if err := restartService("clicd"); err != nil {
|
||||||
|
fmt.Printf("Web panel reload skipped: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("Web panel reloaded to pick up config changes.")
|
||||||
|
}
|
||||||
|
|
||||||
func stopService(name string) error {
|
func stopService(name string) error {
|
||||||
if commandExists("systemctl") {
|
if commandExists("systemctl") {
|
||||||
return exec.Command("systemctl", "stop", name).Run()
|
return exec.Command("systemctl", "stop", name).Run()
|
||||||
@@ -441,6 +484,16 @@ func startService(name string) error {
|
|||||||
return fmt.Errorf("no supported service manager found")
|
return fmt.Errorf("no supported service manager found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func restartService(name string) error {
|
||||||
|
if commandExists("systemctl") {
|
||||||
|
return exec.Command("systemctl", "restart", name).Run()
|
||||||
|
}
|
||||||
|
if commandExists("rc-service") {
|
||||||
|
return exec.Command("rc-service", name, "restart").Run()
|
||||||
|
}
|
||||||
|
return fmt.Errorf("no supported service manager found")
|
||||||
|
}
|
||||||
|
|
||||||
func cliShowInfo() {
|
func cliShowInfo() {
|
||||||
containers, err := manager.ListContainers()
|
containers, err := manager.ListContainers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1915,6 +1915,93 @@ func (m *Manager) ListContainers() ([]config.Container, error) {
|
|||||||
return containers, nil
|
return containers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImportExistingClicdContainers imports LXC containers named ct-{id} into the
|
||||||
|
// CLICD config. Containers with arbitrary LXC names cannot be imported because
|
||||||
|
// CLICD derives the runtime LXC name from the numeric container ID.
|
||||||
|
func (m *Manager) ImportExistingClicdContainers() ([]config.Container, error) {
|
||||||
|
entries, err := os.ReadDir(m.LxcPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
existingIDs := make(map[int]bool)
|
||||||
|
existingNames := make(map[string]bool)
|
||||||
|
maxID := config.AppConfig.NextContainerID - 1
|
||||||
|
for _, c := range config.AppConfig.Containers {
|
||||||
|
existingIDs[c.ID] = true
|
||||||
|
existingNames[c.Name] = true
|
||||||
|
if c.ID > maxID {
|
||||||
|
maxID = c.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
re := regexp.MustCompile(`^ct-([0-9]+)$`)
|
||||||
|
imported := make([]config.Container, 0)
|
||||||
|
for _, entry := range entries {
|
||||||
|
if !entry.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
matches := re.FindStringSubmatch(entry.Name())
|
||||||
|
if len(matches) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := strconv.Atoi(matches[1])
|
||||||
|
if err != nil || id <= 0 || existingIDs[id] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
name := entry.Name()
|
||||||
|
if existingNames[name] {
|
||||||
|
name = fmt.Sprintf("imported-%d", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
status, err := m.GetContainerStatus(entry.Name())
|
||||||
|
if err != nil || status == "" {
|
||||||
|
status = "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
c := config.Container{
|
||||||
|
ID: id,
|
||||||
|
UUID: config.NewContainerUUID(),
|
||||||
|
Name: name,
|
||||||
|
Template: "imported",
|
||||||
|
VCPU: 1,
|
||||||
|
RAMMB: 512,
|
||||||
|
DiskGB: 10,
|
||||||
|
NetworkBWMbps: 100,
|
||||||
|
MonthlyTrafficGB: 1000,
|
||||||
|
TrafficMode: "total",
|
||||||
|
Status: status,
|
||||||
|
CreatedAt: time.Now().Format(time.RFC3339),
|
||||||
|
PortMappingLimit: 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
if status == "running" {
|
||||||
|
if ip, err := m.GetContainerIP(entry.Name()); err == nil {
|
||||||
|
c.IP = ip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config.AppConfig.Containers = append(config.AppConfig.Containers, c)
|
||||||
|
imported = append(imported, c)
|
||||||
|
existingIDs[id] = true
|
||||||
|
existingNames[name] = true
|
||||||
|
if id > maxID {
|
||||||
|
maxID = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(imported) > 0 {
|
||||||
|
config.AppConfig.NextContainerID = maxID + 1
|
||||||
|
if err := config.SaveConfig(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return imported, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ReinstallContainer reinstalls the container OS
|
// ReinstallContainer reinstalls the container OS
|
||||||
func (m *Manager) ReinstallContainer(id int, templateID string) error {
|
func (m *Manager) ReinstallContainer(id int, templateID string) error {
|
||||||
c := config.FindContainer(id)
|
c := config.FindContainer(id)
|
||||||
|
|||||||
Reference in New Issue
Block a user