Skip to content

Commit b3679bf

Browse files
authored
Merge pull request #574 from meolu/2.0.0/feature/project
2.0.0/feature/project
2 parents f90ea68 + 5e1c1da commit b3679bf

File tree

6 files changed

+166
-60
lines changed

6 files changed

+166
-60
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""07_project_include
2+
3+
Revision ID: 5ff964e844a7
4+
Revises: 0af33c7b8832
5+
Create Date: 2019-01-04 18:00:58.941866
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
11+
12+
revision = '5ff964e844a7'
13+
down_revision = '0af33c7b8832'
14+
branch_labels = None
15+
depends_on = None
16+
17+
18+
def upgrade():
19+
op.add_column('projects', sa.Column('is_include', sa.Integer(), nullable=True, server_default='0'))
20+
21+
22+
def downgrade():
23+
op.drop_column('projects', 'is_include')

walle/form/project.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class ProjectForm(FlaskForm):
2323
space_id = StringField('space_id', [validators.Length(min=1, max=10)])
2424
status = StringField('status', [])
2525
excludes = StringField('excludes', [])
26+
is_include = StringField('excludes', [])
2627
master = StringField('master', [])
2728
server_ids = StringField('server_ids', [validators.Length(min=1)])
2829
keep_version_num = StringField('keep_version_num', [])
@@ -70,6 +71,7 @@ def form2dict(self):
7071
'environment_id': self.environment_id.data if self.environment_id.data else '',
7172
'space_id': self.space_id.data if self.space_id.data else current_user.space_id(),
7273
'excludes': self.excludes.data if self.excludes.data else '',
74+
'is_include': self.is_include.data,
7375
'server_ids': self.server_ids.data if self.server_ids.data else '',
7476
'keep_version_num': self.keep_version_num.data if self.keep_version_num.data else 5,
7577

walle/model/project.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class ProjectModel(SurrogatePK, Model):
3737
master = db.Column(String(100))
3838
version = db.Column(String(40))
3939
excludes = db.Column(Text)
40+
is_include = db.Column(Integer)
4041
target_root = db.Column(String(200))
4142
target_releases = db.Column(String(200))
4243
server_ids = db.Column(Text)
@@ -154,6 +155,7 @@ def to_json(self):
154155
'master': UserModel.fetch_by_uid(self.master.split(',')) if self.master else '',
155156
'version': self.version,
156157
'excludes': self.excludes,
158+
'is_include': self.is_include,
157159
'target_root': self.target_root,
158160
'target_releases': self.target_releases,
159161
'server_ids': self.server_ids,

walle/service/deployer.py

Lines changed: 84 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
from walle.model.task import TaskModel
1919
from walle.service.code import Code
2020
from walle.service.error import WalleError
21-
from walle.service.utils import color_clean, suffix_format
22-
from walle.service.utils import excludes_format
21+
from walle.service.utils import color_clean
22+
from walle.service.utils import excludes_format, includes_format
2323
from walle.service.notice import Notice
2424
from walle.service.waller import Waller
2525
from flask_login import current_user
@@ -45,6 +45,7 @@ class Deployer:
4545
TaskRecord = None
4646

4747
console = False
48+
custom_global_env = {}
4849

4950
version = datetime.now().strftime('%Y%m%d%H%M%S')
5051

@@ -56,7 +57,7 @@ class Deployer:
5657
local = None
5758

5859
def __init__(self, task_id=None, project_id=None, console=False):
59-
self.local_codebase = current_app.config.get('CODE_BASE')
60+
self.local_codebase = current_app.config.get('CODE_BASE').rstrip('/') + '/'
6061
self.localhost = Waller(host='127.0.0.1')
6162
self.TaskRecord = RecordModel()
6263

@@ -69,6 +70,37 @@ def __init__(self, task_id=None, project_id=None, console=False):
6970
self.servers = self.taskMdl.get('servers_info')
7071
self.project_info = self.taskMdl.get('project_info')
7172

