commit
a0921219c2
4 changed files with 279 additions and 0 deletions
@ -0,0 +1,28 @@ |
|||||||
|
**service-updater** - Script for updating service/application |
||||||
|
|
||||||
|
## Description |
||||||
|
|
||||||
|
Scenario update application on remote server with full back up of configuration |
||||||
|
files and database. |
||||||
|
|
||||||
|
## Requirements |
||||||
|
|
||||||
|
# install python-dev libffi-dev |
||||||
|
# pip install configparser |
||||||
|
# pip install fabric |
||||||
|
|
||||||
|
## Usage |
||||||
|
|
||||||
|
backup_db Back up distributive |
||||||
|
backup_db_copy Backround upload of backup to remote server |
||||||
|
backup_files Back up files |
||||||
|
copy_config Copy configuration files |
||||||
|
copy_custom_conf Upload custom.conf to remote server |
||||||
|
copy_libs Copy libs |
||||||
|
diff_config Comparison of configuration files |
||||||
|
make_dist Make distributive (play dist) |
||||||
|
start Start service |
||||||
|
stop Stop service |
||||||
|
update Update service |
||||||
|
upload_dist Upload distributive to remote server |
||||||
|
|
@ -0,0 +1,35 @@ |
|||||||
|
[DEFAULT] |
||||||
|
|
||||||
|
config_dirs: [['daemon/root/conf/', 'conf/'], ['daemon/root/modules/catalogs/', 'catalogs/'] |
||||||
|
|
||||||
|
config_exclude: ['custom.conf', 'custom.conf.template'] |
||||||
|
|
||||||
|
[java] |
||||||
|
play: play |
||||||
|
|
||||||
|
[release] |
||||||
|
app_path_stand: /opt/daemon-rc |
||||||
|
app_path_dist: ${release:app_path_stand}/daemon/root/target/universal |
||||||
|
|
||||||
|
make_dist_script: make_dist.sh |
||||||
|
|
||||||
|
custom_conf_template: ${release:app_path_stand}/daemon/root/conf/custom.conf.template |
||||||
|
|
||||||
|
[product] |
||||||
|
host: user@127.0.0.1:10022 |
||||||
|
|
||||||
|
app_path_stand: /root/daemon/ |
||||||
|
app_path_dist: ${product:app_path_stand}/../dist |
||||||
|
app_path_libs: ${product:app_path_stand}/daemon/root/lib |
||||||
|
|
||||||
|
app_pid_file: ${product:app_path_stand}/daemon/root/RUNNING_PID |
||||||
|
|
||||||
|
app_start_script: start_app.sh |
||||||
|
|
||||||
|
backup_files: ['catalogs', 'daemon/catalogs', 'daemon/root/lib', 'conf'] |
||||||
|
|
||||||
|
backup_db_script: /root/scripts/db-backup.sh |
||||||
|
backup_db_copy_script: /root/scripts/db-backup-upload.sh |
||||||
|
|
||||||
|
custom_conf_template: ${product:app_path_stand}/conf/custom.conf.template |
||||||
|
|
@ -0,0 +1,216 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
# -*- coding: utf-8 -*- |
||||||
|
|
||||||
|
# |
||||||
|
# Script for updating service/application on remote server using Fabric. |
||||||
|
# |
||||||
|
# (c) envrm |
||||||
|
# |
||||||
|
|
||||||
|
#Installing fabric: |
||||||
|
# apt-get install python-dev libffi-dev |
||||||
|
# pip install configparser |
||||||
|
# pip install fabric |
||||||
|
|
||||||
|
from __future__ import with_statement |
||||||
|
from fabric.api import * |
||||||
|
|
||||||
|
import os |
||||||
|
import ast |
||||||
|
import glob |
||||||
|
import datetime |
||||||
|
import subprocess |
||||||
|
import configparser |
||||||
|
|
||||||
|
from contextlib import contextmanager |
||||||
|
|
||||||
|
#----------------------------------------------------------------------------- |
||||||
|
# Class for errors catching |
||||||
|
class FabricException(Exception): |
||||||
|
pass |
||||||
|
|
||||||
|
#----------------------------------------------------------------------------- |
||||||
|
|
||||||
|
#Color highlighting of errors |
||||||
|
env.colorize_errors = True |
||||||
|
#Generating exceprions instead fails |
||||||
|
env.abort_exception=FabricException |
||||||
|
|
||||||
|
#----------------------------------------------------------------------------- |
||||||
|
|
||||||
|
@contextmanager |
||||||
|
def if_needed(): |
||||||
|
'''Execution with ignore errors''' |
||||||
|
try: |
||||||
|
yield |
||||||
|
except: |
||||||
|
pass |
||||||
|
|
||||||
|
def readConfig(config_file): |
||||||
|
'''Read config file''' |
||||||
|
global settings |
||||||
|
settings = configparser.ConfigParser() |
||||||
|
settings._interpolation = configparser.ExtendedInterpolation() |
||||||
|
settings.read(config_file) |
||||||
|
return settings |
||||||
|
|
||||||
|
@contextmanager |
||||||
|
def timestamp(*args): |
||||||
|
print '[' + current_time() + ']', args |
||||||
|
yield |
||||||
|
|
||||||
|
def opt(parameter): |
||||||
|
'''Get options''' |
||||||
|
setting = parameter.split(":") |
||||||
|
return settings.get(setting[0], setting[1]) |
||||||
|
|
||||||
|
def background_run(command): |
||||||
|
'''Background execution of commands on remote servers''' |
||||||
|
subprocess.Popen(["nohup", command]) |
||||||
|
|
||||||
|
def current_time(): |
||||||
|
'''Current time in HH:MM:SS format''' |
||||||
|
return datetime.datetime.now().time().strftime('%X') |
||||||
|
|
||||||
|
#----------------------------------------------------------------------------- |
||||||
|
|
||||||
|
@task |
||||||
|
def make_dist(): |
||||||
|
'''Make distributive (play dist)''' |
||||||
|
with lcd(opt('release:app_path_stand')): |
||||||
|
local("sh ./%s" % opt('release:make_dist_script')) |
||||||
|
|
||||||
|
@task |
||||||
|
def diff_config(): |
||||||
|
'''Comparison of configuration files''' |
||||||
|
old_config = opt('product:custom_conf_template') |
||||||
|
new_config = opt('release:custom_conf_template') |
||||||
|
tmp_config = os.path.join('/tmp/', os.path.basename(old_config)) |
||||||
|
|
||||||
|
with hide('commands'): |
||||||
|
get(old_config, '/tmp/') |
||||||
|
|
||||||
|
diff = os.system("diff -w -B %s %s" % (tmp_config, new_config)) |
||||||
|
|
||||||
|
local("rm -f %s" % tmp_config) |
||||||
|
|
||||||
|
if diff == 0: |
||||||
|
return False |
||||||
|
else: |
||||||
|
return True |
||||||
|
|
||||||
|
def copy_dirs(directories): |
||||||
|
'''Upload local directories to remote server''' |
||||||
|
for src_dir, dst_dir in ast.literal_eval(directories): |
||||||
|
for config_file in glob.glob(src_dir + "/*"): |
||||||
|
if os.path.basename(config_file) not in opt('product:config_exclude'): |
||||||
|
src_root = src_dir.split('*')[0] |
||||||
|
config_file_path = config_file.replace(src_root, '') |
||||||
|
remote_file = os.path.join(opt('product:app_path_stand'), dst_dir.split('*')[0], config_file_path) |
||||||
|
|
||||||
|
put(config_file, remote_file) |
||||||
|
|
||||||
|
@task |
||||||
|
def upload_dist(): |
||||||
|
'''Upload distributive to remote server''' |
||||||
|
dist_dir = os.path.join(opt('product:app_path_dist'), date) |
||||||
|
run('mkdir -p %s' % dist_dir) |
||||||
|
|
||||||
|
#Uploading archive |
||||||
|
with lcd(opt('release:app_path_dist')): |
||||||
|
put('*.zip', dist_dir) |
||||||
|
|
||||||
|
#Extracting files |
||||||
|
with cd(dist_dir): |
||||||
|
run('yes All | unzip *.zip') |
||||||
|
|
||||||
|
@task |
||||||
|
def copy_config(): |
||||||
|
'''Copy configuration files''' |
||||||
|
if diff_config(): |
||||||
|
print "#### [WARN] Configuration files on the servers are different. Check them manually" |
||||||
|
print "#### or execute `fab copy_custom_conf' for force upload custom.conf.template" |
||||||
|
exit(1) |
||||||
|
|
||||||
|
copy_dirs(opt('release:config_dirs')) |
||||||
|
|
||||||
|
@task |
||||||
|
def copy_custom_conf(): |
||||||
|
'''Upload custom.conf to remote server''' |
||||||
|
put(opt('release:custom_conf_template'), opt('product:custom_conf_template')) |
||||||
|
|
||||||
|
@task |
||||||
|
def backup_files(): |
||||||
|
'''Back up files''' |
||||||
|
with cd(opt('product:app_path_stand')): |
||||||
|
run('mkdir -p backup/%s' % date) |
||||||
|
for to_backup in ast.literal_eval(opt('product:backup_files')): |
||||||
|
run('cp -rv %s ../backup/%s' % (os.path.join(opt('product:app_path_stand'), to_backup), date)) |
||||||
|
|
||||||
|
@task |
||||||
|
def backup_db(): |
||||||
|
'''Back up distributive''' |
||||||
|
with cd(opt('product:app_path_stand')): |
||||||
|
run("%s" % opt('product:backup_db_script')) |
||||||
|
|
||||||
|
@task |
||||||
|
def backup_db_copy(): |
||||||
|
'''Backround upload of backup to remote server''' |
||||||
|
with cd(opt('product:app_path_stand')): |
||||||
|
background_run(opt('product:backup_db_copy_script')) |
||||||
|
|
||||||
|
@task |
||||||
|
def stop(): |
||||||
|
'''Stop service''' |
||||||
|
run('kill $(cat %s)' % opt('product:app_pid_file')) |
||||||
|
|
||||||
|
def remove_libs(): |
||||||
|
'''Remove previous version''' |
||||||
|
run('rm -rfv %s' % opt('product:app_path_libs')) |
||||||
|
|
||||||
|
@task |
||||||
|
def copy_libs(): |
||||||
|
'''Copy libs''' |
||||||
|
with cd(opt('product:app_path_stand')): |
||||||
|
run('cp -rv %s %s' % (os.path.join(opt('product:app_path_dist'), date, '*/lib'), opt('product:app_path_libs'))) |
||||||
|
|
||||||
|
@task |
||||||
|
def start(): |
||||||
|
'''Start service''' |
||||||
|
with cd(opt('product:app_path_stand')): |
||||||
|
run("sh %s >> nohup.out 2>> nohup.out < /dev/null &" % opt('product:app_start_script'), pty=True) |
||||||
|
|
||||||
|
#----------------------------------------------------------------------------- |
||||||
|
@task |
||||||
|
def update(): |
||||||
|
'''Update service''' |
||||||
|
make_dist() |
||||||
|
upload_dist() |
||||||
|
copy_config() |
||||||
|
backup_files() |
||||||
|
|
||||||
|
with timestamp("MAKING BACKUP"): |
||||||
|
backup_db() |
||||||
|
with timestamp("UPLOADING BACKUP"): |
||||||
|
backup_db_copy() |
||||||
|
with timestamp("NEXT STEP"): |
||||||
|
pass |
||||||
|
|
||||||
|
with if_needed(): stop() |
||||||
|
|
||||||
|
remove_libs() |
||||||
|
copy_libs() |
||||||
|
|
||||||
|
start() |
||||||
|
|
||||||
|
#----------------------------------------------------------------------------- |
||||||
|
|
||||||
|
settings = [] |
||||||
|
|
||||||
|
readConfig('config.ini') |
||||||
|
|
||||||
|
#Remote host |
||||||
|
env.host_string = opt('product:host') |
||||||
|
|
||||||
|
date = datetime.datetime.now().strftime("%Y-%m-%d") |
||||||
|
|
Binary file not shown.
Loading…
Reference in new issue