#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2017, Maxim Lihachev, # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation, version 3. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # more details. # # You should have received a copy of the GNU General Public License along with # this program. If not, see . import re import os import ast import sys import shutil import signal import datetime import subprocess import configparser from string import Template from contextlib import contextmanager @contextmanager def if_needed(): '''Execute with error ignoring''' try: yield except: pass class Dir: '''Execute procedures in directory''' def __init__(self, directory): self.current_dir = directory def __enter__(self): self.previous_dir = os.getcwd() os.chdir(self.current_dir) def __exit__(self, type, value, traceback): os.chdir(self.previous_dir) # ---------------------------------------------------------------------- def readConfig(config_file): '''Loading configuration file''' global settings settings = configparser.ConfigParser() settings._interpolation = configparser.ExtendedInterpolation() settings.read(config_file) return settings def opt(parameter): '''Getting parameters from file''' setting = parameter.split(":") value = settings.get(setting[0], setting[1]) variables = re.findall(r"\$(\w+)", value) substr = {v: globals()[v] for v in variables if v in globals()} return Template(value).safe_substitute(**substr) def saveTimestamp(outfile, mode="a"): '''Saving timestamt to file''' with open(outfile, mode) as log: now = datetime.datetime.now() log.write(now.strftime("%Y-%m-%d %H:%M:%S\n")) # ---------------------------------------------------------------------- def git(command, stand="develop"): '''Executing git command''' with Dir(opt(stand + ':git_path')): git_output = subprocess.check_output('git ' + command, shell=True) return git_output def stop(stand="develop"): '''Stop service''' if os.path.isfile(opt(stand + ':pid')): pid = opt(stand + ':pid') else: pid = os.path.join(opt(stand + ':path'), 'RUNNING_PID') print pid with if_needed(): with open(pid, 'r') as pid_file: os.kill(int(pid_file.read()), signal.SIGTERM) os.remove(pid) print "#### Application stopped" def start(stand): '''Start service''' service_dir = opt(stand + ':path') server = opt(stand + ':server') start_cmd = 'nohup java ' + opt(stand + ':java_options') \ + ' -Dfile.encoding=UTF-8 -Dconfig.file=' + service_dir + '/conf/application.conf ' \ + ' -Dhttp.port=' + opt(stand + ':http_port') + ' -cp "' + service_dir + '/conf/:' + opt(stand + ':class_path') + '/*" ' \ + server + ' ' + service_dir + ' | tee -a ' + opt(stand + ':home') + '/nohup.out &' print start_cmd with Dir(service_dir): return_code = os.system(start_cmd) if return_code == 0: print "#### Application started" else: print return_code exit(1) def restart(stand="develop"): '''Restart service''' stop(stand) start(stand) saveTimestamp(opt(stand + ':restarts_log')) def clean_all(): '''Remove artefacts''' os.system("rm -rfv " + opt('develop:targets')) def build(target="stand", modules=None): '''Build service. [stand|modules|all]''' play_cmd = opt(SERVICE + ':play') + " " + opt(SERVICE + ':sbt_config') print "OK" if target == "modules" or target == "all": with Dir(opt('develop:modules_dir')): if modules is None: modules = os.walk(".").next()[1] modules = filter(None, set(modules)) with open(opt('develop:modules_list')) as f: modules_deps = f.read().splitlines() for mod in modules_deps: if mod in modules: with Dir(os.path.join(opt('develop:modules_dir'), mod)): print "#### Update module " + mod return_code = os.system(play_cmd + " publishLocal") if return_code != 0: exit(1) if target == "stand" or target == "all": print "#### Update application" with Dir(opt('develop:path')): with if_needed(): shutil.rmtree("target") print play_cmd + " " + opt('build:java_options') + " stage | tee " + opt('develop:build_log_file') return_code = os.system(play_cmd + " " + opt('build:java_options') + " stage | tee " + opt('develop:build_log_file')) if return_code != 0: exit(1) def update_from_git(): '''Update app with modules''' git('checkout ' + opt('build:git_branch')) git('branch --set-upstream-to=origin/' + opt('build:git_branch') + ' ' + opt('build:git_branch')) print git('fetch') git_log = git('diff --name-only HEAD..origin/' + opt('build:git_branch')) git('pull') modules = [] for line in git_log.splitlines(): module = re.findall(r'commonmodules/([^/]*)/.*', line) if module: modules.extend(module) build("all", modules) def copy_libs(): '''Copy libraries''' with if_needed(): shutil.rmtree(opt(SERVICE + ':class_path')) shutil.copytree(opt('develop:class_path'), opt(SERVICE + ':class_path')) def update(): '''Update current service from repository''' saveTimestamp(opt('develop:restarts_log')) stop() update_from_git() start(SERVICE) saveTimestamp(opt('develop:update_log'), 'w') def upgrade(): '''Update developing service from repository''' update_from_git() saveTimestamp(opt(SERVICE + ':restarts_log')) stop(SERVICE) git("pull", SERVICE) copy_libs() start(SERVICE) saveTimestamp(opt(SERVICE + ':update_log'), 'w') # ---------------------------------------------------------------------- def show_help(): exe = os.path.basename(sys.argv[0]) '''Show help''' print "USAGE: " + exe + " [service] <" + "|".join(allowed_tasks) + ">" for task in allowed_tasks: print "\t" + task + "\t- " + globals()[task].__doc__ print "\nTo build a specific service you need to pass a parameter [service]:" print "\t./" + exe, opt('service:default'), "\b build all" print "\n[service] can be one of «" + "», «".join(ast.literal_eval(opt('service:projects')).keys()) + "»." print "\nDefault value of [service] is", opt('service:default'), "\n" settings = [] readConfig('config.ini') allowed_tasks = ['start', 'stop', 'restart', 'build', 'update', 'upgrade'] # Services allowed for building services = ast.literal_eval(opt('service:projects')) # Default service SERVICE = ast.literal_eval(opt('service:projects'))[opt('service:default')] # Command line arguments parsing if len(sys.argv) > 2 and sys.argv[2] in allowed_tasks: arg_index = 2 if sys.argv[1] in services: SERVICE = ast.literal_eval(opt('service:projects'))[sys.argv[1]] elif len(sys.argv) > 1 and sys.argv[1] in allowed_tasks: arg_index = 1 else: show_help() exit(1) # Procedure for execution task = globals()[sys.argv[arg_index]] if task: target = { 'update': 'develop', 'upgrade': 'mroot' }.get(sys.argv[1], "develop") if len(sys.argv) > 2 and sys.argv[2] in ['start', 'stop', 'restart']: SERVICE = ast.literal_eval(opt('service:projects'))[sys.argv[1]] print "CMD: " + sys.argv[1] + " " + sys.argv[2], "| SERVICE: ", SERVICE task(SERVICE) elif len(sys.argv) > (arg_index + 1): print "CMD: " + sys.argv[arg_index] + " " + sys.argv[arg_index + 1] task(sys.argv[arg_index + 1]) else: print "CMD: " + sys.argv[arg_index] task()