Skip to content

Commit

Permalink
A 添加发布日志查看功能
Browse files Browse the repository at this point in the history
  • Loading branch information
vapao committed Mar 14, 2020
1 parent 17dfe26 commit 9e6d3b3
Show file tree
Hide file tree
Showing 13 changed files with 101 additions and 37 deletions.
6 changes: 3 additions & 3 deletions spug_api/apps/deploy/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 12 additions & 2 deletions spug_api/apps/deploy/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion spug_api/apps/monitor/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions spug_api/apps/monitor/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion spug_api/apps/schedule/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions spug_api/apps/schedule/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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):
Expand Down
20 changes: 17 additions & 3 deletions spug_api/consumer/consumers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,43 @@
# 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()

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')


Expand Down
2 changes: 1 addition & 1 deletion spug_api/consumer/executors.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
1 change: 1 addition & 0 deletions spug_api/spug/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 24 additions & 8 deletions spug_web/src/pages/deploy/do/Ext1Index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}))
}

Expand All @@ -37,25 +48,29 @@ 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}/`)
.then(({token, outputs}) => {
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');
};
this.socket.onmessage = e => {
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))
}
}
})
Expand Down Expand Up @@ -100,7 +115,8 @@ class Ext1Index extends React.Component {
subTitle={`${app_name} - ${env_name}`}
style={{padding: 0}}
tags={this.getStatusAlias()}
extra={<Button loading={this.state.loading} type="primary" disabled={!['1', '-3'].includes(status)}
extra={<Button loading={this.state.loading} type="primary"
disabled={this.log || !['1', '-3'].includes(status)}
onClick={this.handleDeploy}>发布</Button>}
onBack={() => history.goBack()}/>
<Collapse defaultActiveKey={1} className={styles.collapse}>
Expand Down
31 changes: 23 additions & 8 deletions spug_web/src/pages/deploy/do/Ext2Index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}))
}

Expand All @@ -37,25 +48,29 @@ 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}/`)
.then(({token, outputs}) => {
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');
};
this.socket.onmessage = e => {
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))
}
}
})
Expand Down Expand Up @@ -100,7 +115,7 @@ class Ext1Index extends React.Component {
subTitle={`${app_name} - ${env_name}`}
style={{padding: 0}}
tags={this.getStatusAlias()}
extra={<Button loading={this.state.loading} type="primary" disabled={!['1', '-3'].includes(status)}
extra={<Button loading={this.state.loading} type="primary" disabled={this.log || !['1', '-3'].includes(status)}
onClick={this.handleDeploy}>发布</Button>}
onBack={() => history.goBack()}/>
<Collapse defaultActiveKey={1} className={styles.collapse}>
Expand Down
16 changes: 11 additions & 5 deletions spug_web/src/pages/deploy/request/Table.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ class ComTable extends React.Component {
switch (info.status) {
case '-3':
return <React.Fragment>
<AuthLink auth="deploy.request.do" to={`/deploy/do/ext${info['app_extend']}/${info.id}/1`}>查看</AuthLink>
<Divider type="vertical"/>
<AuthLink auth="deploy.request.do" to={`/deploy/do/ext${info['app_extend']}/${info.id}`}>发布</AuthLink>
<Divider type="vertical"/>
<LinkButton
Expand All @@ -94,11 +96,15 @@ class ComTable extends React.Component {
onClick={() => this.handleRollback(info)}>回滚</LinkButton>
</React.Fragment>;
case '3':
return <LinkButton
auth="deploy.request.do"
disabled={info.type === '2'}
loading={this.state.loading}
onClick={() => this.handleRollback(info)}>回滚</LinkButton>;
return <React.Fragment>
<AuthLink auth="deploy.request.do" to={`/deploy/do/ext${info['app_extend']}/${info.id}/1`}>查看</AuthLink>
<Divider type="vertical"/>
<LinkButton
auth="deploy.request.do"
disabled={info.type === '2'}
loading={this.state.loading}
onClick={() => this.handleRollback(info)}>回滚</LinkButton>
</React.Fragment>;
case '-1':
return <React.Fragment>
<LinkButton auth="deploy.request.edit" onClick={() => store.showForm(info)}>编辑</LinkButton>
Expand Down
2 changes: 2 additions & 0 deletions spug_web/src/pages/deploy/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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),
]

0 comments on commit 9e6d3b3

Please sign in to comment.