Merge pull request 'Migration module' (#8) from migrations-branch-fix into master

Reviewed-on: https://git.selfprivacy.org/SelfPrivacy/selfprivacy-rest-api/pulls/8
This commit is contained in:
Inex Code 2022-01-26 16:58:20 +02:00
commit 4f30017132
5 changed files with 124 additions and 2 deletions

View file

@ -16,6 +16,8 @@ from selfprivacy_api.resources.services import services as api_services
from selfprivacy_api.restic_controller.tasks import huey, init_restic
from selfprivacy_api.migrations import run_migrations
swagger_blueprint = get_swaggerui_blueprint(
"/api/docs", "/api/swagger.json", config={"app_name": "SelfPrivacy API"}
)
@ -59,7 +61,7 @@ def create_app(test_config=None):
def spec():
if app.config["ENABLE_SWAGGER"] == "1":
swag = swagger(app)
swag["info"]["version"] = "1.1.0"
swag["info"]["version"] = "1.1.1"
swag["info"]["title"] = "SelfPrivacy API"
swag["info"]["description"] = "SelfPrivacy API"
swag["securityDefinitions"] = {
@ -83,6 +85,7 @@ def create_app(test_config=None):
if __name__ == "__main__":
monkey.patch_all()
created_app = create_app()
run_migrations()
huey.start()
init_restic()
created_app.run(port=5050, debug=False)

View file

@ -0,0 +1,31 @@
from selfprivacy_api.utils import ReadUserData
from selfprivacy_api.migrations.fix_nixos_config_branch import FixNixosConfigBranch
migrations = [FixNixosConfigBranch()]
def run_migrations():
"""
Go over all migrations. If they are not skipped in userdata file, run them
if the migration needed.
"""
with ReadUserData() as data:
if "api" not in data:
skipped_migrations = []
elif "skippedMigrations" not in data["api"]:
skipped_migrations = []
else:
skipped_migrations = data["api"].get("skippedMigrations", [])
if "DISABLE_ALL" in skipped_migrations:
return
for migration in migrations:
if migration.get_migration_name() not in skipped_migrations:
try:
if migration.is_migration_needed():
migration.migrate()
except Exception as e:
print(f"Error while migrating {migration.get_migration_name()}")
print(e)
print("Skipping this migration")

View file

@ -0,0 +1,60 @@
import os
import subprocess
from selfprivacy_api.migrations.migration import Migration
class FixNixosConfigBranch(Migration):
def get_migration_name(self):
return "fix_nixos_config_branch"
def get_migration_description(self):
return """Mobile SelfPrivacy app introduced a bug in version 0.4.0.
New servers were initialized with a rolling-testing nixos config branch.
This was fixed in app version 0.4.2, but existing servers were not updated.
This migration fixes this by changing the nixos config branch to master.
"""
def is_migration_needed(self):
"""Check the current branch of /etc/nixos and return True if it is rolling-testing"""
current_working_directory = os.getcwd()
try:
os.chdir("/etc/nixos")
nixos_config_branch = subprocess.check_output(
["git", "rev-parse", "--abbrev-ref", "HEAD"], start_new_session=True
)
os.chdir(current_working_directory)
if nixos_config_branch.decode("utf-8").strip() == "rolling-testing":
return True
else:
return False
except subprocess.CalledProcessError:
os.chdir(current_working_directory)
return False
def migrate(self):
"""Affected server pulled the config with the --single-branch flag.
Git config remote.origin.fetch has to be changed, so all branches will be fetched.
Then, fetch all branches, pull and switch to master branch.
"""
print("Fixing Nixos config branch")
current_working_directory = os.getcwd()
try:
os.chdir("/etc/nixos")
subprocess.check_output(
[
"git",
"config",
"remote.origin.fetch",
"+refs/heads/*:refs/remotes/origin/*",
]
)
subprocess.check_output(["git", "fetch", "--all"])
subprocess.check_output(["git", "pull"])
subprocess.check_output(["git", "checkout", "master"])
os.chdir(current_working_directory)
print("Done")
except subprocess.CalledProcessError:
os.chdir(current_working_directory)
print("Error")

View file

@ -0,0 +1,28 @@
from abc import ABC, abstractmethod
"""
Abstract Migration class
This class is used to define the structure of a migration
Migration has a function is_migration_needed() that returns True or False
Migration has a function migrate() that does the migration
Migration has a function get_migration_name() that returns the migration name
Migration has a function get_migration_description() that returns the migration description
"""
class Migration(ABC):
@abstractmethod
def get_migration_name(self):
pass
@abstractmethod
def get_migration_description(self):
pass
@abstractmethod
def is_migration_needed(self):
pass
@abstractmethod
def migrate(self):
pass

View file

@ -24,4 +24,4 @@ class ApiVersion(Resource):
401:
description: Unauthorized
"""
return {"version": "1.1.0"}
return {"version": "1.1.1"}