73+
# copy to a local version
74+
self.release_version = '{project_id}_{task_id}_{timestamp}'.format(
75+
project_id=self.project_info['id'],
76+
task_id=self.task_id,
77+
timestamp=time.strftime('%Y%m%d_%H%M%S', time.localtime(time.time())),
78+
)
79+
current_app.logger.info(self.taskMdl)
80+
81+
self.custom_global_env = {
82+
'WEBROOT': '"{}"'.format(self.project_info['target_root']),
83+
'CURRENT_RELEASE': '"{}"'.format(self.release_version),
84+
'BRANCH': '"{}"'.format(self.taskMdl.get('branch')),
85+
'TAG': '"{}"'.format(self.taskMdl.get('tag')),
86+
'COMMIT_ID': '"{}"'.format(self.taskMdl.get('commit_id')),
87+
'PROJECT_NAME': '"{}"'.format(self.project_info['name']),
88+
'PROJECT_ID': '"{}"'.format(self.project_info['id']),
89+
'TASK_NAME': '"{}"'.format(self.taskMdl.get('name')),
90+
'TASK_ID': '"{}"'.format(self.task_id),
91+
'DEPLOY_USER': '"{}"'.format(self.taskMdl.get('user_name')),
92+
'DEPLOY_TIME': '"{}"'.format(time.strftime('%Y%m%d-%H:%M:%S', time.localtime(time.time()))),
93+
}
94+
if self.project_info['task_vars']:
95+
task_vars = [i.strip() for i in self.project_info['task_vars'].split('\n') if i.strip() and not i.strip().startswith('#')]
96+
for var in task_vars:
97+
var_list = var.split('=', 1)
98+
if len(var_list) != 2:
99+
continue
100+
self.custom_global_env[var_list[0]] = var_list[1]
101+
102+
self.localhost.init_env(env=self.custom_global_env)
103+
72104
if project_id:
73105
self.project_id = project_id
74106
self.project_info = ProjectModel(id=project_id).item()
@@ -118,11 +150,13 @@ def prev_deploy(self):
118150
self.init_repo()
119151

120152
# 用户自定义命令
121-
command = self.project_info['prev_deploy']
122-
if command:
123-
current_app.logger.info(command)
124-
with self.localhost.cd(self.dir_codebase_project):
125-
result = self.localhost.local(command, wenv=self.config())
153+
commands = self.project_info['prev_deploy']
154+
if commands:
155+
for command in commands.split('\n'):
156+
if command.strip().startswith('#') or not command.strip():
157+
continue
158+
with self.localhost.cd(self.dir_codebase_project):
159+
result = self.localhost.local(command, wenv=self.config())
126160

127161
def deploy(self):
128162
'''
@@ -133,10 +167,10 @@ def deploy(self):
133167
'''
134168
self.stage = self.stage_deploy
135169
self.sequence = 2
136-
137-
# copy to a local version
138-
self.release_version = '%s_%s_%s' % (
139-
self.project_name, self.task_id, time.strftime('%Y%m%d_%H%M%S', time.localtime(time.time())))
170+
#
171+
# # copy to a local version
172+
# self.release_version = '%s_%s_%s' % (
173+
# self.project_name, self.task_id, time.strftime('%Y%m%d_%H%M%S', time.localtime(time.time())))
140174

141175
with self.localhost.cd(self.local_codebase):
142176
command = 'cp -rf %s %s' % (self.dir_codebase_project, self.release_version)
@@ -168,26 +202,25 @@ def post_deploy(self):
168202
self.sequence = 3
169203

170204
# 用户自定义命令
171-
command = self.project_info['post_deploy']
172-
if command:
173-
with self.localhost.cd(self.local_codebase + self.release_version):
174-
result = self.localhost.local(command, wenv=self.config())
205+
commands = self.project_info['post_deploy']
206+
if commands:
207+
for command in commands.split('\n'):
208+
if command.strip().startswith('#') or not command.strip():
209+
continue
210+
with self.localhost.cd(self.local_codebase + self.release_version):
211+
result = self.localhost.local(command, wenv=self.config())
175212

176213
# 压缩打包
177214
# 排除文件发布
178215
self.release_version_tar = '%s.tgz' % (self.release_version)
179216
with self.localhost.cd(self.local_codebase):
180-
excludes = excludes_format(self.project_info['excludes'])
181-
command = 'tar zcf %s %s %s' % (self.release_version_tar, excludes, self.release_version)
217+
if self.project_info['is_include']:
218+
files = includes_format(self.release_version, self.project_info['excludes'])
219+
else:
220+
files = excludes_format(self.release_version, self.project_info['excludes'])
221+
command = 'tar zcf %s/%s %s' % (self.local_codebase.rstrip('/'), self.release_version_tar, files)
182222
result = self.localhost.local(command, wenv=self.config())
183223

184-
# # 指定文件发布
185-
# self.release_version_tar = '%s.tgz' % (self.release_version)
186-
# with self.localhost.cd(self.local_codebase):
187-
# excludes = suffix_format(self.dir_codebase_project, self.project_info['excludes'])
188-
# command = 'tar zcf %s %s %s' % (self.release_version_tar, excludes, self.release_version)
189-
# result = self.local.run(command, wenv=self.config())
190-
191224
def prev_release(self, waller):
192225
'''
193226
4.部署代码到目标机器前做的任务
@@ -215,13 +248,15 @@ def prev_release(self, waller):
215248

216249
def prev_release_custom(self, waller):
217250
# 用户自定义命令
218-
command = self.project_info['prev_release']
219-
if command:
220-
current_app.logger.info(command)
221-
# TODO
222-
target_release_version = "%s/%s" % (self.project_info['target_releases'], self.release_version)
223-
with waller.cd(target_release_version):
224-
result = waller.run(command, wenv=self.config())
251+
commands = self.project_info['prev_release']
252+
if commands:
253+
for command in commands.split('\n'):
254+
if command.strip().startswith('#') or not command.strip():
255+
continue
256+
# TODO
257+
target_release_version = "%s/%s" % (self.project_info['target_releases'], self.release_version)
258+
with waller.cd(target_release_version):
259+
result = waller.run(command, wenv=self.config())
225260

226261
def release(self, waller):
227262
'''
@@ -299,14 +334,16 @@ def post_release(self, waller):
299334
'''
300335
self.stage = self.stage_post_release
301336
self.sequence = 6
302-
303337
# 用户自定义命令
304-
command = self.project_info['post_release']
305-
if command:
306-
current_app.logger.info(command)
307-
with waller.cd(self.project_info['target_root']):
308-
result = waller.run(command, wenv=self.config())
309-
338+
commands = self.project_info['post_release']
339+
if commands:
340+
for command in commands.split('\n'):
341+
if command.strip().startswith('#') or not command.strip():
342+
continue
343+
# TODO
344+
with waller.cd(self.project_info['target_root']):
345+
pty = False if command.find('nohup') >= 0 else True
346+
result = waller.run(command, wenv=self.config(), pty=pty)
310347
# 个性化,用户重启的不一定是NGINX,可能是tomcat, apache, php-fpm等
311348
# self.post_release_service(waller)
312349

@@ -495,7 +532,10 @@ def walle_deploy(self):
495532
for server_info in self.servers:
496533
host = server_info['host']
497534
try:
498-
self.connections[host] = Waller(host=host, user=server_info['user'], port=server_info['port'])
535+
waller = Waller(host=host, user=server_info['user'], port=server_info['port'], inline_ssh_env=True)
536+
waller.init_env(env=self.custom_global_env)
537+
538+
self.connections[host] = waller
499539
self.prev_release(self.connections[host])
500540
self.release(self.connections[host])
501541
self.post_release(self.connections[host])
@@ -527,7 +567,10 @@ def walle_rollback(self):
527567
for server_info in self.servers:
528568
host = server_info['host']
529569
try:
530-
self.connections[host] = Waller(host=host, user=server_info['user'], port=server_info['port'])
570+
waller = Waller(host=host, user=server_info['user'], port=server_info['port'], inline_ssh_env=True)
571+
waller.init_env(env=self.custom_global_env)
572+
573+
self.connections[host] = waller
531574
self.prev_release_custom(self.connections[host])
532575
self.release(self.connections[host])
533576
self.post_release(self.connections[host])

walle/service/utils.py

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,16 +81,39 @@ def say_yes():
8181
)
8282

8383

84-
def excludes_format(excludes_string):
85-
excludes = [i for i in excludes_string.split('\n') if i.strip()]
86-
if not excludes:
87-
return ''
88-
excludes = ' --exclude='.join(excludes)
89-
return ' --exclude=' + excludes
90-
91-
92-
# 指定发布文件,支持模糊匹配,如:*.war
93-
def suffix_format(path, suffix_file):
94-
for suffix_file in os.listdir('%s' % path):
95-
if fnmatch.fnmatch(suffix_file, '%s' % suffix_file):
96-
return suffix_file
84+
def excludes_format(path, excludes_string=None):
85+
'''
86+
排除文件,支持正则匹配,支持多选字符串
87+
@param path:
88+
@param excludes_string:
89+
@return:
90+
'''
91+
path = os.path.basename(path) + '/'
92+
if not excludes_string:
93+
return path
94+
95+
prefix = '--exclude='
96+
excludes = [prefix + i for i in excludes_string.split('\n') if i.strip()]
97+
98+
return ' {excludes} {path} '.format(excludes=' '.join(excludes), path=path)
99+
100+
101+
def includes_format(path, includes_string=None):
102+
'''
103+
指定发布文件,支持正则匹配,如:*.war。支持多行字符串。
104+
105+
@param path: release目录,非路径
106+
@param includes_string:
107+
@return:
108+
'''
109+
path = os.path.basename(path) + '/'
110+
if not includes_string:
111+
return path
112+
113+
prefix = path
114+
includes = [prefix + i for i in includes_string.split('\n') if i.strip()]
115+
116+
if not includes:
117+
return path
118+
119+
return ' '.join(includes)

walle/service/waller.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ class Waller(Connection):
2323
connections, success, errors = {}, {}, {}
2424
release_version_tar, release_version = None, None
2525

26+
custom_global_env = {}
27+
28+
def init_env(self, env):
29+
self.custom_global_env = env
30+
2631
def run(self, command, wenv=None, run_mode=run_mode_remote, pty=True, exception=True, **kwargs):
2732
'''
2833
pty=True/False是直接影响到输出.False较适合在获取文本,True更适合websocket
@@ -40,11 +45,12 @@ def run(self, command, wenv=None, run_mode=run_mode_remote, pty=True, exception=
4045
current_app.logger.info(message)
4146
try:
4247
if run_mode == self.run_mode_sudo:
43-
result = super(Waller, self).sudo(command, pty=pty, **kwargs)
48+
result = super(Waller, self).sudo(command, pty=pty, env=self.custom_global_env, **kwargs)
4449
elif run_mode == self.run_mode_local:
45-
result = super(Waller, self).local(command, pty=pty, warn=True, watchers=[say_yes()], **kwargs)
50+
current_app.logger.info(self.custom_global_env)
51+
result = super(Waller, self).local(command, pty=pty, warn=True, watchers=[say_yes()], env=self.custom_global_env, **kwargs)
4652
else:
47-
result = super(Waller, self).run(command, pty=pty, warn=True, watchers=[say_yes()], **kwargs)
53+
result = super(Waller, self).run(command, pty=pty, warn=True, watchers=[say_yes()], env=self.custom_global_env, **kwargs)
4854

4955
if result.failed:
5056
exitcode, stdout, stderr = result.exited, '', result.stdout
@@ -174,17 +180,24 @@ def sync(self, wtype, remote=None, local=None, wenv=None):
174180
except Exception as e:
175181
# TODO 收尾下
176182
current_app.logger.info('put: %s, %s', e, dir(e))
183+
if wtype == 'put':
184+
op_user = pwd.getpwuid(os.getuid())[0]
185+
op_host = '127.0.0.1'
186+
else:
187+
op_user = self.user
188+
op_host = self.host
177189

178190
# TODO command
179191
ws_dict = {
180-
'user': self.user,
181-
'host': self.host,
192+
'user': op_user,
193+
'host': op_host,
182194
'cmd': command,
183195
'status': 1,
184196
'stage': wenv['stage'],
185197
'sequence': wenv['sequence'],
186198
'success': '',
187-
'error': e.message,
199+
'error': str(e),
188200
}
201+
current_app.logger.info(ws_dict)
189202
if wenv['console']:
190203
emit('console', {'event': 'task:console', 'data': ws_dict}, room=wenv['task_id'])

0 commit comments

Comments
 (0)