mirror of https://github.com/openspug/spug
add planned deploy
parent
a28300a641
commit
0e6e618cef
|
@ -32,6 +32,7 @@ class DeployRequest(models.Model, ModelMixin):
|
|||
reason = models.CharField(max_length=255, null=True)
|
||||
version = models.CharField(max_length=50, null=True)
|
||||
spug_version = models.CharField(max_length=50, null=True)
|
||||
plan = models.DateTimeField(null=True)
|
||||
|
||||
created_at = models.CharField(max_length=20, default=human_datetime)
|
||||
created_by = models.ForeignKey(User, models.PROTECT, related_name='+')
|
||||
|
|
|
@ -201,6 +201,7 @@ def post_request_ext1(request):
|
|||
Argument('repository_id', type=int, help='请选择发布版本'),
|
||||
Argument('host_ids', type=list, filter=lambda x: len(x), help='请选择要部署的主机'),
|
||||
Argument('type', default='1'),
|
||||
Argument('plan', required=False),
|
||||
Argument('desc', required=False),
|
||||
).parse(request.body)
|
||||
if error is None:
|
||||
|
@ -232,7 +233,8 @@ def post_request_ext2(request):
|
|||
Argument('name', help='请输申请标题'),
|
||||
Argument('host_ids', type=list, filter=lambda x: len(x), help='请选择要部署的主机'),
|
||||
Argument('extra', type=dict, required=False),
|
||||
Argument('version', required=False),
|
||||
Argument('version', default=''),
|
||||
Argument('plan', required=False),
|
||||
Argument('desc', required=False),
|
||||
).parse(request.body)
|
||||
if error is None:
|
||||
|
|
|
@ -6,8 +6,10 @@ from apps.account.models import History
|
|||
from apps.alarm.models import Alarm
|
||||
from apps.schedule.models import Task
|
||||
from apps.deploy.models import DeployRequest
|
||||
from libs.utils import parse_time
|
||||
from apps.deploy.utils import dispatch
|
||||
from libs.utils import parse_time, human_datetime
|
||||
from datetime import datetime, timedelta
|
||||
from threading import Thread
|
||||
|
||||
|
||||
def auto_run_by_day():
|
||||
|
@ -24,8 +26,15 @@ def auto_run_by_day():
|
|||
|
||||
|
||||
def auto_run_by_minute():
|
||||
close_old_connections()
|
||||
now = datetime.now()
|
||||
for req in DeployRequest.objects.filter(status='2'):
|
||||
if (now - parse_time(req.do_at)).seconds > 3600:
|
||||
req.status = '-3'
|
||||
req.save()
|
||||
for req in DeployRequest.objects.filter(status='1', plan__lte=now):
|
||||
req.status = '2'
|
||||
req.do_at = human_datetime()
|
||||
req.do_by = req.created_by
|
||||
req.save()
|
||||
Thread(target=dispatch, args=(req,)).start()
|
||||
|
|
|
@ -78,7 +78,7 @@ class Scheduler:
|
|||
|
||||
def _init_builtin_jobs(self):
|
||||
self.scheduler.add_job(auto_run_by_day, 'cron', hour=1, minute=20)
|
||||
self.scheduler.add_job(auto_run_by_minute, 'interval', minutes=5)
|
||||
self.scheduler.add_job(auto_run_by_minute, 'interval', minutes=1)
|
||||
|
||||
def _dispatch(self, task_id, command, targets):
|
||||
close_old_connections()
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Modal, Form, Input, Select, message, Button } from 'antd';
|
||||
import { Modal, Form, Input, Select, DatePicker, message, Button } from 'antd';
|
||||
import HostSelector from './HostSelector';
|
||||
import hostStore from 'pages/host/store';
|
||||
import http from 'libs/http';
|
||||
|
@ -19,6 +19,7 @@ export default observer(function () {
|
|||
const [loading, setLoading] = useState(false);
|
||||
const [versions, setVersions] = useState([]);
|
||||
const [host_ids, setHostIds] = useState([]);
|
||||
const [plan, setPlan] = useState(store.record.plan);
|
||||
|
||||
useEffect(() => {
|
||||
const {deploy_id, app_host_ids, host_ids} = store.record;
|
||||
|
@ -38,6 +39,7 @@ export default observer(function () {
|
|||
formData['host_ids'] = host_ids;
|
||||
formData['type'] = store.record.type;
|
||||
formData['deploy_id'] = store.record.deploy_id;
|
||||
if (plan) formData.plan = plan.format('YYYY-MM-DD HH:mm:00');
|
||||
http.post('/api/deploy/request/ext1/', formData)
|
||||
.then(res => {
|
||||
message.success('操作成功');
|
||||
|
@ -46,7 +48,7 @@ export default observer(function () {
|
|||
}, () => setLoading(false))
|
||||
}
|
||||
|
||||
const {app_host_ids, type, rb_id,} = store.record;
|
||||
const {app_host_ids, type, rb_id} = store.record;
|
||||
return (
|
||||
<Modal
|
||||
visible
|
||||
|
@ -56,7 +58,7 @@ export default observer(function () {
|
|||
onCancel={() => store.ext1Visible = false}
|
||||
confirmLoading={loading}
|
||||
onOk={handleSubmit}>
|
||||
<Form form={form} initialValues={store.record} labelCol={{span: 5}} wrapperCol={{span: 17}}>
|
||||
<Form form={form} initialValues={store.record} labelCol={{span: 6}} wrapperCol={{span: 16}}>
|
||||
<Form.Item required name="name" label="申请标题">
|
||||
<Input placeholder="请输入申请标题"/>
|
||||
</Form.Item>
|
||||
|
@ -79,6 +81,18 @@ export default observer(function () {
|
|||
<Form.Item name="desc" label="备注信息">
|
||||
<Input placeholder="请输入备注信息"/>
|
||||
</Form.Item>
|
||||
{type !== '2' && (
|
||||
<Form.Item label="定时发布" tooltip="在到达指定时间后自动发布,会有最多1分钟的延迟。">
|
||||
<DatePicker
|
||||
showTime
|
||||
value={plan}
|
||||
style={{width: 180}}
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
placeholder="请设置发布时间"
|
||||
onChange={setPlan}/>
|
||||
{plan ? <span style={{marginLeft: 24, fontSize: 12, color: '#888'}}>大约 {plan.fromNow()}</span> : null}
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form>
|
||||
{visible && <HostSelector
|
||||
host_ids={host_ids}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { UploadOutlined } from '@ant-design/icons';
|
||||
import { Modal, Form, Input, Upload, message, Button } from 'antd';
|
||||
import { Modal, Form, Input, Upload, DatePicker, message, Button } from 'antd';
|
||||
import hostStore from 'pages/host/store';
|
||||
import HostSelector from './HostSelector';
|
||||
import { http, X_TOKEN } from 'libs';
|
||||
|
@ -21,6 +21,7 @@ export default observer(function () {
|
|||
const [uploading, setUploading] = useState(false);
|
||||
const [fileList, setFileList] = useState([]);
|
||||
const [host_ids, setHostIds] = useState([]);
|
||||
const [plan, setPlan] = useState(store.record.plan);
|
||||
|
||||
useEffect(() => {
|
||||
const {app_host_ids, host_ids, extra} = store.record;
|
||||
|
@ -38,6 +39,7 @@ export default observer(function () {
|
|||
formData['id'] = store.record.id;
|
||||
formData['host_ids'] = host_ids;
|
||||
formData['deploy_id'] = store.record.deploy_id;
|
||||
if (plan) formData.plan = plan.format('YYYY-MM-DD HH:mm:00');
|
||||
if (fileList.length > 0) formData['extra'] = lds.pick(fileList[0], ['path', 'name']);
|
||||
http.post('/api/deploy/request/ext2/', formData)
|
||||
.then(res => {
|
||||
|
@ -67,17 +69,17 @@ export default observer(function () {
|
|||
return false
|
||||
}
|
||||
|
||||
const {app_host_ids, deploy_id} = store.record;
|
||||
const {app_host_ids, deploy_id, type} = store.record;
|
||||
return (
|
||||
<Modal
|
||||
visible
|
||||
width={800}
|
||||
width={600}
|
||||
maskClosable={false}
|
||||
title={`${store.record.id ? '编辑' : '新建'}发布申请`}
|
||||
onCancel={() => store.ext2Visible = false}
|
||||
confirmLoading={loading}
|
||||
onOk={handleSubmit}>
|
||||
<Form form={form} initialValues={store.record} labelCol={{span: 6}} wrapperCol={{span: 14}}>
|
||||
<Form form={form} initialValues={store.record} labelCol={{span: 6}} wrapperCol={{span: 16}}>
|
||||
<Form.Item required name="name" label="申请标题">
|
||||
<Input placeholder="请输入申请标题"/>
|
||||
</Form.Item>
|
||||
|
@ -100,6 +102,18 @@ export default observer(function () {
|
|||
<Form.Item name="desc" label="备注信息">
|
||||
<Input placeholder="请输入备注信息"/>
|
||||
</Form.Item>
|
||||
{type !== '2' && (
|
||||
<Form.Item label="定时发布" tooltip="在到达指定时间后自动发布,会有最多1分钟的延迟。">
|
||||
<DatePicker
|
||||
showTime
|
||||
value={plan}
|
||||
style={{width: 180}}
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
placeholder="请设置发布时间"
|
||||
onChange={setPlan}/>
|
||||
{plan ? <span style={{marginLeft: 24, fontSize: 12, color: '#888'}}>大约 {plan.fromNow()}</span> : null}
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form>
|
||||
{visible && <HostSelector
|
||||
host_ids={host_ids}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { BranchesOutlined, BuildOutlined, TagOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { Radio, Modal, Popover, Tag, Popconfirm, message } from 'antd';
|
||||
import { Radio, Modal, Popover, Tag, Popconfirm, Tooltip, message } from 'antd';
|
||||
import { http, hasPermission } from 'libs';
|
||||
import { Action, AuthButton, TableCard } from 'components';
|
||||
import styles from './index.module.less';
|
||||
|
@ -17,7 +17,8 @@ function ComTable() {
|
|||
title: '申请标题',
|
||||
render: info => (
|
||||
<div>
|
||||
{info.type === '2' && <Tag color="#f50">R</Tag>}
|
||||
{info.type === '2' && <Tooltip title="回滚发布"><Tag color="#f50">R</Tag></Tooltip>}
|
||||
{info.plan && <Tooltip title={`定时发布(${info.plan})`}> <Tag color="#108ee9">P</Tag></Tooltip>}
|
||||
{info.name}
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
import { observable, computed } from "mobx";
|
||||
import http from 'libs/http';
|
||||
import moment from 'moment';
|
||||
import lds from 'lodash';
|
||||
|
||||
class Store {
|
||||
|
@ -106,6 +107,7 @@ class Store {
|
|||
|
||||
showForm = (info) => {
|
||||
this.record = info;
|
||||
if (info.plan) this.record.plan = moment(info.plan);
|
||||
if (info['app_extend'] === '1') {
|
||||
this.ext1Visible = true
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue