2016-03-15 06:54:17 +00:00
|
|
|
# wget setup_frappe.py | python
|
2016-05-25 10:47:48 +00:00
|
|
|
import os, sys, subprocess, getpass, json, multiprocessing, shutil
|
2016-03-15 06:54:17 +00:00
|
|
|
from distutils.spawn import find_executable
|
|
|
|
|
2016-05-25 10:47:48 +00:00
|
|
|
tmp_bench_repo = '/tmp/.bench'
|
2016-03-15 06:54:17 +00:00
|
|
|
|
|
|
|
def install_bench(args):
|
|
|
|
# pre-requisites for bench repo cloning
|
2016-03-17 09:50:21 +00:00
|
|
|
success = run_os_command({
|
2016-03-22 07:44:31 +00:00
|
|
|
'apt-get': [
|
|
|
|
'sudo apt-get update',
|
2016-05-04 07:53:33 +00:00
|
|
|
'sudo apt-get install -y git build-essential python-setuptools python-dev libffi-dev libssl-dev'
|
2016-03-22 07:44:31 +00:00
|
|
|
],
|
|
|
|
'yum': [
|
|
|
|
'sudo yum groupinstall -y "Development tools"',
|
2016-05-25 10:47:48 +00:00
|
|
|
'sudo yum install -y epel-release redhat-lsb-core git python-setuptools python-devel openssl-devel libffi-devel'
|
2016-03-22 07:44:31 +00:00
|
|
|
],
|
2016-05-04 07:53:33 +00:00
|
|
|
# epel-release is required to install redis, so installing it before the playbook-run.
|
|
|
|
# redhat-lsb-core is required, so that ansible can set ansible_lsb variable
|
2016-03-17 09:50:21 +00:00
|
|
|
})
|
|
|
|
|
2016-03-22 07:44:31 +00:00
|
|
|
if not find_executable("git"):
|
|
|
|
success = run_os_command({
|
|
|
|
'brew': 'brew install git'
|
|
|
|
})
|
|
|
|
|
2016-03-17 09:50:21 +00:00
|
|
|
if not success:
|
2016-03-22 07:44:31 +00:00
|
|
|
print 'Could not install pre-requisites. Please check for errors or install them manually.'
|
2016-03-17 09:50:21 +00:00
|
|
|
return
|
|
|
|
|
2016-03-22 07:44:31 +00:00
|
|
|
# secure pip installation
|
|
|
|
if not os.path.exists("get-pip.py"):
|
|
|
|
run_os_command({
|
|
|
|
'apt-get': 'wget https://bootstrap.pypa.io/get-pip.py',
|
|
|
|
'yum': 'wget https://bootstrap.pypa.io/get-pip.py'
|
|
|
|
})
|
|
|
|
|
|
|
|
run_os_command({
|
|
|
|
'apt-get': 'sudo python get-pip.py',
|
|
|
|
'yum': 'sudo python get-pip.py',
|
|
|
|
})
|
|
|
|
|
2016-05-04 07:53:33 +00:00
|
|
|
# In some cases setup_tools are not updated, We will force update it.
|
|
|
|
run_os_command({
|
|
|
|
'pip': 'sudo pip install --upgrade setuptools'
|
|
|
|
})
|
|
|
|
|
2016-03-17 09:50:21 +00:00
|
|
|
success = run_os_command({
|
2016-03-22 07:44:31 +00:00
|
|
|
'pip': 'sudo pip install ansible'
|
2016-03-17 09:50:21 +00:00
|
|
|
})
|
2016-03-15 06:54:17 +00:00
|
|
|
|
2016-03-22 07:44:31 +00:00
|
|
|
if not success:
|
|
|
|
could_not_install('Ansible')
|
|
|
|
|
2016-03-15 06:54:17 +00:00
|
|
|
# clone bench repo
|
2016-05-25 10:47:48 +00:00
|
|
|
clone_bench_repo(args)
|
|
|
|
|
|
|
|
if is_sudo_user() and not args.user and not args.production:
|
|
|
|
raise Exception('Please run this script as a non-root user with sudo privileges, but without using sudo or pass --user=USER')
|
|
|
|
|
|
|
|
# create user if not exists
|
|
|
|
run_playbook('develop/create_user.yml', user=args.user, extra_args=vars(args))
|
2016-03-15 06:54:17 +00:00
|
|
|
|
2016-05-04 07:53:33 +00:00
|
|
|
# args is namespace, but we would like to use it as dict in calling function, so use vars()
|
2016-03-15 06:54:17 +00:00
|
|
|
if args.develop:
|
2016-05-25 10:47:48 +00:00
|
|
|
run_playbook('develop/install.yml', sudo=True, user=args.user, extra_args=vars(args))
|
|
|
|
|
|
|
|
elif args.production:
|
|
|
|
if not args.user:
|
|
|
|
args.user = 'frappe'
|
|
|
|
|
|
|
|
run_playbook('production/install.yml', sudo=True, user=args.user, extra_args=vars(args))
|
|
|
|
|
|
|
|
shutil.rmtree(tmp_bench_repo)
|
2016-03-15 06:54:17 +00:00
|
|
|
|
|
|
|
def install_python27():
|
|
|
|
version = (sys.version_info[0], sys.version_info[1])
|
|
|
|
|
|
|
|
if version == (2, 7):
|
|
|
|
return
|
|
|
|
|
2016-03-22 07:44:31 +00:00
|
|
|
print 'Installing Python 2.7'
|
2016-03-15 06:54:17 +00:00
|
|
|
|
|
|
|
# install python 2.7
|
|
|
|
success = run_os_command({
|
2016-03-22 07:44:31 +00:00
|
|
|
'apt-get': 'sudo apt-get install -y python2.7',
|
|
|
|
'yum': 'sudo yum install -y python27',
|
|
|
|
'brew': 'brew install python'
|
2016-03-15 06:54:17 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
if not success:
|
2016-03-22 07:44:31 +00:00
|
|
|
could_not_install('Python 2.7')
|
2016-03-15 06:54:17 +00:00
|
|
|
|
|
|
|
# replace current python with python2.7
|
2016-03-22 07:44:31 +00:00
|
|
|
os.execvp('python2.7', ([] if is_sudo_user() else ['sudo']) + ['python2.7', __file__] + sys.argv[1:])
|
2016-03-15 06:54:17 +00:00
|
|
|
|
2016-05-25 10:47:48 +00:00
|
|
|
def clone_bench_repo(args):
|
2016-03-22 07:44:31 +00:00
|
|
|
'''Clones the bench repository in the user folder'''
|
2016-05-25 10:47:48 +00:00
|
|
|
if os.path.exists(tmp_bench_repo):
|
2016-03-15 06:54:17 +00:00
|
|
|
return 0
|
2016-03-17 07:29:44 +00:00
|
|
|
|
2016-05-25 10:47:48 +00:00
|
|
|
branch = args.bench_branch or 'develop'
|
2016-03-17 09:50:21 +00:00
|
|
|
|
2016-03-15 06:54:17 +00:00
|
|
|
success = run_os_command(
|
2016-05-25 10:47:48 +00:00
|
|
|
{'git': 'git clone https://github.com/frappe/bench {bench_repo} --depth 1 --branch {branch}'.format(
|
|
|
|
bench_repo=tmp_bench_repo, branch=branch)}
|
2016-03-15 06:54:17 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
return success
|
|
|
|
|
|
|
|
def run_os_command(command_map):
|
2016-03-22 07:44:31 +00:00
|
|
|
'''command_map is a dictionary of {'executable': command}. For ex. {'apt-get': 'sudo apt-get install -y python2.7'} '''
|
|
|
|
success = True
|
|
|
|
for executable, commands in command_map.items():
|
2016-03-15 06:54:17 +00:00
|
|
|
if find_executable(executable):
|
2016-03-22 07:44:31 +00:00
|
|
|
if isinstance(commands, basestring):
|
|
|
|
commands = [commands]
|
|
|
|
|
|
|
|
for command in commands:
|
|
|
|
returncode = subprocess.check_call(command, shell=True)
|
|
|
|
success = success and ( returncode == 0 )
|
|
|
|
|
2016-03-15 06:54:17 +00:00
|
|
|
break
|
|
|
|
|
|
|
|
return success
|
|
|
|
|
|
|
|
def could_not_install(package):
|
2016-03-22 07:44:31 +00:00
|
|
|
raise Exception('Could not install {0}. Please install it manually.'.format(package))
|
2016-03-15 06:54:17 +00:00
|
|
|
|
|
|
|
def is_sudo_user():
|
|
|
|
return os.geteuid() == 0
|
|
|
|
|
2016-05-04 07:53:33 +00:00
|
|
|
def get_passwords(run_travis=False):
|
|
|
|
if not run_travis:
|
|
|
|
mysql_root_password, admin_password = '', ''
|
|
|
|
pass_set = True
|
|
|
|
while pass_set:
|
|
|
|
# mysql root password
|
|
|
|
if not mysql_root_password:
|
|
|
|
mysql_root_password = getpass.unix_getpass(prompt='Please enter mysql root password: ')
|
|
|
|
conf_mysql_passwd = getpass.unix_getpass(prompt='Re-enter mysql root password: ')
|
|
|
|
|
|
|
|
if mysql_root_password != conf_mysql_passwd:
|
|
|
|
mysql_root_password = ''
|
|
|
|
continue
|
|
|
|
|
|
|
|
# admin password
|
|
|
|
if not admin_password:
|
|
|
|
admin_password = getpass.unix_getpass(prompt='Please enter Administrator password: ')
|
|
|
|
conf_admin_passswd = getpass.unix_getpass(prompt='Re-enter Administrator password: ')
|
|
|
|
|
|
|
|
if admin_password != conf_admin_passswd:
|
|
|
|
admin_password = ''
|
|
|
|
continue
|
|
|
|
|
|
|
|
pass_set = False
|
|
|
|
else:
|
|
|
|
mysql_root_password = admin_password = 'travis'
|
|
|
|
|
|
|
|
return {
|
|
|
|
'mysql_root_password': mysql_root_password,
|
|
|
|
'admin_password': admin_password
|
|
|
|
}
|
|
|
|
|
|
|
|
def get_extra_vars_json(extra_args, run_travis=False):
|
2016-05-25 10:47:48 +00:00
|
|
|
# We need to pass production as extra_vars to the playbook to execute conditionals in the
|
2016-05-04 07:53:33 +00:00
|
|
|
# playbook. Extra variables can passed as json or key=value pair. Here, we will use JSON.
|
|
|
|
json_path = os.path.join(os.path.abspath(os.path.expanduser('~')), 'extra_vars.json')
|
|
|
|
extra_vars = dict(extra_args.items())
|
|
|
|
|
|
|
|
# Get mysql root password and admin password
|
|
|
|
run_travis = extra_args.get('run_travis')
|
|
|
|
extra_vars.update(get_passwords(run_travis))
|
|
|
|
|
|
|
|
# Decide for branch to be cloned depending upon whether we setting up production
|
2016-05-25 10:47:48 +00:00
|
|
|
branch = 'master' if extra_args.get('production') else 'develop'
|
2016-05-04 07:53:33 +00:00
|
|
|
extra_vars.update(branch=branch)
|
|
|
|
|
|
|
|
# Get max worker_connections in nginx.
|
|
|
|
# https://www.digitalocean.com/community/tutorials/how-to-optimize-nginx-configuration
|
|
|
|
# The amount of clients that can be served can be multiplied by the amount of cores.
|
|
|
|
extra_vars.update(max_worker_connections=multiprocessing.cpu_count() * 1024)
|
|
|
|
|
|
|
|
with open(json_path, mode='w') as j:
|
|
|
|
json.dump(extra_vars, j, indent=1, sort_keys=True)
|
|
|
|
|
|
|
|
return ('@' + json_path)
|
|
|
|
|
2016-05-25 10:47:48 +00:00
|
|
|
def run_playbook(playbook_name, sudo=False, user=None, extra_args=None):
|
|
|
|
user = user or getpass.getuser()
|
|
|
|
if user == 'root':
|
|
|
|
raise Exception('--user cannot be root')
|
|
|
|
|
|
|
|
extra_args['frappe_user'] = user
|
2016-05-04 07:53:33 +00:00
|
|
|
extra_vars = get_extra_vars_json(extra_args)
|
2016-05-25 10:47:48 +00:00
|
|
|
|
2016-05-04 07:53:33 +00:00
|
|
|
args = ['ansible-playbook', '-c', 'local', playbook_name, '-e', extra_vars]
|
|
|
|
|
2016-03-15 06:54:17 +00:00
|
|
|
if sudo:
|
2016-05-25 10:47:48 +00:00
|
|
|
args.extend(['--become', '--become-user={0}'.format(user)])
|
2016-05-04 07:53:33 +00:00
|
|
|
|
2016-05-18 13:03:19 +00:00
|
|
|
if extra_args.get('verbosity'):
|
2016-05-04 07:53:33 +00:00
|
|
|
args.append('-vvvv')
|
2016-03-17 07:29:44 +00:00
|
|
|
|
2016-05-25 10:47:48 +00:00
|
|
|
success = subprocess.check_call(args, cwd=os.path.join(tmp_bench_repo, 'playbooks'))
|
2016-03-15 06:54:17 +00:00
|
|
|
return success
|
|
|
|
|
|
|
|
def parse_commandline_args():
|
|
|
|
import argparse
|
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(description='Frappe Installer')
|
2016-05-04 07:53:33 +00:00
|
|
|
|
2016-05-25 10:47:48 +00:00
|
|
|
# Arguments develop and production are mutually exclusive both can't be specified together.
|
2016-05-04 07:53:33 +00:00
|
|
|
# Hence, we need to create a group for discouraging use of both options at the same time.
|
|
|
|
args_group = parser.add_mutually_exclusive_group()
|
|
|
|
|
|
|
|
args_group.add_argument('--develop', dest='develop', action='store_true', default=False,
|
|
|
|
help='Install developer setup')
|
|
|
|
|
2016-05-25 10:47:48 +00:00
|
|
|
args_group.add_argument('--production', dest='production', action='store_true',
|
2016-05-04 07:53:33 +00:00
|
|
|
default=False, help='Setup Production environment for bench')
|
|
|
|
|
|
|
|
parser.add_argument('--site', dest='site', action='store', default='site1.local',
|
|
|
|
help='Specifiy name for your first ERPNext site')
|
2016-05-18 13:23:01 +00:00
|
|
|
|
2016-05-18 13:03:19 +00:00
|
|
|
parser.add_argument('--verbose', dest='verbosity', action='store_true', default=False,
|
|
|
|
help='Run the script in verbose mode')
|
2016-05-04 07:53:33 +00:00
|
|
|
|
2016-05-25 10:47:48 +00:00
|
|
|
parser.add_argument('--user', dest='user', help='Install frappe-bench for this user')
|
|
|
|
|
|
|
|
parser.add_argument('--bench-branch', dest='bench_branch', help='Clone a particular branch of bench repository')
|
|
|
|
|
2016-05-04 07:53:33 +00:00
|
|
|
# To enable testing of script using Travis, this should skip the prompt
|
|
|
|
parser.add_argument('--run-travis', dest='run_travis', action='store_true', default=False,
|
|
|
|
help=argparse.SUPPRESS)
|
|
|
|
|
2016-03-15 06:54:17 +00:00
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
return args
|
|
|
|
|
2016-03-22 07:44:31 +00:00
|
|
|
if __name__ == '__main__':
|
2016-03-15 06:54:17 +00:00
|
|
|
try:
|
|
|
|
import argparse
|
|
|
|
except ImportError:
|
|
|
|
# install python2.7
|
|
|
|
install_python27()
|
|
|
|
|
|
|
|
args = parse_commandline_args()
|
|
|
|
|
|
|
|
install_bench(args)
|