diff --git a/spug_api/apps/deploy/utils.py b/spug_api/apps/deploy/utils.py
index 8999dbfc..012aa9ad 100644
--- a/spug_api/apps/deploy/utils.py
+++ b/spug_api/apps/deploy/utils.py
@@ -236,15 +236,15 @@ def parse_filter_rule(self, data: str):
return files
def send_info(self, key, message):
- self.rds.rpush(self.token, json.dumps({'key': key, 'status': 'info', 'data': message}))
+ self.rds.lpush(self.token, json.dumps({'key': key, 'status': 'info', 'data': message}))
def send_error(self, key, message):
message = '\r\n' + message
- self.rds.rpush(self.token, json.dumps({'key': key, 'status': 'error', 'data': message}))
+ self.rds.lpush(self.token, json.dumps({'key': key, 'status': 'error', 'data': message}))
raise Exception(message)
def send_step(self, key, step, data):
- self.rds.rpush(self.token, json.dumps({'key': key, 'step': step, 'data': data}))
+ self.rds.lpush(self.token, json.dumps({'key': key, 'step': step, 'data': data}))
def local(self, command, env=None):
command = 'set -e\n' + command
diff --git a/spug_api/apps/deploy/views.py b/spug_api/apps/deploy/views.py
index 02164e43..01a38751 100644
--- a/spug_api/apps/deploy/views.py
+++ b/spug_api/apps/deploy/views.py
@@ -3,6 +3,8 @@
# Released under the MIT License.
from django.views.generic import View
from django.db.models import F
+from django.conf import settings
+from django_redis import get_redis_connection
from libs import json_response, JsonParser, Argument, human_datetime, human_time
from apps.deploy.models import DeployRequest
from apps.app.models import Deploy
@@ -112,10 +114,17 @@ def get(self, request, r_id):
return json_response(error='未找到指定发布申请')
hosts = Host.objects.filter(id__in=json.loads(req.host_ids))
targets = [{'id': x.id, 'title': f'{x.name}({x.hostname}:{x.port})'} for x in hosts]
- server_actions, host_actions = [], []
+ server_actions, host_actions, outputs = [], [], []
if req.deploy.extend == '2':
server_actions = json.loads(req.deploy.extend_obj.server_actions)
host_actions = json.loads(req.deploy.extend_obj.host_actions)
+ if request.GET.get('log'):
+ rds, key, counter = get_redis_connection(), f'{settings.REQUEST_KEY}:{r_id}', 0
+ data = rds.lrange(key, counter, counter + 9)
+ while data:
+ counter += 10
+ outputs.extend(x.decode() for x in data)
+ data = rds.lrange(key, counter, counter + 9)
return json_response({
'app_name': req.deploy.app.name,
'env_name': req.deploy.env.name,
@@ -124,7 +133,8 @@ def get(self, request, r_id):
'status_alias': req.get_status_display(),
'targets': targets,
'server_actions': server_actions,
- 'host_actions': host_actions
+ 'host_actions': host_actions,
+ 'outputs': outputs
})
def post(self, request, r_id):
diff --git a/spug_api/apps/monitor/scheduler.py b/spug_api/apps/monitor/scheduler.py
index d9023009..3572074b 100644
--- a/spug_api/apps/monitor/scheduler.py
+++ b/spug_api/apps/monitor/scheduler.py
@@ -109,7 +109,7 @@ def run(self):
rds_cli.delete(settings.MONITOR_KEY)
logger.info('Running monitor')
while True:
- _, data = rds_cli.blpop(settings.MONITOR_KEY)
+ _, data = rds_cli.brpop(settings.MONITOR_KEY)
task = AttrDict(json.loads(data))
if task.action in ('add', 'modify'):
trigger = IntervalTrigger(minutes=int(task.rate), timezone=self.timezone)
diff --git a/spug_api/apps/monitor/views.py b/spug_api/apps/monitor/views.py
index f53ef824..f1196204 100644
--- a/spug_api/apps/monitor/views.py
+++ b/spug_api/apps/monitor/views.py
@@ -40,13 +40,13 @@ def post(self, request):
if task and task.is_active:
form.action = 'modify'
rds_cli = get_redis_connection()
- rds_cli.rpush(settings.MONITOR_KEY, json.dumps(form))
+ rds_cli.lpush(settings.MONITOR_KEY, json.dumps(form))
else:
dtt = Detection.objects.create(created_by=request.user, **form)
form.action = 'add'
form.id = dtt.id
rds_cli = get_redis_connection()
- rds_cli.rpush(settings.MONITOR_KEY, json.dumps(form))
+ rds_cli.lpush(settings.MONITOR_KEY, json.dumps(form))
return json_response(error=error)
def patch(self, request):
@@ -64,7 +64,7 @@ def patch(self, request):
else:
message = {'id': form.id, 'action': 'remove'}
rds_cli = get_redis_connection()
- rds_cli.rpush(settings.MONITOR_KEY, json.dumps(message))
+ rds_cli.lpush(settings.MONITOR_KEY, json.dumps(message))
return json_response(error=error)
def delete(self, request):
diff --git a/spug_api/apps/schedule/scheduler.py b/spug_api/apps/schedule/scheduler.py
index 6e94487a..9ad9026f 100644
--- a/spug_api/apps/schedule/scheduler.py
+++ b/spug_api/apps/schedule/scheduler.py
@@ -87,7 +87,7 @@ def run(self):
rds_cli.delete(settings.SCHEDULE_KEY)
logger.info('Running scheduler')
while True:
- _, data = rds_cli.blpop(settings.SCHEDULE_KEY)
+ _, data = rds_cli.brpop(settings.SCHEDULE_KEY)
task = AttrDict(json.loads(data))
if task.action in ('add', 'modify'):
trigger = self.parse_trigger(task.trigger, task.trigger_args)
diff --git a/spug_api/apps/schedule/views.py b/spug_api/apps/schedule/views.py
index 05dd52df..03c802ce 100644
--- a/spug_api/apps/schedule/views.py
+++ b/spug_api/apps/schedule/views.py
@@ -40,7 +40,7 @@ def post(self, request):
form.action = 'modify'
form.targets = json.loads(form.targets)
rds_cli = get_redis_connection()
- rds_cli.rpush(settings.SCHEDULE_KEY, json.dumps(form))
+ rds_cli.lpush(settings.SCHEDULE_KEY, json.dumps(form))
else:
Task.objects.create(created_by=request.user, **form)
return json_response(error=error)
@@ -60,7 +60,7 @@ def patch(self, request):
else:
message = {'id': form.id, 'action': 'remove'}
rds_cli = get_redis_connection()
- rds_cli.rpush(settings.SCHEDULE_KEY, json.dumps(message))
+ rds_cli.lpush(settings.SCHEDULE_KEY, json.dumps(message))
return json_response(error=error)
def delete(self, request):
diff --git a/spug_api/consumer/consumers.py b/spug_api/consumer/consumers.py
index 5c68bc64..01c69c54 100644
--- a/spug_api/consumer/consumers.py
+++ b/spug_api/consumer/consumers.py
@@ -3,17 +3,24 @@
# Released under the MIT License.
from channels.generic.websocket import WebsocketConsumer
from django_redis import get_redis_connection
+from django.conf import settings
from apps.setting.utils import AppSetting
from apps.host.models import Host
from threading import Thread
+from urllib.parse import parse_qs
import json
class ExecConsumer(WebsocketConsumer):
def __init__(self, *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.log_key = f'{settings.REQUEST_KEY}:{e_id}' if e_id else None
self.rds = get_redis_connection()
+ if self.log_key:
+ self.rds.delete(self.log_key)
def connect(self):
self.accept()
@@ -21,11 +28,18 @@ def connect(self):
def disconnect(self, code):
self.rds.close()
+ def get_response(self):
+ if self.log_key:
+ return self.rds.brpoplpush(self.token, self.log_key, timeout=5)
+ else:
+ return self.rds.brpop(self.token, timeout=5)[1]
+
def receive(self, **kwargs):
- response = self.rds.blpop(self.token, timeout=5)
+ response = self.get_response()
while response:
- self.send(text_data=response[1].decode())
- response = self.rds.blpop(self.token, timeout=5)
+ data = response.decode()
+ self.send(text_data=data)
+ response = self.get_response()
self.send(text_data='pong')
diff --git a/spug_api/consumer/executors.py b/spug_api/consumer/executors.py
index 68b06071..6a52f6bc 100644
--- a/spug_api/consumer/executors.py
+++ b/spug_api/consumer/executors.py
@@ -28,7 +28,7 @@ def __init__(self, hostname, port, username, pkey, command, token=None, **kwargs
def _send(self, message, with_expire=False):
if self.rds_cli is None:
self.rds_cli = get_redis_connection()
- self.rds_cli.rpush(self.token, json.dumps(message))
+ self.rds_cli.lpush(self.token, json.dumps(message))
if with_expire:
self.rds_cli.expire(self.token, 300)
diff --git a/spug_api/spug/settings.py b/spug_api/spug/settings.py
index 0570ba37..5a139546 100644
--- a/spug_api/spug/settings.py
+++ b/spug_api/spug/settings.py
@@ -100,6 +100,7 @@
SCHEDULE_KEY = 'spug:schedule'
MONITOR_KEY = 'spug:monitor'
+REQUEST_KEY = 'spug:request'
REPOS_DIR = os.path.join(BASE_DIR, 'repos')
# Internationalization
diff --git a/spug_web/src/pages/deploy/do/Ext1Index.js b/spug_web/src/pages/deploy/do/Ext1Index.js
index c2fcb41b..9d7b246e 100644
--- a/spug_web/src/pages/deploy/do/Ext1Index.js
+++ b/spug_web/src/pages/deploy/do/Ext1Index.js
@@ -26,8 +26,19 @@ class Ext1Index extends React.Component {
componentDidMount() {
this.id = this.props.match.params.id;
- http.get(`/api/deploy/request/${this.id}/`)
- .then(res => store.request = res)
+ this.log = this.props.match.params.log;
+ http.get(`/api/deploy/request/${this.id}/`, {params: {log: this.log}})
+ .then(res => {
+ store.request = res;
+ while (res.outputs.length) {
+ const msg = JSON.parse(res.outputs.pop());
+ if (!store.outputs.hasOwnProperty(msg.key)) {
+ const data = msg.key === 'local' ? '读取数据... ' : '';
+ store.outputs[msg.key] = {data}
+ }
+ this._parse_message(msg)
+ }
+ })
.finally(() => this.setState({fetching: false}))
}
@@ -37,6 +48,13 @@ class Ext1Index extends React.Component {
store.outputs = {};
}
+ _parse_message = (message) => {
+ const {key, data, step, status} = message;
+ if (data !== undefined) store.outputs[key]['data'] += data;
+ if (step !== undefined) store.outputs[key]['step'] = step;
+ if (status !== undefined) store.outputs[key]['status'] = status;
+ };
+
handleDeploy = () => {
this.setState({loading: true});
http.post(`/api/deploy/request/${this.id}/`)
@@ -44,7 +62,7 @@ class Ext1Index extends React.Component {
store.request.status = '2';
store.outputs = outputs;
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
- this.socket = new WebSocket(`${protocol}//${window.location.host}/api/ws/exec/${token}/`);
+ this.socket = new WebSocket(`${protocol}//${window.location.host}/api/ws/exec/${token}/?id=${this.id}`);
this.socket.onopen = () => {
this.socket.send('ok');
};
@@ -52,10 +70,7 @@ class Ext1Index extends React.Component {
if (e.data === 'pong') {
this.socket.send('ping')
} else {
- const {key, data, step, status} = JSON.parse(e.data);
- if (data !== undefined) store.outputs[key]['data'] += data;
- if (step !== undefined) store.outputs[key]['step'] = step;
- if (status !== undefined) store.outputs[key]['status'] = status;
+ this._parse_message(JSON.parse(e.data))
}
}
})
@@ -100,7 +115,8 @@ class Ext1Index extends React.Component {
subTitle={`${app_name} - ${env_name}`}
style={{padding: 0}}
tags={this.getStatusAlias()}
- extra={}
onBack={() => history.goBack()}/>
diff --git a/spug_web/src/pages/deploy/do/Ext2Index.js b/spug_web/src/pages/deploy/do/Ext2Index.js
index db3068ef..dae5a9f3 100644
--- a/spug_web/src/pages/deploy/do/Ext2Index.js
+++ b/spug_web/src/pages/deploy/do/Ext2Index.js
@@ -26,8 +26,19 @@ class Ext1Index extends React.Component {
componentDidMount() {
this.id = this.props.match.params.id;
- http.get(`/api/deploy/request/${this.id}/`)
- .then(res => store.request = res)
+ this.log = this.props.match.params.log;
+ http.get(`/api/deploy/request/${this.id}/`, {params: {log: this.log}})
+ .then(res => {
+ store.request = res;
+ while (res.outputs.length) {
+ const msg = JSON.parse(res.outputs.pop());
+ if (!store.outputs.hasOwnProperty(msg.key)) {
+ const data = msg.key === 'local' ? '读取数据... ' : '';
+ store.outputs[msg.key] = {data}
+ }
+ this._parse_message(msg)
+ }
+ })
.finally(() => this.setState({fetching: false}))
}
@@ -37,6 +48,13 @@ class Ext1Index extends React.Component {
store.outputs = {};
}
+ _parse_message = (message) => {
+ const {key, data, step, status} = message;
+ if (data !== undefined) store.outputs[key]['data'] += data;
+ if (step !== undefined) store.outputs[key]['step'] = step;
+ if (status !== undefined) store.outputs[key]['status'] = status;
+ };
+
handleDeploy = () => {
this.setState({loading: true});
http.post(`/api/deploy/request/${this.id}/`)
@@ -44,7 +62,7 @@ class Ext1Index extends React.Component {
store.request.status = '2';
store.outputs = outputs;
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
- this.socket = new WebSocket(`${protocol}//${window.location.host}/api/ws/exec/${token}/`);
+ this.socket = new WebSocket(`${protocol}//${window.location.host}/api/ws/exec/${token}/?id=${this.id}`);
this.socket.onopen = () => {
this.socket.send('ok');
};
@@ -52,10 +70,7 @@ class Ext1Index extends React.Component {
if (e.data === 'pong') {
this.socket.send('ping')
} else {
- const {key, data, step, status} = JSON.parse(e.data);
- if (data !== undefined) store.outputs[key]['data'] += data;
- if (step !== undefined) store.outputs[key]['step'] = step;
- if (status !== undefined) store.outputs[key]['status'] = status;
+ this._parse_message(JSON.parse(e.data))
}
}
})
@@ -100,7 +115,7 @@ class Ext1Index extends React.Component {
subTitle={`${app_name} - ${env_name}`}
style={{padding: 0}}
tags={this.getStatusAlias()}
- extra={}
onBack={() => history.goBack()}/>
diff --git a/spug_web/src/pages/deploy/request/Table.js b/spug_web/src/pages/deploy/request/Table.js
index 34379bda..9e7430a4 100644
--- a/spug_web/src/pages/deploy/request/Table.js
+++ b/spug_web/src/pages/deploy/request/Table.js
@@ -85,6 +85,8 @@ class ComTable extends React.Component {
switch (info.status) {
case '-3':
return
+ 查看
+
发布
this.handleRollback(info)}>回滚
;
case '3':
- return this.handleRollback(info)}>回滚;
+ return
+ 查看
+
+ this.handleRollback(info)}>回滚
+ ;
case '-1':
return
store.showForm(info)}>编辑
diff --git a/spug_web/src/pages/deploy/routes.js b/spug_web/src/pages/deploy/routes.js
index d1c8fadc..f9cb6172 100644
--- a/spug_web/src/pages/deploy/routes.js
+++ b/spug_web/src/pages/deploy/routes.js
@@ -15,4 +15,6 @@ export default [
makeRoute('/request', request),
makeRoute('/do/ext1/:id', doExt1Index),
makeRoute('/do/ext2/:id', doExt2Index),
+ makeRoute('/do/ext1/:id/:log', doExt1Index),
+ makeRoute('/do/ext2/:id/:log', doExt2Index),
]