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"`
Operation string `json:"operation" validate:"required,oneof=up start restart stop down delete"`
WithFile bool `json:"withFile"`
Force bool `josn:"force"`
}
type ComposeUpdate struct {
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) {
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 stdout, err := compose.Operate(req.Path, "down"); err != nil {
return errors.New(string(stdout))
if err := removeContainerForCompose(req.Name, req.Path); err != nil && !req.Force {
return err
}
if req.WithFile {
_ = os.RemoveAll(path.Dir(req.Path))
@ -240,6 +237,9 @@ func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
_ = composeRepo.DeleteRecord(repo.WithByName(req.Name))
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 stdout, err := compose.Up(req.Path); err != nil {
return errors.New(string(stdout))
@ -308,6 +308,33 @@ func (u *ContainerService) loadPath(req *dto.ComposeCreate) error {
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 {
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {

View file

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

View file

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

View file

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