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()

Reply via email to