Browse Source

Con makefiles y migraciones

politica
Celestino Rey 1 year ago
parent
commit
5e669e73bd
14 changed files with 374 additions and 4 deletions
  1. +13
    -0
      RepostajesPy/K8S/Makefile
  2. +14
    -0
      RepostajesPy/K8S/pv-local-repostajes.yaml
  3. +56
    -0
      RepostajesPy/K8S/repostajes-deployment.yaml
  4. +14
    -0
      RepostajesPy/K8S/reypostajes-prod-persistentvolumeclaim.yaml
  5. +44
    -0
      RepostajesPy/README.md
  6. +2
    -2
      RepostajesPy/servicios/Dockerfile
  7. +8
    -0
      RepostajesPy/servicios/Makefile
  8. +1
    -0
      RepostajesPy/servicios/migrations/README
  9. +50
    -0
      RepostajesPy/servicios/migrations/alembic.ini
  10. +113
    -0
      RepostajesPy/servicios/migrations/env.py
  11. +24
    -0
      RepostajesPy/servicios/migrations/script.py.mako
  12. +16
    -1
      RepostajesPy/servicios/repostajes/__init__.py
  13. +2
    -0
      RepostajesPy/servicios/repostajes/models.py
  14. +17
    -1
      RepostajesPy/servicios/requirements.txt

+ 13
- 0
RepostajesPy/K8S/Makefile View File

@ -0,0 +1,13 @@
export IMG_VERSION = 7.5
# limpia todo
all: imagen clean install
imagen:
cd ../servicios; make
install:
envsubst < repostajes-deployment.yaml |kubectl create -f -
clean:
envsubst < repostajes-deployment.yaml |kubectl delete -f -

+ 14
- 0
RepostajesPy/K8S/pv-local-repostajes.yaml View File

@ -0,0 +1,14 @@
apiVersion: v1
kind: PersistentVolume
metadata:
name: repostajes-app-folder
labels:
app: repostajes
spec:
capacity:
storage: 100Mi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/Externo/repostajes"

+ 56
- 0
RepostajesPy/K8S/repostajes-deployment.yaml View File

@ -0,0 +1,56 @@
apiVersion: v1
kind: Service
metadata:
name: repostajes
spec:
type: NodePort
ports:
- name: http
port: 5000
nodePort: 30340
targetPort: repostajes
selector:
app: repostajes
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: repostajes
labels:
app: repostajes
spec:
replicas: 1
selector:
matchLabels:
app: repostajes
strategy:
type: Recreate
template:
metadata:
labels:
app: repostajes
spec:
containers:
- args:
- gunicorn
- --bind
- 0.0.0.0:5000
- repostajes:create_app()
image: creylopez/repostajes:$IMG_VERSION
name: repostajes
env:
- name: SALUDO_DEMO
value: "Hola, mundo"
ports:
- containerPort: 5000
name: repostajes
resources: {}
volumeMounts:
- mountPath: /repostajes/instance
name: repostajes-prod
restartPolicy: Always
volumes:
- name: repostajes-prod
persistentVolumeClaim:
claimName: repostajes-prod
status: {}

+ 14
- 0
RepostajesPy/K8S/reypostajes-prod-persistentvolumeclaim.yaml View File

@ -0,0 +1,14 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
creationTimestamp: null
labels:
io.kompose.service: repostajes-prod
name: repostajes-prod
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Mi
status: {}

+ 44
- 0
RepostajesPy/README.md View File

@ -0,0 +1,44 @@
# Arreglar el fallo del nombre de constrain en migraciones
https://stackoverflow.com/questions/62640576/flask-migrate-valueerror-constraint-must-have-a-name
# Uso de migraciones
https://www.digitalocean.com/community/tutorials/how-to-perform-flask-sqlalchemy-migrations-using-flask-migrate
# Migración de la base de datos.
Cada vez que se haga un cambio en la bd
- flask db migrate -m "Mensaje"
- flask db upgrade
## Migration Workflow
From this point on you have a project that is fully enabled to use database migrations. The normal migration process goes as follows:
- You will make some changes to your models in your Python source code.
- You will then run flask db migrate to generate a new database migration for these changes.
- You will finally apply the changes to the database by running flask db upgrade.
This cycle repeats every time new changes to the database schema are needed.
# Insertar los datos en la base de datos desde fichero sql
1. Borrar la base de datos.
2. flask db init
3. flask db migrate
4. flask db upgrade
con lo anterior queda creada la base de datos.
5. Insertar los ficheros SQL con:
sqlite3 repostajes.db < vehiculos.sql
sqlite3 repostajes.db < repostajes.sql
¡ojo! estos ficheros son un volcado de mysql. Hay que 'tocarlos' para ajustarse al nuevo modelo. Los que hay aquí ya están adaptados

+ 2
- 2
RepostajesPy/servicios/Dockerfile View File

@ -2,7 +2,7 @@
FROM python:3.8-slim-buster
WORKDIR /repostajespy
WORKDIR /repostajes
# set environment variables
ENV PYTHONDONTWRITEBYTECODE=1
@ -17,4 +17,4 @@ RUN pip3 install -r requirements.txt
COPY . .
# run entrypoint.sh
ENTRYPOINT ["/repostajespy/entrypoint.sh"]
ENTRYPOINT ["/repostajes/entrypoint.sh"]

