mirror of https://github.com/openspug/spug
A api update
parent
0390a9003a
commit
dc4b1cd5bf
|
@ -0,0 +1,82 @@
|
||||||
|
from django.db import models
|
||||||
|
from libs import ModelMixin, human_time
|
||||||
|
from apps.account.models import User
|
||||||
|
from apps.config.models import Environment
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
class App(models.Model, ModelMixin):
|
||||||
|
EXTENDS = (
|
||||||
|
('1', '常规发布'),
|
||||||
|
('2', '自定义发布'),
|
||||||
|
)
|
||||||
|
name = models.CharField(max_length=50)
|
||||||
|
env = models.ForeignKey(Environment, on_delete=models.PROTECT)
|
||||||
|
host_ids = models.TextField()
|
||||||
|
extend = models.CharField(max_length=2, choices=EXTENDS)
|
||||||
|
is_audit = models.BooleanField()
|
||||||
|
|
||||||
|
created_at = models.CharField(max_length=20, default=human_time)
|
||||||
|
created_by = models.ForeignKey(User, models.PROTECT, related_name='+')
|
||||||
|
updated_at = models.CharField(max_length=20, null=True)
|
||||||
|
updated_by = models.ForeignKey(User, models.PROTECT, related_name='+', null=True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def extend_obj(self):
|
||||||
|
cls = AppExtend1 if self.extend == '1' else AppExtend2
|
||||||
|
return cls.objects.filter(app=self).first()
|
||||||
|
|
||||||
|
def to_dict(self, *args, **kwargs):
|
||||||
|
app = super().to_dict(*args, **kwargs)
|
||||||
|
app['host_ids'] = json.loads(self.host_ids)
|
||||||
|
app.update(self.extend_obj.to_dict())
|
||||||
|
return app
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<App %r>' % self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = 'apps'
|
||||||
|
ordering = ('-id',)
|
||||||
|
|
||||||
|
|
||||||
|
class AppExtend1(models.Model, ModelMixin):
|
||||||
|
GIT_TYPES = (
|
||||||
|
('tag', 'tag'),
|
||||||
|
('branch', 'branch')
|
||||||
|
)
|
||||||
|
app = models.OneToOneField(App, primary_key=True, on_delete=models.CASCADE)
|
||||||
|
git_repo = models.CharField(max_length=255)
|
||||||
|
git_type = models.CharField(max_length=10, choices=GIT_TYPES)
|
||||||
|
dst_dir = models.CharField(max_length=255)
|
||||||
|
dst_repo = models.CharField(max_length=255)
|
||||||
|
versions = models.IntegerField()
|
||||||
|
filter_rule = models.TextField()
|
||||||
|
custom_envs = models.TextField()
|
||||||
|
hook_pre_server = models.TextField(null=True)
|
||||||
|
hook_post_server = models.TextField(null=True)
|
||||||
|
hook_pre_host = models.TextField(null=True)
|
||||||
|
hook_post_host = models.TextField(null=True)
|
||||||
|
|
||||||
|
def to_dict(self, *args, **kwargs):
|
||||||
|
tmp = super().to_dict(*args, **kwargs)
|
||||||
|
tmp['filter_rule'] = json.loads(self.filter_rule)
|
||||||
|
tmp['custom_envs'] = '\n'.join(f'{k}={v}' for k, v in json.loads(self.custom_envs).items())
|
||||||
|
return tmp
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<AppExtend1 app_id=%r>' % self.app_id
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = 'app_extend1'
|
||||||
|
|
||||||
|
|
||||||
|
class AppExtend2(models.Model, ModelMixin):
|
||||||
|
app = models.OneToOneField(App, primary_key=True, on_delete=models.CASCADE)
|
||||||
|
actions = models.TextField()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<AppExtend2 app_id=%r>' % self.app_id
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = 'app_extend2'
|
|
@ -0,0 +1,7 @@
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from .views import *
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', AppView.as_view()),
|
||||||
|
]
|
|
@ -0,0 +1,66 @@
|
||||||
|
from django.views.generic import View
|
||||||
|
from libs import JsonParser, Argument, json_response
|
||||||
|
from apps.app.models import App, AppExtend1, AppExtend2
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
class AppView(View):
|
||||||
|
def get(self, request):
|
||||||
|
apps = App.objects.all()
|
||||||
|
return json_response(apps)
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
form, error = JsonParser(
|
||||||
|
Argument('id', type=int, required=False),
|
||||||
|
Argument('name', help='请输入应用名称'),
|
||||||
|
Argument('env_id', type=int, help='请选择环境'),
|
||||||
|
Argument('host_ids', type=list, filter=lambda x: len(x), help='请选择要部署的主机'),
|
||||||
|
Argument('extend', filter=lambda x: x in dict(App.EXTENDS), help='请选择发布类型'),
|
||||||
|
Argument('is_audit', type=bool, default=False)
|
||||||
|
).parse(request.body)
|
||||||
|
if error is None:
|
||||||
|
form.host_ids = json.dumps(form.host_ids)
|
||||||
|
if form.extend == '1':
|
||||||
|
extend_form, error = JsonParser(
|
||||||
|
Argument('git_repo', handler=str.strip, help='请输入git仓库地址'),
|
||||||
|
Argument('git_type', help='参数错误'),
|
||||||
|
Argument('dst_dir', handler=str.strip, help='请输入发布目标路径'),
|
||||||
|
Argument('dst_repo', handler=str.strip, help='请输入目标仓库路径'),
|
||||||
|
Argument('versions', type=int, help='请输入保留历史版本数量'),
|
||||||
|
Argument('filter_rule', type=dict, help='参数错误'),
|
||||||
|
Argument('custom_envs', handler=str.strip, required=False),
|
||||||
|
Argument('hook_pre_server', handler=str.strip, default=''),
|
||||||
|
Argument('hook_post_server', handler=str.strip, default=''),
|
||||||
|
Argument('hook_pre_host', handler=str.strip, default=''),
|
||||||
|
Argument('hook_post_host', handler=str.strip, default='')
|
||||||
|
).parse(request.body)
|
||||||
|
if error:
|
||||||
|
return json_response(error=error)
|
||||||
|
extend_form.filter_rule = json.dumps(extend_form.filter_rule)
|
||||||
|
extend_form.custom_envs = json.dumps(parse_envs(extend_form.custom_envs))
|
||||||
|
if form.id:
|
||||||
|
App.objects.filter(pk=form.id).update(**form)
|
||||||
|
AppExtend1.objects.filter(app_id=form.id).update(**extend_form)
|
||||||
|
else:
|
||||||
|
app = App.objects.create(created_by=request.user, **form)
|
||||||
|
AppExtend1.objects.create(app=app, **extend_form)
|
||||||
|
elif form.extend == '2':
|
||||||
|
extend_form, error = JsonParser(
|
||||||
|
Argument('actions', type=list, filter=lambda x: len(x), help='请输入执行动作')
|
||||||
|
).parse(request.body)
|
||||||
|
if error:
|
||||||
|
return json_response(error=error)
|
||||||
|
app = App.objects.create(created_by=request.user, **form)
|
||||||
|
AppExtend2.objects.create(app=app, actions=json.dumps(extend_form.actions))
|
||||||
|
return json_response(error=error)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_envs(data):
|
||||||
|
tmp = {}
|
||||||
|
if data:
|
||||||
|
for line in data.split('\n'):
|
||||||
|
fields = line.split('=', 1)
|
||||||
|
if len(fields) != 2 or fields[0].strip() == '':
|
||||||
|
raise Exception(f'解析自定义全局变量{line!r}失败,确认其遵循 key = value 格式')
|
||||||
|
tmp[fields[0].strip()] = fields[1].strip()
|
||||||
|
return tmp
|
|
@ -206,11 +206,11 @@ def parse_text(request):
|
||||||
Argument('o_id', type=int, help='缺少必要参数'),
|
Argument('o_id', type=int, help='缺少必要参数'),
|
||||||
Argument('type', filter=lambda x: x in dict(Config.TYPES), help='缺少必要参数'),
|
Argument('type', filter=lambda x: x in dict(Config.TYPES), help='缺少必要参数'),
|
||||||
Argument('env_id', type=int, help='缺少必要参数'),
|
Argument('env_id', type=int, help='缺少必要参数'),
|
||||||
Argument('data', help='缺少必要参数')
|
Argument('data', handler=str.strip, help='缺少必要参数')
|
||||||
).parse(request.body)
|
).parse(request.body)
|
||||||
if error is None:
|
if error is None:
|
||||||
data = {}
|
data = {}
|
||||||
for line in form.pop('data').strip().split('\n'):
|
for line in form.pop('data').split('\n'):
|
||||||
fields = line.split('=', 1)
|
fields = line.split('=', 1)
|
||||||
if len(fields) != 2 or fields[0].strip() == '':
|
if len(fields) != 2 or fields[0].strip() == '':
|
||||||
return json_response(error=f'解析配置{line!r}失败,确认其遵循 key = value 格式')
|
return json_response(error=f'解析配置{line!r}失败,确认其遵循 key = value 格式')
|
||||||
|
|
|
@ -17,7 +17,8 @@ class Argument(object):
|
||||||
:param bool required: is required
|
:param bool required: is required
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, default=None, required=True, type=str, filter=None, help=None, nullable=False):
|
def __init__(self, name, default=None, handler=None, required=True, type=str, filter=None, help=None,
|
||||||
|
nullable=False):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.default = default
|
self.default = default
|
||||||
self.type = type
|
self.type = type
|
||||||
|
@ -25,6 +26,7 @@ class Argument(object):
|
||||||
self.nullable = nullable
|
self.nullable = nullable
|
||||||
self.filter = filter
|
self.filter = filter
|
||||||
self.help = help
|
self.help = help
|
||||||
|
self.handler = handler
|
||||||
if not isinstance(self.name, str):
|
if not isinstance(self.name, str):
|
||||||
raise TypeError('Argument name must be string')
|
raise TypeError('Argument name must be string')
|
||||||
if filter and not callable(self.filter):
|
if filter and not callable(self.filter):
|
||||||
|
@ -63,6 +65,8 @@ class Argument(object):
|
||||||
if not self.filter(value):
|
if not self.filter(value):
|
||||||
raise ParseError(
|
raise ParseError(
|
||||||
self.help or 'Value Error: %s filter check failed' % self.name)
|
self.help or 'Value Error: %s filter check failed' % self.name)
|
||||||
|
if self.handler:
|
||||||
|
value = self.handler(value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ INSTALLED_APPS = [
|
||||||
'apps.monitor',
|
'apps.monitor',
|
||||||
'apps.alarm',
|
'apps.alarm',
|
||||||
'apps.config',
|
'apps.config',
|
||||||
|
'apps.app',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
|
|
@ -24,4 +24,5 @@ urlpatterns = [
|
||||||
path('alarm/', include('apps.alarm.urls')),
|
path('alarm/', include('apps.alarm.urls')),
|
||||||
path('setting/', include('apps.setting.urls')),
|
path('setting/', include('apps.setting.urls')),
|
||||||
path('config/', include('apps.config.urls')),
|
path('config/', include('apps.config.urls')),
|
||||||
|
path('app/', include('apps.app.urls')),
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue