feat: 计划任务脚本执行增加容器中选项 (#1474)

This commit is contained in:
ssongliu 2023-06-28 14:30:11 +08:00 committed by GitHub
parent 506d78cb00
commit c403eb55b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 91 additions and 4 deletions

View File

@ -40,6 +40,23 @@ func (b *BaseApi) SearchContainer(c *gin.Context) {
})
}
// @Tags Container
// @Summary List containers
// @Description 获取容器名称
// @Accept json
// @Produce json
// @Success 200
// @Security ApiKeyAuth
// @Router /containers/list [post]
func (b *BaseApi) ListContainer(c *gin.Context) {
list, err := containerService.List()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Container Compose
// @Summary Page composes
// @Description 获取编排列表分页

View File

@ -13,6 +13,7 @@ type CronjobCreate struct {
Second int `json:"second" validate:"number"`
Script string `json:"script"`
ContainerName string `json:"containerName"`
Website string `json:"website"`
ExclusionRules string `json:"exclusionRules"`
DBName string `json:"dbName"`
@ -34,6 +35,7 @@ type CronjobUpdate struct {
Second int `json:"second" validate:"number"`
Script string `json:"script"`
ContainerName string `json:"containerName"`
Website string `json:"website"`
ExclusionRules string `json:"exclusionRules"`
DBName string `json:"dbName"`
@ -76,6 +78,7 @@ type CronjobInfo struct {
Second int `json:"second"`
Script string `json:"script"`
ContainerName string `json:"containerName"`
Website string `json:"website"`
ExclusionRules string `json:"exclusionRules"`
DBName string `json:"dbName"`

View File

@ -15,6 +15,7 @@ type Cronjob struct {
Minute uint64 `gorm:"type:decimal" json:"minute"`
Second uint64 `gorm:"type:decimal" json:"second"`
ContainerName string `gorm:"type:varchar(64)" json:"containerName"`
Script string `gorm:"longtext" json:"script"`
Website string `gorm:"type:varchar(64)" json:"website"`
DBName string `gorm:"type:varchar(64)" json:"dbName"`

View File

@ -35,6 +35,7 @@ type ContainerService struct{}
type IContainerService interface {
Page(req dto.PageContainer) (int64, interface{}, error)
List() ([]string, error)
PageNetwork(req dto.SearchWithPage) (int64, interface{}, error)
PageVolume(req dto.SearchWithPage) (int64, interface{}, error)
ListVolume() ([]dto.Options, error)
@ -150,6 +151,27 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
return int64(total), backDatas, nil
}
func (u *ContainerService) List() ([]string, error) {
client, err := docker.NewDockerClient()
if err != nil {
return nil, err
}
containers, err := client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
if err != nil {
return nil, err
}
var datas []string
for _, container := range containers {
for _, name := range container.Names {
if len(name) != 0 {
datas = append(datas, strings.TrimLeft(name, "/"))
}
}
}
return datas, nil
}
func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
client, err := docker.NewDockerClient()
if err != nil {

View File

@ -238,6 +238,7 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
upMap["name"] = req.Name
upMap["spec"] = cronjob.Spec
upMap["script"] = req.Script
upMap["container_name"] = req.ContainerName
upMap["spec_type"] = req.SpecType
upMap["week"] = req.Week
upMap["day"] = req.Day

View File

@ -32,7 +32,11 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
if len(cronjob.Script) == 0 {
return
}
message, err = u.handleShell(cronjob.Type, cronjob.Name, cronjob.Script)
if len(cronjob.ContainerName) != 0 {
message, err = u.handleShell(cronjob.Type, cronjob.Name, fmt.Sprintf("docker exec %s %s", cronjob.ContainerName, cronjob.Script))
} else {
message, err = u.handleShell(cronjob.Type, cronjob.Name, cronjob.Script)
}
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
case "curl":
if len(cronjob.URL) == 0 {

View File

@ -394,7 +394,7 @@ var UpdateWebsite = &gormigrate.Migration{
var AddBackupAccountDir = &gormigrate.Migration{
ID: "20200620-add-backup-dir",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.BackupAccount{}); err != nil {
if err := tx.AutoMigrate(&model.BackupAccount{}, &model.Cronjob{}); err != nil {
return err
}
return nil

View File

@ -23,6 +23,7 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) {
baRouter.POST("/upgrade", baseApi.ContainerUpgrade)
baRouter.POST("/info", baseApi.ContainerInfo)
baRouter.POST("/search", baseApi.SearchContainer)
baRouter.POST("/list", baseApi.ListContainer)
baRouter.GET("/search/log", baseApi.ContainerLogs)
baRouter.GET("/limit", baseApi.LoadResouceLimit)
baRouter.POST("/clean/log", baseApi.CleanContainerLog)

View File

@ -13,6 +13,8 @@ export namespace Cronjob {
second: number;
script: string;
inContainer: boolean;
containerName: string;
website: string;
exclusionRules: string;
dbName: string;

View File

@ -5,6 +5,9 @@ import { Container } from '../interface/container';
export const searchContainer = (params: Container.ContainerSearch) => {
return http.post<ResPage<Container.ContainerInfo>>(`/containers/search`, params, 400000);
};
export const listContainer = () => {
return http.post<Array<string>>(`/containers/list`, {});
};
export const loadResourceLimit = () => {
return http.get<Container.ResourceLimit>(`/containers/limit`);
};

View File

@ -661,6 +661,8 @@ const message = {
taskType: 'Task type',
record: 'Records',
shell: 'Shell script',
containerCheckBox: 'In container (no need to enter the container command)',
containerName: 'Container name',
ntp: 'Time synchronization',
website: 'Backup website',
rulesHelper:

View File

@ -655,6 +655,8 @@ const message = {
taskType: '任务类型',
record: '报告',
shell: 'Shell 脚本',
containerCheckBox: '在容器中执行无需再输入进入容器命令',
containerName: '容器名称',
ntp: '时间同步',
website: '备份网站',
rulesHelper: '当存在多个压缩排除规则时需要换行显示\n*.log \n*.sql',

View File

@ -21,7 +21,7 @@
<el-option value="ntp" :label="$t('cronjob.ntp')" />
<el-option value="cutWebsiteLog" :label="$t('cronjob.cutWebsiteLog')" />
</el-select>
<el-tag v-else>{{ dialogData.rowData!.type }}</el-tag>
<el-tag v-else>{{ $t('cronjob.' + dialogData.rowData!.type) }}</el-tag>
</el-form-item>
<el-form-item :label="$t('cronjob.taskName')" prop="name">
@ -75,6 +75,21 @@
</el-input>
</el-form-item>
<el-form-item v-if="hasScript()">
<el-checkbox v-model="dialogData.rowData!.inContainer">
{{ $t('cronjob.containerCheckBox') }}
</el-checkbox>
</el-form-item>
<el-form-item
v-if="hasScript() && dialogData.rowData!.inContainer"
:label="$t('cronjob.containerName')"
prop="containerName"
>
<el-select class="selectClass" v-model="dialogData.rowData!.containerName">
<el-option v-for="item in containerOptions" :key="item" :value="item" :label="item" />
</el-select>
</el-form-item>
<el-form-item v-if="hasScript()" :label="$t('cronjob.shellContent')" prop="script">
<el-input
clearable
@ -205,6 +220,7 @@ import { GetWebsiteOptions } from '@/api/modules/website';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { MsgError, MsgSuccess } from '@/utils/message';
import { useRouter } from 'vue-router';
import { listContainer } from '@/api/modules/container';
const router = useRouter();
interface DialogProps {
@ -226,10 +242,14 @@ const acceptParams = (params: DialogProps): void => {
if (dialogData.value?.rowData?.exclusionRules) {
dialogData.value.rowData.exclusionRules = dialogData.value.rowData.exclusionRules.replaceAll(',', '\n');
}
if (dialogData.value?.rowData?.containerName) {
dialogData.value.rowData.inContainer = true;
}
drawerVisiable.value = true;
checkMysqlInstalled();
loadBackups();
loadWebsites();
loadContainers();
};
const emit = defineEmits<{ (e: 'search'): void }>();
@ -243,6 +263,7 @@ const handleClose = () => {
const localDirID = ref();
const containerOptions = ref();
const websiteOptions = ref();
const backupOptions = ref();
@ -424,7 +445,12 @@ const loadBackups = async () => {
const loadWebsites = async () => {
const res = await GetWebsiteOptions();
websiteOptions.value = res.data;
websiteOptions.value = res.data || [];
};
const loadContainers = async () => {
const res = await listContainer();
containerOptions.value = res.data || [];
};
const checkMysqlInstalled = async () => {
@ -487,6 +513,9 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
if (!dialogData.value.rowData.inContainer) {
dialogData.value.rowData.containerName = '';
}
if (dialogData.value?.rowData?.exclusionRules) {
dialogData.value.rowData.exclusionRules = dialogData.value.rowData.exclusionRules.replaceAll('\n', ',');
}