F 修复发布日志可能记录不完整的问题

pull/59/head
vapao 2020-03-31 13:07:01 +08:00
parent 5e15e348a1
commit bebd5277ee
4 changed files with 60 additions and 42 deletions

View File

@ -21,7 +21,7 @@ def deploy_dispatch(request, req, token):
try: try:
api_token = uuid.uuid4().hex api_token = uuid.uuid4().hex
rds.setex(api_token, 60 * 60, f'{req.deploy.app_id},{req.deploy.env_id}') rds.setex(api_token, 60 * 60, f'{req.deploy.app_id},{req.deploy.env_id}')
helper = Helper(rds, token) helper = Helper(rds, token, req.id)
helper.send_step('local', 1, f'完成\r\n{human_time()} 发布准备... ') helper.send_step('local', 1, f'完成\r\n{human_time()} 发布准备... ')
env = AttrDict( env = AttrDict(
SPUG_APP_NAME=req.deploy.app.name, SPUG_APP_NAME=req.deploy.app.name,
@ -177,9 +177,11 @@ def _deploy_ext2_host(helper, h_id, actions, env):
class Helper: class Helper:
def __init__(self, rds, token): def __init__(self, rds, token, r_id):
self.rds = rds self.rds = rds
self.token = token self.token = token
self.log_key = f'{settings.REQUEST_KEY}:{r_id}'
self.rds.delete(self.log_key)
@staticmethod @staticmethod
def send_deploy_notify(req): def send_deploy_notify(req):
@ -235,16 +237,20 @@ class Helper:
files.append(line) files.append(line)
return files return files
def _send(self, message):
self.rds.lpush(self.token, json.dumps(message))
self.rds.lpush(self.log_key, json.dumps(message))
def send_info(self, key, message): def send_info(self, key, message):
self.rds.lpush(self.token, json.dumps({'key': key, 'status': 'info', 'data': message})) self._send({'key': key, 'status': 'info', 'data': message})
def send_error(self, key, message): def send_error(self, key, message):
message = '\r\n' + message message = '\r\n' + message
self.rds.lpush(self.token, json.dumps({'key': key, 'status': 'error', 'data': message})) self._send({'key': key, 'status': 'error', 'data': message})
raise Exception(message) raise Exception(message)
def send_step(self, key, step, data): def send_step(self, key, step, data):
self.rds.lpush(self.token, json.dumps({'key': key, 'step': step, 'data': data})) self._send({'key': key, 'step': step, 'data': data})
def local(self, command, env=None): def local(self, command, env=None):
command = 'set -e\n' + command command = 'set -e\n' + command

View File

@ -3,24 +3,17 @@
# Released under the MIT License. # Released under the MIT License.
from channels.generic.websocket import WebsocketConsumer from channels.generic.websocket import WebsocketConsumer
from django_redis import get_redis_connection from django_redis import get_redis_connection
from django.conf import settings
from apps.setting.utils import AppSetting from apps.setting.utils import AppSetting
from apps.host.models import Host from apps.host.models import Host
from threading import Thread from threading import Thread
from urllib.parse import parse_qs
import json import json
class ExecConsumer(WebsocketConsumer): class ExecConsumer(WebsocketConsumer):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
query = parse_qs(self.scope['query_string'].decode())
e_id = query.get('id', [None])[0]
self.token = self.scope['url_route']['kwargs']['token'] self.token = self.scope['url_route']['kwargs']['token']
self.log_key = f'{settings.REQUEST_KEY}:{e_id}' if e_id else None
self.rds = get_redis_connection() self.rds = get_redis_connection()
if self.log_key:
self.rds.delete(self.log_key)
def connect(self): def connect(self):
self.accept() self.accept()
@ -29,11 +22,8 @@ class ExecConsumer(WebsocketConsumer):
self.rds.close() self.rds.close()
def get_response(self): def get_response(self):
if self.log_key: response = self.rds.brpop(self.token, timeout=5)
return self.rds.brpoplpush(self.token, self.log_key, timeout=5) return response[1] if response else None
else:
response = self.rds.brpop(self.token, timeout=5)
return response[1] if response else None
def receive(self, **kwargs): def receive(self, **kwargs):
response = self.get_response() response = self.get_response()

View File

@ -17,6 +17,8 @@ import lds from 'lodash';
class Ext1Index extends React.Component { class Ext1Index extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.id = props.match.params.id;
this.log = props.match.params.log;
this.state = { this.state = {
fetching: true, fetching: true,
loading: false, loading: false,
@ -25,11 +27,21 @@ class Ext1Index extends React.Component {
} }
componentDidMount() { componentDidMount() {
this.id = this.props.match.params.id; this.fetch()
this.log = this.props.match.params.log; }
componentWillUnmount() {
if (this.socket) this.socket.close();
store.request = {targets: [], host_actions: [], server_actions: []};
store.outputs = {};
}
fetch = () => {
this.setState({fetching: true});
http.get(`/api/deploy/request/${this.id}/`, {params: {log: this.log}}) http.get(`/api/deploy/request/${this.id}/`, {params: {log: this.log}})
.then(res => { .then(res => {
store.request = res; store.request = res;
store.outputs = {};
while (res.outputs.length) { while (res.outputs.length) {
const msg = JSON.parse(res.outputs.pop()); const msg = JSON.parse(res.outputs.pop());
if (!store.outputs.hasOwnProperty(msg.key)) { if (!store.outputs.hasOwnProperty(msg.key)) {
@ -40,13 +52,7 @@ class Ext1Index extends React.Component {
} }
}) })
.finally(() => this.setState({fetching: false})) .finally(() => this.setState({fetching: false}))
} };
componentWillUnmount() {
if (this.socket) this.socket.close();
store.request = {targets: [], host_actions: [], server_actions: []};
store.outputs = {};
}
_parse_message = (message) => { _parse_message = (message) => {
const {key, data, step, status} = message; const {key, data, step, status} = message;
@ -62,7 +68,7 @@ class Ext1Index extends React.Component {
store.request.status = '2'; store.request.status = '2';
store.outputs = outputs; store.outputs = outputs;
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
this.socket = new WebSocket(`${protocol}//${window.location.host}/api/ws/exec/${token}/?id=${this.id}`); this.socket = new WebSocket(`${protocol}//${window.location.host}/api/ws/exec/${token}/`);
this.socket.onopen = () => { this.socket.onopen = () => {
this.socket.send('ok'); this.socket.send('ok');
}; };
@ -115,9 +121,13 @@ class Ext1Index extends React.Component {
subTitle={`${app_name} - ${env_name}`} subTitle={`${app_name} - ${env_name}`}
style={{padding: 0}} style={{padding: 0}}
tags={this.getStatusAlias()} tags={this.getStatusAlias()}
extra={<Button loading={this.state.loading} type="primary" extra={this.log ? (
disabled={this.log || !['1', '-3'].includes(status)} <Button icon="sync" type="primary" onClick={this.fetch}>刷新</Button>
onClick={this.handleDeploy}>发布</Button>} ) : (
<Button icon="play-circle" loading={this.state.loading} type="primary"
disabled={!['1', '-3'].includes(status)}
onClick={this.handleDeploy}>发布</Button>
)}
onBack={() => history.goBack()}/> onBack={() => history.goBack()}/>
<Collapse defaultActiveKey={1} className={styles.collapse}> <Collapse defaultActiveKey={1} className={styles.collapse}>
<Collapse.Panel showArrow={false} key={1} header={ <Collapse.Panel showArrow={false} key={1} header={

View File

@ -17,6 +17,8 @@ import lds from 'lodash';
class Ext1Index extends React.Component { class Ext1Index extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.id = props.match.params.id;
this.log = props.match.params.log;
this.state = { this.state = {
fetching: true, fetching: true,
loading: false, loading: false,
@ -25,11 +27,22 @@ class Ext1Index extends React.Component {
} }
componentDidMount() { componentDidMount() {
this.id = this.props.match.params.id; this.fetch()
this.log = this.props.match.params.log; }
componentWillUnmount() {
if (this.socket) this.socket.close();
store.request = {targets: [], server_actions: [], host_actions: []};
store.outputs = {};
}
fetch = () => {
this.setState({fetching: true});
http.get(`/api/deploy/request/${this.id}/`, {params: {log: this.log}}) http.get(`/api/deploy/request/${this.id}/`, {params: {log: this.log}})
.then(res => { .then(res => {
store.request = res; store.request = res;
store.outputs = {};
while (res.outputs.length) { while (res.outputs.length) {
const msg = JSON.parse(res.outputs.pop()); const msg = JSON.parse(res.outputs.pop());
if (!store.outputs.hasOwnProperty(msg.key)) { if (!store.outputs.hasOwnProperty(msg.key)) {
@ -40,13 +53,7 @@ class Ext1Index extends React.Component {
} }
}) })
.finally(() => this.setState({fetching: false})) .finally(() => this.setState({fetching: false}))
} };
componentWillUnmount() {
if (this.socket) this.socket.close();
store.request = {targets: [], server_actions: [], host_actions: []};
store.outputs = {};
}
_parse_message = (message) => { _parse_message = (message) => {
const {key, data, step, status} = message; const {key, data, step, status} = message;
@ -62,7 +69,7 @@ class Ext1Index extends React.Component {
store.request.status = '2'; store.request.status = '2';
store.outputs = outputs; store.outputs = outputs;
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
this.socket = new WebSocket(`${protocol}//${window.location.host}/api/ws/exec/${token}/?id=${this.id}`); this.socket = new WebSocket(`${protocol}//${window.location.host}/api/ws/exec/${token}/`);
this.socket.onopen = () => { this.socket.onopen = () => {
this.socket.send('ok'); this.socket.send('ok');
}; };
@ -115,8 +122,13 @@ class Ext1Index extends React.Component {
subTitle={`${app_name} - ${env_name}`} subTitle={`${app_name} - ${env_name}`}
style={{padding: 0}} style={{padding: 0}}
tags={this.getStatusAlias()} tags={this.getStatusAlias()}
extra={<Button loading={this.state.loading} type="primary" disabled={this.log || !['1', '-3'].includes(status)} extra={this.log ? (
onClick={this.handleDeploy}>发布</Button>} <Button icon="sync" type="primary" onClick={this.fetch}>刷新</Button>
) : (
<Button icon="play-circle" loading={this.state.loading} type="primary"
disabled={!['1', '-3'].includes(status)}
onClick={this.handleDeploy}>发布</Button>
)}
onBack={() => history.goBack()}/> onBack={() => history.goBack()}/>
<Collapse defaultActiveKey={1} className={styles.collapse}> <Collapse defaultActiveKey={1} className={styles.collapse}>
<Collapse.Panel showArrow={false} key={1} header={ <Collapse.Panel showArrow={false} key={1} header={