mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/1Panel-dev/1Panel.git
synced 2026-09-20 08:03:55 +08:00
296 lines
8.9 KiB
Go
296 lines
8.9 KiB
Go
package compose
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/app/task"
|
|
"github.com/1Panel-dev/1Panel/agent/buserr"
|
|
"github.com/1Panel-dev/1Panel/agent/global"
|
|
"github.com/1Panel-dev/1Panel/agent/i18n"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
|
)
|
|
|
|
func checkCmd() error {
|
|
if global.CONF.DockerConfig.Command == "" {
|
|
dockerComposeCmd := common.GetDockerComposeCommand()
|
|
if dockerComposeCmd == "" {
|
|
return buserr.New("ErrDockerComposeCmdNotFound")
|
|
}
|
|
global.CONF.DockerConfig.Command = dockerComposeCmd
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getComposeBaseCmd() (string, []string) {
|
|
cmdStr := strings.TrimSpace(global.CONF.DockerConfig.Command)
|
|
parts := strings.Fields(cmdStr)
|
|
if len(parts) == 0 {
|
|
return "", nil
|
|
}
|
|
return parts[0], parts[1:]
|
|
}
|
|
|
|
func Up(filePath string, projectName ...string) (string, error) {
|
|
if err := checkCmd(); err != nil {
|
|
return "", err
|
|
}
|
|
base, extra := getComposeBaseCmd()
|
|
args := appendProjectName(append([]string(nil), extra...), projectName)
|
|
args = append(args, upArgs(filePath, false)...)
|
|
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(base, args...)
|
|
}
|
|
|
|
func UpWithoutPull(filePath string, projectName ...string) (string, error) {
|
|
if err := checkCmd(); err != nil {
|
|
return "", err
|
|
}
|
|
base, extra := getComposeBaseCmd()
|
|
args := appendProjectName(append([]string(nil), extra...), projectName)
|
|
args = append(args, upArgs(filePath, true)...)
|
|
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(base, args...)
|
|
}
|
|
|
|
func upArgs(filePath string, withoutPull bool) []string {
|
|
args := loadFiles(filePath)
|
|
args = append(args, "up", "-d")
|
|
if withoutPull {
|
|
args = append(args, "--pull", "never", "--no-build")
|
|
}
|
|
return args
|
|
}
|
|
|
|
func UpWithTask(filePath string, task *task.Task, forcePull bool, projectName ...string) error {
|
|
if err := PullComposeImages(filePath, forcePull, task, projectName...); err != nil {
|
|
return err
|
|
}
|
|
base, extra := getComposeBaseCmd()
|
|
args := appendProjectName(append([]string(nil), extra...), projectName)
|
|
args = append(args, upArgs(filePath, false)...)
|
|
return cmd.NewCommandMgr(cmd.WithTask(*task), cmd.WithTimeout(20*time.Minute)).Run(base, args...)
|
|
}
|
|
|
|
func BuildWithTask(filePath, projectName string, task *task.Task) error {
|
|
if err := checkCmd(); err != nil {
|
|
return err
|
|
}
|
|
base, extra := getComposeBaseCmd()
|
|
args := append([]string(nil), extra...)
|
|
if projectName != "" {
|
|
args = append(args, "--project-name", projectName)
|
|
}
|
|
args = append(args, loadFiles(filePath)...)
|
|
args = append(args, "build")
|
|
return cmd.NewCommandMgr(cmd.WithTask(*task), cmd.WithTimeout(120*time.Minute)).Run(base, args...)
|
|
}
|
|
|
|
func PullComposeImages(filePath string, forcePull bool, task *task.Task, projectName ...string) error {
|
|
return pullComposeImages(filePath, forcePull, task, projectName...)
|
|
}
|
|
|
|
func pullComposeImages(filePath string, forcePull bool, task *task.Task, projectName ...string) error {
|
|
images, err := GetComposeImages(filePath, projectName...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
dockerCLi, err := docker.NewClient()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, image := range images {
|
|
if !forcePull {
|
|
if exist, _ := dockerCLi.ImageExists(image); exist {
|
|
if task != nil {
|
|
task.Log(i18n.GetMsgByKey("UseExistImage"))
|
|
}
|
|
continue
|
|
}
|
|
}
|
|
|
|
if task != nil {
|
|
task.Log(i18n.GetWithName("PullImageStart", image))
|
|
}
|
|
pullErr := error(nil)
|
|
if task != nil {
|
|
pullErr = dockerCLi.PullImageWithProcess(task, image)
|
|
} else {
|
|
pullErr = docker.PullImage(image)
|
|
}
|
|
if pullErr != nil {
|
|
errMsg := ""
|
|
errOur := pullErr.Error()
|
|
if errOur != "" {
|
|
if strings.Contains(errOur, "no such host") {
|
|
errMsg = i18n.GetMsgByKey("ErrNoSuchHost") + ":"
|
|
}
|
|
if strings.Contains(errOur, "Error response from daemon") {
|
|
errMsg = i18n.GetMsgByKey("PullImageTimeout") + ":"
|
|
}
|
|
}
|
|
message := errMsg + errOur
|
|
installErr := errors.New(message)
|
|
if task != nil {
|
|
task.LogFailedWithErr(i18n.GetMsgByKey("PullImage"), installErr)
|
|
}
|
|
if exist, _ := dockerCLi.ImageExists(image); !exist {
|
|
return installErr
|
|
}
|
|
if task != nil {
|
|
task.Log(i18n.GetMsgByKey("UseExistImage"))
|
|
}
|
|
} else if task != nil {
|
|
task.Log(i18n.GetMsgByKey("PullImageSuccess"))
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func GetComposeImages(filePath string, projectName ...string) ([]string, error) {
|
|
images, err := getComposeImagesByCommand(filePath, projectName...)
|
|
if err == nil {
|
|
return images, nil
|
|
}
|
|
|
|
content, readErr := os.ReadFile(filePath)
|
|
if readErr != nil {
|
|
return nil, readErr
|
|
}
|
|
env, _ := os.ReadFile(path.Join(path.Dir(filePath), ".env"))
|
|
images, parseErr := docker.GetImagesFromDockerCompose(env, content)
|
|
if parseErr != nil {
|
|
return nil, fmt.Errorf("get compose images failed, cmd err: %v, parse err: %v", err, parseErr)
|
|
}
|
|
return images, nil
|
|
}
|
|
|
|
func getComposeImagesByCommand(filePath string, projectName ...string) ([]string, error) {
|
|
if err := checkCmd(); err != nil {
|
|
return nil, err
|
|
}
|
|
base, extra := getComposeBaseCmd()
|
|
args := appendProjectName(append([]string(nil), extra...), projectName)
|
|
args = append(args, loadFiles(filePath)...)
|
|
args = append(args, "config", "--format", "json", "--no-normalize")
|
|
stdout, err := cmd.NewCommandMgr(cmd.WithTimeout(5*time.Minute)).
|
|
RunWithStdout(base, args...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("run compose config --format json --no-normalize failed, std: %s, err: %v", stdout, err)
|
|
}
|
|
|
|
var composeConfig struct {
|
|
Services map[string]struct {
|
|
Image string `json:"image"`
|
|
} `json:"services"`
|
|
}
|
|
if err = json.Unmarshal([]byte(stdout), &composeConfig); err != nil {
|
|
return nil, fmt.Errorf("parse compose config json failed, std: %s, err: %v", stdout, err)
|
|
}
|
|
|
|
var images []string
|
|
seen := make(map[string]struct{})
|
|
for _, service := range composeConfig.Services {
|
|
image := strings.TrimSpace(service.Image)
|
|
if image == "" {
|
|
continue
|
|
}
|
|
if _, ok := seen[image]; ok {
|
|
continue
|
|
}
|
|
seen[image] = struct{}{}
|
|
images = append(images, image)
|
|
}
|
|
if len(images) == 0 {
|
|
return nil, errors.New("no images found from compose config json")
|
|
}
|
|
return images, nil
|
|
}
|
|
|
|
func Down(filePath string, projectName ...string) (string, error) {
|
|
if err := checkCmd(); err != nil {
|
|
return "", err
|
|
}
|
|
base, extra := getComposeBaseCmd()
|
|
args := appendProjectName(append([]string(nil), extra...), projectName)
|
|
args = append(args, loadFiles(filePath)...)
|
|
args = append(args, "down", "--remove-orphans")
|
|
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(base, args...)
|
|
}
|
|
|
|
func Stop(filePath string, projectName ...string) (string, error) {
|
|
if err := checkCmd(); err != nil {
|
|
return "", err
|
|
}
|
|
base, extra := getComposeBaseCmd()
|
|
args := appendProjectName(append([]string(nil), extra...), projectName)
|
|
args = append(args, loadFiles(filePath)...)
|
|
args = append(args, "stop")
|
|
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(base, args...)
|
|
}
|
|
|
|
func Restart(filePath string, projectName ...string) (string, error) {
|
|
if err := checkCmd(); err != nil {
|
|
return "", err
|
|
}
|
|
base, extra := getComposeBaseCmd()
|
|
args := appendProjectName(append([]string(nil), extra...), projectName)
|
|
args = append(args, loadFiles(filePath)...)
|
|
args = append(args, "restart")
|
|
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(base, args...)
|
|
}
|
|
|
|
func Operate(filePath, operation string, projectName ...string) (string, error) {
|
|
if err := checkCmd(); err != nil {
|
|
return "", err
|
|
}
|
|
base, extra := getComposeBaseCmd()
|
|
args := appendProjectName(append([]string(nil), extra...), projectName)
|
|
args = append(args, loadFiles(filePath)...)
|
|
args = append(args, operation)
|
|
return cmd.NewCommandMgr(cmd.WithTimeout(20*time.Minute)).RunWithStdout(base, args...)
|
|
}
|
|
|
|
func DownAndUp(filePath string, projectName ...string) (string, error) {
|
|
if err := checkCmd(); err != nil {
|
|
return "", err
|
|
}
|
|
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(20 * time.Minute))
|
|
base, extra := getComposeBaseCmd()
|
|
argsDown := appendProjectName(append([]string(nil), extra...), projectName)
|
|
argsDown = append(argsDown, loadFiles(filePath)...)
|
|
argsDown = append(argsDown, "down")
|
|
stdout, err := cmdMgr.RunWithStdout(base, argsDown...)
|
|
if err != nil {
|
|
return stdout, err
|
|
}
|
|
argsUp := appendProjectName(append([]string(nil), extra...), projectName)
|
|
argsUp = append(argsUp, loadFiles(filePath)...)
|
|
argsUp = append(argsUp, "up", "-d")
|
|
stdout, err = cmdMgr.RunWithStdout(base, argsUp...)
|
|
return stdout, err
|
|
}
|
|
|
|
func appendProjectName(args []string, projectName []string) []string {
|
|
if len(projectName) > 0 && strings.TrimSpace(projectName[0]) != "" {
|
|
args = append(args, "--project-name", projectName[0])
|
|
}
|
|
return args
|
|
}
|
|
|
|
func loadFiles(filePath string) []string {
|
|
var fileItem []string
|
|
for _, item := range strings.Split(filePath, ",") {
|
|
if len(item) != 0 {
|
|
fileItem = append(fileItem, "-f", item)
|
|
}
|
|
}
|
|
return fileItem
|
|
}
|