APYE

Proyecto para la asignatura de Infraestructura Virtual 2016-2017

This project is maintained by adalsa91

APYE

Build Status

Proyecto para la asignatura de Infraestructura Virtual 2016-2017

Ejercicios Tema 2

Descripción

La aplicación consiste en un gestor de ficheros que junto a un editor de texto permitirá al usuario la creación y edición de scripts de python, quedando estos almacenados permanentemente entre sesiones, posibilitando la opción de ejecutar estos scripts en un entorno virtual y seguro que devolverá los resultados del programa en pantalla.

Hito 2: Integración continua

Requisitos

Para la descripción de las dependencias de la aplciación se ha creado un archivo requeriments.txt que suele ser el nombre usado por convención en Python.

click==6.6
Flask==0.11.1
gunicorn==19.6.0
itsdangerous==0.24
Jinja2==2.8
MarkupSafe==0.23
Werkzeug==0.11.11
psycopg2==2.6.2
SQLAlchemy==1.1.2
Flask-SQLAlchemy==2.1
Flask-Migrate==2.0.0
Flask-Testing==0.6.1
Flask-Login==0.4.0
Flask-WTF==0.13.1
WTForms==2.1

Tests

Para asegurar el correcto funcionamiento de la aplicación antes de ser desplegada se han escrito los siguientes tests. Para la automatización de los test se ha usado el framework de test unittest de Python, que además de orquestar la ejecución de los test proporciona una clase base llamada TestCase que podemos usar para crear nuevos casos de prueba, esta clase incluye métodos como *setUp o tearDown que permiten configurar un entorno funcional para la realización de los test y limpiarlo todo tras las pruebas.

import unittest
import os
import shutil
from app import app, db
from models import User
from flask_login import current_user


class ApyeTestCase(unittest.TestCase):
    """A base test case for flask-tracking."""

    def create_app(self):
        app.config.from_object('config.TestConfiguration')
        return app

    def setUp(self):
        db.session.close()
        db.drop_all()
        db.create_all()
        self.app = app.test_client()
        if not os.path.exists('users/'):
            os.makedirs('users/')

    def tearDown(self):
        db.session.remove()
        db.drop_all()
        shutil.rmtree('users')

    def test_adduser(self):
        usuario = User('adrian', 'adalsa@correo.ugr.es',
                       'password', 'Adrian', True)
        db.session.add(usuario)
        db.session.commit()
        usuarios = User.query.all()
        assert usuario in usuarios
        print("[OK] Usuario creado satisfactoriamente\n")

    def test_home_user(self):
        if not os.path.exists('users/' + 'adrian'):
            os.makedirs('users/' + 'adrian')
        f = open('users/adrian/Welcome', 'w+')
        f.write('Welcome!')
        f.seek(0)
        assert f.read() == 'Welcome!'
        f.close()
        print("[OK] Entorno de usuario creado correctamente\n")

    def test_login_logout(self):
        usuario = User('jorge', 'jorge@correo.ugr.es',
                       'password', 'Jorge Nitales', True)
        db.session.add(usuario)
        db.session.commit()

        with self.app:
            # Login
            response = self.app.post(
                '/login', data={'username': 'jorge', 'password': 'password'},
                follow_redirects=True)
            assert current_user.username == 'jorge'
            assert b'Success' in response.data

            # Logout
            response = self.app.post(
                '/logout', follow_redirects=True)
            assert current_user.is_anonymous

        print("[OK] Usuarios registrados pueden identificarse correctamente")

if __name__ == '__main__':
    unittest.main()

Para ejecutar los tests utilizamos la función discover de unittest:

    python -m unittest discover

Este comando busca recursivamente tests desde el directorio en el que lo lanzamos.

Resultados tests

Para simplificar el lanzamiento de tests y otras tareas creamos un Makefile:

install:
    pip install -r requirements.txt

test:
    python -m unittest discover

run:    
    python manage.py runserver

Integración Continua

Para este paso usamos el sistema de integración continua Travis, el mótivo de la elección es debido a su popularidad y documentación.

El primer paso es sincronizar Travis con nuestro GitHub, para ello iniciamos sesión en Travis con nuestra cuenta de GitHub y activamos el repositorio que queremos conectar en la página de nuestro perfil de Travis.

Activación repositorio en perfil Travis

A continuación creamos el archivo .travis.yml en el que especificaremosel lenguaje y versión usados, además de otros datos necesarios para construir el entorno de ejecución y el comando para ejecutar los tests.

