tfstate: very basic terraform state backend in flask

This commit is contained in:
Iain Learmonth 2022-08-29 19:16:35 +01:00
parent 29870639b8
commit affa0f0149
4 changed files with 88 additions and 0 deletions

View file

@ -6,6 +6,7 @@ from app.extensions import db
from app.extensions import migrate from app.extensions import migrate
from app.extensions import bootstrap from app.extensions import bootstrap
from app.portal import portal from app.portal import portal
from app.tfstate import tfstate
app = Flask(__name__) app = Flask(__name__)
app.config.from_file("../config.yaml", load=yaml.safe_load) app.config.from_file("../config.yaml", load=yaml.safe_load)
@ -14,6 +15,7 @@ migrate.init_app(app, db, render_as_batch=True)
bootstrap.init_app(app) bootstrap.init_app(app)
app.register_blueprint(portal, url_prefix="/portal") app.register_blueprint(portal, url_prefix="/portal")
app.register_blueprint(tfstate, url_prefix="/tfstate")
@app.route('/') @app.route('/')

7
app/models/tfstate.py Normal file
View file

@ -0,0 +1,7 @@
from app.extensions import db
class TerraformState(db.Model):
key = db.Column(db.String, primary_key=True)
state = db.Column(db.String)
lock = db.Column(db.String)

51
app/tfstate.py Normal file
View file

@ -0,0 +1,51 @@
import json
from flask import Blueprint, request, Response
from app.extensions import db
from app.models.tfstate import TerraformState
tfstate = Blueprint("tfstate", __name__)
@tfstate.route("/<key>", methods=['GET'])
def handle_get(key):
state = TerraformState.query.filter(TerraformState.key == key).first()
if state is None or state.state is None:
return "Not Found", 404
return Response(state.state, content_type="application/json")
@tfstate.route("/<key>", methods=['POST', 'DELETE', 'UNLOCK'])
def handle_update(key):
state = TerraformState.query.filter(TerraformState.key == key).first()
if not state:
if request.method in ["DELETE", "UNLOCK"]:
return "OK", 200
state = TerraformState(key=key)
if state.lock and not (request.method == "UNLOCK" and request.args.get('ID') is None):
if json.loads(state.lock)['ID'] != request.args.get('ID'):
return Response(state.lock, status=409, content_type="application/json")
if request.method == "POST":
state.state = json.dumps(request.json)
elif request.method == "DELETE":
db.session.delete(state)
elif request.method == "UNLOCK":
state.lock = None
db.session.commit()
return "OK", 200
@tfstate.route("/<key>", methods=['LOCK'])
def handle_lock(key):
state = TerraformState.query.filter(TerraformState.key == key).with_for_update().first()
if state is None:
state = TerraformState(key=key)
db.session.add(state)
if state.lock is not None:
lock = state.lock
db.session.rollback()
return Response(lock, status=423, content_type="application/json")
state.lock = json.dumps(request.json)
db.session.commit()
return "OK", 200

View file

@ -0,0 +1,28 @@
"""add terraform state
Revision ID: 665e340dbe09
Revises: c644bb20d0e3
Create Date: 2022-08-29 17:10:05.447985
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '665e340dbe09'
down_revision = 'c644bb20d0e3'
branch_labels = None
depends_on = None
def upgrade():
op.create_table('terraform_state',
sa.Column('key', sa.String(), nullable=False),
sa.Column('state', sa.String(), nullable=True),
sa.Column('lock', sa.String(), nullable=True),
sa.PrimaryKeyConstraint('key', name=op.f('pk_terraform_state'))
)
def downgrade():
op.drop_table('terraform_state')