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 [email protected].
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()