language: python
python:
  - "3.4"

install: "pip install -r requirements.txt"

services:
  postgresql

before_script:
  - psql -c "create user apye with password 'iDDLpkP1uv' " -U postgres
  - psql -c "create database apye_users;" -U postgres
  - psql -c "grant all privileges on database apye_users to apye;" -U postgres
  - python manage.py db upgrade

script: python test_base.py

También necesitaremos definir las variables de entorno necsarias para la aplicación, podemos hacerlo en el fichero anterior o en la interfaz web.

Variables de entorno en Travis

Una vez todo esté configurado hacemos un add --> commit --> push y Travis detectará los cambios en nuestro repositorio de GitHub y lanzará los tests. Podemos comprobar el resultado en la página web de Travis. Build History

Log Travis

Además Travis nos ofrece un incrustable que demuestra el estado actual del repositorio.

Build Status

Hito 3: Despliegue en un Paas

En primer lugar instalamos Heroku CLI con el siguiente comando:

$ wget -O- https://toolbelt.heroku.com/install-ubuntu.sh | sh

Una vez instalado iniciamos sesión.

Inicio de sesión en Heroku con CLI

Ahora creamos la aplicación, en mi caso he creado una para producción y otra para pruebas.

$ heroku create apye-pro
$ heroku create apye-stage

Y los añadimos a los remotos.

$ git remote add pro git@heroku.com:apye-pro.git
$ git remote add stage git@heroku.com:apye-stage.git

Creamos un fichero Procfile en la raíz de la aplciación para declarar que comandos deben ser ejecutados al iniciar la aplicación.

web: gunicorn app:app

También tenemos que especificar la versión de Python que necesita nuestra aplicación, para ello creamos el fichero runtime.txt con el siguiente contenido.

python-3.4.2

El resto de dependencias de la aplicación están en el fichero requeriments.txt en la raíz del proyecto y Heroku lo detectará automáticamente.

Debemos definir las variables de entorno, para ello usaremos las herramientas de la CLI.

$ heroku config:set APP_SETTINGS=config.ProductionConfig --remote pro
$ heroku config:set SECRET_KEY=SuperSecretKey --remote pro
$ heroku config:set APP_SETTINGS=config.ProductionConfig --remote stage
$ heroku config:set SECRET_KEY=SuperSecretKey --remote stage

Ya podemos hacer un push a heroku. Push a Heroku

Ahora podemos iniciar la aplicación en local con heroku local.

Heroku local

    heroku local web

Antes de poder lanzar la aplicación en remoto tendremos que configurar PostgreSQL en Heroku, para ello añadimos un addon tanto al servidor de stage como al pro.

$ heroku addons:create heroku-postgresql:hobby-dev --app apye-stage
$ heroku addons:create heroku-postgresql:hobby-dev --app apye-pro

Con el comando config del CLI de Heroku podemos comprobar que efectivamente se ha creado la base de datos y la correspondiente variable de entorno.

Variable de entorno bd Heroku

Actualizamos la base de datos con las migraciones.

$ heroku run python manage.py db upgrade --app apye-pro
$ heroku run python manage.py db upgrade --app apye-stage

Actualización base de datos Heroku

Podemos comprobar que todo está funcionando correctamente lanzando los tests.

Resultado tests Heroku

Conectamos la aplicación de Heroku con el respositorio de GitHub para activar el despliegue automático, activando la opción de comprobocación de tests para que el despliegue no se realice hasta que Travis CI ejecute los tests con éxito.

Despliegue automático de GitHub a Heroku

Por último añadimos un botón para poder desplegar el repositorio de GitHub automáticamente a Heroku, para ello creamos una fichero app.json en la raíz del proyecto con la descripción de la aplicación, enlace al repositorio, addons de Heroku y variables de entorno necesarios.

{
  "name": "APYE",
  "description": "Editor online de Python",
  "image": "heroku/python",
  "repository": "https://github.com/adalsa91/APYE",
  "logo": "https://www.herokucdn.com/deploy/button.svg",
  "keywords": ["python", "flask" ],
  "addons": [ "heroku-postgresql" ],
  "env": {
      "SECRET_KEY": {
          "description": "A secret key for verifying the integrity of signed cookies.",
          "generator": "secret"
      },
      "APP_SETTINGS": {
          "description": "Determine environment.",
          "value": "config.ProductionConfig"
      }
  }
}

Con el siguiente fragmento en Markdown insertamos el botón.

[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy)

Deploy

Adrián Álvarez Sáez