mirror of
https://git.selfprivacy.org/SelfPrivacy/selfprivacy-rest-api.git
synced 2024-11-25 21:41:27 +00:00
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:
commit
4f30017132
|
@ -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.restic_controller.tasks import huey, init_restic
|
||||||
|
|
||||||
|
from selfprivacy_api.migrations import run_migrations
|
||||||
|
|
||||||
swagger_blueprint = get_swaggerui_blueprint(
|
swagger_blueprint = get_swaggerui_blueprint(
|
||||||
"/api/docs", "/api/swagger.json", config={"app_name": "SelfPrivacy API"}
|
"/api/docs", "/api/swagger.json", config={"app_name": "SelfPrivacy API"}
|
||||||
)
|
)
|
||||||
|
@ -59,7 +61,7 @@ def create_app(test_config=None):
|
||||||
def spec():
|
def spec():
|
||||||
if app.config["ENABLE_SWAGGER"] == "1":
|
if app.config["ENABLE_SWAGGER"] == "1":
|
||||||
swag = swagger(app)
|
swag = swagger(app)
|
||||||
swag["info"]["version"] = "1.1.0"
|
swag["info"]["version"] = "1.1.1"
|
||||||
swag["info"]["title"] = "SelfPrivacy API"
|
swag["info"]["title"] = "SelfPrivacy API"
|
||||||
swag["info"]["description"] = "SelfPrivacy API"
|
swag["info"]["description"] = "SelfPrivacy API"
|
||||||
swag["securityDefinitions"] = {
|
swag["securityDefinitions"] = {
|
||||||
|
@ -83,6 +85,7 @@ def create_app(test_config=None):
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
monkey.patch_all()
|
monkey.patch_all()
|
||||||
created_app = create_app()
|
created_app = create_app()
|
||||||
|
run_migrations()
|
||||||
huey.start()
|
huey.start()
|
||||||
init_restic()
|
init_restic()
|
||||||
created_app.run(port=5050, debug=False)
|
created_app.run(port=5050, debug=False)
|
||||||
|
|
31
selfprivacy_api/migrations/__init__.py
Normal file
31
selfprivacy_api/migrations/__init__.py
Normal 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")
|
60
selfprivacy_api/migrations/fix_nixos_config_branch.py
Normal file
60
selfprivacy_api/migrations/fix_nixos_config_branch.py
Normal 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")
|
28
selfprivacy_api/migrations/migration.py
Normal file
28
selfprivacy_api/migrations/migration.py
Normal 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
|
|
@ -24,4 +24,4 @@ class ApiVersion(Resource):
|
||||||
401:
|
401:
|
||||||
description: Unauthorized
|
description: Unauthorized
|
||||||
"""
|
"""
|
||||||
return {"version": "1.1.0"}
|
return {"version": "1.1.1"}
|
||||||
|
|
Loading…
Reference in a new issue