mirror of https://github.com/openspug/spug
F 修复发布日志可能记录不完整的问题
parent
5e15e348a1
commit
bebd5277ee
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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={
|
||||||
|
|
|
@ -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={
|
||||||
|
|
Loading…
Reference in New Issue