fix: Optimize container compose deletion logic

This commit is contained in:
ssongliu 2025-08-29 11:09:20 +08:00
parent 5c5904216b
commit 29952f48ca
5 changed files with 44 additions and 5 deletions

View file

@ -260,6 +260,7 @@ type ComposeOperation struct {
Path string `json:"path"` Path string `json:"path"`
Operation string `json:"operation" validate:"required,oneof=up start restart stop down delete"` Operation string `json:"operation" validate:"required,oneof=up start restart stop down delete"`
WithFile bool `json:"withFile"` WithFile bool `json:"withFile"`
Force bool `josn:"force"`
} }
type ComposeUpdate struct { type ComposeUpdate struct {
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`

View file

@ -227,12 +227,9 @@ func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
if cmd.CheckIllegal(req.Path, req.Operation) { if cmd.CheckIllegal(req.Path, req.Operation) {
return buserr.New("ErrCmdIllegal") return buserr.New("ErrCmdIllegal")
} }
if _, err := os.Stat(req.Path); err != nil {
return fmt.Errorf("load file with path %s failed, %v", req.Path, err)
}
if req.Operation == "delete" { if req.Operation == "delete" {
if stdout, err := compose.Operate(req.Path, "down"); err != nil { if err := removeContainerForCompose(req.Name, req.Path); err != nil && !req.Force {
return errors.New(string(stdout)) return err
} }
if req.WithFile { if req.WithFile {
_ = os.RemoveAll(path.Dir(req.Path)) _ = os.RemoveAll(path.Dir(req.Path))
@ -240,6 +237,9 @@ func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
_ = composeRepo.DeleteRecord(repo.WithByName(req.Name)) _ = composeRepo.DeleteRecord(repo.WithByName(req.Name))
return nil return nil
} }
if _, err := os.Stat(req.Path); err != nil {
return fmt.Errorf("load file with path %s failed, %v", req.Path, err)
}
if req.Operation == "up" { if req.Operation == "up" {
if stdout, err := compose.Up(req.Path); err != nil { if stdout, err := compose.Up(req.Path); err != nil {
return errors.New(string(stdout)) return errors.New(string(stdout))
@ -308,6 +308,33 @@ func (u *ContainerService) loadPath(req *dto.ComposeCreate) error {
return nil return nil
} }
func removeContainerForCompose(composeName, composePath string) error {
if _, err := os.Stat(composePath); err == nil {
if stdout, err := compose.Operate(composePath, "down"); err != nil {
return errors.New(stdout)
}
return nil
}
var options container.ListOptions
options.All = true
options.Filters = filters.NewArgs()
options.Filters.Add("label", "com.docker.compose.project="+composeName)
client, err := docker.NewDockerClient()
if err != nil {
return err
}
defer client.Close()
ctx := context.Background()
containers, err := client.ContainerList(ctx, options)
if err != nil {
return err
}
for _, c := range containers {
_ = client.ContainerRemove(ctx, c.ID, container.RemoveOptions{RemoveVolumes: true, Force: true})
}
return nil
}
func recreateCompose(content, path string) error { func recreateCompose(content, path string) error {
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640) file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil { if err != nil {

View file

@ -309,6 +309,7 @@ export namespace Container {
operation: string; operation: string;
path: string; path: string;
withFile: boolean; withFile: boolean;
force: boolean;
} }
export interface ComposeUpdate { export interface ComposeUpdate {
name: string; name: string;

View file

@ -7,6 +7,12 @@
{{ $t('container.deleteComposeHelper') }} {{ $t('container.deleteComposeHelper') }}
</span> </span>
</el-form-item> </el-form-item>
<el-form-item>
<el-checkbox v-model="force" :label="$t('website.forceDelete')" />
<span class="input-help">
{{ $t('website.forceDeleteHelper') }}
</span>
</el-form-item>
<el-form-item> <el-form-item>
<div class="font"> <div class="font">
<span>{{ $t('database.delete') }}</span> <span>{{ $t('database.delete') }}</span>
@ -40,6 +46,7 @@ let loading = ref(false);
let deleteInfo = ref(''); let deleteInfo = ref('');
const deleteFile = ref(); const deleteFile = ref();
const force = ref();
const composeName = ref(); const composeName = ref();
const composePath = ref(); const composePath = ref();
@ -53,6 +60,7 @@ const emit = defineEmits<{ (e: 'search'): void }>();
const acceptParams = async (prop: DialogProps) => { const acceptParams = async (prop: DialogProps) => {
deleteFile.value = false; deleteFile.value = false;
force.value = false;
composeName.value = prop.name; composeName.value = prop.name;
composePath.value = prop.path; composePath.value = prop.path;
deleteInfo.value = ''; deleteInfo.value = '';
@ -66,6 +74,7 @@ const submit = async () => {
path: composePath.value, path: composePath.value,
operation: 'delete', operation: 'delete',
withFile: deleteFile.value, withFile: deleteFile.value,
force: force.value,
}; };
await composeOperator(params) await composeOperator(params)
.then(() => { .then(() => {

View file

@ -203,6 +203,7 @@ const onComposeOperate = async (operation: string, row: any) => {
path: row.path, path: row.path,
operation: operation, operation: operation,
withFile: false, withFile: false,
force: false,
}; };
loading.value = true; loading.value = true;
await composeOperator(params) await composeOperator(params)