+ 8
- 0
RepostajesPy/servicios/Makefile View File

@ -0,0 +1,8 @@
install:
echo "Creando imagen con version ${IMG_VERSION}"
docker build --no-cache -t creylopez/repostajes:${IMG_VERSION} .
docker push creylopez/repostajes:${IMG_VERSION}

+ 1
- 0
RepostajesPy/servicios/migrations/README View File

@ -0,0 +1 @@
Single-database configuration for Flask.

+ 50
- 0
RepostajesPy/servicios/migrations/alembic.ini View File

@ -0,0 +1,50 @@
# A generic, single database configuration.
[alembic]
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic,flask_migrate
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[logger_flask_migrate]
level = INFO
handlers =
qualname = flask_migrate
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

+ 113
- 0
RepostajesPy/servicios/migrations/env.py View File

@ -0,0 +1,113 @@
import logging
from logging.config import fileConfig
from flask import current_app
from alembic import context
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)
logger = logging.getLogger('alembic.env')
def get_engine():
try:
# this works with Flask-SQLAlchemy<3 and Alchemical
return current_app.extensions['migrate'].db.get_engine()
except (TypeError, AttributeError):
# this works with Flask-SQLAlchemy>=3
return current_app.extensions['migrate'].db.engine
def get_engine_url():
try:
return get_engine().url.render_as_string(hide_password=False).replace(
'%', '%%')
except AttributeError:
return str(get_engine().url).replace('%', '%%')
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
config.set_main_option('sqlalchemy.url', get_engine_url())
target_db = current_app.extensions['migrate'].db
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
def get_metadata():
if hasattr(target_db, 'metadatas'):
return target_db.metadatas[None]
return target_db.metadata
def run_migrations_offline():
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url, target_metadata=get_metadata(), literal_binds=True
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
# this callback is used to prevent an auto-migration from being generated
# when there are no changes to the schema
# reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
def process_revision_directives(context, revision, directives):
if getattr(config.cmd_opts, 'autogenerate', False):
script = directives[0]
if script.upgrade_ops.is_empty():
directives[:] = []
logger.info('No changes in schema detected.')
conf_args = current_app.extensions['migrate'].configure_args
if conf_args.get("process_revision_directives") is None:
conf_args["process_revision_directives"] = process_revision_directives
connectable = get_engine()
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=get_metadata(),
**conf_args
)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()

+ 24
- 0
RepostajesPy/servicios/migrations/script.py.mako View File

@ -0,0 +1,24 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}

+ 16
- 1
RepostajesPy/servicios/repostajes/__init__.py View File

@ -2,9 +2,22 @@ import os
from flask import Flask, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from flask_migrate import Migrate
from sqlalchemy import MetaData
convention = {
"ix": 'ix_%(column_0_label)s',
"uq": "uq_%(table_name)s_%(column_0_name)s",
"ck": "ck_%(table_name)s_%(constraint_name)s",
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
"pk": "pk_%(table_name)s"
}
# init SQLAlchemy so we can use it later in our models
db = SQLAlchemy()
metadata = MetaData(naming_convention=convention)
db = SQLAlchemy(metadata=metadata)
migrate = Migrate(render_as_batch=True)
from repostajes import paginas, auth
@ -12,6 +25,7 @@ def create_app():
app = Flask(__name__)
app.config.from_prefixed_env()
app.config['SECRET_KEY'] = 'secret-key-goes-here'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///repostajes.db'
app.config['UPLOAD_FOLDER'] = os.path.join(app.instance_path, 'uploads')
@ -22,6 +36,7 @@ def create_app():
from .models import db
db.init_app(app)
migrate.init_app(app, db)
login_manager = LoginManager()
login_manager.login_view = 'auth.login'


+ 2
- 0
RepostajesPy/servicios/repostajes/models.py View File

@ -3,9 +3,11 @@ from flask_login import UserMixin
from . import db
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(150), unique=True, nullable=False)
email = db.Column(db.String(150), unique=True, nullable=True)
password = db.Column(db.String(150), nullable=False)
photo = db.Column(db.String(150), nullable=False)


+ 17
- 1
RepostajesPy/servicios/requirements.txt View File

@ -11,4 +11,20 @@ MarkupSafe==2.1.5
packaging==24.1
SQLAlchemy==2.0.31
typing_extensions==4.12.2
Werkzeug==3.0.3
Werkzeug==3.0.3alembic==1.13.2
blinker==1.8.2
click==8.1.7
Flask==3.0.3
Flask-Login==0.6.3
Flask-Migrate==4.0.7
Flask-SQLAlchemy==3.1.1
greenlet==3.0.3
gunicorn==22.0.0
itsdangerous==2.2.0
Jinja2==3.1.4
Mako==1.3.5
MarkupSafe==2.1.5
packaging==24.1
SQLAlchemy==2.0.31
typing_extensions==4.12.2
Werkzeug==3.0.3

Loading…
Cancel
Save