After the oficial announce from webfaction (goDaddy) about the inminent shutdown of Web2Py support, I decided to migrate my Apps to Opalstack.com .
I want to share with you the procedure what I received from the support guys (It works!!!). The initial web2py installer script is attached. There are a couple of manual steps that must be done before you can run the script: 1. Go to https://my.opalstack.com/tokens/ and create an API token if you do not have one already. Make a note of the API token value. 2. Go to https://my.opalstack.com/applications/ and create a new "Proxy Port" application. Make a note of the app's UUID. You can get the UUID by clicking the edit icon for the app in the app list and then retrieve the UUID from the last part of the edit page URL. For example if the edit page URL is https://my.opalstack.com/application/edit/abcd123-def234-123abc-678fed then the UUID is abcd123-def234-123abc-678fed. 3. Upload the attached script to your app's shell user home directory on the server. 4. Run the script as follows: python3 ~/web2py_install.py -u APP_UUID -t TOKEN (replace APP_UUID and TOKEN with the values from steps 1 and 2 above.) The script will then install web2py for you, showing its progress as it goes. After the script completes, you can then run the following commands to convert the application to Python 2.7 (be sure to change the app name to the name of your installed app): *pip2.7 install --user zipp==1.1* * pip2.7 install --user virtualenv==16.7.10 * *cd ~/apps/appname * *mv env env.old * *virtualenv -p /bin/python2.7 env * *source env/bin/activate * *pip install uwsgi * *./stop * *./start * -- Resources: - http://web2py.com - http://web2py.com/book (Documentation) - http://github.com/web2py/web2py (Source code) - https://code.google.com/p/web2py/issues/list (Report Issues) --- You received this message because you are subscribed to the Google Groups "web2py-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to web2py+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/web2py/7344dbd9-9541-423f-af41-310d12552a70n%40googlegroups.com.
#! /usr/bin/python3.6 import argparse import sys import logging import os import http.client import json import textwrap import secrets import string import subprocess import shlex from urllib.parse import urlparse import hashlib API_HOST = 'my.opalstack.com' API_BASE_URI = '/api/v0' CMD_ENV = {'PATH': '/usr/local/bin:/usr/bin:/bin','UMASK': '0002',} class OpalstackAPITool(): """simple wrapper for http.client get and post""" def __init__(self, host, base_uri, authtoken): self.host = host self.base_uri = base_uri # exit if there is no auth token if not authtoken: logging.warn('no auth token provided, exiting.') sys.exit() self.headers = { 'Content-type': 'application/json', 'Authorization': f'Token {authtoken}' } def get(self, endpoint): """GETs an API endpoint""" endpoint = self.base_uri + endpoint conn = http.client.HTTPSConnection(self.host) conn.request('GET', endpoint, headers=self.headers) return json.loads(conn.getresponse().read()) def post(self, endpoint, payload): """POSTs data to an API endpoint""" endpoint = self.base_uri + endpoint conn = http.client.HTTPSConnection(self.host) conn.request('POST', endpoint, payload, headers=self.headers) return json.loads(conn.getresponse().read()) def create_file(path, contents, writemode='w', perms=0o600): """make a file, perms are passed as octal""" with open(path, writemode) as f: f.write(contents) os.chmod(path, perms) logging.info(f'Created file {path} with permissions {oct(perms)}') def download(url, localfile, writemode='wb', perms=0o600): """save a remote file, perms are passed as octal""" logging.info(f'Downloading {url} as {localfile} with permissions {oct(perms)}') u = urlparse(url) if u.scheme == 'http': conn = http.client.HTTPConnection(u.netloc) else: conn = http.client.HTTPSConnection(u.netloc) conn.request('GET', u.path) r = conn.getresponse() with open(localfile, writemode) as f: while True: data = r.read(4096) if data: f.write(data) else: break os.chmod(localfile, perms) logging.info(f'Downloaded {url} as {localfile} with permissions {oct(perms)}') def gen_password(length=20): """makes a random password""" chars = string.ascii_letters + string.digits return ''.join(secrets.choice(chars) for i in range(length)) def run_command(cmd, cwd, env=CMD_ENV): """runs a command, returns output""" logging.info(f'Running: {cmd}') try: result = subprocess.check_output(shlex.split(cmd), cwd=cwd, env=env) except subprocess.CalledProcessError as e: logging.debug(e.output) return result def add_cronjob(cronjob, appdir): """appends a cron job to the user's crontab""" homedir = os.path.expanduser('~') tmpname = f'{homedir}/.tmp{gen_password()}' tmp = open(tmpname, 'w') subprocess.run('crontab -l'.split(),stdout=tmp) tmp.write(f'{cronjob}\n') tmp.close() cmd = f'crontab {tmpname}' doit = run_command(cmd, cwd=appdir) cmd = run_command(f'rm -f {tmpname}', cwd=appdir) logging.info(f'Added cron job: {cronjob}') def main(): """run it""" # grab args from cmd or env parser = argparse.ArgumentParser( description='Installs web2py on Opalstack account') parser.add_argument('-u', dest='app_uuid', help='UUID of the base app', default=os.environ.get('UUID')) parser.add_argument('-t', dest='opal_token', help='API auth token', default=os.environ.get('OPAL_TOKEN')) args = parser.parse_args() # init logging logging.basicConfig(level=logging.INFO, format='[%(asctime)s] %(levelname)s: %(message)s') # go! logging.info(f'Started installation of web2py app') api = OpalstackAPITool(API_HOST, API_BASE_URI, args.opal_token) appinfo = api.get(f'/app/read/{args.app_uuid}') appdir = f'/home/{appinfo["app_user"]}/apps/{appinfo["name"]}' # create tmp dir os.mkdir(f'{appdir}/tmp', 0o700) logging.info(f'Created directory {appdir}/tmp') # create virtualenv cmd = f'/bin/python3.6 -m venv {appdir}/env' doit = run_command(cmd, cwd=appdir) logging.info(f'Created virtualenv at {appdir}/env') # install uwsgi cmd = f'{appdir}/env/bin/pip install uwsgi' doit = run_command(cmd, cwd=appdir) perms = run_command(f'chmod 700 {appdir}/env/bin/uwsgi', cwd=appdir) logging.info('Installed latest uWSGI into virtualenv') # download and extract web2py download('http://www.web2py.com/examples/static/web2py_src.zip', f'{appdir}/web2py_src.zip') cmd = f'/usr/bin/unzip -qq {appdir}/web2py_src.zip' doit = run_command(cmd, cwd=appdir) logging.info(f'Downloaded web2py to {appdir}/web2py') # copy wsgi handler cmd = f'cp {appdir}/web2py/handlers/wsgihandler.py {appdir}/web2py/' doit = run_command(cmd, cwd=appdir) logging.info(f'Prepared web2py WSGI handler') # create password file pw = gen_password() pwhash = hashlib.md5('{pw}'.encode()).hexdigest() pwfile = f'password = "{pwhash}"' create_file(f'{appdir}/web2py/parameters_{appinfo["port"]}.py', pwfile, perms=0o600) logging.info(f'Created web2py admin password file') # uwsgi config uwsgi_conf = textwrap.dedent(f'''\ [uwsgi] master = True http = 127.0.0.1:{appinfo["port"]} virtualenv = {appdir}/env/ daemonize = /home/{appinfo["app_user"]}/logs/apps/{appinfo["name"]}/uwsgi.log pidfile = {appdir}/tmp/uwsgi.pid workers = 2 threads = 2 # adjust the following to point to your project wsgi-file = {appdir}/web2py/wsgihandler.py touch-reload = {appdir}/web2py/wsgihandler.py ''') create_file(f'{appdir}/uwsgi.ini', uwsgi_conf, perms=0o600) logging.info(f'Created web2py uwsgi config') # start script start_script = textwrap.dedent(f'''\ #!/bin/bash export TMPDIR={appdir}/tmp mkdir -p {appdir}/tmp PIDFILE="{appdir}/tmp/uwsgi.pid" if [ -e "$PIDFILE" ] && (pgrep -u {appinfo["app_user"]} | grep -x -f $PIDFILE &> /dev/null); then echo "uWSGI for {appinfo["name"]} already running." exit 99 fi {appdir}/env/bin/uwsgi --ini {appdir}/uwsgi.ini echo "Started uWSGI for {appinfo["name"]}." ''') create_file(f'{appdir}/start', start_script, perms=0o700) logging.info('Created start script') # stop script stop_script = textwrap.dedent(f'''\ #!/bin/bash PIDFILE="{appdir}/tmp/uwsgi.pid" if [ ! -e "$PIDFILE" ]; then echo "$PIDFILE missing, maybe uWSGI is already stopped?" exit 99 fi PID=$(cat $PIDFILE) if [ -e "$PIDFILE" ] && (pgrep -u {appinfo["app_user"]} | grep -x -f $PIDFILE &> /dev/null); then {appdir}/env/bin/uwsgi --stop $PIDFILE sleep 3 fi if [ -e "$PIDFILE" ] && (pgrep -u {appinfo["app_user"]} | grep -x -f $PIDFILE &> /dev/null); then echo "uWSGI did not stop, killing it." sleep 3 kill -9 $PID fi rm -f $PIDFILE echo "Stopped." ''') create_file(f'{appdir}/stop', stop_script, perms=0o700) logging.info('Created stop script') # cron croncmd = f'*/10 * * * * {appdir}/start > /dev/null 2>&1' cronjob = add_cronjob(croncmd, appdir) logging.info('Created cron job') # make README readme = textwrap.dedent(f'''\ # Opalstack web2py README Your web2py admin password is: {pw} A md5 hash of the admin password is stored in: {appdir}/web2py/parameters_{appinfo["port"]} Your web2py applications directory is: {appdir}/web2py/applications The following scripts have been created to control your web2py uwsgi instance: {appdir}/stop {appdir}/start ''') create_file(f'{appdir}/README', readme) # start it cmd = f'{appdir}/start' startit = run_command(cmd, cwd=appdir) # finished, push a notice with credentials msg = f'See README in app directory for your admin password and other info.' payload = json.dumps({'id': args.app_uuid, 'init_created': True, 'note': msg}) finished=api.post('/app/init_created/', payload) logging.info(f'Completed installation of web2py app {appinfo["name"]}') logging.info(f'See {appdir}/README for your admin password and other info.') if __name__ == '__main__': main()