From bf9a37623a0803de2e888702a876b8e057e446e8 Mon Sep 17 00:00:00 2001 From: zhengkunwang223 Date: Tue, 28 Mar 2023 10:52:17 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AF=B9=E6=8E=A5=20docker=20compose?= =?UTF-8?q?=20SDK?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/service/app_utils.go | 8 +-- backend/app/service/container.go | 4 +- backend/utils/compose/compose.go | 23 ------- backend/utils/docker/compose.go | 114 +++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 31 deletions(-) create mode 100644 backend/utils/docker/compose.go diff --git a/backend/app/service/app_utils.go b/backend/app/service/app_utils.go index f193cb147..a1efea3bb 100644 --- a/backend/app/service/app_utils.go +++ b/backend/app/service/app_utils.go @@ -23,6 +23,7 @@ import ( "github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/utils/common" "github.com/1Panel-dev/1Panel/backend/utils/compose" + composeV2 "github.com/1Panel-dev/1Panel/backend/utils/docker" "github.com/1Panel-dev/1Panel/backend/utils/files" "github.com/pkg/errors" ) @@ -223,12 +224,7 @@ func updateInstall(installId uint, detailId uint) error { } func getContainerNames(install model.AppInstall) ([]string, error) { - composeMap := install.DockerCompose - envMap := make(map[string]interface{}) - _ = json.Unmarshal([]byte(install.Env), &envMap) - newEnvMap := make(map[string]string, len(envMap)) - handleMap(envMap, newEnvMap) - project, err := compose.GetComposeProject([]byte(composeMap), newEnvMap) + project, err := composeV2.GetComposeProject(install.Name, install.GetPath(), []byte(install.DockerCompose), []byte(install.Env)) if err != nil { return nil, err } diff --git a/backend/app/service/container.go b/backend/app/service/container.go index 7ae2709a7..f8b97120e 100644 --- a/backend/app/service/container.go +++ b/backend/app/service/container.go @@ -225,9 +225,9 @@ func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error case constant.ContainerOpStart: err = client.ContainerStart(ctx, req.Name, types.ContainerStartOptions{}) case constant.ContainerOpStop: - err = client.ContainerStop(ctx, req.Name, nil) + err = client.ContainerStop(ctx, req.Name, container.StopOptions{}) case constant.ContainerOpRestart: - err = client.ContainerRestart(ctx, req.Name, nil) + err = client.ContainerRestart(ctx, req.Name, container.StopOptions{}) case constant.ContainerOpKill: err = client.ContainerKill(ctx, req.Name, "SIGKILL") case constant.ContainerOpPause: diff --git a/backend/utils/compose/compose.go b/backend/utils/compose/compose.go index b990275f0..e0ed15037 100644 --- a/backend/utils/compose/compose.go +++ b/backend/utils/compose/compose.go @@ -2,8 +2,6 @@ package compose import ( "github.com/1Panel-dev/1Panel/backend/utils/cmd" - "github.com/compose-spec/compose-go/loader" - "github.com/compose-spec/compose-go/types" ) func Up(filePath string) (string, error) { @@ -35,24 +33,3 @@ func Operate(filePath, operation string) (string, error) { stdout, err := cmd.Execf("docker-compose -f %s %s", filePath, operation) return stdout, err } - -func GetComposeProject(yml []byte, env map[string]string) (*types.Project, error) { - var configFiles []types.ConfigFile - configFiles = append(configFiles, types.ConfigFile{ - Filename: "docker-compose.yml", - Content: yml}, - ) - details := types.ConfigDetails{ - WorkingDir: "", - ConfigFiles: configFiles, - Environment: env, - } - - project, err := loader.Load(details, func(options *loader.Options) { - - }) - if err != nil { - return nil, err - } - return project, nil -} diff --git a/backend/utils/docker/compose.go b/backend/utils/docker/compose.go new file mode 100644 index 000000000..882ea5bdd --- /dev/null +++ b/backend/utils/docker/compose.go @@ -0,0 +1,114 @@ +package docker + +import ( + "context" + "github.com/compose-spec/compose-go/loader" + "github.com/compose-spec/compose-go/types" + "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/flags" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/compose" + "github.com/docker/docker/client" + "github.com/joho/godotenv" + "strings" + "time" +) + +type ComposeService struct { + api.Service + project *types.Project +} + +func NewComposeService(ops ...command.DockerCliOption) (*ComposeService, error) { + apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + if err != nil { + return nil, err + } + ops = append(ops, command.WithAPIClient(apiClient), command.WithDefaultContextStoreConfig()) + cli, err := command.NewDockerCli(ops...) + if err != nil { + return nil, err + } + cliOp := flags.NewClientOptions() + if err := cli.Initialize(cliOp); err != nil { + return nil, err + } + service := compose.NewComposeService(cli) + return &ComposeService{service, nil}, nil +} + +func (s *ComposeService) SetProject(project *types.Project) { + s.project = project + for i, s := range project.Services { + s.CustomLabels = map[string]string{ + api.ProjectLabel: project.Name, + api.ServiceLabel: s.Name, + api.VersionLabel: api.ComposeVersion, + api.WorkingDirLabel: project.WorkingDir, + api.ConfigFilesLabel: strings.Join(project.ComposeFiles, ","), + api.OneoffLabel: "False", + } + project.Services[i] = s + } +} + +func (s *ComposeService) ComposeUp() error { + return s.Up(context.Background(), s.project, api.UpOptions{ + Create: api.CreateOptions{ + Timeout: getComposeTimeout(), + }, + Start: api.StartOptions{ + WaitTimeout: *getComposeTimeout(), + }, + }) +} + +func (s *ComposeService) ComposeDown() error { + return s.Down(context.Background(), s.project.Name, api.DownOptions{}) +} + +func (s *ComposeService) ComposeStart() error { + return s.Start(context.Background(), s.project.Name, api.StartOptions{}) +} + +func (s *ComposeService) ComposeRestart() error { + return s.Restart(context.Background(), s.project.Name, api.RestartOptions{}) +} + +func (s *ComposeService) ComposeStop() error { + return s.Stop(context.Background(), s.project.Name, api.StopOptions{}) +} + +func (s *ComposeService) ComposeCreate() error { + return s.Create(context.Background(), s.project, api.CreateOptions{}) +} + +func GetComposeProject(projectName, workDir string, yml []byte, env []byte) (*types.Project, error) { + var configFiles []types.ConfigFile + configFiles = append(configFiles, types.ConfigFile{ + Filename: "docker-compose.yml", + Content: yml}, + ) + envMap, err := godotenv.UnmarshalBytes(env) + if err != nil { + return nil, err + } + details := types.ConfigDetails{ + WorkingDir: workDir, + ConfigFiles: configFiles, + Environment: envMap, + } + project, err := loader.Load(details, func(options *loader.Options) { + options.SetProjectName(projectName, true) + options.ResolvePaths = true + }) + if err != nil { + return nil, err + } + return project, nil +} + +func getComposeTimeout() *time.Duration { + timeout := time.Minute * time.Duration(10) + return &timeout +}