Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions agent/app/repo/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ func WithByName(name string) DBOption {
}
}

func WithByPath(path string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("path = ?", path)
}
}

func WithByAddr(addr string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("addr = ?", addr)
Expand Down
167 changes: 126 additions & 41 deletions agent/app/service/container_compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,25 +218,14 @@ func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) {
if err := validateComposeCreateName(req); err != nil {
return false, err
}
if cmd.CheckIllegal(req.Name, req.DirName, req.Path) {
if hasIllegalComposeCreateInput(req) {
return false, buserr.New("ErrCmdIllegal")
}
if req.From != "path" {
if err := checkComposeRecordName(composeCreateDirName(req)); err != nil {
return false, err
}
}
if err := u.loadPath(&req); err != nil {
return false, err
}
if err := newComposeEnv(req.Path, req.Env); err != nil {
return false, err
}
projectName, err := resolveComposeProjectName(req.Path, req.Name)
projectName, err := resolveComposeCreateProjectName(req)
if err != nil {
return false, err
}
if err := checkComposeRecordName(projectName); err != nil {
if err := checkComposeCreateDuplicate(req, projectName); err != nil {
return false, err
}
return true, nil
Expand All @@ -246,28 +235,23 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error {
if err := validateComposeCreateName(req); err != nil {
return err
}
if cmd.CheckIllegal(req.Name, req.DirName, req.Path) {
if hasIllegalComposeCreateInput(req) {
return buserr.New("ErrCmdIllegal")
}
if req.From != "path" {
if err := checkComposeRecordName(composeCreateDirName(req)); err != nil {
return err
}
}
if err := u.loadPath(&req); err != nil {
projectName, err := resolveComposeCreateProjectName(req)
if err != nil {
return err
}
if err := newComposeEnv(req.Path, req.Env); err != nil {
if err := checkComposeCreateDuplicate(req, projectName); err != nil {
return err
}
projectName, err := resolveComposeProjectName(req.Path, req.Name)
if err != nil {
if err := u.loadPath(&req); err != nil {
return err
}
req.Name = projectName
if err := checkComposeRecordName(req.Name); err != nil {
if err := newComposeEnv(req.Path, req.Env); err != nil {
return err
}
req.Name = projectName
taskItem, err := task.NewTaskWithOps(req.Name, task.TaskCreate, task.TaskScopeCompose, req.TaskID, 1)
if err != nil {
return fmt.Errorf("new task for image build failed, err: %v", err)
Expand Down Expand Up @@ -303,17 +287,41 @@ func checkComposeRecordName(name string) error {
return nil
}

func checkComposeCreateDuplicate(req dto.ComposeCreate, projectName string) error {
if err := checkComposeRecordName(projectName); err != nil {
return err
}
if req.From == "path" {
return nil
}
composeItem, _ := composeRepo.GetRecord(repo.WithByPath(composeCreatePath(req)))
if composeItem.ID != 0 && composeItem.Path != "" {
return buserr.New("ErrRecordExist")
}
return nil
}

func validateComposeCreateName(req dto.ComposeCreate) error {
name := strings.TrimSpace(req.Name)
if name != "" && !re.GetRegex(re.ComposeNamePattern).MatchString(name) {
return buserr.New("ErrComposeNameInvalid")
if req.From == "path" {
name := strings.TrimSpace(req.Name)
if name != "" && !re.GetRegex(re.ComposeNamePattern).MatchString(name) {
return buserr.New("ErrComposeNameInvalid")
}
return nil
}
if req.From != "path" && !re.GetRegex(re.ComposeNamePattern).MatchString(composeCreateDirName(req)) {
if !re.GetRegex(re.ComposeNamePattern).MatchString(composeCreateDirName(req)) {
return buserr.New("ErrComposeNameInvalid")
}
return nil
}

func hasIllegalComposeCreateInput(req dto.ComposeCreate) bool {
if req.From == "path" {
return cmd.CheckIllegal(req.Name, req.Path)
}
return cmd.CheckIllegal(composeCreateDirName(req))
}

func composeCreateDirName(req dto.ComposeCreate) string {
dirName := strings.TrimSpace(req.DirName)
if dirName == "" {
Expand All @@ -324,12 +332,89 @@ func composeCreateDirName(req dto.ComposeCreate) string {
return dirName
}

func resolveComposeProjectName(composePath, fallbackName string) (string, error) {
func composeCreatePath(req dto.ComposeCreate) string {
return filepath.Join(global.Dir.DataDir, "docker", "compose", composeCreateDirName(req), "docker-compose.yml")
}

func resolveComposeCreateProjectName(req dto.ComposeCreate) (string, error) {
if req.From == "path" {
envPath, err := createComposeTempFile(
filepath.Dir(primaryComposePath(req.Path)),
".1panel-compose-*.env",
req.Env,
)
if err != nil {
return "", err
}
defer os.Remove(envPath)
return resolveComposeProjectName(req.Path, req.Name, envPath)
}

dir := filepath.Dir(composeCreatePath(req))
cleanupDir, err := prepareComposeStagingDir(dir)
if err != nil {
return "", err
}
defer cleanupDir()

composePath, err := createComposeTempFile(dir, ".1panel-compose-*.yml", req.File)
if err != nil {
return "", err
}
defer os.Remove(composePath)

envPath, err := createComposeTempFile(dir, ".1panel-compose-*.env", req.Env)
if err != nil {
return "", err
}
defer os.Remove(envPath)

return resolveComposeProjectName(composePath, "", envPath)
}

func createComposeTempFile(dir, pattern, content string) (string, error) {
file, err := os.CreateTemp(dir, pattern)
if err != nil {
return "", err
}
filePath := file.Name()
if _, err := file.WriteString(content); err != nil {
_ = file.Close()
_ = os.Remove(filePath)
return "", err
}
if err := file.Close(); err != nil {
_ = os.Remove(filePath)
return "", err
}
return filePath, nil
}

func prepareComposeStagingDir(dir string) (func(), error) {
if err := os.MkdirAll(filepath.Dir(dir), os.ModePerm); err != nil {
return nil, err
}
created := false
if err := os.Mkdir(dir, os.ModePerm); err != nil {
if !errors.Is(err, os.ErrExist) {
return nil, err
}
} else {
created = true
}
Comment on lines +398 to +404
return func() {
if created {
_ = os.Remove(dir)
}
}, nil
}

func resolveComposeProjectName(composePath, fallbackName, envFile string) (string, error) {
// Preserve the name resolved by Compose (including a top-level name) so the
// container label and the local record always use the same project identity.
parentName := normalizeComposeProjectName(path.Base(path.Dir(primaryComposePath(composePath))))
fallbackName = strings.TrimSpace(fallbackName)
stdout, err := runComposeConfig(composePath, "")
stdout, err := runComposeConfig(composePath, "", envFile)
if err == nil {
projectName, parseErr := loadComposeProjectName(stdout)
if parseErr != nil {
Expand All @@ -345,7 +430,7 @@ func resolveComposeProjectName(composePath, fallbackName string) (string, error)
return parentName, nil
}
if fallbackName != "" {
if _, fallbackErr := runComposeConfig(composePath, fallbackName); fallbackErr != nil {
if _, fallbackErr := runComposeConfig(composePath, fallbackName, envFile); fallbackErr != nil {
return "", fallbackErr
}
return fallbackName, nil
Expand All @@ -358,15 +443,15 @@ func resolveComposeProjectName(composePath, fallbackName string) (string, error)

resolveErr := err
if parentName != "" {
if _, parentErr := runComposeConfig(composePath, parentName); parentErr == nil {
if _, parentErr := runComposeConfig(composePath, parentName, envFile); parentErr == nil {
return parentName, nil
} else {
resolveErr = parentErr
}
}

if fallbackName != "" && fallbackName != parentName {
if _, fallbackErr := runComposeConfig(composePath, fallbackName); fallbackErr == nil {
if _, fallbackErr := runComposeConfig(composePath, fallbackName, envFile); fallbackErr == nil {
return fallbackName, nil
} else {
return "", fallbackErr
Expand All @@ -378,8 +463,8 @@ func resolveComposeProjectName(composePath, fallbackName string) (string, error)
return "", resolveErr
}

func runComposeConfig(composePath, projectName string) ([]byte, error) {
configCmd := getComposeCmd(composePath, "config", projectName)
func runComposeConfig(composePath, projectName, envFile string) ([]byte, error) {
configCmd := getComposeCmdWithEnv(composePath, "config", envFile, projectName)
stdout, err := configCmd.Output()
if err != nil {
var stderr []byte
Expand Down Expand Up @@ -590,23 +675,23 @@ func (u *ContainerService) LoadComposeEnv(name string) (string, error) {

func (u *ContainerService) loadPath(req *dto.ComposeCreate) error {
if req.From == "template" || req.From == "edit" {
dir := fmt.Sprintf("%s/docker/compose/%s", global.Dir.DataDir, composeCreateDirName(*req))
composePath := composeCreatePath(*req)
dir := filepath.Dir(composePath)
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
return err
}
}

path := fmt.Sprintf("%s/docker-compose.yml", dir)
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, constant.FilePerm)
file, err := os.OpenFile(composePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, constant.FilePerm)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(string(req.File))
write.Flush()
req.Path = path
req.Path = composePath
}
return nil
}
Expand Down
9 changes: 8 additions & 1 deletion agent/app/service/runtime_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,15 @@ func reCreateRuntime(runtime *model.Runtime) {
}

func getComposeCmd(composePath, operate string, projectName ...string) *exec.Cmd {
return getComposeCmdWithEnv(composePath, operate, "", projectName...)
}

func getComposeCmdWithEnv(composePath, operate, envFile string, projectName ...string) *exec.Cmd {
dockerCommand := global.CONF.DockerConfig.Command
args := make([]string, 0, 7)
args := make([]string, 0, 9)
if envFile != "" {
args = append(args, "--env-file", envFile)
}
if len(projectName) > 0 && strings.TrimSpace(projectName[0]) != "" {
args = append(args, "--project-name", projectName[0])
}
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lang/modules/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1745,7 +1745,8 @@ const message = {
compose: 'Compose | Composes',
composeFile: 'Compose File',
fromChangeHelper: 'Switching the source will clean the current edited content. Continue?',
composePathHelper: 'Configuration file save path: {0}',
composePathHelper:
'Compose file save path: {0}; if a project name is specified in Docker Compose, it takes precedence; otherwise, {1} is used.',
composeNamePriorityHelper:
'Project name priority: name specified in Docker Compose config > file parent directory > name entered here.',
composeHelper:
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lang/modules/es-es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1772,7 +1772,8 @@ const message = {
compose: 'Compose | Composes',
composeFile: 'Archivo de Orquestación',
fromChangeHelper: 'Cambiar la fuente limpiará el contenido actualmente editado. ¿Desea continuar?',
composePathHelper: 'Ruta de guardado del archivo de configuración: {0}',
composePathHelper:
'Ruta de guardado del archivo Compose: {0}; si se especifica un nombre de proyecto en Docker Compose, tendrá prioridad; de lo contrario, se usará {1}.',
composeNamePriorityHelper:
'Prioridad del nombre del proyecto: nombre especificado en la configuración de Docker Compose > directorio padre del archivo > nombre indicado aquí.',
composeHelper:
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lang/modules/fa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1732,7 +1732,8 @@ const message = {
compose: 'Compose | Compose‌ها',
composeFile: 'فایل Compose',
fromChangeHelper: 'تغییر منبع محتوای ویرایش شده فعلی را پاک می‌کند. ادامه می‌دهید؟',
composePathHelper: 'مسیر ذخیره فایل پیکربندی: {0}',
composePathHelper:
'مسیر ذخیره فایل Compose: {0}؛ اگر نام پروژه‌ای در Docker Compose مشخص شده باشد، در اولویت است؛ در غیر این صورت از {1} استفاده می‌شود.',
composeNamePriorityHelper:
'اولویت نام پروژه: نام مشخص‌شده در پیکربندی Docker Compose > پوشه والد فایل > نام واردشده در اینجا.',
composeHelper: 'ترکیب ایجاد شده از طریق ویرایشگر یا قالب 1Panel در دایرکتوری {0}/docker/compose ذخیره می‌شود.',
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lang/modules/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1735,7 +1735,8 @@ const message = {
compose: '構成|作曲',
composeFile: 'オーケストレーションファイル',
fromChangeHelper: 'ソースを切り替えると、現在の編集されたコンテンツがきれいになります。続けたいですか?',
composePathHelper: '構成ファイル保存パス:{0}',
composePathHelper:
'Compose ファイルの保存パス:{0}。Docker Compose でプロジェクト名が指定されている場合はその名前を優先し、指定されていない場合は {1} を使用します。',
composeNamePriorityHelper:
'プロジェクト名の優先順位:Docker Compose 設定で指定された名前 > ファイルの親ディレクトリ名 > ここで入力した名前。',
composeHelper:
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lang/modules/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1716,7 +1716,8 @@ const message = {
compose: '컴포즈 | 컴포즈들',
composeFile: '컴포즈 파일',
fromChangeHelper: '소스를 변경하면 현재 편집한 내용이 삭제됩니다. 계속 하시겠습니까?',
composePathHelper: '구성 파일 저장 경로: {0}',
composePathHelper:
'Compose 파일 저장 경로: {0}. Docker Compose에 프로젝트 이름이 지정되어 있으면 해당 이름을 우선 사용하고, 지정되어 있지 않으면 {1}을(를) 사용합니다.',
composeNamePriorityHelper:
'프로젝트 이름 우선순위: Docker Compose 구성에 지정된 이름 > 파일의 상위 디렉터리 이름 > 여기에 입력한 이름.',
composeHelper: '1Panel 에디터나 템플릿을 통해 생성된 컴포지션은 {0}/docker/compose 디렉토리에 저장됩니다.',
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lang/modules/lo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1706,7 +1706,8 @@ const message = {
compose: 'Compose',
composeFile: 'ໄຟລ໌ Compose',
fromChangeHelper: 'ການປ່ຽນແຫຼ່ງທີ່ມາຈະລ້າງເນື້ອຫາທີ່ແກ້ໄຂໄວ້. ຕ້ອງການຕໍ່ຫຼືບໍ່?',
composePathHelper: 'ເສັ້ນທາງບັນທຶກໄຟລ໌ກຳນົດຄ່າ: {0}',
composePathHelper:
'ເສັ້ນທາງບັນທຶກໄຟລ໌ Compose: {0}; ຖ້າມີການລະບຸຊື່ໂຄງການໃນ Docker Compose ຈະໃຊ້ຊື່ນັ້ນກ່ອນ, ຖ້າບໍ່ມີຈະໃຊ້ {1}.',
composeNamePriorityHelper:
'ລຳດັບຊື່ໂຄງການ: ຊື່ທີ່ລະບຸໃນການຕັ້ງຄ່າ Docker Compose > ໂຟນເດີແມ່ຂອງໄຟລ໌ > ຊື່ທີ່ປ້ອນຢູ່ນີ້.',
composeHelper: 'Compose ທີ່ສ້າງຜ່ານ 1Panel editor ຫຼື template ຈະຖືກບັນທຶກໄວ້ໃນໂຟນເດີ {0}/docker/compose.',
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lang/modules/ms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1763,7 +1763,8 @@ const message = {
compose: 'Compose | Compose-compose',
composeFile: 'Fail Susunan',
fromChangeHelper: 'Menukar sumber akan membersihkan kandungan yang sedang diedit. Adakah anda mahu meneruskan?',
composePathHelper: 'Laluan simpan fail konfigurasi: {0}',
composePathHelper:
'Laluan simpan fail Compose: {0}; jika nama projek ditentukan dalam Docker Compose, nama tersebut akan diutamakan; jika tidak, {1} akan digunakan.',
composeNamePriorityHelper:
'Keutamaan nama projek: nama dalam konfigurasi Docker Compose > direktori induk fail > nama yang dimasukkan di sini.',
composeHelper:
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lang/modules/pt-br.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1764,7 +1764,8 @@ const message = {
compose: 'Compose | Composições',
composeFile: 'Arquivo de Orquestração',
fromChangeHelper: 'Trocar a origem limpará o conteúdo editado atual. Deseja continuar?',
composePathHelper: 'Caminho de salvamento do arquivo de configuração: {0}',
composePathHelper:
'Caminho de salvamento do arquivo Compose: {0}; se um nome de projeto for definido no Docker Compose, ele terá prioridade; caso contrário, será usado {1}.',
composeNamePriorityHelper:
'Prioridade do nome do projeto: nome definido na configuração do Docker Compose > diretório pai do arquivo > nome informado aqui.',
composeHelper:
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lang/modules/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1753,7 +1753,8 @@ const message = {
compose: 'Compose | Composes',
composeFile: 'Файл Оркестрации',
fromChangeHelper: 'Переключение источника очистит текущее отредактированное содержимое. Хотите продолжить?',
composePathHelper: 'Путь сохранения файла конфигурации: {0}',
composePathHelper:
'Путь сохранения файла Compose: {0}; если имя проекта указано в Docker Compose, оно имеет приоритет; иначе используется {1}.',
composeNamePriorityHelper:
'Приоритет имени проекта: имя из конфигурации Docker Compose > родительский каталог файла > имя, введённое здесь.',
composeHelper:
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lang/modules/tr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1764,7 +1764,8 @@ const message = {
composeFile: 'Düzenleme Dosyası',
fromChangeHelper:
'Kaynağın değiştirilmesi mevcut düzenlenen içeriği temizleyecektir. Devam etmek istiyor musunuz?',
composePathHelper: 'Yapılandırma dosyası kaydetme yolu: {0}',
composePathHelper:
'Compose dosyası kaydetme yolu: {0}; Docker Compose içinde bir proje adı belirtilmişse bu ad önceliklidir; aksi halde {1} kullanılır.',
composeNamePriorityHelper:
'Proje adı önceliği: Docker Compose yapılandırmasında belirtilen ad > dosyanın üst dizini > buraya girilen ad.',
composeHelper:
Expand Down
Loading
Loading